Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

# Conflicts:
#	dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts
#	dmp-frontend/src/assets/i18n/en.json
This commit is contained in:
Diamantis Tziotzios 2024-01-31 11:37:28 +02:00
commit 95568c5f7c
17 changed files with 232 additions and 51 deletions

View File

@ -36,14 +36,17 @@ public class DmpPersist {
public static final String _properties = "properties"; public static final String _properties = "properties";
private String description; private String description;
public static final String _description = "description";
private String language; private String language;
public static final String _language = "language";
private UUID blueprint; private UUID blueprint;
public static final String _blueprint = "blueprint"; public static final String _blueprint = "blueprint";
private DmpAccessType accessType; private DmpAccessType accessType;
public static final String _accessType = "accessType";
// private List<DmpReferencePersist> references; // private List<DmpReferencePersist> references;
// //
@ -193,6 +196,14 @@ public class DmpPersist {
.on(DmpPersist._properties) .on(DmpPersist._properties)
.over(item.getProperties()) .over(item.getProperties())
.using(() -> this.validatorFactory.validator(DmpPropertiesPersist.DmpPropertiesPersistValidator.class).setStatus(item.getStatus())), .using(() -> this.validatorFactory.validator(DmpPropertiesPersist.DmpPropertiesPersistValidator.class).setStatus(item.getStatus())),
this.spec()
.iff(() -> item.getStatus() == DmpStatus.Finalized)
.must(() -> !this.isNull(item.getLanguage()))
.failOn(DmpPersist._language).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpPersist._language}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> item.getStatus() == DmpStatus.Finalized)
.must(() -> !this.isNull(item.getAccessType()))
.failOn(DmpPersist._accessType).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpPersist._accessType}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(() -> item.getStatus() == DmpStatus.Finalized) .iff(() -> item.getStatus() == DmpStatus.Finalized)
.must(() -> !this.isListNullOrEmpty(item.getDescriptionTemplates())) .must(() -> !this.isListNullOrEmpty(item.getDescriptionTemplates()))

View File

@ -13,8 +13,10 @@ import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
public class SectionPersist { public class SectionPersist {
@ -148,7 +150,11 @@ public class SectionPersist {
.iff(() -> !this.isListNullOrEmpty(item.getDescriptionTemplates())) .iff(() -> !this.isListNullOrEmpty(item.getDescriptionTemplates()))
.on(SectionPersist._descriptionTemplates) .on(SectionPersist._descriptionTemplates)
.over(item.getDescriptionTemplates()) .over(item.getDescriptionTemplates())
.using((itm) -> this.validatorFactory.validator(DescriptionTemplatePersist.DescriptionTemplatePersistValidator.class)) .using((itm) -> this.validatorFactory.validator(DescriptionTemplatePersist.DescriptionTemplatePersistValidator.class)),
this.spec()
.iff(() -> !this.isListNullOrEmpty(item.getDescriptionTemplates()))
.must(() -> item.getDescriptionTemplates().stream().map(DescriptionTemplatePersist::getDescriptionTemplateGroupId).distinct().collect(Collectors.toList()).size() == item.getDescriptionTemplates().size())
.failOn(SectionPersist._descriptionTemplates).failWith(messageSource.getMessage("Validation_Unique", new Object[]{SectionPersist._descriptionTemplates}, LocaleContextHolder.getLocale()))
); );
} }

View File

