From bbe4975176bf5d9a8dfcfb3a440c5136cd539ae6 Mon Sep 17 00:00:00 2001 From: Aldo Mihasi Date: Mon, 11 Sep 2023 08:59:47 +0300 Subject: [PATCH] add hasTemplates property to sections, it indicates if descriptions templates can be added in a section --- .../dmpprofiledefinition/Section.java | 10 + .../types/SystemFieldType.java | 5 +- .../src/main/resources/documents/h2020.docx | Bin 20971 -> 30266 bytes .../model/dmp/dmp-blueprint/dmp-blueprint.ts | 1 + .../admin/dmp-profile/dmp-profile.module.ts | 4 +- .../editor/dmp-blueprint-editor.model.ts | 113 +++-- .../editor/dmp-profile-editor.component.html | 448 +++++++++--------- .../editor/dmp-profile-editor.component.ts | 255 +++++----- 8 files changed, 429 insertions(+), 407 deletions(-) diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/Section.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/Section.java index bcb128653..5a5da9a85 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/Section.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/Section.java @@ -20,6 +20,7 @@ public class Section implements XmlSerializable
{ private String description; private Integer ordinal; private List fields; + private Boolean hasTemplates; private List descriptionTemplates; public UUID getId() { @@ -57,6 +58,13 @@ public class Section implements XmlSerializable
{ this.fields = fields; } + public Boolean getHasTemplates() { + return hasTemplates; + } + public void setHasTemplates(Boolean hasTemplates) { + this.hasTemplates = hasTemplates; + } + public List getDescriptionTemplates() { return descriptionTemplates; } @@ -71,6 +79,7 @@ public class Section implements XmlSerializable
{ rootElement.setAttribute("label", this.label); rootElement.setAttribute("description", this.description); rootElement.setAttribute("ordinal", String.valueOf(this.ordinal)); + rootElement.setAttribute("hasTemplates", String.valueOf(this.hasTemplates)); List temp = this.fields.stream().filter(f -> f.getCategory().equals(FieldCategory.SYSTEM)).collect(Collectors.toList()); List systemFieldsList = temp.stream().map(FieldModel::toSystemField).collect(Collectors.toList()); Element systemFields = doc.createElement("systemFields"); @@ -100,6 +109,7 @@ public class Section implements XmlSerializable
{ this.label = item.getAttribute("label"); this.description = item.getAttribute("description"); this.ordinal = Integer.valueOf(item.getAttribute("ordinal")); + this.hasTemplates = Boolean.valueOf(item.getAttribute("hasTemplates")); this.fields = new LinkedList<>(); Element systemFields = (Element) XmlBuilder.getNodeFromListByTagName(item.getChildNodes(), "systemFields"); if (systemFields != null) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/types/SystemFieldType.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/types/SystemFieldType.java index 852680213..d7c36023a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/types/SystemFieldType.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/entities/xmlmodels/dmpprofiledefinition/types/SystemFieldType.java @@ -12,8 +12,7 @@ public enum SystemFieldType { GRANT(7), PROJECT(8), LICENSE(9), - ACCESS_RIGHTS(10), - DESCRIPTION_TEMPLATES(11); + ACCESS_RIGHTS(10); private int type; @@ -49,8 +48,6 @@ public enum SystemFieldType { return LICENSE; case 10: return ACCESS_RIGHTS; - case 11: - return DESCRIPTION_TEMPLATES; default: throw new RuntimeException("Unsupported System Field Type"); } diff --git a/dmp-backend/web/src/main/resources/documents/h2020.docx b/dmp-backend/web/src/main/resources/documents/h2020.docx index c821032e1ef8eb77bb43c1abfa1f5ab27bc37c34..b0e721ab0234053b1488fd2d6b2b8af8a066d1c2 100644 GIT binary patch literal 30266 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⋘GtWx7yn2#=O-m5*C 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 AsHFdC@ml^`Eug$wO|*OI~H)i|r3LwTnjdUxIC zyC(z$PGkBi#1qd%ea^HI=IS=q1(*x8cv7kJ7E>|UhSaM)mlB~o-elbfguDN1uE)&3 zfcFNK9D(v34P>MSa!>pDPKeRK(&lHXgI2!yuX`G(k#$uV_~D+`NPdD6d6b{*a~hyP zPP`i^A%^K$hv#C7RAVrAxOZgzjUyYvdSNl1=wKLN+cZt9>2+4YPOgLq*WY@qBF0LP zzO}>EA{37@2$!P)NnQmWUb3?z-eEDM2^(Hgz2XIxDonZd0U&BZ>{ZNs>580i&2Jb7 z=UydKiK@ui5p%-G0jY&<6UGkpLQS_0W6+{Bz&^_Bdz3v;2_2Qt8v^NtqZT59>k5q3 z{Nr3i1c}tkj_&r<$=9=$Gdh=@Q)cXH%7OS>jK_QHK$_wN$qA<}@G+6&SG{SUIId8; zdl{GBc=}s>mDL|4DjR)u!|-L?Cgs?zxC7epFbnBKZecT1fY)E2Pil38k0M|JsYyO# z=!MO2qxrFA-JVt1=SASoF}?gvw5ttD6Olht0L1gUiq5CV5Dhi|c=-@_NnLcIh zh=hn*Kx5{EciNKlaf|+!=)xqWiHd^4RW)nI2wwTYLF|)o>bSdWny=%r<03tb5-tCl zxmj15Cw8EcUQp(u{+_vdR#rdS=>H}zC~HBVq+f|Ee^@+$P~gQ;B1hNhDVh!AT$3cN z51frTP&q%EV?*_-i<}rPa^U4*F5=TjCLDt`l1>@UQBO#VtWnAto|sv6uaJsH^`-Hx zfjK+(A=5q?csA?2H!A8&Mh1^@S`|39$bxO?T@0saMzE_iUHcsUoV`*xn*vb8TV?V* z@%@|^P*g}!pWE77cb;yEc+Eptl8(m%Sj@#ez9y%;R~68k+8~Z(PMTIGzb#PRyA`~L z_YnqBQ{G_3R@V*OWlyqQQr8W7PsEXoysPJZFr6+rqQkH$rEr$+b#&AcjAqyR#y-c+ zre|n4m_?bO(=ly|BaAsBP#&*7b?^cj?=0i8o&CLyk1myOH~YabT{X&B zy0^^_H)Z;$FDFS!@zNU#)xEf4`>54*ypy6!JHAQdQIT6DS11cUZFNoljXC7 znDvNQ5qXc}a(l}~M@_4t?6cI;`O2|#*ykb3VM3F$VXUhu0@A`qaicOhGVC`fk?ou+ zqpv?0GKjgnYFg$P-=rLqbzH-&)^kGSt0Wz&0D~@RK~S6#TpCC62X+c8`rKuwmUMLO zKI5ub+=ujEo7Zt8+i0fy9LgK7PPtqyAIM2ey71gFIY^iBVS@OwrfJ15DpNma$BIMZ z)wn&NJJk8G4_ih#q-Ce7^X}~#vKv;01cH{SbIwRdB9K4)P3@r!K-dqd36l2^h}4E9 zZ7~V5a)*9dZnk`MB;|N--^cl{S!nl$+qoT7LJi79Vo(oBBUGnE#rl{>n$N zkdg8rklY1k<$h(U&BQY4n_NFyt|W~ej|6f(zNomA!GM~X>2mXC4xQaLb>QQzd_$A3 zur_mLu1Q+HnH(+=5}yo=`|Z6MWKdUk8w2K8D-mAJ;+CjOXO{&0$v#G5h7bo+aF%89 z+z>9k>Sv~y0Woeh8iN$OibHd%$#)-{rmtJT7o+o$y**uV@#XGc19>JNm{*`XmJ9NO;OjqJOn5AUTARPxB5;V8py$MYU()cms{Y*k zmjw>xB>yD%bD6Z?2@XJq{B6;+r@*HRM*V^kg48!Z-_ZYV-uo2(r|0i4Fc?@INS^Z( z{=YZ?KV^FA(fW&vh462tKO9@1;-8vd{=(ydR+jsDCcj%@K7~KE75oK%Ap7m`f136QqJWBsvi$Ant zPw`JRD!=ew<^OXP{ztL$6#R4$=ok2l{@>syLqbm(o(^;TVz@B)o52{g+UfrcdOXGd z`RMoy4O&3a7!2$m50X#efA-&hhbNl;2LHP^{}laapXztCu=#K3zix6rdRR{h{=6Oj hPA~xYJq7;l<|ro#{lfTK`#(BWS%?4t literal 20971 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

t7H96hwzWY=cr;s}Td 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 j_4E&$y_+b&_ORvkp}qSL^`&=G diff --git a/dmp-frontend/src/app/core/model/dmp/dmp-blueprint/dmp-blueprint.ts b/dmp-frontend/src/app/core/model/dmp/dmp-blueprint/dmp-blueprint.ts index d9f4f4f36..eda8fd506 100644 --- a/dmp-frontend/src/app/core/model/dmp/dmp-blueprint/dmp-blueprint.ts +++ b/dmp-frontend/src/app/core/model/dmp/dmp-blueprint/dmp-blueprint.ts @@ -18,6 +18,7 @@ export interface SectionDmpBlueprint { description: string; ordinal: number; fields: FieldInSection[]; + hasTemplates: boolean; descriptionTemplates?: DescriptionTemplatesInSection[]; } diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts index f85a1a0d3..b4b9496af 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts @@ -11,6 +11,7 @@ import { DmpProfileCriteriaComponent } from './listing/criteria/dmp-profile-crit import { DmpProfileListingComponent } from './listing/dmp-profile-listing.component'; import { NgxDropzoneModule } from "ngx-dropzone"; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { AutoCompleteModule } from "@app/library/auto-complete/auto-complete.module"; @NgModule({ imports: [ @@ -20,7 +21,8 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; ConfirmationDialogModule, DmpProfileRoutingModule, NgxDropzoneModule, - DragDropModule + DragDropModule, + AutoCompleteModule ], declarations: [ DmpProfileEditorComponent, diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-blueprint-editor.model.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-blueprint-editor.model.ts index a2ad7e5f2..c90354afd 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-blueprint-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-blueprint-editor.model.ts @@ -1,5 +1,8 @@ -import { FormBuilder, FormGroup } from "@angular/forms"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, FieldCategory, FieldInSection, SectionDmpBlueprint } from "@app/core/model/dmp/dmp-blueprint/dmp-blueprint"; +import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; +import { ValidationContext } from "@common/forms/validation/validation-context"; export class DmpBlueprintEditor { public id: string; @@ -8,6 +11,7 @@ export class DmpBlueprintEditor { public status: number; public created: Date; public modified: Date; + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); fromModel(item: DmpBlueprint): DmpBlueprintEditor { this.id = item.id; @@ -19,18 +23,30 @@ export class DmpBlueprintEditor { return this; } - buildForm(): FormGroup { + buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup { + if (context == null) { context = this.createValidationContext(); } const formGroup = new FormBuilder().group({ - id: [this.id], - label: [this.label], - status: [this.status], - created: [this.created], - modified: [this.modified] + id: [{ value: this.id, disabled: disabled }, context.getValidation('id')], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label')], + status: [{ value: this.status, disabled: disabled }, context.getValidation('status')], + created: [{ value: this.created, disabled: disabled }, context.getValidation('created')], + modified: [{ value: this.modified, disabled: disabled }, context.getValidation('modified')], }); formGroup.addControl('definition', this.definition.buildForm()); return formGroup; } + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseContext.validation.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] }); + baseContext.validation.push({ key: 'definition', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'definition')] }); + baseContext.validation.push({ key: 'created', validators: [] }); + baseContext.validation.push({ key: 'modified', validators: [] }); + return baseContext; + } + } export class DmpBlueprintDefinitionEditor { @@ -61,7 +77,9 @@ export class SectionDmpBlueprintEditor { public description: string; public ordinal: number; public fields: FieldInSectionEditor[] = new Array(); + public hasTemplates: boolean; public descriptionTemplates: DescriptionTemplatesInSectionEditor[] = new Array(); + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); fromModel(item: SectionDmpBlueprint): SectionDmpBlueprintEditor { this.id = item.id; @@ -69,16 +87,19 @@ export class SectionDmpBlueprintEditor { this.description = item.description; this.ordinal = item.ordinal; if (item.fields) { item.fields.map(x => this.fields.push(new FieldInSectionEditor().fromModel(x))); } + this.hasTemplates = item.hasTemplates; if (item.descriptionTemplates) { item.descriptionTemplates.map(x => this.descriptionTemplates.push(new DescriptionTemplatesInSectionEditor().fromModel(x))); } return this; } - buildForm(): FormGroup { + buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup { + if (context == null) { context = this.createValidationContext(); } const formGroup = new FormBuilder().group({ - id: [this.id], - label: [this.label], - description: [this.description], - ordinal: [this.ordinal] + id: [{ value: this.id, disabled: disabled }, context.getValidation('id')], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label')], + description: [{ value: this.description, disabled: disabled }, context.getValidation('description')], + ordinal: [{ value: this.ordinal, disabled: disabled }, context.getValidation('ordinal')], + hasTemplates: [{ value: this.hasTemplates, disabled: disabled }, context.getValidation('hasTemplates')] }); const formBuilder = new FormBuilder(); const fieldsFormArray = new Array(); @@ -95,6 +116,17 @@ export class SectionDmpBlueprintEditor { formGroup.addControl('descriptionTemplates', formBuilder.array(descriptionTemplatesFormArray)); return formGroup; } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseContext.validation.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] }); + baseContext.validation.push({ key: 'ordinal', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'ordinal')] }); + baseContext.validation.push({ key: 'hasTemplates', validators: [BackendErrorValidator(this.validationErrorModel, 'hasTemplates')] }); + baseContext.validation.push({ key: 'descriptionTemplates', validators: [BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] }); + return baseContext; + } } export class FieldInSectionEditor { @@ -106,6 +138,7 @@ export class FieldInSectionEditor { public description: string; public required: boolean; public ordinal: number; + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); fromModel(item: FieldInSection): FieldInSectionEditor { this.id = item.id; @@ -119,19 +152,33 @@ export class FieldInSectionEditor { return this; } - buildForm(): FormGroup { + buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup { + if (context == null) { context = this.createValidationContext(); } const formGroup = new FormBuilder().group({ - id: [this.id], - category: [this.category], - type: [this.type], - label: [this.label], - placeholder: [this.placeholder], - description: [this.description], - required: [this.required], - ordinal: [this.ordinal] + id: [{ value: this.id, disabled: disabled }, context.getValidation('id')], + category: [{ value: this.category, disabled: disabled }, context.getValidation('category')], + type: [{ value: this.type, disabled: disabled }, context.getValidation('type')], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label')], + placeholder: [{ value: this.placeholder, disabled: disabled }, context.getValidation('placeholder')], + description: [{ value: this.description, disabled: disabled }, context.getValidation('description')], + required: [{ value: this.required, disabled: disabled }, context.getValidation('required')], + ordinal: [{ value: this.ordinal, disabled: disabled }, context.getValidation('ordinal')] }); return formGroup; } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseContext.validation.push({ key: 'category', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'category')] }); + baseContext.validation.push({ key: 'type', validators: [BackendErrorValidator(this.validationErrorModel, 'type')] }); + baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseContext.validation.push({ key: 'placeholder', validators: [BackendErrorValidator(this.validationErrorModel, 'placeholder')] }); + baseContext.validation.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] }); + baseContext.validation.push({ key: 'required', validators: [BackendErrorValidator(this.validationErrorModel, 'required')] }); + baseContext.validation.push({ key: 'ordinal', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'ordinal')] }); + return baseContext; + } } export class DescriptionTemplatesInSectionEditor { @@ -140,6 +187,7 @@ export class DescriptionTemplatesInSectionEditor { public label: string; public minMultiplicity: number; public maxMultiplicity: number; + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); fromModel(item: DescriptionTemplatesInSection): DescriptionTemplatesInSectionEditor { this.id = item.id; @@ -150,16 +198,27 @@ export class DescriptionTemplatesInSectionEditor { return this; } - buildForm(): FormGroup { + buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup { + if (context == null) { context = this.createValidationContext(); } const formGroup = new FormBuilder().group({ - id: [this.id], - descriptionTemplateId: [this.descriptionTemplateId], - label: [this.label], - minMultiplicity: [this.minMultiplicity], - maxMultiplicity: [this.maxMultiplicity] + id: [{ value: this.id, disabled: disabled }, context.getValidation('id')], + descriptionTemplateId: [{ value: this.descriptionTemplateId, disabled: disabled }, context.getValidation('descriptionTemplateId')], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label')], + minMultiplicity: [{ value: this.minMultiplicity, disabled: disabled }, context.getValidation('minMultiplicity')], + maxMultiplicity: [{ value: this.maxMultiplicity, disabled: disabled }, context.getValidation('maxMultiplicity')] }); return formGroup; } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseContext.validation.push({ key: 'descriptionTemplateId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplateId')] }); + baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseContext.validation.push({ key: 'minMultiplicity', validators: [BackendErrorValidator(this.validationErrorModel, 'minMultiplicity')] }); + baseContext.validation.push({ key: 'maxMultiplicity', validators: [BackendErrorValidator(this.validationErrorModel, 'maxMultiplicity')] }); + return baseContext; + } } // export class ExtraFieldsInSectionEditor { diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html index 09ec4be95..65f6940c2 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html @@ -18,10 +18,10 @@

+ [disabled]="!this.isFormValid()" type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.FINALIZE' | translate }}
-
+ - + +
+
+ +
+
+ + + Section {{sectionIndex + 1}} + drag_indicator + +
+
+
+ + Section name + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Section description + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} +
- -
- -
-
- -
-
-
-
-
- {{fieldIndex + 1}} -
-
- drag_indicator -
-
-
- -
- - System Field - - -
-
- - Label - - -
-
- - Placeholder - - -
-
- - Description - - -
-
- Required -
-
- delete - {{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} -
-
- - -
- - Type - - - {{getExtraFieldTypeValue(extraFieldType)}} - - - -
-
- - Label - - -
-
- - Placeholder - - -
-
- - Description - - -
-
- - Required - -
-
- delete - {{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} -
-
- -
-
-
-
-
- -
- - - - - -
-
-
-
- - - - - - -
-
+ +
+ +
+
+ +
+
+
+
+
+ {{fieldIndex + 1}} +
+
+ drag_indicator +
+
+
+ +
+ + System Field + + +
+
+ + Label + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Placeholder + + +
+
+ + Description + + +
+
+ Required +
+
+ delete + {{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} +
+
+ + +
+ + Type + + + {{getExtraFieldTypeValue(extraFieldType)}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Label + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Placeholder + + +
+
+ + Description + + +
+
+ + Required + +
+
+ delete + {{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} +
+
+ +
+
+
+
+
+ +
+ + - --> -
-
-
- -
- delete - {{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} + +
+
+
+ + Description Templates + +
+
+
+ +
+
+
+ + Description Templates + + + + +
+
+
+ +
+ +
+
+
+ + Label + + +
+
+ + Min Multiplicity + + +
+
+ + Max Multiplicity + + +
+
+
+
+
+
+ +
+
+ +
+ delete + {{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}} +
-
- - + + - +
- +
@@ -386,7 +378,7 @@ -->
-
diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts index b8cf5cd85..5c2c65ad1 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts @@ -1,5 +1,5 @@ -import { AfterViewInit, Component } from '@angular/core'; +import { AfterViewInit, Component, ViewChild } from '@angular/core'; import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { DmpProfileFieldDataType } from '@app/core/common/enum/dmp-profile-field-type'; @@ -34,9 +34,11 @@ import { AvailableProfilesComponent } from '@app/ui/dmp/editor/available-profile import { DatasetPreviewDialogComponent } from '@app/ui/dmp/dataset-preview/dataset-preview-dialog.component'; import { CdkDragDrop, CdkDropList, CdkDrag, moveItemInArray } from '@angular/cdk/drag-drop'; import { DmpBlueprint, DmpBlueprintDefinition, ExtraFieldType, FieldCategory, SystemFieldType } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint'; -import { DmpBlueprintEditor } from './dmp-blueprint-editor.model'; +import { DescriptionTemplatesInSectionEditor, DmpBlueprintEditor, FieldInSectionEditor, SectionDmpBlueprintEditor } from './dmp-blueprint-editor.model'; import { Guid } from '@common/types/guid'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { DmpBlueprintListing } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint-listing'; +import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; @Component({ @@ -73,6 +75,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie {label: 'Access Rights', type: SystemFieldType.ACCESS_RIGHTS} ]; selectedSystemFields: string[] = []; + systemFieldListPerSection: Array> = new Array(); constructor( private dmpProfileService: DmpProfileService, @@ -112,12 +115,13 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie if (this.dmpProfileId != null) { this.isNew = false; - this.dmpProfileService.getSingle(this.dmpProfileId).pipe(map(data => data as DmpProfile)) + this.dmpProfileService.getSingleBlueprint(this.dmpProfileId).pipe(map(data => data as DmpBlueprint)) .pipe(takeUntil(this._destroyed)) .subscribe(data => { - this.dmpProfileModel = new DmpProfileEditorModel().fromModel(data); - this.formGroup = this.dmpProfileModel.buildForm(); - if (this.dmpProfileModel.status == DmpProfileStatus.Finalized) { + this.dmpBlueprintModel = new DmpBlueprintEditor().fromModel(data); + this.formGroup = this.dmpBlueprintModel.buildForm(); + this.buildSystemFields(); + if (this.dmpBlueprintModel.status == DmpProfileStatus.Finalized) { this.formGroup.disable(); this.viewOnly = true } @@ -133,6 +137,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie setTimeout(() => { // this.formGroup = this.dmpProfileModel.buildForm(); // this.addField(); + this.dmpBlueprintModel.status = DmpProfileStatus.Draft; this.formGroup = this.dmpBlueprintModel.buildForm(); }); this.breadCrumbs = observableOf([{ @@ -143,10 +148,20 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie } }); - this.initDmpBlueptintForm(); - } + buildSystemFields(){ + const sections = this.sectionsArray().controls.length; + for(let i = 0; i < sections; i++){ + let systemFieldsInSection = new Array(); + this.fieldsArray(i).controls.forEach((field) => { + if((field.get('category').value == FieldCategory.SYSTEM || field.get('category').value == 'SYSTEM')){ + systemFieldsInSection.push(this.fieldList.find(f => f.type == field.get('type').value).label); + } + }) + this.systemFieldListPerSection.push(systemFieldsInSection); + } + } filterProfiles(value: string): Observable { const request = new DataTableRequest(null, null, { fields: ['+label'] }); @@ -156,33 +171,17 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie return this._service.searchDMPProfiles(request); } - initDmpBlueptintForm(): void { - this.dmpBlueprintsFormGroup = this.fb.group({ - label: this.fb.control(''), - sections: this.fb.array([]) - }); - } - sectionsArray(): FormArray { - return this.dmpBlueprintsFormGroup.get('sections') as FormArray; - } - - initSection(ordinal: number): FormGroup { - return this.fb.group({ - id: this.fb.control(Guid.create().toString()), - label: this.fb.control(''), - description: this.fb.control(''), - ordinal: this.fb.control(ordinal), - fields: this.fb.array([]), - //systemFields: this.fb.array([]), - descriptionTemplates: this.fb.control(''), - // this.fb.array([this.initDescriptionTemplate()]), - //extraFields: this.fb.array([]) - }); + //return this.dmpBlueprintsFormGroup.get('sections') as FormArray; + return this.formGroup.get('definition').get('sections') as FormArray; } addSection(): void { - this.sectionsArray().push(this.initSection(this.sectionsArray().length)); + const section: SectionDmpBlueprintEditor = new SectionDmpBlueprintEditor(); + section.id = Guid.create().toString(); + section.ordinal = this.sectionsArray().length + 1; + section.hasTemplates = false; + this.sectionsArray().push(section.buildForm()); } removeSection(sectionIndex: number): void { @@ -192,22 +191,17 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie fieldsArray(sectionIndex: number): FormArray { return this.sectionsArray().at(sectionIndex).get('fields') as FormArray; } - - initField(fieldCategory: FieldCategory, fieldType?: number): FormGroup { - return this.fb.group({ - id: this.fb.control(Guid.create().toString()), - category: this.fb.control(fieldCategory), - label: this.fb.control(''), - placeholder: this.fb.control(''), - description: this.fb.control(''), - type: (isNullOrUndefined(fieldType)) ? this.fb.control('') : this.fb.control(fieldType), - required: (!isNullOrUndefined(fieldType) && (fieldType == 0 || fieldType == 1)) ? this.fb.control(true) : this.fb.control(false), - ordinal: this.fb.control('') - }); - } addField(sectionIndex: number, fieldCategory: FieldCategory, fieldType?: number): void { - this.fieldsArray(sectionIndex).push(this.initField(fieldCategory, fieldType)); + const field: FieldInSectionEditor = new FieldInSectionEditor(); + field.id = Guid.create().toString(); + field.ordinal = this.fieldsArray(sectionIndex).length + 1; + field.category = fieldCategory; + if(!isNullOrUndefined(fieldType)){ + field.type = fieldType + } + field.required = (!isNullOrUndefined(fieldType) && (fieldType == 0 || fieldType == 1)) ? true : false; + this.fieldsArray(sectionIndex).push(field.buildForm()); } removeField(sectionIndex: number, fieldIndex: number): void { @@ -231,7 +225,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie } addSystemField(sectionIndex: number, systemField?: SystemFieldType): void { - this.fieldsArray(sectionIndex).push(this.initField(FieldCategory.SYSTEM, systemField)); + this.addField(sectionIndex, FieldCategory.SYSTEM, systemField); } transfromEnumToString(type: SystemFieldType): string{ @@ -255,7 +249,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie for (let s in this.sectionsArray().controls) { if (i != sectionIndex) { for (let f of this.fieldsArray(i).controls) { - if (f.get('category').value == FieldCategory.SYSTEM && f.get('type').value == systemField) { + if ((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField) { return true; } } @@ -272,7 +266,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie removeSystemField(sectionIndex: number, systemField: SystemFieldType): void { let i = 0; for(let f of this.fieldsArray(sectionIndex).controls){ - if(f.get('category').value == FieldCategory.SYSTEM && f.get('type').value == systemField){ + if((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField){ this.fieldsArray(sectionIndex).removeAt(i); return; } @@ -284,15 +278,6 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie return this.sectionsArray().at(sectionIndex).get('descriptionTemplates') as FormArray; } - initDescriptionTemplate(): FormGroup { - return this.fb.group({ - descriptionTemplateId: this.fb.control(''), - label: this.fb.control(''), - minMultiplicity: this.fb.control(''), - maxMultiplicity: this.fb.control('') - }); - } - addDescriptionTemplate(descriptionTemplate, sectionIndex: number): void { this.descriptionTemplatesArray(sectionIndex).push(this.fb.group({ label: this.fb.control(descriptionTemplate.value) @@ -305,22 +290,10 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie extraFieldsArray(sectionIndex: number): FormArray { return this.sectionsArray().at(sectionIndex).get('extraFields') as FormArray; - } + } - initExtraField(): FormGroup { - return this.fb.group({ - id: this.fb.control(Guid.create().toString()), - label: this.fb.control(''), - placeholder: this.fb.control(''), - description: this.fb.control(''), - type: this.fb.control(''), - required: this.fb.control(false), - ordinal: this.fb.control('') - }); - } - addExtraField(sectionIndex: number): void { - this.fieldsArray(sectionIndex).push(this.initField(FieldCategory.EXTRA)); + this.addField(sectionIndex, FieldCategory.EXTRA); } removeExtraField(sectionIndex: number, fieldIndex: number): void { @@ -343,9 +316,6 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie } } - onSubmitTest(): void { - } - drop(event: CdkDragDrop, sectionIndex: number) { moveItemInArray(this.fieldsArray(sectionIndex).controls, event.previousIndex, event.currentIndex); moveItemInArray(this.fieldsArray(sectionIndex).value, event.previousIndex, event.currentIndex); @@ -371,86 +341,58 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie // this.dmpBlueprintsFormGroup.reset(); // } - canGoUp(index: number): boolean { - return index > 0; - } - - canGoDown(index: number): boolean { - return index < (this.sectionsArray().length - 1); - } - onRemoveTemplate(event, sectionIndex: number) { - // let found = false; - // const profiles = this.descriptionTemplatesArray(sectionIndex).value;//this.formGroup.get('profiles').value; - // this.formGroup.get('datasets')['controls'].forEach(element => { - // if (element.get('profile').value.id === event.id) { - // found = true; - // this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-REMOVE-TEMPLATE'), SnackBarNotificationLevel.Success); - // } - // }); - // if (found) { - // this.formGroup.get('profiles').setValue(profiles); - // 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' - // }; - - // } + const profiles = this.descriptionTemplatesArray(sectionIndex).controls; + const foundIndex = profiles.findIndex(profile => profile.get('descriptionTemplateId').value === event.id); + foundIndex !== -1 && this.descriptionTemplatesArray(sectionIndex).removeAt(foundIndex); } - onPreviewTemplate(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) { - let profiles = this.descriptionTemplatesArray(sectionIndex).value;//this.formGroup.get('profiles').value; - profiles.push(event); - this.descriptionTemplatesArray(sectionIndex).setValue(profiles);//this.formGroup.get('profiles').setValue(profiles); - 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' - }; - } - }); - } - onOptionSelected(sectionIndex: number){ - try{ - const profiles = this.descriptionTemplatesArray(sectionIndex).value as {id:string, label:string}[];//this.formGroup.get('profiles').value as {id:string, label:string}[]; - 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') - } + // onPreviewTemplate(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) { + // let profiles = this.sectionsArray().at(sectionIndex).get('descriptionTemplates').value;//this.formGroup.get('profiles').value; + // const profile: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor(); + // profile.id = Guid.create().toString(); + // profile.descriptionTemplateId = event.id; + // profile.label = event.label; + // profiles.push(profile.buildForm()); + // this.sectionsArray().at(sectionIndex).get('descriptionTemplates').setValue(profiles);//this.formGroup.get('profiles').setValue(profiles); + // 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' + // }; + // } + // }); + // } + + onOptionSelected(item, sectionIndex){ + const profile: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor(); + profile.id = Guid.create().toString(); + profile.descriptionTemplateId = item.id; + profile.label = item.label; + this.descriptionTemplatesArray(sectionIndex).push(profile.buildForm()); } formSubmit(): void { this.formService.touchAllFormFields(this.formGroup); if (!this.isFormValid()) { return; } + if(!this.hasDescriptionTemplates()) { + this.showValidationErrorsDialog(undefined, ["At least one section should have description templates."]); + return; + } this.onSubmit(); } @@ -458,8 +400,27 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie return this.formGroup.valid; } + hasDescriptionTemplates(): boolean { + const dmpBlueprint: DmpBlueprint = this.formGroup.value; + return (dmpBlueprint.definition.sections.filter(s => s.hasTemplates == true).length > 0) ? true : false; + } + + private showValidationErrorsDialog(projectOnly?: boolean, errmess?: string[]) { + + const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + disableClose: true, + autoFocus: false, + restoreFocus: false, + data: { + errorMessages:errmess, + projectOnly: projectOnly + }, + }); + + } + onSubmit(): void { - this.dmpProfileService.createDmp(this.formGroup.value) + this.dmpProfileService.createBlueprint(this.formGroup.value) .pipe(takeUntil(this._destroyed)) .subscribe( complete => this.onCallbackSuccess(),