added progress indication at DMP Editor.

This commit is contained in:
Diamantis Tziotzios 2024-04-15 18:19:56 +03:00
parent f05d629c14
commit be9b8df468
11 changed files with 284 additions and 89 deletions

View File

@ -1,19 +1,16 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template'; import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description } from '@app/core/model/description/description'; import { Description } from '@app/core/model/description/description';
import { DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
import { DescriptionService } from '@app/core/services/description/description.service'; import { DescriptionService } from '@app/core/services/description/description.service';
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
import { BaseComponent } from '@common/base/base.component'; import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { DeprecatedDescriptionTemplateDialog } from './dialog-description-template/deprecated-description-template-dialog.component'; import { DeprecatedDescriptionTemplateDialog } from './dialog-description-template/deprecated-description-template-dialog.component';
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
@Component({ @Component({
selector: 'app-description-base-fields-editor-component', selector: 'app-description-base-fields-editor-component',
@ -47,15 +44,17 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate); const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates); this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous; if (this.description?.descriptionTemplate != null) {
if (isPreviousVersion === true) { const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
if (this.description.status === DescriptionStatus.Draft) { if (isPreviousVersion === true) {
this.openDeprecatedDescriptionTemplateDialog(); if (this.description.status === DescriptionStatus.Draft) {
this.openDeprecatedDescriptionTemplateDialog();
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
}
} else { } else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate); this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
} }
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
} }
} }
@ -67,19 +66,19 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
}); });
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe( dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(
result => { result => {
if(result) { if (result) {
this.descriptionService.updateDescriptionTemplate({ this.descriptionService.updateDescriptionTemplate({
id: this.description.id, id: this.description.id,
hash: this.description.hash hash: this.description.hash
}) })
.subscribe( .subscribe(
result => { result => {
this.refresh.emit(result); this.refresh.emit(result);
}, },
error => console.error(error)); error => console.error(error));
} else { } else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate); this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
} }
}); });
} }
} }

View File

@ -111,13 +111,13 @@
<span class="material-icons">chevron_left</span> <span class="material-icons">chevron_left</span>
<div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div> <div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div>
</div> </div>
<div *ngIf="this.step < this.maxStep" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxStep, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()"> <div *ngIf="this.step < this.maxSteps" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxSteps, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()">
<div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div> <div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div>
<span class="material-icons">chevron_right</span> <span class="material-icons">chevron_right</span>
</div> </div>
</div> </div>
<div class="col-auto pr-0" *ngIf="this.step !== 0"> <div class="col-auto pr-0" *ngIf="this.step !== 0">
<!-- <app-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup" [isDmpEditor]="true"></app-form-progress-indication> --> <app-dmp-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup"></app-dmp-form-progress-indication>
</div> </div>
</div> </div>

View File