@ -5,22 +5,28 @@ import gr.cite.tools.validation.specification.Specification;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.errorcode.ErrorThesaurusProperties;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public class DmpContactPersist { public class DmpContactPersist {
private UUID userId; private UUID userId;
public static final String _userId = "userId";
private String firstName; private String firstName;
public static final String _firstName = "firstName";
private String lastName; private String lastName;
public static final String _lastName = "lastName";
private String email; private String email;
public static final String _email = "email";
public UUID getUserId() { public UUID getUserId() {
return userId; return userId;
@ -59,9 +65,11 @@ public class DmpContactPersist {
public static class DmpContactPersistValidator extends BaseValidator<DmpContactPersist> { public static class DmpContactPersistValidator extends BaseValidator<DmpContactPersist> {
public static final String ValidatorName = "DmpContactPersistValidator"; public static final String ValidatorName = "DmpContactPersistValidator";
private final MessageSource messageSource;
protected DmpContactPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors) { protected DmpContactPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors); super(conventionService, errors);
this.messageSource = messageSource;
} }
@Override @Override
@ -71,7 +79,24 @@ public class DmpContactPersist {
@Override @Override
protected List<Specification> specifications(DmpContactPersist item) { protected List<Specification> specifications(DmpContactPersist item) {
return new ArrayList<>(); return Arrays.asList(
this.spec()
.iff(() -> this.isEmpty(item.getEmail()) && this.isEmpty(item.getFirstName()) && this.isEmpty(item.getLastName()))
.must(() -> !this.isNull(item.getUserId()))
.failOn(DmpContactPersist._userId).failWith(messageSource.getMessage("Validation_Required", new Object[]{"user"}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> this.isNull(item.getUserId()))
.must(() -> !this.isEmpty(item.getEmail()))
.failOn(DmpContactPersist._email).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpContactPersist._email}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> this.isNull(item.getUserId()))
.must(() -> !this.isEmpty(item.getFirstName()))
.failOn(DmpContactPersist._firstName).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpContactPersist._firstName}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> this.isNull(item.getUserId()))
.must(() -> !this.isEmpty(item.getLastName()))
.failOn(DmpContactPersist._lastName).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpContactPersist._lastName}, LocaleContextHolder.getLocale()))
);
} }
} }

View File

