diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index beeb94565..9caca21d7 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -247,6 +247,14 @@ const appRoutes: Routes = [ title: 'GENERAL.TITLES.SUPPORTIVE-MATERIAL' }, }, + { + path: 'references', + loadChildren: () => import('./ui/admin/reference/reference.module').then(m => m.ReferenceModule), + data: { + breadcrumb: true, + title: 'GENERAL.TITLES.REFERENCES' + }, + }, { path: 'reference-type', loadChildren: () => import('./ui/admin/reference-type/reference-type.module').then(m => m.ReferenceTypeModule), diff --git a/dmp-frontend/src/app/core/common/enum/permission.enum.ts b/dmp-frontend/src/app/core/common/enum/permission.enum.ts index 6918b587c..d0072ab5a 100644 --- a/dmp-frontend/src/app/core/common/enum/permission.enum.ts +++ b/dmp-frontend/src/app/core/common/enum/permission.enum.ts @@ -28,5 +28,10 @@ export enum AppPermission { BrowseTenant = "BrowseTenant", EditTenant = "EditTenant", DeleteTenant = "DeleteTenant", + + //Reference + BrowseReference = "BrowseReference", + EditReference = "EditReference", + DeleteReference = "DeleteReference", } diff --git a/dmp-frontend/src/app/core/model/reference/reference.ts b/dmp-frontend/src/app/core/model/reference/reference.ts index adf834699..f243d3cd5 100644 --- a/dmp-frontend/src/app/core/model/reference/reference.ts +++ b/dmp-frontend/src/app/core/model/reference/reference.ts @@ -1,12 +1,11 @@ -import { IsActive } from "@app/core/common/enum/is-active.enum"; import { ReferenceFieldDataType } from "@app/core/common/enum/reference-field-data-type"; import { ReferenceSourceType } from "@app/core/common/enum/reference-source-type"; import { ReferenceType } from "@app/core/common/enum/reference-type"; import { UUID } from "crypto"; import { DmpModel } from "../dmp/dmp"; +import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model"; -export interface Reference { - id: UUID; +export interface Reference extends BaseEntity{ label: string; type: ReferenceType; description: string; @@ -15,9 +14,6 @@ export interface Reference { abbreviation: string; source: string; sourceType: ReferenceSourceType; - isActive: IsActive; - createdAt: Date; - updatedAt: Date; dmpReferences: DmpReference[]; } @@ -61,12 +57,11 @@ export interface FetcherReference { // Persist -export interface ReferencePersist { - id: UUID; +export interface ReferencePersist extends BaseEntityPersist { label: string; type: ReferenceType; description: string; - definition: DefinitionPersist; + definition?: DefinitionPersist; reference: string; abbreviation: string; source: string; @@ -74,7 +69,7 @@ export interface ReferencePersist { } export interface DefinitionPersist { - fields?: FieldPersist[]; + fields: FieldPersist[]; } export interface FieldPersist { diff --git a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts index 2e1457ed2..979107814 100644 --- a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts +++ b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts @@ -22,6 +22,8 @@ import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-da import { ReferenceTypeSourceType } from '@app/core/common/enum/reference-type-source-type'; import { ReferenceTypeExternalApiHTTPMethodType } from '@app/core/common/enum/reference-type-external-api-http-method-type'; import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-description-template-role'; +import { ReferenceType } from '@app/core/common/enum/reference-type'; +import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; @Injectable() export class EnumUtils { @@ -304,4 +306,32 @@ export class EnumUtils { case UserDescriptionTemplateRole.Owner: return this.language.instant('TYPES.USER-DESCRIPTION-TEMPLATE-ROLE.OWNER'); } } + + toReferenceTypeString(status: ReferenceType): string { + switch (status) { + case ReferenceType.Taxonomies: return this.language.instant('TYPES.REFERENCE-TYPE.TAXONOMY'); + case ReferenceType.Licenses: return this.language.instant('TYPES.REFERENCE-TYPE.LICENCE'); + case ReferenceType.Publications: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Journals: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.PubRepositories: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.DataRepositories: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Registries: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Services: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Project: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Funder: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Datasets: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Organizations: return this.language.instant('TYPES.REFERENCE-TYPE.ORGANISATION'); + case ReferenceType.Grants: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + case ReferenceType.Researcher: return this.language.instant('TYPES.REFERENCE-TYPE.GET'); + + } + } + + toReferenceSourceTypeString(status: ReferenceSourceType): string { + switch (status) { + case ReferenceSourceType.Internal: return this.language.instant('TYPES.REFERENCE-SOURCE-TYPE.INTERNAL'); + case ReferenceSourceType.External: return this.language.instant('TYPES.REFERENCE-SOURCE-TYPE.EXTERNAL'); + } + } + } diff --git a/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.html b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.html new file mode 100644 index 000000000..80e0cadbd --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.html @@ -0,0 +1,157 @@ +
+
+
+
+

{{'REFERENCE-EDITOR.NEW' | translate}}

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + {{'REFERENCE-EDITOR.NEW' | translate}} + + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.LABEL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.TYPE' | translate}} + + + {{enumUtils.toReferenceTypeString(type)}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+

{{'REFERENCE-EDITOR.FIELDS.DESCRIPTION' | translate}}

+
+ + +
+ + {{'GENERAL.VALIDATION.REQUIRED'| translate}} + +
+
+
+
+ + {{'REFERENCE-EDITOR.FIELDS.SOURCE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.SOURCE-TYPE' | translate}} + + + {{enumUtils.toReferenceSourceTypeString(sourceType)}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.REFERENCE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.ABBREVIATION' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ + +
+

+ {{'REFERENCE-EDITOR.FIELDS.FIELDS' | translate}} + +

+
+
+
+
+ {{'REFERENCE-EDITOR.FIELDS.FIELD' | translate}} {{fieldIndex + 1}} +
+
+ +
+
+
+
+ + {{'REFERENCE-EDITOR.FIELDS.CODE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.DATA-TYPE' | translate}} + + + {{enumUtils.toReferenceFieldDataTypeString(dataType)}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-EDITOR.FIELDS.VALUE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.scss b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.scss new file mode 100644 index 000000000..306d4650e --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.scss @@ -0,0 +1,43 @@ +.reference-editor { + margin-top: 1.3rem; + margin-left: 1em; + margin-right: 3em; + + .remove { + background-color: white; + color: black; + } + + .add { + background-color: white; + color: #009700; + } +} + +::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background, .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background { + background-color: var(--primary-color-3); + // background-color: #0070c0; +} + +::ng-deep .mat-checkbox-disabled.mat-checkbox-checked .mat-checkbox-background, .mat-checkbox-disabled.mat-checkbox-indeterminate .mat-checkbox-background { + background-color: #b0b0b0; +} + +.action-btn { + border-radius: 30px; + background-color: var(--secondary-color); + border: 1px solid transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + + transition-property: background-color, color; + transition-duration: 200ms; + transition-delay: 50ms; + transition-timing-function: ease-in-out; + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.ts b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.ts new file mode 100644 index 000000000..f18bb26e6 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.component.ts @@ -0,0 +1,202 @@ + +import { Component, OnInit } from '@angular/core'; +import { FormArray, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ReferenceService } from '@app/core/services/reference/reference.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { DatePipe } from '@angular/common'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { Reference, ReferencePersist } from '@app/core/model/reference/reference'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { LoggingService } from '@app/core/services/logging/logging-service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { BaseEditor } from '@common/base/base-editor'; +import { FormService } from '@common/forms/form-service'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { map, takeUntil } from 'rxjs/operators'; +import { ReferenceEditorResolver } from './reference-editor.resolver'; +import { ReferenceEditorService } from './reference-editor.service'; +import { FieldEditorModel, ReferenceEditorModel } from './reference-editor.model'; +import { ReferenceType } from '@app/core/common/enum/reference-type'; +import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; +import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type'; + + +@Component({ + selector: 'app-reference-editor-component', + templateUrl: 'reference-editor.component.html', + styleUrls: ['./reference-editor.component.scss'], + providers: [ReferenceEditorService] +}) +export class ReferenceEditorComponent extends BaseEditor implements OnInit { + + isNew = true; + isDeleted = false; + formGroup: UntypedFormGroup = null; + showInactiveDetails = false; + public referenceTypeEnum = this.enumUtils.getEnumValues(ReferenceType); + public referenceSourceTypeEnum = this.enumUtils.getEnumValues(ReferenceSourceType); + public referenceFieldDataTypeEnum = this.enumUtils.getEnumValues(ReferenceFieldDataType); + + protected get canDelete(): boolean { + return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteReference); + } + + protected get canSave(): boolean { + return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditReference); + } + + protected get canFinalize(): boolean { + return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditReference); + } + + + private hasPermission(permission: AppPermission): boolean { + return this.authService.hasPermission(permission) || this.editorModel?.permissions?.includes(permission); + } + + constructor( + // BaseFormEditor injected dependencies + protected dialog: MatDialog, + protected language: TranslateService, + protected formService: FormService, + protected router: Router, + protected uiNotificationService: UiNotificationService, + protected httpErrorHandlingService: HttpErrorHandlingService, + protected filterService: FilterService, + protected datePipe: DatePipe, + protected route: ActivatedRoute, + protected queryParamsService: QueryParamsService, + // Rest dependencies. Inject any other needed deps here: + public authService: AuthService, + public enumUtils: EnumUtils, + private referenceService: ReferenceService, + private logger: LoggingService, + private referenceEditorService: ReferenceEditorService, + private fileUtils: FileUtils, + private matomoService: MatomoService + ) { + super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService); + } + + ngOnInit(): void { + this.matomoService.trackPageView('Admin: References'); + super.ngOnInit(); + } + + getItem(itemId: Guid, successFunction: (item: Reference) => void) { + this.referenceService.getSingle(itemId, ReferenceEditorResolver.lookupFields()) + .pipe(map(data => data as Reference), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + error => this.onCallbackError(error) + ); + } + + prepareForm(data: Reference) { + try { + this.editorModel = data ? new ReferenceEditorModel().fromModel(data) : new ReferenceEditorModel(); + this.isDeleted = data ? data.isActive === IsActive.Inactive : false; + this.buildForm(); + } catch (error) { + this.logger.error('Could not parse Reference item: ' + data + error); + this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); + } + } + + buildForm() { + this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.authService.hasPermission(AppPermission.EditReference)); + this.referenceEditorService.setValidationErrorModel(this.editorModel.validationErrorModel); + } + + refreshData(): void { + this.getItem(this.editorModel.id, (data: Reference) => this.prepareForm(data)); + } + + refreshOnNavigateToData(id?: Guid): void { + this.formGroup.markAsPristine(); + let route = []; + + if (id === null) { + route.push('../..'); + } else if (this.isNew) { + route.push('../' + id); + } else { + route.push('..'); + } + + this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route }); + } + + persistEntity(onSuccess?: (response) => void): void { + const formData = this.formService.getValue(this.formGroup.value) as ReferencePersist; + + this.referenceService.persist(formData) + .pipe(takeUntil(this._destroyed)).subscribe( + complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete), + error => this.onCallbackError(error) + ); + } + + formSubmit(): void { + this.formService.touchAllFormFields(this.formGroup); + if (!this.isFormValid()) { + return; + } + + this.persistEntity(); + } + + public delete() { + const value = this.formGroup.value; + if (value.id) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '300px', + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL') + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.referenceService.delete(value.id).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + } + + clearErrorModel() { + this.editorModel.validationErrorModel.clear(); + this.formService.validateAllFormFields(this.formGroup); + } + + // + // fields + // + addField(): void { + const field: FieldEditorModel = new FieldEditorModel(); + (this.formGroup.get('definition').get('fields') as FormArray).push(field.buildForm()); + } + + removeField(fieldIndex: number): void { + (this.formGroup.get('definition').get('fields') as FormArray).removeAt(fieldIndex); + } + + +} diff --git a/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.model.ts b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.model.ts new file mode 100644 index 000000000..215f3f824 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.model.ts @@ -0,0 +1,195 @@ +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { ReferenceFieldDataType } from "@app/core/common/enum/reference-field-data-type"; +import { ReferenceSourceType } from "@app/core/common/enum/reference-source-type"; +import { ReferenceType } from "@app/core/common/enum/reference-type"; +import { Definition, DefinitionPersist, Field, FieldPersist, Reference, ReferencePersist } from "@app/core/model/reference/reference"; +import { BaseEditorModel } from "@common/base/base-form-editor-model"; +import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; +import { Validation, ValidationContext } from "@common/forms/validation/validation-context"; + +export class ReferenceEditorModel extends BaseEditorModel implements ReferencePersist { + label: string; + type: ReferenceType; + description: string; + definition: DefinitionEditorModel = new DefinitionEditorModel(); + reference: string; + abbreviation: string; + source: string; + sourceType: ReferenceSourceType; + + permissions: string[]; + + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor() { super(); } + + public fromModel(item: Reference): ReferenceEditorModel { + if (item) { + super.fromModel(item); + this.label = item.label; + this.type = item.type; + this.description = item.description; + if (item.definition) this.definition = new DefinitionEditorModel().fromModel(item.definition); + this.reference = item.reference; + this.abbreviation = item.abbreviation; + this.source = item.source; + this.sourceType = item.sourceType; + } + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + if (context == null) { context = this.createValidationContext(); } + + return this.formBuilder.group({ + id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], + description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators], + definition: this.definition.buildForm({ + rootPath: `definition.` + }), + reference: [{ value: this.reference, disabled: disabled }, context.getValidation('reference').validators], + abbreviation: [{ value: this.abbreviation, disabled: disabled }, context.getValidation('abbreviation').validators], + source: [{ value: this.source, disabled: disabled }, context.getValidation('source').validators], + sourceType: [{ value: this.sourceType, disabled: disabled }, context.getValidation('sourceType').validators], + + hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] + }); + } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'type')] }); + baseValidationArray.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] }); + baseValidationArray.push({ key: 'reference', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'reference')] }); + baseValidationArray.push({ key: 'abbreviation', validators: [BackendErrorValidator(this.validationErrorModel, 'abbreviation')] }); + baseValidationArray.push({ key: 'source', validators: [BackendErrorValidator(this.validationErrorModel, 'source')] }); + baseValidationArray.push({ key: 'sourceType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'sourceType')] }); + baseValidationArray.push({ key: 'hash', validators: [] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class DefinitionEditorModel implements DefinitionPersist { + fields: FieldEditorModel[]= []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: Definition): DefinitionEditorModel { + if (item) { + if (item.fields) { item.fields.map(x => this.fields.push(new FieldEditorModel().fromModel(x))); } + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = DefinitionEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + fields: this.formBuilder.array( + (this.fields ?? []).map( + (item, index) => new FieldEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `fields[${index}].` + }), context.getValidation('fields') + ) + ), + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'fields', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fields`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class FieldEditorModel implements FieldPersist { + code: string; + dataType: ReferenceFieldDataType; + value: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: Field): FieldEditorModel { + if (item) { + this.code = item.code; + this.dataType = item.dataType; + this.value = item.value; + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = FieldEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], + dataType: [{ value: this.dataType, disabled: disabled }, context.getValidation('dataType').validators], + value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + + baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); + baseValidationArray.push({ key: 'dataType', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}dataType`)] }); + baseValidationArray.push({ key: 'value', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + diff --git a/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.resolver.ts b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.resolver.ts new file mode 100644 index 000000000..3a2465f6f --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.resolver.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Reference } from '@app/core/model/reference/reference'; +import { ReferenceService } from '@app/core/services/reference/reference.service'; +import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; +import { BaseEditorResolver } from '@common/base/base-editor.resolver'; +import { Guid } from '@common/types/guid'; +import { takeUntil, tap } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Injectable() +export class ReferenceEditorResolver extends BaseEditorResolver { + + constructor(private referenceService: ReferenceService, private breadcrumbService: BreadcrumbService) { + super(); + } + + public static lookupFields(): string[] { + return [ + ...BaseEditorResolver.lookupFields(), + nameof(x => x.id), + nameof(x => x.label), + nameof(x => x.type), + nameof(x => x.description), + nameof(x => x.reference), + nameof(x => x.abbreviation), + nameof(x => x.source), + nameof(x => x.sourceType), + nameof(x => x.createdAt), + nameof(x => x.updatedAt), + nameof(x => x.hash), + nameof(x => x.isActive) + ] + } + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + + const fields = [ + ...ReferenceEditorResolver.lookupFields() + ]; + const id = route.paramMap.get('id'); + + if (id != null) { + return this.referenceService.getSingle(Guid.parse(id), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed)); + } + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.service.ts b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.service.ts new file mode 100644 index 000000000..0f94d946e --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/editor/reference-editor.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from "@angular/core"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; + +@Injectable() +export class ReferenceEditorService { + private validationErrorModel: ValidationErrorModel; + + public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void { + this.validationErrorModel = validationErrorModel; + } + + public getValidationErrorModel(): ValidationErrorModel { + return this.validationErrorModel; + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.html b/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.html new file mode 100644 index 000000000..f129c7105 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.html @@ -0,0 +1,36 @@ +
+ + + + + +
+
+
+

{{'REFERENCE-LISTING.FILTER.TITLE' | translate}}

+ +
+ + + {{'REFERENCE-LISTING.FILTER.IS-ACTIVE' | translate}} + + +
+ + +
+
+
+
+ + +
diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.scss b/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.scss new file mode 100644 index 000000000..ea00a215c --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.scss @@ -0,0 +1,21 @@ +::ng-deep.mat-mdc-menu-panel { + max-width: 100% !important; + height: 100% !important; +} + +:host::ng-deep.mat-mdc-menu-content:not(:empty) { + padding-top: 0 !important; +} + + +.filter-button{ + padding-top: .6rem; + padding-bottom: .6rem; + // .mat-icon{ + // font-size: 1.5em; + // width: 1.2em; + // height: 1.2em; + // } +} + + diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.ts b/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.ts new file mode 100644 index 000000000..609bc8186 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/listing/filters/reference-listing-filters.component.ts @@ -0,0 +1,94 @@ +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { TenantFilter } from '@app/core/query/tenant.lookup'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { BaseComponent } from '@common/base/base.component'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + selector: 'app-reference-listing-filters', + templateUrl: './reference-listing-filters.component.html', + styleUrls: ['./reference-listing-filters.component.scss'] +}) +export class ReferenceListingFiltersComponent extends BaseComponent implements OnInit, OnChanges { + + @Input() readonly filter: TenantFilter; + @Output() filterChange = new EventEmitter(); + + // * State + internalFilters: ReferenceListingFilters = this._getEmptyFilters(); + + protected appliedFilterCount: number = 0; + constructor( + public enumUtils: EnumUtils, + ) { super(); } + + ngOnInit() { + } + + ngOnChanges(changes: SimpleChanges): void { + const filterChange = changes[nameof(x => x.filter)]?.currentValue as TenantFilter; + if (filterChange) { + this.updateFilters() + } + } + + + onSearchTermChange(searchTerm: string): void { + this.applyFilters() + } + + + protected updateFilters(): void { + this.internalFilters = this._parseToInternalFilters(this.filter); + this.appliedFilterCount = this._computeAppliedFilters(this.internalFilters); + } + + protected applyFilters(): void { + const { isActive, like } = this.internalFilters ?? {} + this.filterChange.emit({ + ...this.filter, + like, + isActive: isActive ? [IsActive.Active] : [IsActive.Inactive] + }) + } + + + private _parseToInternalFilters(inputFilter: TenantFilter): ReferenceListingFilters { + if (!inputFilter) { + return this._getEmptyFilters(); + } + + let { excludedIds, ids, isActive, like } = inputFilter; + + return { + isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length, + like: like + } + + } + + private _getEmptyFilters(): ReferenceListingFilters { + return { + isActive: true, + like: null, + } + } + + private _computeAppliedFilters(filters: ReferenceListingFilters): number { + let count = 0; + if (filters?.isActive) { + count++ + } + return count; + } + + clearFilters() { + this.internalFilters = this._getEmptyFilters(); + } +} + +interface ReferenceListingFilters { + isActive: boolean; + like: string; +} diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html new file mode 100644 index 000000000..c1a42cc4b --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.html @@ -0,0 +1,86 @@ +
+
+ +
+
+

{{'REFERENCE-LISTING.TITLE' | translate}}

+ + +
+
+ +
+
+ + + + + + + + +
+
+ + + + +
+
+ + {{item?.name | nullifyValue}} +
+
+ + + + {{'REFERENCE-LISTING.FIELDS.CREATED-AT' | translate}}: + + {{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}} + + +
+
+ + + {{'REFERENCE-LISTING.FIELDS.UPDATED-AT' | translate}}: + + {{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}} + + + +
+
+
+ + +
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.scss b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.scss new file mode 100644 index 000000000..6e1b48814 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.scss @@ -0,0 +1,60 @@ +.description-template-type-listing { + margin-top: 1.3rem; + margin-left: 1rem; + margin-right: 2rem; + + .mat-header-row{ + background: #f3f5f8; + } + .mat-card { + margin: 16px 0; + padding: 0px; + } + + .mat-row { + cursor: pointer; + min-height: 4.5em; + } + + mat-row:hover { + background-color: #eef5f6; + } + .mat-fab-bottom-right { + float: right; + z-index: 5; + } +} +.create-btn { + border-radius: 30px; + background-color: var(--secondary-color); + padding-left: 2em; + padding-right: 2em; + // color: #000; + + .button-text{ + display: inline-block; + } +} + +.dlt-btn { + color: rgba(0, 0, 0, 0.54); +} + +.status-chip{ + + border-radius: 20px; + padding-left: 1em; + padding-right: 1em; + padding-top: 0.2em; + font-size: .8em; +} + +.status-chip-finalized{ + color: #568b5a; + background: #9dd1a1 0% 0% no-repeat padding-box; +} + +.status-chip-draft{ + color: #00c4ff; + background: #d3f5ff 0% 0% no-repeat padding-box; +} diff --git a/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.ts b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.ts new file mode 100644 index 000000000..f1250d2c5 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/listing/reference-listing.component.ts @@ -0,0 +1,175 @@ +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { Reference } from '@app/core/model/reference/reference'; +import { ReferenceLookup } from '@app/core/query/reference.lookup'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { ReferenceService } from '@app/core/services/reference/reference.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { BaseListingComponent } from '@common/base/base-listing-component'; +import { PipeService } from '@common/formatting/pipe.service'; +import { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.pipe'; +import { QueryResult } from '@common/model/query-result'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { ColumnDefinition, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + templateUrl: './REFERENCE-LISTING.component.html', + styleUrls: ['./REFERENCE-LISTING.component.scss'] +}) +export class ReferenceListingComponent extends BaseListingComponent implements OnInit { + publish = false; + userSettingsKey = { key: 'ReferenceListingUserSettings' }; + propertiesAvailableForOrder: ColumnDefinition[]; + + @ViewChild('actions', { static: true }) actions?: TemplateRef; + @ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent; + + private readonly lookupFields: string[] = [ + nameof(x => x.id), + nameof(x => x.label), + nameof(x => x.source), + nameof(x => x.type), + nameof(x => x.updatedAt), + nameof(x => x.createdAt), + nameof(x => x.hash), + nameof(x => x.isActive) + ]; + + rowIdentity = x => x.id; + + constructor( + protected router: Router, + protected route: ActivatedRoute, + protected uiNotificationService: UiNotificationService, + protected httpErrorHandlingService: HttpErrorHandlingService, + protected queryParamsService: QueryParamsService, + private ReferenceService: ReferenceService, + public authService: AuthService, + private pipeService: PipeService, + public enumUtils: EnumUtils, + private language: TranslateService, + private dialog: MatDialog + ) { + super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService); + // Lookup setup + // Default lookup values are defined in the user settings class. + this.lookup = this.initializeLookup(); + } + + ngOnInit() { + super.ngOnInit(); + } + + protected initializeLookup(): ReferenceLookup { + const lookup = new ReferenceLookup(); + lookup.metadata = { countAll: true }; + lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE }; + lookup.isActive = [IsActive.Active]; + lookup.order = { items: [this.toDescSortField(nameof(x => x.createdAt))] }; + this.updateOrderUiFields(lookup.order); + + lookup.project = { + fields: this.lookupFields + }; + + return lookup; + } + + protected setupColumns() { + this.gridColumns.push(...[{ + prop: nameof(x => x.label), + sortable: true, + languageName: 'REFERENCE-LISTING.FIELDS.LABEL' + }, + { + prop: nameof(x => x.source), + sortable: true, + languageName: 'REFERENCE-LISTING.FIELDS.SOURCE', + }, + { + prop: nameof(x => x.type), + sortable: true, + languageName: 'REFERENCE-LISTING.FIELDS.TYPE', + }, + { + prop: nameof(x => x.createdAt), + sortable: true, + languageName: 'REFERENCE-LISTING.FIELDS.CREATED-AT', + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') + }, + { + prop: nameof(x => x.updatedAt), + sortable: true, + languageName: 'REFERENCE-LISTING.FIELDS.UPDATED-AT', + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') + }, + { + alwaysShown: true, + cellTemplate: this.actions, + maxWidth: 120 + } + ]); + this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable); + } + + // + // Listing Component functions + // + onColumnsChanged(event: ColumnsChangedEvent) { + super.onColumnsChanged(event); + this.onColumnsChangedInternal(event.properties.map(x => x.toString())); + } + + private onColumnsChangedInternal(columns: string[]) { + // Here are defined the projection fields that always requested from the api. + const fields = new Set(this.lookupFields); + this.gridColumns.map(x => x.prop) + .filter(x => !columns?.includes(x as string)) + .forEach(item => { + fields.delete(item as string) + }); + this.lookup.project = { fields: [...fields] }; + this.onPageLoad({ offset: 0 } as PageLoadEvent); + } + + protected loadListing(): Observable> { + return this.ReferenceService.query(this.lookup); + } + + public deleteType(id: Guid) { + if (id) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + isDeleteConfirmation: true, + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL') + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.ReferenceService.delete(id).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + } + + onCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success); + this.ngOnInit(); + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference/reference.module.ts b/dmp-frontend/src/app/ui/admin/reference/reference.module.ts new file mode 100644 index 000000000..8bcc715e9 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/reference.module.ts @@ -0,0 +1,41 @@ +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { NgModule } from "@angular/core"; +import { AutoCompleteModule } from "@app/library/auto-complete/auto-complete.module"; +import { UrlListingModule } from '@app/library/url-listing/url-listing.module'; +import { CommonFormattingModule } from '@common/formatting/common-formatting.module'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; +import { HybridListingModule } from "@common/modules/hybrid-listing/hybrid-listing.module"; +import { TextFilterModule } from "@common/modules/text-filter/text-filter.module"; +import { UserSettingsModule } from "@common/modules/user-settings/user-settings.module"; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { NgxDropzoneModule } from "ngx-dropzone"; +import { ReferenceRoutingModule } from './reference.routing'; +import { ReferenceEditorComponent } from './editor/reference-editor.component'; +import { ReferenceListingComponent } from './listing/reference-listing.component'; +import { ReferenceListingFiltersComponent } from './listing/filters/reference-listing-filters.component'; +import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + UrlListingModule, + ConfirmationDialogModule, + ReferenceRoutingModule, + NgxDropzoneModule, + DragDropModule, + AutoCompleteModule, + HybridListingModule, + TextFilterModule, + UserSettingsModule, + CommonFormattingModule, + RichTextEditorModule + ], + declarations: [ + ReferenceEditorComponent, + ReferenceListingComponent, + ReferenceListingFiltersComponent + ] +}) +export class ReferenceModule { } diff --git a/dmp-frontend/src/app/ui/admin/reference/reference.routing.ts b/dmp-frontend/src/app/ui/admin/reference/reference.routing.ts new file mode 100644 index 000000000..758a54610 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference/reference.routing.ts @@ -0,0 +1,58 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AdminAuthGuard } from '@app/core/admin-auth-guard.service'; +import { ReferenceEditorComponent } from './editor/reference-editor.component'; +import { ReferenceListingComponent } from './listing/reference-listing.component'; +import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { AuthGuard } from '@app/core/auth-guard.service'; +import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; +import { PendingChangesGuard } from '@common/forms/pending-form-changes/pending-form-changes-guard.service'; +import { ReferenceEditorResolver } from './editor/reference-editor.resolver'; + +const routes: Routes = [ + { + path: '', + component: ReferenceListingComponent, + canActivate: [AuthGuard] + }, + { + path: 'new', + canActivate: [AuthGuard], + component: ReferenceEditorComponent, + canDeactivate: [PendingChangesGuard], + data: { + authContext: { + permissions: [AppPermission.EditReference] + }, + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.NEW-TENANT' + }) + } + }, + { + path: ':id', + canActivate: [AuthGuard], + component: ReferenceEditorComponent, + canDeactivate: [PendingChangesGuard], + resolve: { + 'entity': ReferenceEditorResolver + }, + data: { + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.EDIT-TENANT' + }), + authContext: { + permissions: [AppPermission.EditReference] + } + } + + }, + { path: '**', loadChildren: () => import('@common/modules/page-not-found/page-not-found.module').then(m => m.PageNotFoundModule) }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [ReferenceEditorResolver] +}) +export class ReferenceRoutingModule { } diff --git a/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html b/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html index 827f5421f..93669a2f5 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/tenant/listing/tenant-listing.component.html @@ -9,7 +9,7 @@
@@ -78,15 +75,6 @@ - -
@@ -101,7 +89,6 @@ delete {{'TENANT-LISTING.ACTIONS.DELETE' | translate}} -
diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts index 3de6e9d34..a0a54b546 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts @@ -53,6 +53,7 @@ export const ADMIN_ROUTES: RouteInfo[] = [ { path: '/dmp-blueprints', title: 'SIDE-BAR.DMP-BLUEPRINTS', icon: 'library_books' }, { path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'library_books' }, { path: '/description-template-type', title: 'SIDE-BAR.DESCRIPTION-TEMPLATE-TYPES', icon: 'library_books' }, + { path: '/references', title: 'SIDE-BAR.REFERENCES', icon: 'library_books' }, { path: '/reference-type', title: 'SIDE-BAR.REFERENCE-TYPES', icon: 'library_books' }, { path: '/tenants', title: 'SIDE-BAR.TENANTS', icon: 'library_books' }, { path: '/users', title: 'SIDE-BAR.USERS', icon: 'people' }, diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 60414cdc1..114d55643 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -173,7 +173,8 @@ "LANGUAGE": "Language", "SIGN-IN": "Sign in to account", "REFERENCE-TYPES": "Reference Types", - "TENANTS": "Tenants" + "TENANTS": "Tenants", + "REFERENCES": "References" }, "FILE-TYPES": { "PDF": "PDF", @@ -340,7 +341,8 @@ "FEEDBACK": "Send feedback", "SUPPORTIVE-MATERIAL": "Supportive Material", "REFERENCE-TYPES":"Reference Types", - "TENANTS": "Tenants" + "TENANTS": "Tenants", + "REFERENCES": "References" }, "DESCRIPTION-TEMPLATE-EDITOR": { "TITLE": { @@ -1109,6 +1111,34 @@ "SUCCESSFUL-DELETE": "Successful Delete", "UNSUCCESSFUL-DELETE": "This item could not be deleted." }, + "REFERENCE-LISTING": { + "TITLE": "References", + "CREATE": "Create Reference", + "FIELDS": { + "LABEL": "Label", + "SOURCE": "Source", + "TYPE": "Type", + "UPDATED-AT": "Updated", + "CREATED-AT": "Created" + }, + "FILTER": { + "TITLE": "Filters", + "IS-ACTIVE": "Is Active", + "CANCEL": "Cancel", + "APPLY-FILTERS": "Apply filters" + }, + "CONFIRM-DELETE-DIALOG": { + "MESSAGE": "Would you like to delete this Reference?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" + }, + "ACTIONS": { + "DELETE": "Delete", + "EDIT": "Edit" + }, + "SUCCESSFUL-DELETE": "Successful Delete", + "UNSUCCESSFUL-DELETE": "This item could not be deleted." + }, "DATASET-UPLOAD": { "TITLE": "Import Dataset", "UPLOAD-BUTTON": "Upload", @@ -1233,6 +1263,36 @@ "CANCEL-BUTTON": "No" } }, + "REFERENCE-EDITOR": { + "NEW": "New Reference", + "FIELDS": { + "LABEL": "Label", + "TYPE": "Type", + "DESCRIPTION": "Description", + "DESCRIPTION-PLACEHOLDER": "Reference description", + "REFERENCE":"reference", + "ABBREVIATION": "abbreviation", + "SOURCE": "source", + "SOURCE-TYPE":"Source Type", + "FIELDS":"Fields", + "FIELD":"Field", + "CODE":"Code", + "DATA-TYPE":"Data Type", + "VALUE":"Value" + }, + "ACTIONS": { + "SAVE": "Save", + "CANCEL": "Cancel", + "DELETE": "Delete", + "ADD-FIELD": "Add Field", + "REMOVE-FIELD": "Remove Field" + }, + "CONFIRM-DELETE-DIALOG": { + "MESSAGE": "Would you like to delete this Rederence?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" + } + }, "DMP-BLUEPRINT-EDITOR": { "TITLE": { "NEW": "New DMP Blueprint", @@ -1869,6 +1929,15 @@ "REFERENCE-TYPE-EXTERNAL-API-HTTP-METHOD-TYPE":{ "GET": "GET", "POST": "POST" + }, + "REFERENCE-TYPE":{ + "TAXONOMY": "Taxonomy", + "LICENCE": "License", + "ORGANISATION": "Organization" + }, + "REFERENCE-SOURCE-TYPE":{ + "INTERNAL": "Internal", + "EXTERNAL": "External" } }, "ADDRESEARCHERS-EDITOR": {