@ -88,7 +88,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)), filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])), getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
displayFn: (item: DmpBlueprint) => item.label, displayFn: (item: DmpBlueprint) => item.label,
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' '+ item.version, subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' ' + item.version,
titleFn: (item: DmpBlueprint) => item.label, titleFn: (item: DmpBlueprint) => item.label,
valueAssign: (item: DmpBlueprint) => item.id, valueAssign: (item: DmpBlueprint) => item.id,
}; };
@ -174,14 +174,14 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
try { try {
this.editorModel = data ? new DmpEditorModel().fromModel(data) : new DmpEditorModel(); this.editorModel = data ? new DmpEditorModel().fromModel(data) : new DmpEditorModel();
if (data) { if (data) {
if(data.descriptions){ if (data.descriptions) {
if (data.status == DmpStatus.Finalized) { if (data.status == DmpStatus.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized); data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else { } else {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled); data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
} }
} }
if(data.dmpDescriptionTemplates){ if (data.dmpDescriptionTemplates) {
data.dmpDescriptionTemplates = data.dmpDescriptionTemplates.filter(x => x.isActive === IsActive.Active); data.dmpDescriptionTemplates = data.dmpDescriptionTemplates.filter(x => x.isActive === IsActive.Active);
} }
@ -304,6 +304,11 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
// Steps // Steps
// //
// //
get maxSteps(): number {
return this.item?.blueprint?.definition?.sections?.length ?? 0;
}
changeStep(index: number) { changeStep(index: number) {
this.step = index; this.step = index;
this.resetScroll(); this.resetScroll();
@ -389,7 +394,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
DmpEditorModel.reApplyPropertiesValidators( DmpEditorModel.reApplyPropertiesValidators(
{ {
formGroup: this.formGroup, formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel validationErrorModel: this.editorModel.validationErrorModel,
blueprint: this.item.blueprint
} }
); );
this.formGroup.get('properties').get('contacts').markAsDirty(); this.formGroup.get('properties').get('contacts').markAsDirty();
@ -404,7 +410,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
DmpEditorModel.reApplyPropertiesValidators( DmpEditorModel.reApplyPropertiesValidators(
{ {
formGroup: this.formGroup, formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel validationErrorModel: this.editorModel.validationErrorModel,
blueprint: this.item.blueprint
} }
); );
this.formGroup.get('properties').get('contacts').markAsDirty(); this.formGroup.get('properties').get('contacts').markAsDirty();
@ -455,32 +462,32 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
}); });
} }
canAddDescription(section: DmpBlueprintDefinitionSection ): boolean{ canAddDescription(section: DmpBlueprintDefinitionSection): boolean {
if(section.hasTemplates){ if (section.hasTemplates) {
if (section.descriptionTemplates?.length > 0){ if (section.descriptionTemplates?.length > 0) {
const descriptions = this.descriptionsInSection(section.id) const descriptions = this.descriptionsInSection(section.id)
if (this.item.dmpDescriptionTemplates.filter(x => x.sectionId == section.id).length > descriptions.map(x => x.dmpDescriptionTemplate).length){ if (this.item.dmpDescriptionTemplates.filter(x => x.sectionId == section.id).length > descriptions.map(x => x.dmpDescriptionTemplate).length) {
return true; return true;
} }
let multiplicityValidResults :boolean[] = []; let multiplicityValidResults: boolean[] = [];
section.descriptionTemplates.forEach(sectionDescriptionTemplate => { section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
if (sectionDescriptionTemplate.maxMultiplicity != null){ if (sectionDescriptionTemplate.maxMultiplicity != null) {
const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0; const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0;
if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false); if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false);
else multiplicityValidResults.push(true); else multiplicityValidResults.push(true);
}else{ } else {
multiplicityValidResults.push(true); multiplicityValidResults.push(true);
} }
}) })
if(multiplicityValidResults.includes(true)) return true if (multiplicityValidResults.includes(true)) return true
else return false; else return false;
}else{ } else {
return true; return true;
} }
}else{ } else {
return false; return false;
} }
} }
@ -504,10 +511,10 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(groupId => { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(groupId => {
if (groupId) { if (groupId) {
let data = this.formGroup.get('descriptionTemplates').get(sectionId.toString()).value as Guid[]; let data = this.formGroup.get('descriptionTemplates').get(sectionId.toString()).value as Guid[];
if (data){ if (data) {
data.push(groupId); data.push(groupId);
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue(data); this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue(data);
} else{ } else {
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue([groupId]); this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue([groupId]);
} }
} }

View File

@ -1,11 +1,12 @@
import { FormArray, FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { FormArray, FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { DmpAccessType } from "@app/core/common/enum/dmp-access-type"; import { DmpAccessType } from "@app/core/common/enum/dmp-access-type";
import { DmpBlueprintFieldCategory } from "@app/core/common/enum/dmp-blueprint-field-category";
import { DmpContactType } from "@app/core/common/enum/dmp-contact-type"; import { DmpContactType } from "@app/core/common/enum/dmp-contact-type";
import { DmpStatus } from "@app/core/common/enum/dmp-status"; import { DmpStatus } from "@app/core/common/enum/dmp-status";
import { DmpUserRole } from "@app/core/common/enum/dmp-user-role"; import { DmpUserRole } from "@app/core/common/enum/dmp-user-role";
import { DmpUserType } from "@app/core/common/enum/dmp-user-type"; import { DmpUserType } from "@app/core/common/enum/dmp-user-type";
import { IsActive } from "@app/core/common/enum/is-active.enum"; import { IsActive } from "@app/core/common/enum/is-active.enum";
import { DmpBlueprint } from "@app/core/model/dmp-blueprint/dmp-blueprint"; import { DmpBlueprint, FieldInSection } from "@app/core/model/dmp-blueprint/dmp-blueprint";
import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist, DmpUser, DmpUserPersist } from "@app/core/model/dmp/dmp"; import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist, DmpUser, DmpUserPersist } from "@app/core/model/dmp/dmp";
import { DmpReference } from "@app/core/model/dmp/dmp-reference"; import { DmpReference } from "@app/core/model/dmp/dmp-reference";
import { ReferencePersist } from "@app/core/model/reference/reference"; import { ReferencePersist } from "@app/core/model/reference/reference";
@ -103,8 +104,8 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
const descriptionTemplatesFormGroup = this.formBuilder.group({}); const descriptionTemplatesFormGroup = this.formBuilder.group({});
(this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map( (this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map(
(item, index) => descriptionTemplatesFormGroup.addControl(item.toString(), (item, index) => descriptionTemplatesFormGroup.addControl(item.toString(),
new FormControl(this.descriptionTemplates?.filter(x => x.sectionId === item)?.filter(x => x.descriptionTemplateGroupId).map(x => x.descriptionTemplateGroupId) || [], context.getValidation('descriptionTemplates').validators)) new FormControl(this.descriptionTemplates?.filter(x => x.sectionId === item)?.filter(x => x.descriptionTemplateGroupId).map(x => x.descriptionTemplateGroupId) || [], context.getValidation('descriptionTemplates').validators))
); );
// // buildForm({ // // buildForm({
// // rootPath: `descriptionTemplates[${index}].` // // rootPath: `descriptionTemplates[${index}].`
@ -120,13 +121,16 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
const baseValidationArray: Validation[] = new Array<Validation>(); const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
baseValidationArray.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] }); baseValidationArray.push({ key: 'status', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] });
baseValidationArray.push({ key: 'properties', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'properties')] }); baseValidationArray.push({ key: 'properties', validators: [BackendErrorValidator(this.validationErrorModel, 'properties')] });
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] }); baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
baseValidationArray.push({ key: 'language', validators: [BackendErrorValidator(this.validationErrorModel, 'language')] }); 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: 'blueprint', validators: [BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
baseValidationArray.push({ key: 'accessType', validators: [BackendErrorValidator(this.validationErrorModel, 'accessType')] }); baseValidationArray.push({ key: 'accessType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'accessType')] });
baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] }); baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] });
baseValidationArray.push({ key: 'dmpDescriptionValidator', validators: [] });
baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, `users`)] }); baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, `users`)] });
baseValidationArray.push({ key: 'hash', validators: [] }); baseValidationArray.push({ key: 'hash', validators: [] });
@ -143,6 +147,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
static reApplyPropertiesValidators(params: { static reApplyPropertiesValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,
blueprint: DmpBlueprint
}): void { }): void {
const { formGroup, validationErrorModel } = params; const { formGroup, validationErrorModel } = params;
@ -150,20 +155,36 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
DmpPropertiesEditorModel.reapplyValidators({ DmpPropertiesEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup, formGroup: control as UntypedFormGroup,
rootPath: `properties.`, rootPath: `properties.`,
validationErrorModel: validationErrorModel validationErrorModel: validationErrorModel,
blueprint: params.blueprint
}); });
}
static reApplyDescriptionTemplateValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
}): void {
const { formGroup, validationErrorModel } = params;
const descriptionTemplates = formGroup?.get('descriptionTemplates') as UntypedFormGroup; const descriptionTemplates = formGroup?.get('descriptionTemplates') as UntypedFormGroup;
const keys = Object.keys(descriptionTemplates.value as Object); const keys = Object.keys(descriptionTemplates.value as Object);
keys.forEach((key) => { keys.forEach((key) => {
const control = descriptionTemplates?.get(key); const control = descriptionTemplates?.get(key);
DmpBlueprintValueEditorModel.reapplyValidators({ DmpDescriptionTemplateEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup, formGroup: control as UntypedFormGroup,
rootPath: `descriptionTemplates[${key}].`, rootPath: `descriptionTemplates[${key}].`,
validationErrorModel: validationErrorModel validationErrorModel: validationErrorModel
}) })
}); });
}
static reApplyUsersValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
}): void {
const { formGroup, validationErrorModel } = params;
(formGroup.get('users') as FormArray).controls?.forEach( (formGroup.get('users') as FormArray).controls?.forEach(
(control, index) => DmpUserEditorModel.reapplyValidators({ (control, index) => DmpUserEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup, formGroup: control as UntypedFormGroup,
@ -186,14 +207,16 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
fromModel(item: DmpProperties, dmpReferences: DmpReference[], dmpBlueprint: DmpBlueprint): DmpPropertiesEditorModel { fromModel(item: DmpProperties, dmpReferences: DmpReference[], dmpBlueprint: DmpBlueprint): DmpPropertiesEditorModel {
dmpBlueprint.definition.sections.forEach(section => { dmpBlueprint.definition.sections.forEach(section => {
section.fields?.forEach(field => { section.fields?.forEach(field => {
this.dmpBlueprintValues.set(field.id, new DmpBlueprintValueEditorModel(this.validationErrorModel).fromModel( if (field.category !== DmpBlueprintFieldCategory.System) {
{ this.dmpBlueprintValues.set(field.id, new DmpBlueprintValueEditorModel(this.validationErrorModel).fromModel(
fieldId: field.id, {
fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue, fieldId: field.id,
}, dmpReferences)); fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue,
}, dmpReferences, field));
}
}); });
}); });
if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel(this.validationErrorModel).fromModel(x))); } if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel(this.validationErrorModel).fromModel(x))); }
@ -228,8 +251,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
const dmpBlueprintValuesFormGroup = this.formBuilder.group({}); const dmpBlueprintValuesFormGroup = this.formBuilder.group({});
this.dmpBlueprintValues.forEach((value, key) => dmpBlueprintValuesFormGroup.addControl(key.toString(), value.buildForm({ this.dmpBlueprintValues.forEach((value, key) => dmpBlueprintValuesFormGroup.addControl(key.toString(), value.buildForm({
rootPath: `${rootPath}dmpBlueprintValues[${key}].` rootPath: `${rootPath}dmpBlueprintValues[${key}].`
})), context.getValidation('dmpBlueprintValues') })), context.getValidation('dmpBlueprintValues')
) )
formGroup.addControl('dmpBlueprintValues', dmpBlueprintValuesFormGroup); formGroup.addControl('dmpBlueprintValues', dmpBlueprintValuesFormGroup);
@ -254,7 +277,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
static reapplyValidators(params: { static reapplyValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,
rootPath: string rootPath: string,
blueprint: DmpBlueprint
}): void { }): void {
const { formGroup, rootPath, validationErrorModel } = params; const { formGroup, rootPath, validationErrorModel } = params;
@ -266,9 +290,10 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
DmpBlueprintValueEditorModel.reapplyValidators({ DmpBlueprintValueEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup, formGroup: control as UntypedFormGroup,
rootPath: `${rootPath}dmpBlueprintValues[${key}].`, rootPath: `${rootPath}dmpBlueprintValues[${key}].`,
validationErrorModel: validationErrorModel validationErrorModel: validationErrorModel,
isRequired: params.blueprint.definition.sections.flatMap(x => x.fields).find(x => x.id.toString() == key).required
}) })
}); });
(formGroup.get('contacts') as FormArray).controls?.forEach( (formGroup.get('contacts') as FormArray).controls?.forEach(
(control, index) => DmpContactEditorModel.reapplyValidators({ (control, index) => DmpContactEditorModel.reapplyValidators({
@ -284,6 +309,8 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
fieldId: Guid; fieldId: Guid;
fieldValue: string; fieldValue: string;
references: DmpReferencePersist[] = []; references: DmpReferencePersist[] = [];
isRequired: boolean = false;
category: DmpBlueprintFieldCategory;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -291,7 +318,7 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
) { } ) { }
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[]): DmpBlueprintValueEditorModel { fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[], field: FieldInSection): DmpBlueprintValueEditorModel {
this.fieldId = item.fieldId; this.fieldId = item.fieldId;
this.fieldValue = item.fieldValue; this.fieldValue = item.fieldValue;
this.references = dmpReferences?.filter(x => x.data.blueprintFieldId == this.fieldId && x.isActive == IsActive.Active).map(x => { this.references = dmpReferences?.filter(x => x.data.blueprintFieldId == this.fieldId && x.isActive == IsActive.Active).map(x => {
@ -311,6 +338,10 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
} }
}); });
this.isRequired = field.required;
if (this.isRequired) console.log(field);
this.category = field.category;
return this; return this;
} }
@ -323,28 +354,39 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
if (context == null) { if (context == null) {
context = DmpBlueprintValueEditorModel.createValidationContext({ context = DmpBlueprintValueEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel, validationErrorModel: this.validationErrorModel,
rootPath rootPath,
isRequired: this.isRequired
}); });
} }
return this.formBuilder.group({ const formGroup = this.formBuilder.group({
fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators], fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators],
fieldValue: [{ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators],
references: [{ value: this.references?.map(x => x.reference), disabled: disabled }, context.getValidation('references').validators],
}); });
switch (this.category) {
case DmpBlueprintFieldCategory.ReferenceType:
formGroup.addControl('references', new FormControl({ value: this.references?.map(x => x.reference), disabled: disabled }, context.getValidation('references').validators));
break;
case DmpBlueprintFieldCategory.System:
case DmpBlueprintFieldCategory.Extra:
formGroup.addControl('fieldValue', new FormControl({ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators));
break;
}
return formGroup;
} }
static createValidationContext(params: { static createValidationContext(params: {
rootPath?: string, rootPath?: string,
validationErrorModel: ValidationErrorModel validationErrorModel: ValidationErrorModel,
isRequired: boolean,
}): ValidationContext { }): ValidationContext {
const { rootPath = '', validationErrorModel } = params; const { rootPath = '', validationErrorModel } = params;
const baseContext: ValidationContext = new ValidationContext(); const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>(); const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'fieldId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldId`)] }); baseValidationArray.push({ key: 'fieldId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldId`)] });
baseValidationArray.push({ key: 'fieldValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] }); baseValidationArray.push({ key: 'fieldValue', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] });
baseValidationArray.push({ key: 'references', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] }); baseValidationArray.push({ key: 'references', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}references`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
@ -353,13 +395,15 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
static reapplyValidators(params: { static reapplyValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,
rootPath: string rootPath: string,
isRequired: boolean
}): void { }): void {
const { formGroup, rootPath, validationErrorModel } = params; const { formGroup, rootPath, validationErrorModel } = params;
const context = DmpBlueprintValueEditorModel.createValidationContext({ const context = DmpBlueprintValueEditorModel.createValidationContext({
rootPath, rootPath,
validationErrorModel validationErrorModel,
isRequired: params.isRequired
}); });
['fieldId', 'fieldValue', 'references'].forEach(keyField => { ['fieldId', 'fieldValue', 'references'].forEach(keyField => {
@ -375,7 +419,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
firstName: string; firstName: string;
lastName: string; lastName: string;
email: string; email: string;
contactType: DmpContactType= DmpContactType.Internal; contactType: DmpContactType = DmpContactType.Internal;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -384,11 +428,11 @@ export class DmpContactEditorModel implements DmpContactPersist {
) { } ) { }
fromModel(item: DmpContact): DmpContactEditorModel { fromModel(item: DmpContact): DmpContactEditorModel {
if(item?.user?.id) this.userId = item.user.id; if (item?.user?.id) this.userId = item.user.id;
this.firstName = item.firstName; this.firstName = item.firstName;
this.lastName = item.lastName; this.lastName = item.lastName;
this.email = item.email; this.email = item.email;
this.contactType = (item == null || this.userId != null) ? DmpContactType.Internal : DmpContactType.External; this.contactType = (item == null || this.userId != null) ? DmpContactType.Internal : DmpContactType.External;
return this; return this;
} }
@ -457,7 +501,7 @@ export class DmpUserEditorModel implements DmpUserPersist {
user: Guid; user: Guid;
role: DmpUserRole; role: DmpUserRole;
email: string; email: string;
userType: DmpUserType= DmpUserType.Internal; userType: DmpUserType = DmpUserType.Internal;
sectionId: Guid; sectionId: Guid;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -467,10 +511,10 @@ export class DmpUserEditorModel implements DmpUserPersist {
) { } ) { }
fromModel(item: DmpUser): DmpUserEditorModel { fromModel(item: DmpUser): DmpUserEditorModel {
if(item?.user?.id) this.user = item.user.id; if (item?.user?.id) this.user = item.user.id;
this.role = item.role; this.role = item.role;
// TODO this.email = item.email; // TODO this.email = item.email;
this.userType = (item == null || this.user != null) ? DmpUserType.Internal : DmpUserType.External; this.userType = (item == null || this.user != null) ? DmpUserType.Internal : DmpUserType.External;
this.sectionId = item.sectionId; this.sectionId = item.sectionId;
return this; return this;
@ -623,7 +667,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
if (context == null) { if (context == null) {
context = DmpDescriptionTemplateEditorModel.createValidationContext({ context = DmpDescriptionTemplateEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel, validationErrorModel: this.validationErrorModel,
rootPath rootPath: rootPath,
}); });
} }
@ -635,7 +679,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
static createValidationContext(params: { static createValidationContext(params: {
rootPath?: string, rootPath?: string,
validationErrorModel: ValidationErrorModel validationErrorModel: ValidationErrorModel,
}): ValidationContext { }): ValidationContext {
const { rootPath = '', validationErrorModel } = params; const { rootPath = '', validationErrorModel } = params;

View File

@ -1,15 +1,16 @@
import { DragDropModule } from '@angular/cdk/drag-drop';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormattingModule } from '@app/core/formatting.module'; import { FormattingModule } from '@app/core/formatting.module';
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module'; import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module';
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
import { CommonFormsModule } from '@common/forms/common-forms.module'; import { CommonFormsModule } from '@common/forms/common-forms.module';
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module';
import { CommonUiModule } from '@common/ui/common-ui.module'; import { CommonUiModule } from '@common/ui/common-ui.module';
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
import { DmpEditorComponent } from './dmp-editor.component'; import { DmpEditorComponent } from './dmp-editor.component';
import { DmpEditorRoutingModule } from './dmp-editor.routing'; import { DmpEditorRoutingModule } from './dmp-editor.routing';
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; import { DmpFormProgressIndicationModule } from './form-progress-indication/dmp-form-progress-indication.module';
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
@NgModule({ @NgModule({
imports: [ imports: [
@ -22,7 +23,8 @@ import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
AutoCompleteModule, AutoCompleteModule,
ReferenceFieldModule, ReferenceFieldModule,
DragDropModule, DragDropModule,
DmpUserFieldModule DmpUserFieldModule,
DmpFormProgressIndicationModule
], ],
declarations: [ declarations: [
DmpEditorComponent, DmpEditorComponent,

View File

@ -0,0 +1,5 @@
<div class="demo-progress-bar-container">
<div class="percentage d-flex justify-content-center">{{progressSoFar}} {{'GENERAL.PREPOSITIONS.OF' | translate}} {{total}}</div>
<mat-progress-bar class="form-progress-bar" [ngClass]="{'progress-bar': true}" mode="determinate" [value]="value"></mat-progress-bar>
<div class="percentage" [ngStyle]="{'padding-left': value ? value - 10 + '%' : 0 + '%' }">{{value}}%</div>
</div>

View File

@ -0,0 +1,16 @@
.percentage {
color: #212121;
opacity: 0.7;
font-weight: 400;
font-size: 0.875rem;
}
.progress-bar {
border-radius: 20px;
height: 11px;
}
::ng-deep .mat-progress-bar .mat-progress-bar-fill::after {
border-radius: 20px !important;
}

View File

@ -0,0 +1,104 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-dmp-form-progress-indication',
templateUrl: './dmp-form-progress-indication.component.html',
styleUrls: ['./dmp-form-progress-indication.component.scss']
})
export class DmpFormProgressIndicationComponent extends BaseComponent implements OnInit, OnChanges {
@Input() formGroup: UntypedFormGroup;
@Input() public progressValueAccuracy = 2;
progressSoFar: number;
total: number;
percent: number;
constructor(private visibilityRulesService: VisibilityRulesService) { super(); }
public value = 0;
ngOnInit() {
this.init();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.formGroup) {
this.init();
}
}
init() {
setTimeout(() => { this.calculateValueForProgressbar(); });
this.formGroup
.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(control => {
setTimeout(() => { this.calculateValueForProgressbar(); });
});
}
calculateValueForProgressbar() {
this.progressSoFar = this.countFormControlsValidForProgress(this.formGroup);
this.total = this.countFormControlsRequiredFieldsForTotal(this.formGroup);
this.percent = (this.progressSoFar / this.total) * 100;
this.value = Number.parseFloat(this.percent.toPrecision(this.progressValueAccuracy));
}
countFormControlsValidForProgress(formControl: AbstractControl): number {
let valueCurrent = 0;
if (formControl instanceof UntypedFormControl) {
if (this.controlRequired(formControl) && this.controlEnabled(formControl) && formControl.valid) {
valueCurrent++;
}
} else if (formControl instanceof UntypedFormGroup) {
Object.keys(formControl.controls).forEach(item => {
const control = formControl.get(item);
valueCurrent = valueCurrent + this.countFormControlsValidForProgress(control);
});
} else if (formControl instanceof UntypedFormArray) {
formControl.controls.forEach(item => {
valueCurrent = valueCurrent + this.countFormControlsValidForProgress(item);
});
}
return valueCurrent;
}
countFormControlsRequiredFieldsForTotal(formControl: AbstractControl, checkVisibility = false): number {
let valueCurrent = 0;
if (formControl instanceof UntypedFormControl) {
if (this.controlRequired(formControl) && this.controlEnabled(formControl)) {
valueCurrent++;
}
} else if (formControl instanceof UntypedFormGroup) {
if (!checkVisibility || (!formControl.get('id')?.value || (this.visibilityRulesService.isVisibleMap[formControl.get('id').value] ?? true))) {
Object.keys(formControl.controls).forEach(item => {
const control = formControl.get(item);
valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(control, checkVisibility);
});
}
} else if (formControl instanceof UntypedFormArray) {
formControl.controls.forEach(item => {
valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(item, checkVisibility);
});
}
return valueCurrent;
}
controlRequired(formControl: AbstractControl) {
if (formControl.validator) {
const validator = formControl.validator({} as AbstractControl);
if (validator && validator.required) {
return true;
}
} else { return false }
}
controlEnabled(formControl: AbstractControl) {
if (formControl.enabled) {
return true;
} else { return false }
}
}

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { DmpFormProgressIndicationComponent } from './dmp-form-progress-indication.component';
@NgModule({
imports: [
CommonUiModule,
CommonFormsModule
],
declarations: [
DmpFormProgressIndicationComponent
],
exports: [
DmpFormProgressIndicationComponent
]
})
export class DmpFormProgressIndicationModule { }

View File

@ -43,7 +43,7 @@
</div> </div>
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal"> <div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS.USER' | translate}}*</mat-label> <mat-label>{{'DMP-EDITOR.FIELDS.USER' | translate}}</mat-label>
<app-single-auto-complete [formControl]="user.get('user')" [hidePlaceholder]="true" [configuration]="userService.singleAutoCompleteDmpAssociatedUserConfiguration"></app-single-auto-complete> <app-single-auto-complete [formControl]="user.get('user')" [hidePlaceholder]="true" [configuration]="userService.singleAutoCompleteDmpAssociatedUserConfiguration"></app-single-auto-complete>
<mat-error *ngIf="user.get('user').hasError('backendError')">{{user.get('user').getError('backendError').message}}</mat-error> <mat-error *ngIf="user.get('user').hasError('backendError')">{{user.get('user').getError('backendError').message}}</mat-error>
<mat-error *ngIf="user.get('user').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="user.get('user').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -51,7 +51,7 @@
</div> </div>
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.External"> <div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS.EMAIL' | translate}}*</mat-label> <mat-label>{{'DMP-EDITOR.FIELDS.EMAIL' | translate}}</mat-label>
<input matInput type="text" name="email" [formControl]="user.get('email')"> <input matInput type="text" name="email" [formControl]="user.get('email')">
<mat-error *ngIf="user.get('email').hasError('backendError')">{{user.get('email').getError('backendError').message}}</mat-error> <mat-error *ngIf="user.get('email').hasError('backendError')">{{user.get('email').getError('backendError').message}}</mat-error>
<mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>

View File

@ -57,10 +57,10 @@ export class DmpUserFieldComponent extends BaseComponent implements OnInit {
removeUser(userIndex: number): void { removeUser(userIndex: number): void {
(this.form.get('users') as FormArray).removeAt(userIndex); (this.form.get('users') as FormArray).removeAt(userIndex);
DmpEditorModel.reApplyPropertiesValidators( DmpEditorModel.reApplyUsersValidators(
{ {
formGroup: this.form, formGroup: this.form,
validationErrorModel: this.validationErrorModel validationErrorModel: this.validationErrorModel,
} }
); );
this.form.get('users').markAsDirty(); this.form.get('users').markAsDirty();
@ -78,7 +78,7 @@ export class DmpUserFieldComponent extends BaseComponent implements OnInit {
moveItemInArray(usersFormArray.controls, event.previousIndex, event.currentIndex); moveItemInArray(usersFormArray.controls, event.previousIndex, event.currentIndex);
usersFormArray.updateValueAndValidity(); usersFormArray.updateValueAndValidity();
DmpEditorModel.reApplyPropertiesValidators( DmpEditorModel.reApplyUsersValidators(
{ {
formGroup: this.form, formGroup: this.form,
validationErrorModel: this.validationErrorModel validationErrorModel: this.validationErrorModel