@ -8,7 +8,9 @@ import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.model.persist.validation.StatusAware; import eu.eudat.model.persist.validation.StatusAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Arrays; import java.util.Arrays;
@ -49,12 +51,14 @@ public class DmpPropertiesPersist {
public static final String ValidatorName = "DmpPropertiesPersistValidator"; public static final String ValidatorName = "DmpPropertiesPersistValidator";
private final ValidatorFactory validatorFactory; private final ValidatorFactory validatorFactory;
private final MessageSource messageSource;
private DmpStatus status; private DmpStatus status;
protected DmpPropertiesPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory) { protected DmpPropertiesPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) {
super(conventionService, errors); super(conventionService, errors);
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.messageSource = messageSource;
} }
@Override @Override
@ -66,13 +70,16 @@ public class DmpPropertiesPersist {
protected List<Specification> specifications(DmpPropertiesPersist item) { protected List<Specification> specifications(DmpPropertiesPersist item) {
return Arrays.asList( return Arrays.asList(
this.mapSpec() this.mapSpec()
.iff(() -> this.status == DmpStatus.Finalized && !this.isNull(item.getDmpBlueprintValues())) .iff(() ->!this.isNull(item.getDmpBlueprintValues()))
.on(DmpPropertiesPersist._dmpBlueprintValues) .on(DmpPropertiesPersist._dmpBlueprintValues)
.over(item.getDmpBlueprintValues()) .over(item.getDmpBlueprintValues())
.mapKey((k) -> ((UUID)k).toString()) .mapKey((k) -> ((UUID)k).toString())
.using((itm) -> this.validatorFactory.validator(DmpBlueprintValuePersist.DmpBlueprintValuePersistValidator.class)), .using((itm) -> this.validatorFactory.validator(DmpBlueprintValuePersist.DmpBlueprintValuePersistValidator.class)),
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getContacts()))
.failOn(DmpPropertiesPersist._contacts).failWith(messageSource.getMessage("Validation_Required", new Object[]{DmpPropertiesPersist._contacts}, LocaleContextHolder.getLocale())),
this.navSpec() this.navSpec()
.iff(() -> this.status == DmpStatus.Finalized && !this.isListNullOrEmpty(item.getContacts())) .iff(() -> !this.isListNullOrEmpty(item.getContacts()))
.on(DmpPropertiesPersist._contacts) .on(DmpPropertiesPersist._contacts)
.over(item.getContacts()) .over(item.getContacts())
.using((itm) -> this.validatorFactory.validator(DmpContactPersist.DmpContactPersistValidator.class)) .using((itm) -> this.validatorFactory.validator(DmpContactPersist.DmpContactPersistValidator.class))

View File

@ -20,3 +20,4 @@ Validation_Required={0} is required
Validation_OverPosting=Too much info Validation_OverPosting=Too much info
Validation_MaxLength={0} too long Validation_MaxLength={0} too long
Validation_UnexpectedValue=Unexpected value in field {0} Validation_UnexpectedValue=Unexpected value in field {0}
Validation_Unique= {0} must be unique

View File

@ -0,0 +1,4 @@
export enum DmpContactType {
Internal = 0,
External = 1
}

View File

@ -45,7 +45,7 @@ export interface DmpBlueprintValue {
} }
export interface DmpContact { export interface DmpContact {
userId: Guid; user?: User;
firstName: string; firstName: string;
lastName: string; lastName: string;
email: string; email: string;

View File

@ -1,16 +1,26 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DescriptionStatus } from '@app/core/common/enum/description-status'; import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionTemplateFieldDataExternalDatasetType } from '@app/core/common/enum/description-template-field-data-external-dataset-type';
import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-template-field-type'; import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-template-field-type';
import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status'; import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status'; import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status'; import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status';
import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type';
import { DmpContactType } from '@app/core/common/enum/dmp-contact-type';
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role'; import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
import { EmailOverrideMode } from '@app/core/common/enum/email-override-mode'; import { EmailOverrideMode } from '@app/core/common/enum/email-override-mode';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { NotificationContactType } from '@app/core/common/enum/notification-contact-type';
import { NotificationDataType } from '@app/core/common/enum/notification-data-type'; import { NotificationDataType } from '@app/core/common/enum/notification-data-type';
import { NotificationInAppTracking } from '@app/core/common/enum/notification-inapp-tracking.enum';
import { NotificationNotifyState } from '@app/core/common/enum/notification-notify-state';
import { NotificationTemplateChannel } from '@app/core/common/enum/notification-template-channel'; import { NotificationTemplateChannel } from '@app/core/common/enum/notification-template-channel';
import { NotificationTemplateKind } from '@app/core/common/enum/notification-template-kind'; import { NotificationTemplateKind } from '@app/core/common/enum/notification-template-kind';
import { NotificationTrackingProcess } from '@app/core/common/enum/notification-tracking-process';
import { NotificationTrackingState } from '@app/core/common/enum/notification-tracking-state';
import { NotificationType } from '@app/core/common/enum/notification-type';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type'; import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type';
import { ReferenceType } from '@app/core/common/enum/reference-type'; import { ReferenceType } from '@app/core/common/enum/reference-type';
@ -25,15 +35,6 @@ import { DmpBlueprintExtraFieldDataType } from '../../common/enum/dmp-blueprint-
import { DmpBlueprintType } from '../../common/enum/dmp-blueprint-type'; import { DmpBlueprintType } from '../../common/enum/dmp-blueprint-type';
import { DmpStatus } from '../../common/enum/dmp-status'; import { DmpStatus } from '../../common/enum/dmp-status';
import { ValidationType } from '../../common/enum/validation-type'; import { ValidationType } from '../../common/enum/validation-type';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { NotificationType } from '@app/core/common/enum/notification-type';
import { NotificationInAppTracking } from '@app/core/common/enum/notification-inapp-tracking.enum';
import { NotificationContactType } from '@app/core/common/enum/notification-contact-type';
import { NotificationNotifyState } from '@app/core/common/enum/notification-notify-state';
import { NotificationTrackingState } from '@app/core/common/enum/notification-tracking-state';
import { NotificationTrackingProcess } from '@app/core/common/enum/notification-tracking-process';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { DescriptionTemplateFieldDataExternalDatasetType } from '@app/core/common/enum/description-template-field-data-external-dataset-type';
@Injectable() @Injectable()
export class EnumUtils { export class EnumUtils {
@ -395,6 +396,10 @@ export class EnumUtils {
} }
} }
public toDmpContactTypeString(value: DmpContactType): string {
switch (value) {
case DmpContactType.Internal: return this.language.instant('TYPES.DMP-CONTACT-TYPE.INTERNAL');
case DmpContactType.External: return this.language.instant('TYPES.DMP-CONTACT-TYPE.EXTERNAL');
}
}
} }

