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 { UntypedFormGroup } from '@angular/forms';
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 { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description } from '@app/core/model/description/description';
import { DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
import { DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
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 { takeUntil } from 'rxjs/operators';
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({
selector: 'app-description-base-fields-editor-component',
@ -47,15 +44,17 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
if (isPreviousVersion === true) {
if (this.description.status === DescriptionStatus.Draft) {
this.openDeprecatedDescriptionTemplateDialog();
if (this.description?.descriptionTemplate != null) {
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
if (isPreviousVersion === true) {
if (this.description.status === DescriptionStatus.Draft) {
this.openDeprecatedDescriptionTemplateDialog();
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
}
} else {
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(
result => {
if(result) {
if (result) {
this.descriptionService.updateDescriptionTemplate({
id: this.description.id,
hash: this.description.hash
})
.subscribe(
result => {
this.refresh.emit(result);
},
error => console.error(error));
.subscribe(
result => {
this.refresh.emit(result);
},
error => console.error(error));
} else {
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
}
});
});
}
}

View File

@ -111,13 +111,13 @@
<span class="material-icons">chevron_left</span>
<div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</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>
<span class="material-icons">chevron_right</span>
</div>
</div>
<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>

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)),
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
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,
valueAssign: (item: DmpBlueprint) => item.id,
};
@ -174,14 +174,14 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
try {
this.editorModel = data ? new DmpEditorModel().fromModel(data) : new DmpEditorModel();
if (data) {
if(data.descriptions){
if (data.descriptions) {
if (data.status == DmpStatus.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
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);
}
@ -304,6 +304,11 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
// Steps
//
//
get maxSteps(): number {
return this.item?.blueprint?.definition?.sections?.length ?? 0;
}
changeStep(index: number) {
this.step = index;
this.resetScroll();
@ -389,7 +394,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
DmpEditorModel.reApplyPropertiesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
validationErrorModel: this.editorModel.validationErrorModel,
blueprint: this.item.blueprint
}
);
this.formGroup.get('properties').get('contacts').markAsDirty();
@ -404,7 +410,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
DmpEditorModel.reApplyPropertiesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
validationErrorModel: this.editorModel.validationErrorModel,
blueprint: this.item.blueprint
}
);
this.formGroup.get('properties').get('contacts').markAsDirty();
@ -455,32 +462,32 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
});
}
canAddDescription(section: DmpBlueprintDefinitionSection ): boolean{
if(section.hasTemplates){
if (section.descriptionTemplates?.length > 0){
canAddDescription(section: DmpBlueprintDefinitionSection): boolean {
if (section.hasTemplates) {
if (section.descriptionTemplates?.length > 0) {
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;
}
let multiplicityValidResults :boolean[] = [];
let multiplicityValidResults: boolean[] = [];
section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
if (sectionDescriptionTemplate.maxMultiplicity != null){
if (sectionDescriptionTemplate.maxMultiplicity != null) {
const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0;
if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false);
else multiplicityValidResults.push(true);
}else{
} else {
multiplicityValidResults.push(true);
}
})
if(multiplicityValidResults.includes(true)) return true
if (multiplicityValidResults.includes(true)) return true
else return false;
}else{
} else {
return true;
}
}else{
} else {
return false;
}
}
@ -504,10 +511,10 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(groupId => {
if (groupId) {
let data = this.formGroup.get('descriptionTemplates').get(sectionId.toString()).value as Guid[];
if (data){
if (data) {
data.push(groupId);
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue(data);
} else{
} else {
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 { DmpBlueprintFieldCategory } from "@app/core/common/enum/dmp-blueprint-field-category";
import { DmpContactType } from "@app/core/common/enum/dmp-contact-type";
import { DmpStatus } from "@app/core/common/enum/dmp-status";
import { DmpUserRole } from "@app/core/common/enum/dmp-user-role";
import { DmpUserType } from "@app/core/common/enum/dmp-user-type";
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 { DmpReference } from "@app/core/model/dmp/dmp-reference";
import { ReferencePersist } from "@app/core/model/reference/reference";
@ -104,7 +105,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
const descriptionTemplatesFormGroup = this.formBuilder.group({});
(this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map(
(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({
// // rootPath: `descriptionTemplates[${index}].`
@ -120,13 +121,16 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
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: 'properties', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'properties')] });
baseValidationArray.push({ key: 'status', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] });
baseValidationArray.push({ key: 'properties', validators: [BackendErrorValidator(this.validationErrorModel, 'properties')] });
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
baseValidationArray.push({ key: 'language', validators: [BackendErrorValidator(this.validationErrorModel, 'language')] });
baseValidationArray.push({ key: 'blueprint', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
baseValidationArray.push({ key: 'accessType', validators: [BackendErrorValidator(this.validationErrorModel, 'accessType')] });
baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] });
baseValidationArray.push({ key: 'blueprint', validators: [BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
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: 'dmpDescriptionValidator', validators: [] });
baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, `users`)] });
baseValidationArray.push({ key: 'hash', validators: [] });
@ -143,6 +147,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
static reApplyPropertiesValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
blueprint: DmpBlueprint
}): void {
const { formGroup, validationErrorModel } = params;
@ -150,20 +155,36 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
DmpPropertiesEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
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 keys = Object.keys(descriptionTemplates.value as Object);
keys.forEach((key) => {
const control = descriptionTemplates?.get(key);
DmpBlueprintValueEditorModel.reapplyValidators({
DmpDescriptionTemplateEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
rootPath: `descriptionTemplates[${key}].`,
validationErrorModel: validationErrorModel
})
});
}
static reApplyUsersValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
}): void {
const { formGroup, validationErrorModel } = params;
(formGroup.get('users') as FormArray).controls?.forEach(
(control, index) => DmpUserEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
@ -189,11 +210,13 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
dmpBlueprint.definition.sections.forEach(section => {
section.fields?.forEach(field => {
this.dmpBlueprintValues.set(field.id, new DmpBlueprintValueEditorModel(this.validationErrorModel).fromModel(
{
fieldId: field.id,
fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue,
}, dmpReferences));
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,
}, dmpReferences, field));
}
});
});
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({});
this.dmpBlueprintValues.forEach((value, key) => dmpBlueprintValuesFormGroup.addControl(key.toString(), value.buildForm({
rootPath: `${rootPath}dmpBlueprintValues[${key}].`
})), context.getValidation('dmpBlueprintValues')
rootPath: `${rootPath}dmpBlueprintValues[${key}].`
})), context.getValidation('dmpBlueprintValues')
)
formGroup.addControl('dmpBlueprintValues', dmpBlueprintValuesFormGroup);
@ -254,7 +277,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
static reapplyValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
rootPath: string
rootPath: string,
blueprint: DmpBlueprint
}): void {
const { formGroup, rootPath, validationErrorModel } = params;
@ -266,7 +290,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
DmpBlueprintValueEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
rootPath: `${rootPath}dmpBlueprintValues[${key}].`,
validationErrorModel: validationErrorModel
validationErrorModel: validationErrorModel,
isRequired: params.blueprint.definition.sections.flatMap(x => x.fields).find(x => x.id.toString() == key).required
})
});
@ -284,6 +309,8 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
fieldId: Guid;
fieldValue: string;
references: DmpReferencePersist[] = [];
isRequired: boolean = false;
category: DmpBlueprintFieldCategory;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -291,7 +318,7 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
) { }
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[]): DmpBlueprintValueEditorModel {
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[], field: FieldInSection): DmpBlueprintValueEditorModel {
this.fieldId = item.fieldId;
this.fieldValue = item.fieldValue;
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;
}
@ -323,28 +354,39 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
if (context == null) {
context = DmpBlueprintValueEditorModel.createValidationContext({
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],
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: {
rootPath?: string,
validationErrorModel: ValidationErrorModel
validationErrorModel: ValidationErrorModel,
isRequired: boolean,
}): ValidationContext {
const { rootPath = '', validationErrorModel } = params;
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
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`)] });
baseValidationArray.push({ key: 'fieldValue', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] });
baseValidationArray.push({ key: 'references', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}references`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
baseContext.validation = baseValidationArray;
return baseContext;
@ -353,13 +395,15 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
static reapplyValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
rootPath: string
rootPath: string,
isRequired: boolean
}): void {
const { formGroup, rootPath, validationErrorModel } = params;
const context = DmpBlueprintValueEditorModel.createValidationContext({
rootPath,
validationErrorModel
validationErrorModel,
isRequired: params.isRequired
});
['fieldId', 'fieldValue', 'references'].forEach(keyField => {
@ -375,7 +419,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
firstName: string;
lastName: string;
email: string;
contactType: DmpContactType= DmpContactType.Internal;
contactType: DmpContactType = DmpContactType.Internal;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -384,11 +428,11 @@ export class DmpContactEditorModel implements DmpContactPersist {
) { }
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.lastName = item.lastName;
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;
}
@ -457,7 +501,7 @@ export class DmpUserEditorModel implements DmpUserPersist {
user: Guid;
role: DmpUserRole;
email: string;
userType: DmpUserType= DmpUserType.Internal;
userType: DmpUserType = DmpUserType.Internal;
sectionId: Guid;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -467,10 +511,10 @@ export class DmpUserEditorModel implements DmpUserPersist {
) { }
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;
// 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;
return this;
@ -623,7 +667,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
if (context == null) {
context = DmpDescriptionTemplateEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel,
rootPath
rootPath: rootPath,
});
}
@ -635,7 +679,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
static createValidationContext(params: {
rootPath?: string,
validationErrorModel: ValidationErrorModel
validationErrorModel: ValidationErrorModel,
}): ValidationContext {
const { rootPath = '', validationErrorModel } = params;

View File

@ -1,15 +1,16 @@
import { DragDropModule } from '@angular/cdk/drag-drop';
import { NgModule } from '@angular/core';
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 { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.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 { 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';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
import { DmpFormProgressIndicationModule } from './form-progress-indication/dmp-form-progress-indication.module';
@NgModule({
imports: [
@ -22,7 +23,8 @@ import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
AutoCompleteModule,
ReferenceFieldModule,
DragDropModule,
DmpUserFieldModule
DmpUserFieldModule,
DmpFormProgressIndicationModule
],
declarations: [
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 class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
<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>
<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>
@ -51,7 +51,7 @@
</div>
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
<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')">
<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>

View File

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