From d760209d2089f3c997efafa225da7e45ec6d5f10 Mon Sep 17 00:00:00 2001 From: Diamantis Tziotzios Date: Mon, 15 Jan 2024 18:47:34 +0200 Subject: [PATCH 1/2] more dmp editor frontend changes --- .../DmpDescriptionTemplateBuilder.java | 1 + .../reference/ReferenceServiceImpl.java | 2 +- .../main/resources/config/storage-devel.yml | 2 +- .../description-template-version-status.ts | 4 + .../src/app/core/model/dmp/dmp-reference.ts | 7 +- dmp-frontend/src/app/core/model/dmp/dmp.ts | 49 +- .../core/query/description-template.lookup.ts | 5 + .../description-template.service.ts | 44 ++ .../description/description.service.ts | 13 +- .../services/reference/reference.service.ts | 72 ++- .../services/utilities/enum-utils.service.ts | 9 + .../dmp-blueprint-listing.component.html | 2 +- .../dmp-editor.component.html | 95 ++-- .../dmp-editor.component.scss | 15 - .../dmp-editor.component.ts | 96 +++- .../dmp-editor-blueprint/dmp-editor.model.ts | 245 ++++++++- .../dmp-editor-blueprint/dmp-editor.module.ts | 4 +- .../dmp-editor.resolver.ts | 48 +- .../reference-field.component.html | 506 +----------------- .../reference-field.component.scss | 47 +- .../reference-field.component.ts | 85 ++- .../reference-field/reference-field.module.ts | 5 + dmp-frontend/src/assets/config/config.json | 2 +- dmp-frontend/src/assets/i18n/en.json | 12 + 24 files changed, 636 insertions(+), 734 deletions(-) create mode 100644 dmp-frontend/src/app/core/common/enum/description-template-version-status.ts diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpDescriptionTemplateBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpDescriptionTemplateBuilder.java index 731be14fc..be0652c44 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpDescriptionTemplateBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpDescriptionTemplateBuilder.java @@ -70,6 +70,7 @@ public class DmpDescriptionTemplateBuilder extends BaseBuilder x.getLabel().toLowerCase().contains(lookup.getLike().toLowerCase())).collect(Collectors.toList()); + if (!this.conventionService.isNullOrEmpty(lookup.getLike())) { models = models.stream().filter(x -> x.getLabel().toLowerCase().contains(lookup.getLike().toLowerCase())).collect(Collectors.toList()); } models.sort(Comparator.comparing(Reference::getLabel)); if (lookup.getPage() != null && !lookup.getPage().isEmpty()){ diff --git a/dmp-backend/web/src/main/resources/config/storage-devel.yml b/dmp-backend/web/src/main/resources/config/storage-devel.yml index cee91a319..76e2c374a 100644 --- a/dmp-backend/web/src/main/resources/config/storage-devel.yml +++ b/dmp-backend/web/src/main/resources/config/storage-devel.yml @@ -7,7 +7,7 @@ storage: - type: Main basePath: ./storage/main static-files: - externalUrls: externalUrls/ExternalUrls.xml + externalUrls: dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml semantics: Semantics.json h2020Template: documents/h2020.docx h2020DescriptionTemplate: documents/h2020_dataset.docx diff --git a/dmp-frontend/src/app/core/common/enum/description-template-version-status.ts b/dmp-frontend/src/app/core/common/enum/description-template-version-status.ts new file mode 100644 index 000000000..130e01b1e --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/description-template-version-status.ts @@ -0,0 +1,4 @@ +export enum DescriptionTemplateVersionStatus { + Current = 0, + Previous = 1 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts b/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts index df308ce03..d2a2f6491 100644 --- a/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts +++ b/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts @@ -1,9 +1,14 @@ import { BaseEntity } from "@common/base/base-entity.model"; import { Reference } from "../reference/reference"; import { Dmp } from "./dmp"; +import { Guid } from "@common/types/guid"; export interface DmpReference extends BaseEntity { dmp?: Dmp; reference?: Reference; - data: string; + data: DmpReferenceData; +} + +export interface DmpReferenceData { + blueprintFieldId: Guid; } \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/dmp/dmp.ts b/dmp-frontend/src/app/core/model/dmp/dmp.ts index aa6452d44..710d9ba08 100644 --- a/dmp-frontend/src/app/core/model/dmp/dmp.ts +++ b/dmp-frontend/src/app/core/model/dmp/dmp.ts @@ -17,7 +17,7 @@ export interface Dmp extends BaseEntity { version: number; status: DmpStatus; versionStatus: DmpVersionStatus; - properties: string; + properties: DmpProperties; groupId: String; description: String; finalizedAt: Date; @@ -34,6 +34,24 @@ export interface Dmp extends BaseEntity { entityDois: EntityDoi[]; } +export interface DmpProperties { + dmpBlueprintValues: DmpBlueprintValue[]; + contacts: DmpContact[]; +} + +export interface DmpBlueprintValue { + fieldId: Guid; + fieldValue: string; +} + +export interface DmpContact { + userId: Guid; + firstName: string; + lastName: string; + email: string; +} + + export interface DmpUser extends BaseEntity { dmp: Dmp; user: User; @@ -54,18 +72,39 @@ export interface DmpDescriptionTemplate extends BaseEntity { export interface DmpPersist extends BaseEntityPersist { label: string; status: DmpStatus; - properties: string; + properties: DmpPropertiesPersist; description: String; language: String; blueprint: Guid; accessType: DmpAccessType; - references: DmpReferencePersist[]; descriptionTemplates: DmpDescriptionTemplatePersist[]; } -export interface DmpReferencePersist extends BaseEntityPersist { +export interface DmpPropertiesPersist { + dmpBlueprintValues: DmpBlueprintValuePersist[]; + contacts: DmpContactPersist[]; +} + +export interface DmpBlueprintValuePersist { + fieldId: Guid; + fieldValue: string; + references: DmpReferencePersist[]; +} + +export interface DmpContactPersist { + userId: Guid; + firstName: string; + lastName: string; + email: string; +} + +export interface DmpReferencePersist { reference?: ReferencePersist; - data?: string; + data?: DmpReferenceDataPersist; +} + +export interface DmpReferenceDataPersist { + blueprintFieldId: Guid; } export interface DmpDescriptionTemplatePersist extends BaseEntityPersist { diff --git a/dmp-frontend/src/app/core/query/description-template.lookup.ts b/dmp-frontend/src/app/core/query/description-template.lookup.ts index 1e7f2b836..b2ef3e146 100644 --- a/dmp-frontend/src/app/core/query/description-template.lookup.ts +++ b/dmp-frontend/src/app/core/query/description-template.lookup.ts @@ -2,6 +2,7 @@ import { Lookup } from '@common/model/lookup'; import { Guid } from '@common/types/guid'; import { DescriptionTemplateStatus } from '../common/enum/description-template-status'; import { IsActive } from '../common/enum/is-active.enum'; +import { DescriptionTemplateVersionStatus } from '../common/enum/description-template-version-status'; export class DescriptionTemplateLookup extends Lookup implements DescriptionTemplateFilter { ids: Guid[]; @@ -10,6 +11,8 @@ export class DescriptionTemplateLookup extends Lookup implements DescriptionTemp isActive: IsActive[]; typeIds: Guid[]; statuses: DescriptionTemplateStatus[]; + groupIds: Guid[]; + versionStatuses: DescriptionTemplateVersionStatus[]; constructor() { super(); @@ -23,4 +26,6 @@ export interface DescriptionTemplateFilter { isActive: IsActive[]; typeIds: Guid[]; statuses: DescriptionTemplateStatus[]; + groupIds: Guid[]; + versionStatuses: DescriptionTemplateVersionStatus[]; } diff --git a/dmp-frontend/src/app/core/services/description-template/description-template.service.ts b/dmp-frontend/src/app/core/services/description-template/description-template.service.ts index e4cda2701..cf8d41c8c 100644 --- a/dmp-frontend/src/app/core/services/description-template/description-template.service.ts +++ b/dmp-frontend/src/app/core/services/description-template/description-template.service.ts @@ -16,6 +16,8 @@ import { catchError, map } from 'rxjs/operators'; import { nameof } from 'ts-simple-nameof'; import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service'; +import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status'; +import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status'; @Injectable() export class DescriptionTemplateService { @@ -126,4 +128,46 @@ export class DescriptionTemplateService { if (like) { lookup.like = this.filterService.transformLike(like); } return lookup; } + + // + // Description Tempalte Group Autocomplete Commons + // + // tslint:disable-next-line: member-ordering + descriptionTempalteGroupSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { + initialItems: (data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup()).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery)).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])), + displayFn: (item: DescriptionTemplate) => item.label, + titleFn: (item: DescriptionTemplate) => item.label, + valueAssign: (item: DescriptionTemplate) => item.groupId, + }; + + // tslint:disable-next-line: member-ordering + descriptionTempalteGroupMultipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = { + initialItems: (excludedItems: any[], data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)), + getSelectedItems: (selectedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)), + displayFn: (item: DescriptionTemplate) => item.label, + titleFn: (item: DescriptionTemplate) => item.label, + valueAssign: (item: DescriptionTemplate) => item.groupId, + }; + + public buildDescriptionTempalteGroupAutocompleteLookup(like?: string, excludedIds?: Guid[], groupIds?: Guid[]): DescriptionTemplateLookup { + const lookup: DescriptionTemplateLookup = new DescriptionTemplateLookup(); + lookup.page = { size: 100, offset: 0 }; + if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } + if (groupIds && groupIds.length > 0) { lookup.groupIds = groupIds; } + lookup.isActive = [IsActive.Active]; + lookup.versionStatuses = [DescriptionTemplateVersionStatus.Current]; + lookup.statuses = [DescriptionTemplateStatus.Finalized]; + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label) + ] + }; + lookup.order = { items: [nameof(x => x.label)] }; + if (like) { lookup.like = this.filterService.transformLike(like); } + return lookup; + } } diff --git a/dmp-frontend/src/app/core/services/description/description.service.ts b/dmp-frontend/src/app/core/services/description/description.service.ts index e27e95092..8b352b54a 100644 --- a/dmp-frontend/src/app/core/services/description/description.service.ts +++ b/dmp-frontend/src/app/core/services/description/description.service.ts @@ -46,9 +46,6 @@ export class DescriptionService { const options: HttpParamsOptions = { fromObject: { f: reqFields } }; let params: BaseHttpParams = new BaseHttpParams(options); - params.interceptorContext = { - excludedInterceptors: [InterceptorType.AuthToken] - }; return this.http .get(url, { params: params }).pipe( @@ -57,10 +54,16 @@ export class DescriptionService { getPublicSingle(id: Guid, reqFields: string[] = []): Observable { const url = `${this.apiBase}/public/${id}`; - const options = { params: { f: reqFields } }; + const options: HttpParamsOptions = { fromObject: { f: reqFields } }; + + let params: BaseHttpParams = new BaseHttpParams(options); + params.interceptorContext = { + excludedInterceptors: [InterceptorType.AuthToken] + }; + return this.http - .get(url, options).pipe( + .get(url, { params: params }).pipe( catchError((error: any) => throwError(error))); } diff --git a/dmp-frontend/src/app/core/services/reference/reference.service.ts b/dmp-frontend/src/app/core/services/reference/reference.service.ts index 16ba3f344..49e5480ea 100644 --- a/dmp-frontend/src/app/core/services/reference/reference.service.ts +++ b/dmp-frontend/src/app/core/services/reference/reference.service.ts @@ -3,7 +3,7 @@ import { IsActive } from '@app/core/common/enum/is-active.enum'; import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; import { ReferenceType } from '@app/core/common/enum/reference-type'; import { DmpReference } from '@app/core/model/dmp/dmp-reference'; -import { Reference, ReferencePersist } from '@app/core/model/reference/reference'; +import { Definition, Field, Reference, ReferencePersist } from '@app/core/model/reference/reference'; import { ReferenceSearchDefinitionLookup, ReferenceSearchLookup } from '@app/core/query/reference-search.lookup'; import { ReferenceLookup } from '@app/core/query/reference.lookup'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; @@ -70,31 +70,31 @@ export class ReferenceService { } // - // Autocomplete Commons + // Autocomplete Commons - Query // - public getSingleAutocompleteConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): SingleAutoCompleteConfiguration { + public getSingleAutocompleteQueryConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): SingleAutoCompleteConfiguration { return { - initialItems: (data?: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes)).pipe(map(x => x.items)), - filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes, searchQuery)).pipe(map(x => x.items)), - getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes, null, null, [selectedItem])).pipe(map(x => x.items[0])), + initialItems: (data?: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes)).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, searchQuery)).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, null, null, [selectedItem])).pipe(map(x => x.items[0])), displayFn: (item: Reference) => item.label, titleFn: (item: Reference) => item.label, valueAssign: (item: Reference) => item.id, }; }; - public getMultipleAutoCompleteConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): MultipleAutoCompleteConfiguration { + public getMultipleAutoCompleteQueryConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): MultipleAutoCompleteConfiguration { return { - initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes, null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), - filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteLookup(types, sourceTypes, searchQuery, excludedItems)).pipe(map(x => x.items)), - getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteLookup(types, sourceTypes, null, null, selectedItems)).pipe(map(x => x.items)), + initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, searchQuery, excludedItems)).pipe(map(x => x.items)), + getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, null, null, selectedItems)).pipe(map(x => x.items)), displayFn: (item: Reference) => item.label, titleFn: (item: Reference) => item.label, valueAssign: (item: Reference) => item.id, }; } - private buildAutocompleteLookup(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[], like?: string, excludedIds?: Guid[], ids?: Guid[]): ReferenceLookup { + private buildAutocompleteQueryLookup(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[], like?: string, excludedIds?: Guid[], ids?: Guid[]): ReferenceLookup { const lookup: ReferenceLookup = new ReferenceLookup(); lookup.page = { size: 100, offset: 0 }; if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } @@ -113,6 +113,56 @@ export class ReferenceService { return lookup; } + // + // Autocomplete Commons - Search + // + public getSingleAutocompleteSearchConfiguration(type: ReferenceType): SingleAutoCompleteConfiguration { + return { + initialItems: (data?: any) => this.search(this.buildAutocompleteSearchLookup(type)).pipe(map(x => x)), + filterFn: (searchQuery: string, data?: any) => this.search(this.buildAutocompleteSearchLookup(type, searchQuery)).pipe(map(x => x)), + getSelectedItem: (selectedItem: any) => this.search(this.buildAutocompleteSearchLookup(type)).pipe(map(x => x[0])), + displayFn: (item: Reference) => item.label, + titleFn: (item: Reference) => item.label, + valueAssign: (item: Reference) => item.id, + }; + }; + + public getMultipleAutoCompleteSearchConfiguration(type: ReferenceType): MultipleAutoCompleteConfiguration { + return { + initialItems: (excludedItems: any[], data?: any) => this.search(this.buildAutocompleteSearchLookup(type, null)).pipe(map(x => x)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.search(this.buildAutocompleteSearchLookup(type, searchQuery)).pipe(map(x => x)), + getSelectedItems: (selectedItems: any[]) => this.search(this.buildAutocompleteSearchLookup(type, null)).pipe(map(x => x)), + displayFn: (item: Reference) => item.label, + titleFn: (item: Reference) => item.label, + valueAssign: (item: Reference) => item, + }; + } + + private buildAutocompleteSearchLookup(type: ReferenceType, like?: string): ReferenceSearchLookup { + const lookup: ReferenceSearchLookup = new ReferenceSearchLookup(); + lookup.page = { size: 100, offset: 0 }; + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label), + nameof(x => x.type), + nameof(x => x.description), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.value)].join('.'), + nameof(x => x.reference), + nameof(x => x.abbreviation), + nameof(x => x.source), + nameof(x => x.sourceType), + ] + }; + lookup.type = type; + lookup.order = { items: [nameof(x => x.label)] }; + if (like) { lookup.like = this.filterService.transformLike(like); } + return lookup; + } + + // // // UI Helpers 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 88bc7eb69..c403def60 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 @@ -32,6 +32,7 @@ import { NotificationContactType } from '@app/core/common/enum/notification-cont import { NotificationNotifyState } from '@app/core/common/enum/notification-notify-state'; import { NotificationTrackingState } from '@app/core/common/enum/notification-tracking-state'; import { NotificationTrackingProcess } from '@app/core/common/enum/notification-tracking-process'; +import { DmpAccessType } from '@app/core/common/enum/dmp-access-type'; @Injectable() export class EnumUtils { @@ -386,4 +387,12 @@ export class EnumUtils { case NotificationTrackingProcess.OMITTED: return this.language.instant('TYPES.NOTIFICATION-TRACKING-PROCESS.OMITTED'); } } + + public toDmpAccessTypeString(value: DmpAccessType): string { + switch (value) { + case DmpAccessType.Public: return this.language.instant('TYPES.DMP-ACCESS-TYPE.PUBLIC'); + case DmpAccessType.Restricted: return this.language.instant('TYPES.DMP-ACCESS-TYPE.RESTRICTED'); + } + } + } diff --git a/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.html b/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.html index 447ae70fb..bdbd7d444 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-blueprint/listing/dmp-blueprint-listing.component.html @@ -16,7 +16,7 @@
- diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.html b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.html index 1f09c0cee..50e410696 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.html +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.html @@ -150,48 +150,35 @@
- - {{'DMP-EDITOR.PLACEHOLDER.RESEARCHERS' | translate}} - - - - {{formGroup.get('researchers').getError('backendError').message}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - -
- {{'DMP-EDITOR.FUNDING-INFO.FIND' | translate}} - {{'DMP-EDITOR.ACTIONS.INSERT-MANUALLY' | translate}} -
+
-
- - - {{vis.name | translate}} + + + {{enumUtils.toDmpAccessTypeString(dmpAccessType)}} - - {{formGroup.get('extraProperties').get('visible').getError('backendError').message}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('accessType').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} -
--> +
@@ -274,18 +240,17 @@
+ {{formGroup.get('descriptionTemplates').value | json}}
{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}
{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}} - + - - {{formGroup.get('profiles').getError('backendError').message}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('descriptionTemplates').get(section.id).get('descriptionTemplateGroupId').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.scss b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.scss index 3d4fd45d8..447cb06ab 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.scss +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.scss @@ -366,21 +366,6 @@ a:hover { margin-bottom: 1rem; } - .insert-manually { - text-decoration: underline; - color: var(--primary-color-3); - cursor: pointer; - font-size: 1rem; - font-weight: 400; - } - - .not-found { - // cursor: pointer; - font-size: 1rem; - font-weight: 400; - padding: 0rem 0.5rem 0rem 0rem; - } - .not-found-template { font-size: 0.875rem; font-weight: 400; diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts index 06c75f60e..f9417430e 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.component.ts @@ -33,6 +33,13 @@ import { map, takeUntil } from 'rxjs/operators'; import { DmpEditorModel } from './dmp-editor.model'; import { DmpEditorResolver } from './dmp-editor.resolver'; import { DmpEditorService } from './dmp-editor.service'; +import { ReferenceType } from '@app/core/common/enum/reference-type'; +import { LanguageInfo } from '@app/core/model/language-info'; +import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; +import { DmpAccessType } from '@app/core/common/enum/dmp-access-type'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { UntypedFormArray } from '@angular/forms'; +import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service'; @Component({ selector: 'app-dmp-editor', @@ -52,6 +59,9 @@ export class DmpEditorComponent extends BaseEditor implemen dmpBlueprintSectionFieldCategoryEnum = DmpBlueprintSectionFieldCategory; dmpBlueprintSystemFieldTypeEnum = DmpBlueprintSystemFieldType; dmpBlueprintExtraFieldDataTypeEnum = DmpBlueprintExtraFieldDataType; + referenceTypeEnum = ReferenceType; + dmpAccessTypeEnum = DmpAccessType; + dmpAccessTypeEnumValues = this.enumUtils.getEnumValues(DmpAccessType); protected get canDelete(): boolean { return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDmp); @@ -84,7 +94,6 @@ export class DmpEditorComponent extends BaseEditor implemen protected queryParamsService: QueryParamsService, // Rest dependencies. Inject any other needed deps here: public authService: AuthService, - // public enumUtils: EnumUtils, private dmpService: DmpService, private logger: LoggingService, // private descriptionEditorService: DescriptionEditorService, @@ -92,7 +101,10 @@ export class DmpEditorComponent extends BaseEditor implemen private matomoService: MatomoService, private lockService: LockService, private configurationService: ConfigurationService, - // public visibilityRulesService: VisibilityRulesService + // public visibilityRulesService: VisibilityRulesService, + private languageInfoService: LanguageInfoService, + private enumUtils: EnumUtils, + public descriptionTemplateService: DescriptionTemplateService ) { super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService); @@ -343,6 +355,81 @@ export class DmpEditorComponent extends BaseEditor implemen // this.onSubmit(true, false); } + // + // + // Description Template + // + // + + onRemoveDescriptionTemplate(event, sectionIndex: number) { + let found = false; + const section = this.selectedBlueprint.definition.sections[sectionIndex]; + let sectionDescriptionTemplates = (this.formGroup.get('descriptionTemplates') as UntypedFormArray).controls.find(x => x.get('sectionId').value === event.id); + } + + onPreviewDescriptionTemplate(event, sectionIndex: number) { + // const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, { + // width: '590px', + // minHeight: '200px', + // restoreFocus: false, + // data: { + // template: event + // }, + // panelClass: 'custom-modalbox' + // }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result) { + // this.addProfile(event, sectionIndex); + // this.profilesAutoCompleteConfiguration = { + // filterFn: this.filterProfiles.bind(this), + // initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + // displayFn: (item) => item['label'], + // titleFn: (item) => item['label'], + // subtitleFn: (item) => item['description'], + // popupItemActionIcon: 'visibility' + // }; + // } + // }); + } + + onDescriptionTemplateSelected(event, sectionIndex: number) { + try { + this.addProfile(event, sectionIndex); + // const profileCounts: Map = new Map(); + // profiles.forEach((value) => profileCounts.set(value.id, (profileCounts.get(value.id) !== undefined ? profileCounts.get(value.id): 0 ) + 1)); + // const duplicateProfiles = profiles.filter((value) => { + // let isOk = profileCounts.get(value.id) > 1; + // if (isOk) { + // profileCounts.set(value.id, 0); + // } + // return isOk; + // }); + // duplicateProfiles.forEach((value) => profiles.splice(profiles.lastIndexOf(value), 1)); + // profiles.sort((a,b)=> a.label.localeCompare(b.label)); + } catch { + console.info('Could not sort Dataset Templates') + } + } + + addProfile(event, sectionIndex: number) { + // const profiles = this.formGroup.get('profiles').value as DmpDatasetProfile[]; + // let found = profiles.find((value) => value.id === event.id); + // if (found !== undefined) { + // if (found.data.dmpSectionIndex.indexOf(sectionIndex) === -1) { + // found.data.dmpSectionIndex.push(sectionIndex); + // } + // else { + // this.sectionTemplates[sectionIndex].pop(); + // } + // } + // else { + // let dmpDatasetProfileSection: DmpDatasetProfileSectionsFormModel = new DmpDatasetProfileSectionsFormModel(); + // dmpDatasetProfileSection.dmpSectionIndex = [sectionIndex]; + // profiles.push({ id: null, descriptionTemplateId: event.id, label: event.label, data: dmpDatasetProfileSection }); + // } + // this.formGroup.get('profiles').setValue(profiles); + } + // // // Misc @@ -352,6 +439,9 @@ export class DmpEditorComponent extends BaseEditor implemen return this.formGroup && this.formGroup.dirty; //&& this.hasChanges; } + getLanguageInfos(): LanguageInfo[] { + return this.languageInfoService.getLanguageInfoValues(); + } // canDeactivate(): boolean { // return !this.isDirty(); @@ -1470,7 +1560,7 @@ export class DmpEditorComponent extends BaseEditor implemen // this.formGroup.get('profiles').setValue(profiles); // } - // onPreviewTemplate(event, sectionIndex: number) { + // onPreviewDescriptionTemplate(event, sectionIndex: number) { // const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, { // width: '590px', // minHeight: '200px', diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.model.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.model.ts index 7ba14a1bf..925190814 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.model.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.model.ts @@ -1,7 +1,8 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { DmpAccessType } from "@app/core/common/enum/dmp-access-type"; import { DmpStatus } from "@app/core/common/enum/dmp-status"; -import { Dmp, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpReferencePersist } from "@app/core/model/dmp/dmp"; +import { DmpBlueprint } from "@app/core/model/dmp-blueprint/dmp-blueprint"; +import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist } from "@app/core/model/dmp/dmp"; import { DmpReference } from "@app/core/model/dmp/dmp-reference"; import { ReferencePersist } from "@app/core/model/reference/reference"; import { BaseEditorModel } from "@common/base/base-form-editor-model"; @@ -13,12 +14,12 @@ import { Guid } from "@common/types/guid"; export class DmpEditorModel extends BaseEditorModel implements DmpPersist { label: string; status: DmpStatus; - properties: string; + properties: DmpPropertiesEditorModel; description: String; language: String; blueprint: Guid; accessType: DmpAccessType; - references: DmpReferenceEditorModel[] = []; + // references: DmpReferenceEditorModel[] = []; descriptionTemplates: DmpDescriptionTemplateEditorModel[] = []; permissions: string[]; @@ -32,12 +33,12 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist { super.fromModel(item); this.label = item.label; this.status = item.status; - this.properties = item.properties; + this.properties = new DmpPropertiesEditorModel().fromModel(item.properties, item.dmpReferences, item.blueprint); this.description = item.description; this.language = item.language; this.blueprint = item.blueprint?.id; this.accessType = item.accessType; - if (item.dmpReferences) { item.dmpReferences.map(x => this.references.push(new DmpReferenceEditorModel().fromModel(x))); } + //if (item.dmpReferences) { item.dmpReferences.map(x => this.references.push(new DmpReferenceEditorModel().fromModel(x))); } if (item.dmpDescriptionTemplates) { item.dmpDescriptionTemplates.map(x => this.descriptionTemplates.push(new DmpDescriptionTemplateEditorModel().fromModel(x))); } } return this; @@ -50,7 +51,9 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist { id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators], - properties: [{ value: this.properties, disabled: disabled }, context.getValidation('properties').validators], + properties: this.properties.buildForm({ + rootPath: `properties.` + }), description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators], language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators], blueprint: [{ value: this.blueprint, disabled: disabled }, context.getValidation('blueprint').validators], @@ -64,25 +67,24 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist { // }), context.getValidation('references') // ) // ), - // descriptionTemplates: this.formBuilder.array( - // (this.descriptionTemplates ?? []).map( - // (item, index) => new DmpDescriptionTemplateEditorModel( - // this.validationErrorModel - // ).fromModel(item).buildForm({ - // rootPath: `descriptionTemplates[${index}].` - // }), context.getValidation('descriptionTemplates') - // ) - // ), hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] }); - (this.references ?? []).map( - (item, index) => formGroup.addControl(item.id.toString(), new DmpReferenceEditorModel( - this.validationErrorModel - ).fromModel(item).buildForm({ - rootPath: `references[${index}].` - })), context.getValidation('references') + + // (this.references ?? []).filter(x => x?.data?.blueprintFieldId).map(x => x?.data?.blueprintFieldId).map( + // (item, index) => formGroup.addControl(item.data.blueprintFieldId.toString(), new DmpReferenceEditorModel( + // this.validationErrorModel + // ).fromModel(item).buildForm({ + // rootPath: `references[${index}].` + // })), context.getValidation('references') + // ) + const descriptionTemplatesFormGroup = this.formBuilder.group({}); + (this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map( + (item, index) => descriptionTemplatesFormGroup.addControl(item.toString(), this.descriptionTemplates.find(x => x.sectionId === item).buildForm({ + rootPath: `descriptionTemplates[${index}].` + })), context.getValidation('descriptionTemplates') ) + formGroup.addControl('descriptionTemplates', descriptionTemplatesFormGroup); return formGroup; } @@ -98,7 +100,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist { baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] }); baseValidationArray.push({ key: 'blueprint', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprint')] }); baseValidationArray.push({ key: 'accessType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'accessType')] }); - baseValidationArray.push({ key: 'references', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'references')] }); + //baseValidationArray.push({ key: 'references', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'references')] }); baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] }); baseValidationArray.push({ key: 'hash', validators: [] }); @@ -107,10 +109,205 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist { } } +export class DmpPropertiesEditorModel implements DmpPropertiesPersist { + dmpBlueprintValues: DmpBlueprintValueEditorModel[] = []; + contacts: DmpContactEditorModel[] = []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: DmpProperties, dmpReferences: DmpReference[], dmpBlueprint: DmpBlueprint): DmpPropertiesEditorModel { + + dmpBlueprint.definition.sections.forEach(section => { + section.fields.forEach(field => { + this.dmpBlueprintValues.push(new DmpBlueprintValueEditorModel().fromModel( + { + fieldId: field.id, + fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue + }, dmpReferences)); + }); + }); + // if (item?.dmpBlueprintValues) { item.dmpBlueprintValues.map(x => this.dmpBlueprintValues.push(new DmpBlueprintValueEditorModel().fromModel(x, dmpReferences))); } + if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel().fromModel(x))); } + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = DmpPropertiesEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + const formGroup = this.formBuilder.group({ + contacts: this.formBuilder.array( + (this.contacts ?? []).map( + (item, index) => new DmpContactEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `contacts[${index}].` + }), context.getValidation('contacts') + ) + ), + + }); + + (this.dmpBlueprintValues ?? []).filter(x => x?.fieldId).map( + (item, index) => formGroup.addControl(item.fieldId.toString(), item.buildForm({ + rootPath: `dmpBlueprintValues[${index}].` + })), context.getValidation('dmpBlueprintValues') + ) + + return formGroup; + } + + 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: 'dmpBlueprintValues', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}dmpBlueprintValues`)] }); + baseValidationArray.push({ key: 'contacts', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}contacts`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist { + fieldId: Guid; + fieldValue: string; + references: DmpReferencePersist[]; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[]): DmpBlueprintValueEditorModel { + this.fieldId = item.fieldId; + this.fieldValue = item.fieldValue; + this.references = dmpReferences.filter(x => x.data?.blueprintFieldId === this.fieldId); + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = DmpBlueprintValueEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators], + fieldValue: [{ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators], + references: [{ value: this.references, disabled: disabled }, context.getValidation('references').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: 'fieldId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldId`)] }); + baseValidationArray.push({ key: 'fieldValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] }); + baseValidationArray.push({ key: 'references', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class DmpContactEditorModel implements DmpContactPersist { + userId: Guid; + firstName: string; + lastName: string; + email: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: DmpContact): DmpContactEditorModel { + this.userId = item.userId; + this.firstName = item.firstName; + this.lastName = item.lastName; + this.email = item.email; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = DmpReferenceEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + userId: [{ value: this.userId, disabled: disabled }, context.getValidation('userId').validators], + firstName: [{ value: this.firstName, disabled: disabled }, context.getValidation('firstName').validators], + lastName: [{ value: this.lastName, disabled: disabled }, context.getValidation('lastName').validators], + email: [{ value: this.email, disabled: disabled }, context.getValidation('email').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: 'userId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}userId`)] }); + baseValidationArray.push({ key: 'firstName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}firstName`)] }); + baseValidationArray.push({ key: 'lastName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}lastName`)] }); + baseValidationArray.push({ key: 'email', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}email`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + export class DmpReferenceEditorModel implements DmpReferencePersist { id: Guid; reference: ReferencePersist; - data: string; + referenceId: Guid; + data: DmpReferenceDataPersist; hash: string; protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); @@ -203,7 +400,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate return this.formBuilder.group({ id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], - reference: [{ value: this.descriptionTemplateGroupId, disabled: disabled }, context.getValidation('descriptionTemplateGroupId').validators], + descriptionTemplateGroupId: [{ value: this.descriptionTemplateGroupId, disabled: disabled }, context.getValidation('descriptionTemplateGroupId').validators], sectionId: [{ value: this.sectionId, disabled: disabled }, context.getValidation('sectionId').validators], hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] }); diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.module.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.module.ts index caf122f54..fab72aad7 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.module.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.module.ts @@ -7,6 +7,7 @@ import { CommonUiModule } from '@common/ui/common-ui.module'; import { DmpEditorComponent } from './dmp-editor.component'; import { DmpEditorRoutingModule } from './dmp-editor.routing'; import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; +import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module'; @NgModule({ imports: [ @@ -16,7 +17,8 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod ConfirmationDialogModule, DmpEditorRoutingModule, RichTextEditorModule, - AutoCompleteModule + AutoCompleteModule, + ReferenceFieldModule ], declarations: [ DmpEditorComponent, diff --git a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts index 73072cee4..210fbe08d 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp-editor-blueprint/dmp-editor.resolver.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Description } from '@app/core/model/description/description'; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp'; +import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties } from '@app/core/model/dmp/dmp'; import { DmpReference } from '@app/core/model/dmp/dmp-reference'; import { Reference } from '@app/core/model/reference/reference'; import { DmpService } from '@app/core/services/dmp/dmp.service'; @@ -33,6 +33,14 @@ export class DmpEditorResolver extends BaseEditorResolver { nameof(x => x.version), nameof(x => x.updatedAt), + [nameof(x => x.properties), nameof(x => x.dmpBlueprintValues), nameof(x => x.fieldId)].join('.'), + [nameof(x => x.properties), nameof(x => x.dmpBlueprintValues), nameof(x => x.fieldValue)].join('.'), + [nameof(x => x.properties), nameof(x => x.contacts), nameof(x => x.userId)].join('.'), + [nameof(x => x.properties), nameof(x => x.contacts), nameof(x => x.firstName)].join('.'), + [nameof(x => x.properties), nameof(x => x.contacts), nameof(x => x.lastName)].join('.'), + [nameof(x => x.properties), nameof(x => x.contacts), nameof(x => x.email)].join('.'), + + // [nameof(x => x.entityDois), nameof(x => x.id)].join('.'), // [nameof(x => x.entityDois), nameof(x => x.repositoryId)].join('.'), // [nameof(x => x.entityDois), nameof(x => x.doi)].join('.'), @@ -55,6 +63,10 @@ export class DmpEditorResolver extends BaseEditorResolver { [nameof(x => x.dmpReferences), nameof(x => x.reference), nameof(x => x.reference)].join('.'), + [nameof(x => x.dmpDescriptionTemplates), nameof(x => x.sectionId)].join('.'), + [nameof(x => x.dmpDescriptionTemplates), nameof(x => x.descriptionTemplateGroupId)].join('.'), + + // nameof(x => x.id), // nameof(x => x.label), // nameof(x => x.status), @@ -67,23 +79,23 @@ export class DmpEditorResolver extends BaseEditorResolver { public static blueprintLookupFields(prefix?: string): string[] { return [ - prefix ? prefix + '.' : '' + [nameof(x => x.id)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.id)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.label)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.ordinal)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.hasTemplates)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.id)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.descriptionTemplateId)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.id)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.category)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.systemFieldType)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.label)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.placeholder)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.description)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.required)].join('.'), - prefix ? prefix + '.' : '' + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.ordinal)].join('.') + (prefix ? prefix + '.' : '') + [nameof(x => x.id)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.id)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.label)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.ordinal)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.hasTemplates)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.id)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.descriptionTemplateId)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.id)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.category)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.systemFieldType)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.label)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.placeholder)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.description)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.required)].join('.'), + (prefix ? prefix + '.' : '') + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.ordinal)].join('.') ] } diff --git a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.html b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.html index 58f36952a..901107a2d 100644 --- a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.html +++ b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.html @@ -1,495 +1,11 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + {{'REFERENCE-FIELD.PLACEHOLDER' | translate}} {{enumUtils.toReferenceTypeString(referenceType)}} + + + {{form.getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ {{'REFERENCE-FIELD.COULD-NOT-FIND-MESSAGE' | translate}} + {{'REFERENCE-FIELD.ACTIONS.INSERT-MANUALLY' | translate}} +
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.scss b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.scss index 5221d29c7..3b3d40edc 100644 --- a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.scss +++ b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.scss @@ -1,35 +1,14 @@ -.dataset-external-references-editor { - .heading { - text-align: left; - font-weight: 700; - font-size: 18px; - letter-spacing: 0px; - color: #212121; - opacity: 0.81; - margin-top: 1.625rem; - margin-bottom: 0.625rem; - } + .insert-manually { + text-decoration: underline; + color: var(--primary-color-3); + cursor: pointer; + font-size: 1rem; + font-weight: 400; + } - .hint { - text-align: left; - font-weight: 400; - font-size: 16px; - letter-spacing: 0px; - color: #212121; - opacity: 0.81; - margin-bottom: 2.125rem; - } - - .external-item-card { - margin-bottom: 1em; - } -} - -::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-outline { - background: #fafafa !important; -} - -::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-infix { - font-size: 1rem; - padding: 0.6em 0 1em 0 !important; -} + .not-found { + // cursor: pointer; + font-size: 1rem; + font-weight: 400; + padding: 0rem 0.5rem 0rem 0rem; + } diff --git a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.ts b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.ts index 248433f89..e1df7fc62 100644 --- a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.ts +++ b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.component.ts @@ -1,36 +1,10 @@ -import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core'; -import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; -import { MatDialog } from '@angular/material/dialog'; -import { Router } from '@angular/router'; -import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; -import { ExternalSourcesConfiguration } from '@app/core/model/external-sources/external-sources-configuration'; -import { DataRepositoryCriteria } from '@app/core/query/data-repository/data-repository-criteria'; -import { ExternalDatasetCriteria } from '@app/core/query/external-dataset/external-dataset-criteria'; -import { RegistryCriteria } from '@app/core/query/registry/registry-criteria'; -import { RequestItem } from '@app/core/query/request-item'; -import { ServiceCriteria } from '@app/core/query/service/service-criteria'; -import { TagCriteria } from '@app/core/query/tag/tag-criteria'; -import { ExternalSourcesConfigurationService } from '@app/core/services/external-sources/external-sources-configuration.service'; -import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; -import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; -import { ExternalDataRepositoryEditorModel, ExternalDatasetEditorModel, ExternalRegistryEditorModel, ExternalServiceEditorModel, ExternalTagEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; -import { DatasetExternalDataRepositoryDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component'; -import { DatasetExternalDatasetDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component'; -import { DatasetExternalRegistryDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component'; -import { DatasetExternalServiceDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component'; -import { BaseComponent } from '@common/base/base.component'; -import { TranslateService } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; -import { takeUntil, map } from 'rxjs/operators'; -import { ENTER, COMMA } from '@angular/cdk/keycodes'; -import { MatChipInputEvent } from '@angular/material/chips'; -import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; -import { ExternalDataRepositoryService } from '@app/core/services/external-sources/data-repository/extternal-data-repository.service'; -import { ExternalDatasetService } from '@app/core/services/external-sources/dataset/external-dataset.service'; -import { ExternalRegistryService } from '@app/core/services/external-sources/registry/external-registry.service'; -import { ExternalServiceService } from '@app/core/services/external-sources/service/external-service.service'; -import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { Component, Input, OnInit } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; import { ReferenceType } from '@app/core/common/enum/reference-type'; +import { ReferenceService } from '@app/core/services/reference/reference.service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { BaseComponent } from '@common/base/base.component'; @Component({ selector: 'app-reference-field-component', @@ -42,7 +16,9 @@ export class ReferenceFieldComponent extends BaseComponent implements OnInit { @Input() referenceType: ReferenceType = null; @Input() form: UntypedFormGroup = null; - referenceTypeEnum = ReferenceType; + multipleAutoCompleteSearchConfiguration: MultipleAutoCompleteConfiguration; + // referenceTypeEnum = ReferenceType; + // @Input() viewOnly = false; // @Output() formChanged: EventEmitter = new EventEmitter(); @@ -104,31 +80,34 @@ export class ReferenceFieldComponent extends BaseComponent implements OnInit { // private externalDataRepositoryService: ExternalDataRepositoryService, // private externalDatasetService: ExternalDatasetService, // private externalRegistryService: ExternalRegistryService, - // private externalServiceService: ExternalServiceService, + private referenceService: ReferenceService, + public enumUtils: EnumUtils, ) { super(); } ngOnInit() { + this.multipleAutoCompleteSearchConfiguration = this.referenceService.getMultipleAutoCompleteSearchConfiguration(this.referenceType); - // this.externalSourcesConfigurationService.getExternalSourcesConfiguration() - // .pipe(takeUntil(this._destroyed)) - // .subscribe(result => { - // this.externalSourcesConfiguration = result; - // this.externalSourcesConfiguration.dataRepositories.push({ key: '', label: 'All' }); - // this.externalSourcesConfiguration.externalDatasets.push({ key: '', label: 'All' }); - // this.externalSourcesConfiguration.registries.push({ key: '', label: 'All' }); - // this.externalSourcesConfiguration.services.push({ key: '', label: 'All' }); - // if (!isNullOrUndefined(this.externalSourcesConfiguration.tags)) { - // this.externalSourcesConfiguration.tags.push({ key: '', label: 'All' }); - // } else { - // this.externalSourcesConfiguration.tags = [{ key: '', label: 'All' }]; - // } - // }); - // this.formGroup.valueChanges - // .pipe(takeUntil(this._destroyed)) - // .subscribe(val => { - // this.formChanged.emit(val); - // }); + // this.externalSourcesConfigurationService.getExternalSourcesConfiguration() + // .pipe(takeUntil(this._destroyed)) + // .subscribe(result => { + // this.externalSourcesConfiguration = result; + // this.externalSourcesConfiguration.dataRepositories.push({ key: '', label: 'All' }); + // this.externalSourcesConfiguration.externalDatasets.push({ key: '', label: 'All' }); + // this.externalSourcesConfiguration.registries.push({ key: '', label: 'All' }); + // this.externalSourcesConfiguration.services.push({ key: '', label: 'All' }); + // if (!isNullOrUndefined(this.externalSourcesConfiguration.tags)) { + // this.externalSourcesConfiguration.tags.push({ key: '', label: 'All' }); + // } else { + // this.externalSourcesConfiguration.tags = [{ key: '', label: 'All' }]; + // } + // }); + + // this.formGroup.valueChanges + // .pipe(takeUntil(this._destroyed)) + // .subscribe(val => { + // this.formChanged.emit(val); + // }); } // public cancel(): void { diff --git a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.module.ts b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.module.ts index 699af2811..96caafc21 100644 --- a/dmp-frontend/src/app/ui/reference/reference-field/reference-field.module.ts +++ b/dmp-frontend/src/app/ui/reference/reference-field/reference-field.module.ts @@ -1,8 +1,10 @@ import { NgModule } from '@angular/core'; import { FormattingModule } from '@app/core/formatting.module'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; import { DescriptionRoutingModule } from '@app/ui/description/description.routing'; import { CommonFormsModule } from '@common/forms/common-forms.module'; import { CommonUiModule } from '@common/ui/common-ui.module'; +import { ReferenceFieldComponent } from './reference-field.component'; @NgModule({ imports: [ @@ -10,10 +12,13 @@ import { CommonUiModule } from '@common/ui/common-ui.module'; CommonFormsModule, FormattingModule, DescriptionRoutingModule, + AutoCompleteModule ], declarations: [ + ReferenceFieldComponent ], exports: [ + ReferenceFieldComponent ] }) export class ReferenceFieldModule { } diff --git a/dmp-frontend/src/assets/config/config.json b/dmp-frontend/src/assets/config/config.json index 5b5690136..b23aa9b8e 100644 --- a/dmp-frontend/src/assets/config/config.json +++ b/dmp-frontend/src/assets/config/config.json @@ -15,7 +15,7 @@ "flow": "standard", "clientId": null, "silentCheckSsoRedirectUri": "http://localhost:4200/assets/silent-check-sso.html", - "scope": "openid profile email address phone dmp_web", + "scope": "openid profile email address phone dmp_web dmp_notification identity_provider", "clientSecret": null, "grantType": "code" }, diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 21c6af833..a6eae7118 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -1677,6 +1677,7 @@ "LICENSE": "License", "ACCESS-TYPE": "Access Rights", "CONTACT": "Contact", + "LANGUAGE": "Language", "DESCRIPTION-TEMPLATES": "Description templates", "DESCRIPTION-TEMPLATES-HINT": "Select a template to describe your descriptions" }, @@ -1831,6 +1832,13 @@ "MESSAGE": "Somebody else is modifying the DMP at this moment. You may view the dataset but you cannot make any changes." } }, + "REFERENCE-FIELD": { + "PLACEHOLDER":"Select", + "COULD-NOT-FIND-MESSAGE":"Couldn't find it?", + "ACTIONS": { + "INSERT-MANUALLY":"Insert it manually" + } + }, "DMP-CLONE-DIALOG": { "TITLE": "Clone", "FIELDS": { @@ -2391,6 +2399,10 @@ "SUCCESSFUL": "Successful", "ERROR":"Error", "OMITTED": "Omitted" + }, + "DMP-ACCESS-TYPE": { + "PUBLIC": "Public", + "RESTRICTED": "Restricted Access" } }, "ADDRESEARCHERS-EDITOR": { From 412c778d8c8463255cfd307986763061102927a2 Mon Sep 17 00:00:00 2001 From: amentis Date: Tue, 16 Jan 2024 11:55:46 +0200 Subject: [PATCH 2/2] add backend validation in language, references, tenant,supportive material editor forms. --- .../commons/validation/BaseValidator.java | 3 + .../eudat/model/persist/ReferencePersist.java | 3 - .../TenantDepositConfigPersist.java | 2 +- .../TenantFileTransformersConfigPersist.java | 2 +- .../tenantconfig/TenantSourcePersist.java | 2 +- .../editor/language-editor.component.html | 16 ++-- .../editor/reference-editor.component.html | 47 ++++++------ .../editor/reference-editor.component.ts | 11 ++- .../editor/reference-editor.model.ts | 55 +++++++++++++- .../editor/tenant-editor.component.html | 57 +++++++------- .../tenant/editor/tenant-editor.component.ts | 24 +++++- .../tenant/editor/tenant-editor.model.ts | 74 ++++++++++++++++++- .../supportive-material-editor.component.html | 10 ++- 13 files changed, 229 insertions(+), 77 deletions(-) diff --git a/dmp-backend/core/src/main/java/eu/eudat/commons/validation/BaseValidator.java b/dmp-backend/core/src/main/java/eu/eudat/commons/validation/BaseValidator.java index 5992c55e3..3a2e5ac9c 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/commons/validation/BaseValidator.java +++ b/dmp-backend/core/src/main/java/eu/eudat/commons/validation/BaseValidator.java @@ -40,6 +40,9 @@ public abstract class BaseValidator extends AbstractValidator { protected Boolean isEmpty(String value) { return this.conventionService.isNullOrEmpty(value); } + protected Boolean isListNullOrEmpty(List value) { + return this.conventionService.isListNullOrEmpty(value); + } protected Boolean isNull(Object value) { return value == null; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java index 978cdc09b..95feb91e6 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/ReferencePersist.java @@ -186,9 +186,6 @@ public class ReferencePersist { .iff(() -> !this.isEmpty(item.getReference())) .must(() -> this.lessEqualLength(item.getReference(), ReferenceEntity._referenceLength)) .failOn(ReferencePersist._reference).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{ReferencePersist._reference}, LocaleContextHolder.getLocale())), - this.spec() - .must(() -> !this.isEmpty(item.getAbbreviation())) - .failOn(ReferencePersist._abbreviation).failWith(messageSource.getMessage("Validation_Required", new Object[]{ReferencePersist._abbreviation}, LocaleContextHolder.getLocale())), this.spec() .iff(() -> !this.isEmpty(item.getAbbreviation())) .must(() -> this.lessEqualLength(item.getAbbreviation(), ReferenceEntity._abbreviationLength)) diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java index 431d422e0..e2bbd6ea0 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantDepositConfigPersist.java @@ -47,7 +47,7 @@ public class TenantDepositConfigPersist { @Override protected List specifications(TenantDepositConfigPersist item) { return Collections.singletonList( - this.refSpec() + this.navSpec() .iff(() -> !this.isNull(item.getSources())) .on(TenantDepositConfigPersist._sources) .over(item.getSources()) diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java index e72b6ffa8..b8ede05c5 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantFileTransformersConfigPersist.java @@ -47,7 +47,7 @@ public class TenantFileTransformersConfigPersist { @Override protected List specifications(TenantFileTransformersConfigPersist item) { return Collections.singletonList( - this.refSpec() + this.navSpec() .iff(() -> !this.isNull(item.getSources())) .on(TenantFileTransformersConfigPersist._sources) .over(item.getSources()) diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java index 0185b7b10..46add9237 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/persist/tenantconfig/TenantSourcePersist.java @@ -112,7 +112,7 @@ public class TenantSourcePersist { .must(() -> !this.isEmpty(item.getUrl())) .failOn(TenantSourcePersist._url).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantSourcePersist._url}, LocaleContextHolder.getLocale())), this.spec() - .must(() -> !this.isNull(item.getCodes())) + .must(() -> !this.isListNullOrEmpty(item.getCodes())) .failOn(TenantSourcePersist._codes).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantSourcePersist._codes}, LocaleContextHolder.getLocale())), this.spec() .must(() -> !this.isEmpty(item.getIssuerUrl())) diff --git a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html index d63d0769c..3bc5faa99 100644 --- a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.html @@ -33,8 +33,8 @@ {{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -45,24 +45,24 @@ {{languageCode}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'LANGUAGE-EDITOR.FIELDS.ORDINAL' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('ordinal').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'LANGUAGE-EDITOR.FIELDS.PAYLOAD' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('payload').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} {{'LANGUAGE-EDITOR.FIELDS.OVERRIDE' | translate}} 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 index 80e0cadbd..f527e3962 100644 --- 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 @@ -33,8 +33,8 @@ {{'REFERENCE-EDITOR.FIELDS.LABEL' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('label').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -44,9 +44,9 @@ {{enumUtils.toReferenceTypeString(type)}} - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + {{formGroup.get('type').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -55,9 +55,8 @@
- - {{'GENERAL.VALIDATION.REQUIRED'| translate}} - + {{formGroup.get('description').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED'| translate}}
@@ -65,8 +64,8 @@ {{'REFERENCE-EDITOR.FIELDS.SOURCE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('source').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -76,25 +75,25 @@ {{enumUtils.toReferenceSourceTypeString(sourceType)}} - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + {{formGroup.get('sourceType').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'REFERENCE-EDITOR.FIELDS.REFERENCE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('reference').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'REFERENCE-EDITOR.FIELDS.ABBREVIATION' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('abbreviation').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -121,8 +120,8 @@ {{'REFERENCE-EDITOR.FIELDS.CODE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{field.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -132,17 +131,17 @@ {{enumUtils.toReferenceFieldDataTypeString(dataType)}} - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + {{field.get('dataType').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'REFERENCE-EDITOR.FIELDS.VALUE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{field.get('value').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
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 index f18bb26e6..a5741084e 100644 --- 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 @@ -190,12 +190,19 @@ export class ReferenceEditorComponent extends BaseEditor FieldEditorModel.reapplyValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}fields[${index}].`, + validationErrorModel: validationErrorModel + }) + ); + } } export class FieldEditorModel implements FieldPersist { @@ -191,5 +225,24 @@ export class FieldEditorModel implements FieldPersist { baseContext.validation = baseValidationArray; return baseContext; } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = FieldEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['code', 'dataType', 'value'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }) + } } diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html index 48e3f4bcd..377ee3ef5 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.html @@ -32,16 +32,16 @@ {{'TENANT-EDITOR.FIELDS.NAME' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('name').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.CODE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('code').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -50,9 +50,8 @@
- - {{'GENERAL.VALIDATION.REQUIRED'| translate}} - + {{formGroup.get('description').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED'| translate}}
@@ -79,40 +78,40 @@ {{'TENANT-EDITOR.FIELDS.URL' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('url').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('issuerUrl').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('clientId').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('clientSecret').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('scope').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -134,6 +133,8 @@ [matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addDepositCode($event)"/> + {{source.get('codes').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -162,40 +163,40 @@ {{'TENANT-EDITOR.FIELDS.URL' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('url').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('issuerUrl').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('clientId').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('clientSecret').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{source.get('scope').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -217,6 +218,8 @@ [matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addFileCode($event)"/> + {{source.get('codes').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts index a2e83aadf..b3269e787 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.component.ts @@ -192,12 +192,20 @@ export class TenantEditorComponent extends BaseEditor // deposit source // addDepositSource(): void { - const source: TenantSourceEditorModel = new TenantSourceEditorModel(); - (this.formGroup.get('config').get('deposit').get('sources') as FormArray).push(source.buildForm()); + (this.formGroup.get('config').get('deposit').get('sources') as FormArray).push(this.editorModel.createChildDeposit((this.formGroup.get('config').get('deposit').get('sources') as FormArray).length)); } removeDepositSource(sourceIndex: number): void { (this.formGroup.get('config').get('deposit').get('sources') as FormArray).removeAt(sourceIndex); + + //Reapply validators + TenantEditorModel.reApplyDepositSourcesValidators( + { + formGroup: this.formGroup, + validationErrorModel: this.editorModel.validationErrorModel + } + ) + this.formGroup.get('config').get('deposit').get('sources').markAsDirty(); } // deposit source codes @@ -232,12 +240,20 @@ export class TenantEditorComponent extends BaseEditor // fileTransformers source // addFileSource(): void { - const source: TenantSourceEditorModel = new TenantSourceEditorModel(); - (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).push(source.buildForm()); + (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).push(this.editorModel.createChildFileTransformer((this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).length)); } removeFileSource(sourceIndex: number): void { (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).removeAt(sourceIndex); + + //Reapply validators + TenantEditorModel.reApplyFileTransformerSourcesValidators( + { + formGroup: this.formGroup, + validationErrorModel: this.editorModel.validationErrorModel + } + ) + this.formGroup.get('config').get('fileTransformers').get('sources').markAsDirty(); } // fileTransformers source codes diff --git a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts index 268d4554d..ec5775981 100644 --- a/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/tenant/editor/tenant-editor.model.ts @@ -1,4 +1,4 @@ -import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { Tenant, TenantConfig, TenantConfigPersist, TenantDepositConfig, TenantDepositConfigPersist, TenantFileTransformersConfig, TenantFileTransformersConfigPersist, TenantPersist, TenantSource, TenantSourcePersist } from "@app/core/model/tenant/tenant"; import { BaseEditorModel } from "@common/base/base-form-editor-model"; import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; @@ -55,6 +55,44 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist baseContext.validation = baseValidationArray; return baseContext; } + + createChildDeposit(index: number): UntypedFormGroup { + const deposit: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel); + return deposit.buildForm({ rootPath: 'config.deposit.sources[' + index + '].' }); + } + + static reApplyDepositSourcesValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + }): void { + + const { formGroup, validationErrorModel } = params; + const control = formGroup?.get('config').get('deposit'); + TenantDepositConfigEditorModel.reapplySourcesFieldsValidators({ + formArray: control.get('sources') as UntypedFormArray, + rootPath: `config.deposit.`, + validationErrorModel: validationErrorModel + }); + } + + createChildFileTransformer(index: number): UntypedFormGroup { + const deposit: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel); + return deposit.buildForm({ rootPath: 'config.fileTransformers.sources[' + index + '].' }); + } + + static reApplyFileTransformerSourcesValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + }): void { + + const { formGroup, validationErrorModel } = params; + const control = formGroup?.get('config').get('fileTransformers'); + TenantDepositConfigEditorModel.reapplySourcesFieldsValidators({ + formArray: control.get('sources') as UntypedFormArray, + rootPath: `config.fileTransformers.`, + validationErrorModel: validationErrorModel + }); + } } export class TenantConfigEditorModel implements TenantConfigPersist { @@ -167,6 +205,21 @@ export class TenantDepositConfigEditorModel implements TenantDepositConfigPersis baseContext.validation = baseValidationArray; return baseContext; } + + static reapplySourcesFieldsValidators(params: { + formArray: UntypedFormArray, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + const { validationErrorModel, rootPath, formArray } = params; + formArray?.controls?.forEach( + (control, index) => TenantSourceEditorModel.reapplyValidators({ + formGroup: control as UntypedFormGroup, + rootPath: `${rootPath}sources[${index}].`, + validationErrorModel: validationErrorModel + }) + ); + } } export class TenantFileTransformersConfigEditorModel implements TenantFileTransformersConfigPersist { @@ -294,4 +347,23 @@ export class TenantSourceEditorModel implements TenantSourcePersist { baseContext.validation = baseValidationArray; return baseContext; } + + static reapplyValidators(params: { + formGroup: UntypedFormGroup, + validationErrorModel: ValidationErrorModel, + rootPath: string + }): void { + + const { formGroup, rootPath, validationErrorModel } = params; + const context = TenantSourceEditorModel.createValidationContext({ + rootPath, + validationErrorModel + }); + + ['url', 'codes', 'issuerUrl', 'clientId', 'clientSecret', 'scope'].forEach(keyField => { + const control = formGroup?.get(keyField); + control?.clearValidators(); + control?.addValidators(context.getValidation(keyField).validators); + }) + } } diff --git a/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html b/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html index fc2b9ae43..82e7ea6c5 100644 --- a/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html +++ b/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html @@ -11,8 +11,8 @@ {{enumUtils.toSupportiveMaterialTypeString(type)}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('type').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -23,8 +23,8 @@ {{languageCode}} - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('languageCode').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -50,6 +50,8 @@ alignleft aligncenter alignright alignjustify | \ bullist numlist outdent indent | code codesample | searchreplace | preview | removeformat | help' }" [formControl]="formGroup.get('payload')"> + {{formGroup.get('payload').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}