View File

@ -190,7 +190,7 @@
<div class="col-auto d-flex"><mat-icon [ngClass]="{'drag-handle-disabled': formGroup.disabled}" cdkDragHandle class="drag-handle">drag_indicator</mat-icon></div> <div class="col-auto d-flex"><mat-icon [ngClass]="{'drag-handle-disabled': formGroup.disabled}" cdkDragHandle class="drag-handle">drag_indicator</mat-icon></div>
<div class="col"> <div class="col">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}</mat-label> <mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.DESCRIPTION-TEMPLATE' | translate}}</mat-label>
<app-single-auto-complete [formControl]="descriptionTemplate.get('descriptionTemplateGroupId')" [hidePlaceholder]="true" [configuration]="descriptionTempalteGroupSingleAutocompleteConfiguration" (optionActionClicked)="onPreviewDescriptionTemplate($event, i)"></app-single-auto-complete> <app-single-auto-complete [formControl]="descriptionTemplate.get('descriptionTemplateGroupId')" [hidePlaceholder]="true" [configuration]="descriptionTempalteGroupSingleAutocompleteConfiguration" (optionActionClicked)="onPreviewDescriptionTemplate($event, i)"></app-single-auto-complete>
<mat-error *ngIf="descriptionTemplate.get('descriptionTemplateGroupId').hasError('backendError')">{{descriptionTemplate.get('descriptionTemplateGroupId').getError('backendError').message}}</mat-error> <mat-error *ngIf="descriptionTemplate.get('descriptionTemplateGroupId').hasError('backendError')">{{descriptionTemplate.get('descriptionTemplateGroupId').getError('backendError').message}}</mat-error>
<mat-error *ngIf="descriptionTemplate.get('descriptionTemplateGroupId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="descriptionTemplate.get('descriptionTemplateGroupId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -226,6 +226,7 @@
</button> </button>
</div> </div>
</div> </div>
<mat-error *ngIf="section.get('descriptionTemplates').hasError('backendError')">{{section.get('descriptionTemplates').getError('backendError').message}}</mat-error>
</div> </div>
</mat-card> </mat-card>

View File

@ -234,7 +234,7 @@ export class DmpBlueprintDefinitionSectionEditorModel implements DmpBlueprintDef
rootPath: `${rootPath}fields[${index}].`, rootPath: `${rootPath}fields[${index}].`,
disabled: disabled disabled: disabled
}) })
), context.getValidation('fields') ), context.getValidation('fields').validators
), ),
descriptionTemplates: this.formBuilder.array( descriptionTemplates: this.formBuilder.array(
(this.descriptionTemplates ?? []).map( (this.descriptionTemplates ?? []).map(
@ -244,7 +244,7 @@ export class DmpBlueprintDefinitionSectionEditorModel implements DmpBlueprintDef
rootPath: `${rootPath}descriptionTemplates[${index}].`, rootPath: `${rootPath}descriptionTemplates[${index}].`,
disabled: disabled disabled: disabled
}) })
), context.getValidation('descriptionTemplates') ), context.getValidation('descriptionTemplates').validators
) )
}); });
} }

View File

@ -179,19 +179,67 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.CONTACT"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.CONTACT">
<!-- <div class="contact-form"> <div class="row">
<div class="col">
<button mat-icon-button (click)="addContact()" [disabled]="formGroup.disabled">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
<div cdkDropList class="col-12" (cdkDropListDropped)="dropContacts($event)">
<div *ngFor="let contact of formGroup.get('properties').get('contacts').controls; let contactIndex=index;" cdkDrag class="row align-items-center" [cdkDragDisabled]="formGroup.disabled">
<div class="col-auto">
<span style="font-size: 15px;">{{contactIndex + 1}}</span>
</div>
<div class="col-auto d-flex"><mat-icon [ngClass]="{'drag-handle-disabled': formGroup.disabled}" cdkDragHandle class="drag-handle">drag_indicator</mat-icon></div>
<div class="col-auto">
<mat-button-toggle-group name="fontStyle" aria-label="Font Style" [formContol]= "contact.get('contactType')">
<div *ngFor="let contactType of dmpContactTypeEnumValues">
<mat-button-toggle class="lang-button" [value]="contactType">{{enumUtils.toDmpContactTypeString(contactType)}}</mat-button-toggle>
</div>
</mat-button-toggle-group>
</div>
<div class="col-auto d-flex" *ngIf="contact.get('contactType').value == dmpContactTypeEnum.Internal">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-select [formControl]="formGroup.get('properties').get('contacts')" placeholder="{{'DMP-EDITOR.FIELDS.CONTACT' | translate}}"> <mat-label>{{'DMP-EDITOR.FIELDS1.USER' | translate}}</mat-label>
<mat-option *ngFor="let vis of getAssociates()" [value]="vis.id"> <app-single-auto-complete [formControl]="contact.get('userId')" [hidePlaceholder]="true" [configuration]= "userService.singleAutocompleteConfiguration"></app-single-auto-complete>
{{vis.name | translate}} <mat-error *ngIf="contact.get('userId').hasError('backendError')">{{contact.get('userId').getError('backendError').message}}</mat-error>
</mat-option> <mat-error *ngIf="contact.get('userId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-select>
<mat-error *ngIf="formGroup.get('properties').get('contacts').hasError('backendError')">
{{formGroup.get('properties').get('contacts').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('properties').get('contacts').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> --> </div>
<div class="col-auto d-flex" *ngIf="contact.get('contactType').value == dmpContactTypeEnum.External">
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.FIRST-NAME' | translate}}</mat-label>
<input matInput type="text" name="firstName" [formControl]="contact.get('firstName')">
<mat-error *ngIf="contact.get('firstName').hasError('backendError')">{{contact.get('firstName').getError('backendError').message}}</mat-error>
<mat-error *ngIf="contact.get('firstName').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.LAST-NAME' | translate}}</mat-label>
<input matInput type="text" name="lastName" [formControl]="contact.get('lastName')">
<mat-error *ngIf="contact.get('lastName').hasError('backendError')">{{contact.get('lastName').getError('backendError').message}}</mat-error>
<mat-error *ngIf="contact.get('lastName').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS1.EMAIL' | translate}}</mat-label>
<input matInput type="text" name="email" [formControl]="contact.get('email')">
<mat-error *ngIf="contact.get('email').hasError('backendError')">{{contact.get('email').getError('backendError').message}}</mat-error>
<mat-error *ngIf="contact.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
<button mat-icon-button class="action-list-icon" matTooltip="{{'DMP-EDITOR.FIELDS.REMOVE-CONTACT' | translate}}" (click)="removeContact(contactIndex)" [disabled]="formGroup.disabled">
<mat-icon>delete</mat-icon>
</button>
</div>
<mat-error *ngIf="formGroup.get('properties').get('contacts').hasError('backendError')">{{formGroup.get('properties').get('contacts').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('properties').get('contacts').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</div>
</div> </div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.ACCESS_RIGHTS"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.ACCESS_RIGHTS">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
@ -204,7 +252,6 @@
<mat-error *ngIf="formGroup.get('accessType').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('accessType').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div>
<div *ngIf="field.category === dmpBlueprintSectionFieldCategoryEnum.EXTRA"> <div *ngIf="field.category === dmpBlueprintSectionFieldCategoryEnum.EXTRA">
<div *ngIf="field.dataType === dmpBlueprintExtraFieldDataTypeEnum.TEXT"> <div *ngIf="field.dataType === dmpBlueprintExtraFieldDataTypeEnum.TEXT">
<mat-form-field class="w-100"> <mat-form-field class="w-100">

View File

@ -412,3 +412,12 @@ a:hover {
font-size: 1rem; font-size: 1rem;
padding: 0.6em 0 1em 0 !important; padding: 0.6em 0 1em 0 !important;
} }
.drag-handle {
cursor: move;
color: var(--primary-color);
}
.drag-handle-disabled {
cursor: auto;
color: rgba(0, 0, 0, 0.38);;
}

View File

@ -38,10 +38,14 @@ import { LanguageInfo } from '@app/core/model/language-info';
import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type'; import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { FormArray, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service'; import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
import { LockPersist } from '@app/core/model/lock/lock.model'; import { LockPersist } from '@app/core/model/lock/lock.model';
import { LockTargetType } from '@app/core/common/enum/lock-target-type'; import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { UserService } from '@app/core/services/user/user.service';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { DmpContactType } from '@app/core/common/enum/dmp-contact-type';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
@Component({ @Component({
selector: 'app-dmp-editor', selector: 'app-dmp-editor',
@ -64,6 +68,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
referenceTypeEnum = ReferenceType; referenceTypeEnum = ReferenceType;
dmpAccessTypeEnum = DmpAccessType; dmpAccessTypeEnum = DmpAccessType;
dmpAccessTypeEnumValues = this.enumUtils.getEnumValues<DmpAccessType>(DmpAccessType); dmpAccessTypeEnumValues = this.enumUtils.getEnumValues<DmpAccessType>(DmpAccessType);
dmpContactTypeEnum = DmpContactType;
dmpContactTypeEnumValues = this.enumUtils.getEnumValues<DmpContactType>(DmpContactType);
protected get canDelete(): boolean { protected get canDelete(): boolean {
return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDmp); return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDmp);
@ -106,7 +112,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
// public visibilityRulesService: VisibilityRulesService, // public visibilityRulesService: VisibilityRulesService,
private languageInfoService: LanguageInfoService, private languageInfoService: LanguageInfoService,
private enumUtils: EnumUtils, private enumUtils: EnumUtils,
public descriptionTemplateService: DescriptionTemplateService public descriptionTemplateService: DescriptionTemplateService,
public userService: UserService
) { ) {
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService); super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService);
@ -227,9 +234,9 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
formSubmit(): void { formSubmit(): void {
this.formService.touchAllFormFields(this.formGroup); this.formService.touchAllFormFields(this.formGroup);
if (!this.isFormValid()) { // if (!this.isFormValid()) {
return; // return;
} // }
this.persistEntity(); this.persistEntity();
} }
@ -318,6 +325,42 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
this.prepareForm(dmp); this.prepareForm(dmp);
} }
//
//
// Contacts
//
//
addContact(): void {
const contactArray = this.formGroup.get('properties').get('contacts') as FormArray;
(this.formGroup.get('properties').get('contacts') as FormArray).push(this.editorModel.createChildContact(contactArray.length));
}
removeContact(contactIndex: number): void {
(this.formGroup.get('properties').get('contacts') as FormArray).removeAt(contactIndex);
DmpEditorModel.reApplyPropertiesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
);
this.formGroup.get('properties').get('contacts').markAsDirty();
}
dropContacts(event: CdkDragDrop<string[]>) {
const sectionsFormArray = (this.formGroup.get('properties').get('contacts') as FormArray);
moveItemInArray(sectionsFormArray.controls, event.previousIndex, event.currentIndex);
sectionsFormArray.updateValueAndValidity();
DmpEditorModel.reApplyPropertiesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
);
this.formGroup.get('properties').get('contacts').markAsDirty();
}
// //
// //

View File

@ -1,5 +1,6 @@
import { FormArray, FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { FormArray, FormControl, UntypedFormArray, 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 { 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 { 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 } from "@app/core/model/dmp-blueprint/dmp-blueprint";
@ -15,7 +16,7 @@ import { Guid } from "@common/types/guid";
export class DmpEditorModel extends BaseEditorModel implements DmpPersist { export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
label: string; label: string;
status: DmpStatus; status: DmpStatus;
properties: DmpPropertiesEditorModel = new DmpPropertiesEditorModel(); properties: DmpPropertiesEditorModel = new DmpPropertiesEditorModel(this.validationErrorModel);
description: String; description: String;
language: String; language: String;
blueprint: Guid; blueprint: Guid;
@ -121,6 +122,11 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
return baseContext; return baseContext;
} }
createChildContact(index: number): UntypedFormGroup {
const contact: DmpContactEditorModel = new DmpContactEditorModel(this.validationErrorModel);
return contact.buildForm({ rootPath: 'properties.contacts[' + index + '].' });
}
static reApplyPropertiesValidators(params: { static reApplyPropertiesValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,
@ -191,8 +197,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
this.validationErrorModel this.validationErrorModel
).fromModel(item).buildForm({ ).fromModel(item).buildForm({
rootPath: `${rootPath}contacts[${index}].` rootPath: `${rootPath}contacts[${index}].`
}), context.getValidation('contacts') })
) ), context.getValidation('contacts').validators
), ),
}); });
@ -331,6 +337,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
firstName: string; firstName: string;
lastName: string; lastName: string;
email: string; email: string;
contactType: DmpContactType;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -339,10 +346,11 @@ export class DmpContactEditorModel implements DmpContactPersist {
) { } ) { }
fromModel(item: DmpContact): DmpContactEditorModel { fromModel(item: DmpContact): DmpContactEditorModel {
this.userId = item.userId; 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 ? DmpContactType.Internal : (this.userId != null ? DmpContactType.Internal : DmpContactType.External);
return this; return this;
} }
@ -354,7 +362,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
}): UntypedFormGroup { }): UntypedFormGroup {
let { context = null, disabled = false, rootPath } = params ?? {} let { context = null, disabled = false, rootPath } = params ?? {}
if (context == null) { if (context == null) {
context = DmpReferenceEditorModel.createValidationContext({ context = DmpContactEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel, validationErrorModel: this.validationErrorModel,
rootPath rootPath
}); });
@ -365,6 +373,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
firstName: [{ value: this.firstName, disabled: disabled }, context.getValidation('firstName').validators], firstName: [{ value: this.firstName, disabled: disabled }, context.getValidation('firstName').validators],
lastName: [{ value: this.lastName, disabled: disabled }, context.getValidation('lastName').validators], lastName: [{ value: this.lastName, disabled: disabled }, context.getValidation('lastName').validators],
email: [{ value: this.email, disabled: disabled }, context.getValidation('email').validators], email: [{ value: this.email, disabled: disabled }, context.getValidation('email').validators],
contactType: [{ value: this.contactType, disabled: disabled }, context.getValidation('contactType').validators],
}); });
} }
@ -380,6 +389,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
baseValidationArray.push({ key: 'firstName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}firstName`)] }); baseValidationArray.push({ key: 'firstName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}firstName`)] });
baseValidationArray.push({ key: 'lastName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}lastName`)] }); baseValidationArray.push({ key: 'lastName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}lastName`)] });
baseValidationArray.push({ key: 'email', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}email`)] }); baseValidationArray.push({ key: 'email', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}email`)] });
baseValidationArray.push({ key: 'contactType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}contactType`)] });
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;

View File

@ -8,6 +8,7 @@ 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 { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module'; import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
import { DragDropModule } from '@angular/cdk/drag-drop';
@NgModule({ @NgModule({
imports: [ imports: [
@ -18,7 +19,8 @@ import { ReferenceFieldModule } from '@app/ui/reference/reference-field/referenc
DmpEditorRoutingModule, DmpEditorRoutingModule,
RichTextEditorModule, RichTextEditorModule,
AutoCompleteModule, AutoCompleteModule,
ReferenceFieldModule ReferenceFieldModule,
DragDropModule,
], ],
declarations: [ declarations: [
DmpEditorComponent, DmpEditorComponent,

View File

@ -5,6 +5,7 @@ import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, Dm
import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties } from '@app/core/model/dmp/dmp'; import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties } from '@app/core/model/dmp/dmp';
import { DmpReference, DmpReferenceData } from '@app/core/model/dmp/dmp-reference'; import { DmpReference, DmpReferenceData } from '@app/core/model/dmp/dmp-reference';
import { Reference } from '@app/core/model/reference/reference'; import { Reference } from '@app/core/model/reference/reference';
import { User } from '@app/core/model/user/user';
import { DmpService } from '@app/core/services/dmp/dmp.service'; import { DmpService } from '@app/core/services/dmp/dmp.service';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { BaseEditorResolver } from '@common/base/base-editor.resolver'; import { BaseEditorResolver } from '@common/base/base-editor.resolver';
@ -37,7 +38,7 @@ export class DmpEditorResolver extends BaseEditorResolver {
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldId)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldId)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldValue)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldValue)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.userId)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.user), nameof<User>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.firstName)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.firstName)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.lastName)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.lastName)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.email)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.email)].join('.'),

View File

@ -1611,6 +1611,7 @@
"FIELD-DATA-TYPE": "Data Type", "FIELD-DATA-TYPE": "Data Type",
"FIELD-REQUIRED": "Required", "FIELD-REQUIRED": "Required",
"DESCRIPTION-TEMPLATES": "Description Templates", "DESCRIPTION-TEMPLATES": "Description Templates",
"DESCRIPTION-TEMPLATE": "Description Template",
"DESCRIPTION-TEMPLATE-LABEL": "Label", "DESCRIPTION-TEMPLATE-LABEL": "Label",
"DESCRIPTION-TEMPLATE-MIN-MULTIPLICITY": "Min Multiplicity", "DESCRIPTION-TEMPLATE-MIN-MULTIPLICITY": "Min Multiplicity",
"DESCRIPTION-TEMPLATE-MAX-MULTIPLICITY": "Max Multiplicity", "DESCRIPTION-TEMPLATE-MAX-MULTIPLICITY": "Max Multiplicity",
@ -1741,7 +1742,11 @@
"PUBLICATION": "Publication Date", "PUBLICATION": "Publication Date",
"CONTACT": "Contact", "CONTACT": "Contact",
"COST": "Costs", "COST": "Costs",
"DESCRIPTIONS": "Descriptions" "DESCRIPTIONS": "Descriptions",
"USER": "User",
"FIRST-NAME": "First Name",
"LAST-NAME": "Last Name",
"EMAIL": "Email"
}, },
"ACTIONS1": { "ACTIONS1": {
"GO-TO-GRANT": "Go To DMP Grant", "GO-TO-GRANT": "Go To DMP Grant",
@ -2415,6 +2420,10 @@
"PRODUCED": "Produced dataset", "PRODUCED": "Produced dataset",
"REUSED": "Reused dataset", "REUSED": "Reused dataset",
"OTHER": "Other" "OTHER": "Other"
},
"DMP-CONTACT-TYPE": {
"INTERNAL": "Internal",
"EXTERNAL": "External"
} }
}, },
"ADDRESEARCHERS-EDITOR": { "ADDRESEARCHERS-EDITOR": {