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

This commit is contained in:
Efstratios Giannopoulos 2024-01-16 12:28:36 +02:00
commit 1828e28485
37 changed files with 865 additions and 811 deletions

View File

@ -40,6 +40,9 @@ public abstract class BaseValidator<T> extends AbstractValidator<T> {
protected Boolean isEmpty(String value) { protected Boolean isEmpty(String value) {
return this.conventionService.isNullOrEmpty(value); return this.conventionService.isNullOrEmpty(value);
} }
protected Boolean isListNullOrEmpty(List<?> value) {
return this.conventionService.isListNullOrEmpty(value);
}
protected Boolean isNull(Object value) { protected Boolean isNull(Object value) {
return value == null; return value == null;
} }

View File

@ -70,6 +70,7 @@ public class DmpDescriptionTemplateBuilder extends BaseBuilder<DmpDescriptionTem
DmpDescriptionTemplate m = new DmpDescriptionTemplate(); DmpDescriptionTemplate m = new DmpDescriptionTemplate();
if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._id))) m.setId(d.getId()); if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._sectionId))) m.setSectionId(d.getSectionId()); if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._sectionId))) m.setSectionId(d.getSectionId());
if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._descriptionTemplateGroupId))) m.setDescriptionTemplateGroupId(d.getDescriptionTemplateGroupId());
if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._createdAt))) m.setCreatedAt(d.getCreatedAt()); if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._hash))) m.setHash(this.hashValue(d.getUpdatedAt())); if (fields.hasField(this.asIndexer(DmpDescriptionTemplate._hash))) m.setHash(this.hashValue(d.getUpdatedAt()));

View File

@ -186,9 +186,6 @@ public class ReferencePersist {
.iff(() -> !this.isEmpty(item.getReference())) .iff(() -> !this.isEmpty(item.getReference()))
.must(() -> this.lessEqualLength(item.getReference(), ReferenceEntity._referenceLength)) .must(() -> this.lessEqualLength(item.getReference(), ReferenceEntity._referenceLength))
.failOn(ReferencePersist._reference).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{ReferencePersist._reference}, LocaleContextHolder.getLocale())), .failOn(ReferencePersist._reference).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{ReferencePersist._reference}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getAbbreviation()))
.failOn(ReferencePersist._abbreviation).failWith(messageSource.getMessage("Validation_Required", new Object[]{ReferencePersist._abbreviation}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(() -> !this.isEmpty(item.getAbbreviation())) .iff(() -> !this.isEmpty(item.getAbbreviation()))
.must(() -> this.lessEqualLength(item.getAbbreviation(), ReferenceEntity._abbreviationLength)) .must(() -> this.lessEqualLength(item.getAbbreviation(), ReferenceEntity._abbreviationLength))

View File

@ -47,7 +47,7 @@ public class TenantDepositConfigPersist {
@Override @Override
protected List<Specification> specifications(TenantDepositConfigPersist item) { protected List<Specification> specifications(TenantDepositConfigPersist item) {
return Collections.singletonList( return Collections.singletonList(
this.refSpec() this.navSpec()
.iff(() -> !this.isNull(item.getSources())) .iff(() -> !this.isNull(item.getSources()))
.on(TenantDepositConfigPersist._sources) .on(TenantDepositConfigPersist._sources)
.over(item.getSources()) .over(item.getSources())

View File

@ -47,7 +47,7 @@ public class TenantFileTransformersConfigPersist {
@Override @Override
protected List<Specification> specifications(TenantFileTransformersConfigPersist item) { protected List<Specification> specifications(TenantFileTransformersConfigPersist item) {
return Collections.singletonList( return Collections.singletonList(
this.refSpec() this.navSpec()
.iff(() -> !this.isNull(item.getSources())) .iff(() -> !this.isNull(item.getSources()))
.on(TenantFileTransformersConfigPersist._sources) .on(TenantFileTransformersConfigPersist._sources)
.over(item.getSources()) .over(item.getSources())

View File

@ -112,7 +112,7 @@ public class TenantSourcePersist {
.must(() -> !this.isEmpty(item.getUrl())) .must(() -> !this.isEmpty(item.getUrl()))
.failOn(TenantSourcePersist._url).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantSourcePersist._url}, LocaleContextHolder.getLocale())), .failOn(TenantSourcePersist._url).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantSourcePersist._url}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.must(() -> !this.isNull(item.getCodes())) .must(() -> !this.isListNullOrEmpty(item.getCodes()))
.failOn(TenantSourcePersist._codes).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantSourcePersist._codes}, LocaleContextHolder.getLocale())), .failOn(TenantSourcePersist._codes).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantSourcePersist._codes}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.must(() -> !this.isEmpty(item.getIssuerUrl())) .must(() -> !this.isEmpty(item.getIssuerUrl()))

View File

@ -213,7 +213,7 @@ public class ReferenceServiceImpl implements ReferenceService {
models.addAll(externalModels); models.addAll(externalModels);
models = models.stream().filter(x -> x.getLabel().toLowerCase().contains(lookup.getLike().toLowerCase())).collect(Collectors.toList()); if (!this.conventionService.isNullOrEmpty(lookup.getLike())) { models = models.stream().filter(x -> x.getLabel().toLowerCase().contains(lookup.getLike().toLowerCase())).collect(Collectors.toList()); }
models.sort(Comparator.comparing(Reference::getLabel)); models.sort(Comparator.comparing(Reference::getLabel));
if (lookup.getPage() != null && !lookup.getPage().isEmpty()){ if (lookup.getPage() != null && !lookup.getPage().isEmpty()){

View File

@ -7,7 +7,7 @@ storage:
- type: Main - type: Main
basePath: ./storage/main basePath: ./storage/main
static-files: static-files:
externalUrls: externalUrls/ExternalUrls.xml externalUrls: dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml
semantics: Semantics.json semantics: Semantics.json
h2020Template: documents/h2020.docx h2020Template: documents/h2020.docx
h2020DescriptionTemplate: documents/h2020_dataset.docx h2020DescriptionTemplate: documents/h2020_dataset.docx

View File

@ -0,0 +1,4 @@
export enum DescriptionTemplateVersionStatus {
Current = 0,
Previous = 1
}

View File

@ -1,9 +1,14 @@
import { BaseEntity } from "@common/base/base-entity.model"; import { BaseEntity } from "@common/base/base-entity.model";
import { Reference } from "../reference/reference"; import { Reference } from "../reference/reference";
import { Dmp } from "./dmp"; import { Dmp } from "./dmp";
import { Guid } from "@common/types/guid";
export interface DmpReference extends BaseEntity { export interface DmpReference extends BaseEntity {
dmp?: Dmp; dmp?: Dmp;
reference?: Reference; reference?: Reference;
data: string; data: DmpReferenceData;
}
export interface DmpReferenceData {
blueprintFieldId: Guid;
} }

View File

@ -17,7 +17,7 @@ export interface Dmp extends BaseEntity {
version: number; version: number;
status: DmpStatus; status: DmpStatus;
versionStatus: DmpVersionStatus; versionStatus: DmpVersionStatus;
properties: string; properties: DmpProperties;
groupId: String; groupId: String;
description: String; description: String;
finalizedAt: Date; finalizedAt: Date;
@ -34,6 +34,24 @@ export interface Dmp extends BaseEntity {
entityDois: EntityDoi[]; entityDois: EntityDoi[];
} }
export interface DmpProperties {
dmpBlueprintValues: DmpBlueprintValue[];
contacts: DmpContact[];
}
export interface DmpBlueprintValue {
fieldId: Guid;
fieldValue: string;
}
export interface DmpContact {
userId: Guid;
firstName: string;
lastName: string;
email: string;
}
export interface DmpUser extends BaseEntity { export interface DmpUser extends BaseEntity {
dmp: Dmp; dmp: Dmp;
user: User; user: User;
@ -54,18 +72,39 @@ export interface DmpDescriptionTemplate extends BaseEntity {
export interface DmpPersist extends BaseEntityPersist { export interface DmpPersist extends BaseEntityPersist {
label: string; label: string;
status: DmpStatus; status: DmpStatus;
properties: string; properties: DmpPropertiesPersist;
description: String; description: String;
language: String; language: String;
blueprint: Guid; blueprint: Guid;
accessType: DmpAccessType; accessType: DmpAccessType;
references: DmpReferencePersist[];
descriptionTemplates: DmpDescriptionTemplatePersist[]; descriptionTemplates: DmpDescriptionTemplatePersist[];
} }
export interface DmpReferencePersist extends BaseEntityPersist { export interface DmpPropertiesPersist {
dmpBlueprintValues: DmpBlueprintValuePersist[];
contacts: DmpContactPersist[];
}
export interface DmpBlueprintValuePersist {
fieldId: Guid;
fieldValue: string;
references: DmpReferencePersist[];
}
export interface DmpContactPersist {
userId: Guid;
firstName: string;
lastName: string;
email: string;
}
export interface DmpReferencePersist {
reference?: ReferencePersist; reference?: ReferencePersist;
data?: string; data?: DmpReferenceDataPersist;
}
export interface DmpReferenceDataPersist {
blueprintFieldId: Guid;
} }
export interface DmpDescriptionTemplatePersist extends BaseEntityPersist { export interface DmpDescriptionTemplatePersist extends BaseEntityPersist {

View File

@ -2,6 +2,7 @@ import { Lookup } from '@common/model/lookup';
import { Guid } from '@common/types/guid'; import { Guid } from '@common/types/guid';
import { DescriptionTemplateStatus } from '../common/enum/description-template-status'; import { DescriptionTemplateStatus } from '../common/enum/description-template-status';
import { IsActive } from '../common/enum/is-active.enum'; import { IsActive } from '../common/enum/is-active.enum';
import { DescriptionTemplateVersionStatus } from '../common/enum/description-template-version-status';
export class DescriptionTemplateLookup extends Lookup implements DescriptionTemplateFilter { export class DescriptionTemplateLookup extends Lookup implements DescriptionTemplateFilter {
ids: Guid[]; ids: Guid[];
@ -10,6 +11,8 @@ export class DescriptionTemplateLookup extends Lookup implements DescriptionTemp
isActive: IsActive[]; isActive: IsActive[];
typeIds: Guid[]; typeIds: Guid[];
statuses: DescriptionTemplateStatus[]; statuses: DescriptionTemplateStatus[];
groupIds: Guid[];
versionStatuses: DescriptionTemplateVersionStatus[];
constructor() { constructor() {
super(); super();
@ -23,4 +26,6 @@ export interface DescriptionTemplateFilter {
isActive: IsActive[]; isActive: IsActive[];
typeIds: Guid[]; typeIds: Guid[];
statuses: DescriptionTemplateStatus[]; statuses: DescriptionTemplateStatus[];
groupIds: Guid[];
versionStatuses: DescriptionTemplateVersionStatus[];
} }

View File

@ -16,6 +16,8 @@ import { catchError, map } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof'; import { nameof } from 'ts-simple-nameof';
import { ConfigurationService } from '../configuration/configuration.service'; import { ConfigurationService } from '../configuration/configuration.service';
import { BaseHttpV2Service } from '../http/base-http-v2.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service';
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status';
@Injectable() @Injectable()
export class DescriptionTemplateService { export class DescriptionTemplateService {
@ -126,4 +128,46 @@ export class DescriptionTemplateService {
if (like) { lookup.like = this.filterService.transformLike(like); } if (like) { lookup.like = this.filterService.transformLike(like); }
return lookup; return lookup;
} }
//
// Description Tempalte Group Autocomplete Commons
//
// tslint:disable-next-line: member-ordering
descriptionTempalteGroupSingleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
initialItems: (data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup()).pipe(map(x => x.items)),
filterFn: (searchQuery: string, data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery)).pipe(map(x => x.items)),
getSelectedItem: (selectedItem: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
displayFn: (item: DescriptionTemplate) => item.label,
titleFn: (item: DescriptionTemplate) => item.label,
valueAssign: (item: DescriptionTemplate) => item.groupId,
};
// tslint:disable-next-line: member-ordering
descriptionTempalteGroupMultipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = {
initialItems: (excludedItems: any[], data?: any) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)),
getSelectedItems: (selectedItems: any[]) => this.query(this.buildDescriptionTempalteGroupAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)),
displayFn: (item: DescriptionTemplate) => item.label,
titleFn: (item: DescriptionTemplate) => item.label,
valueAssign: (item: DescriptionTemplate) => item.groupId,
};
public buildDescriptionTempalteGroupAutocompleteLookup(like?: string, excludedIds?: Guid[], groupIds?: Guid[]): DescriptionTemplateLookup {
const lookup: DescriptionTemplateLookup = new DescriptionTemplateLookup();
lookup.page = { size: 100, offset: 0 };
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
if (groupIds && groupIds.length > 0) { lookup.groupIds = groupIds; }
lookup.isActive = [IsActive.Active];
lookup.versionStatuses = [DescriptionTemplateVersionStatus.Current];
lookup.statuses = [DescriptionTemplateStatus.Finalized];
lookup.project = {
fields: [
nameof<DescriptionTemplate>(x => x.id),
nameof<DescriptionTemplate>(x => x.label)
]
};
lookup.order = { items: [nameof<DescriptionTemplate>(x => x.label)] };
if (like) { lookup.like = this.filterService.transformLike(like); }
return lookup;
}
} }

View File

@ -46,9 +46,6 @@ export class DescriptionService {
const options: HttpParamsOptions = { fromObject: { f: reqFields } }; const options: HttpParamsOptions = { fromObject: { f: reqFields } };
let params: BaseHttpParams = new BaseHttpParams(options); let params: BaseHttpParams = new BaseHttpParams(options);
params.interceptorContext = {
excludedInterceptors: [InterceptorType.AuthToken]
};
return this.http return this.http
.get<Description>(url, { params: params }).pipe( .get<Description>(url, { params: params }).pipe(
@ -57,10 +54,16 @@ export class DescriptionService {
getPublicSingle(id: Guid, reqFields: string[] = []): Observable<Description> { getPublicSingle(id: Guid, reqFields: string[] = []): Observable<Description> {
const url = `${this.apiBase}/public/${id}`; const url = `${this.apiBase}/public/${id}`;
const options = { params: { f: reqFields } }; const options: HttpParamsOptions = { fromObject: { f: reqFields } };
let params: BaseHttpParams = new BaseHttpParams(options);
params.interceptorContext = {
excludedInterceptors: [InterceptorType.AuthToken]
};
return this.http return this.http
.get<Description>(url, options).pipe( .get<Description>(url, { params: params }).pipe(
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }

View File

@ -3,7 +3,7 @@ import { IsActive } from '@app/core/common/enum/is-active.enum';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type'; import { 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';
import { DmpReference } from '@app/core/model/dmp/dmp-reference'; import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { Reference, ReferencePersist } from '@app/core/model/reference/reference'; import { Definition, Field, Reference, ReferencePersist } from '@app/core/model/reference/reference';
import { ReferenceSearchDefinitionLookup, ReferenceSearchLookup } from '@app/core/query/reference-search.lookup'; import { ReferenceSearchDefinitionLookup, ReferenceSearchLookup } from '@app/core/query/reference-search.lookup';
import { ReferenceLookup } from '@app/core/query/reference.lookup'; import { ReferenceLookup } from '@app/core/query/reference.lookup';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
@ -70,31 +70,31 @@ export class ReferenceService {
} }
// //
// Autocomplete Commons // Autocomplete Commons - Query
// //
public getSingleAutocompleteConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): SingleAutoCompleteConfiguration { public getSingleAutocompleteQueryConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): SingleAutoCompleteConfiguration {
return { return {
initialItems: (data?: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes)).pipe(map(x => x.items)), initialItems: (data?: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes)).pipe(map(x => x.items)),
filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes, searchQuery)).pipe(map(x => x.items)), filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, searchQuery)).pipe(map(x => x.items)),
getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes, null, null, [selectedItem])).pipe(map(x => x.items[0])), getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, null, null, [selectedItem])).pipe(map(x => x.items[0])),
displayFn: (item: Reference) => item.label, displayFn: (item: Reference) => item.label,
titleFn: (item: Reference) => item.label, titleFn: (item: Reference) => item.label,
valueAssign: (item: Reference) => item.id, valueAssign: (item: Reference) => item.id,
}; };
}; };
public getMultipleAutoCompleteConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): MultipleAutoCompleteConfiguration { public getMultipleAutoCompleteQueryConfiguration(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[]): MultipleAutoCompleteConfiguration {
return { return {
initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteLookup(types, sourceTypes, null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteLookup(types, sourceTypes, searchQuery, excludedItems)).pipe(map(x => x.items)), filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, searchQuery, excludedItems)).pipe(map(x => x.items)),
getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteLookup(types, sourceTypes, null, null, selectedItems)).pipe(map(x => x.items)), getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteQueryLookup(types, sourceTypes, null, null, selectedItems)).pipe(map(x => x.items)),
displayFn: (item: Reference) => item.label, displayFn: (item: Reference) => item.label,
titleFn: (item: Reference) => item.label, titleFn: (item: Reference) => item.label,
valueAssign: (item: Reference) => item.id, valueAssign: (item: Reference) => item.id,
}; };
} }
private buildAutocompleteLookup(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[], like?: string, excludedIds?: Guid[], ids?: Guid[]): ReferenceLookup { private buildAutocompleteQueryLookup(types?: ReferenceType[], sourceTypes?: ReferenceSourceType[], like?: string, excludedIds?: Guid[], ids?: Guid[]): ReferenceLookup {
const lookup: ReferenceLookup = new ReferenceLookup(); const lookup: ReferenceLookup = new ReferenceLookup();
lookup.page = { size: 100, offset: 0 }; lookup.page = { size: 100, offset: 0 };
if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
@ -113,6 +113,56 @@ export class ReferenceService {
return lookup; return lookup;
} }
//
// Autocomplete Commons - Search
//
public getSingleAutocompleteSearchConfiguration(type: ReferenceType): SingleAutoCompleteConfiguration {
return {
initialItems: (data?: any) => this.search(this.buildAutocompleteSearchLookup(type)).pipe(map(x => x)),
filterFn: (searchQuery: string, data?: any) => this.search(this.buildAutocompleteSearchLookup(type, searchQuery)).pipe(map(x => x)),
getSelectedItem: (selectedItem: any) => this.search(this.buildAutocompleteSearchLookup(type)).pipe(map(x => x[0])),
displayFn: (item: Reference) => item.label,
titleFn: (item: Reference) => item.label,
valueAssign: (item: Reference) => item.id,
};
};
public getMultipleAutoCompleteSearchConfiguration(type: ReferenceType): MultipleAutoCompleteConfiguration {
return {
initialItems: (excludedItems: any[], data?: any) => this.search(this.buildAutocompleteSearchLookup(type, null)).pipe(map(x => x)),
filterFn: (searchQuery: string, excludedItems: any[]) => this.search(this.buildAutocompleteSearchLookup(type, searchQuery)).pipe(map(x => x)),
getSelectedItems: (selectedItems: any[]) => this.search(this.buildAutocompleteSearchLookup(type, null)).pipe(map(x => x)),
displayFn: (item: Reference) => item.label,
titleFn: (item: Reference) => item.label,
valueAssign: (item: Reference) => item,
};
}
private buildAutocompleteSearchLookup(type: ReferenceType, like?: string): ReferenceSearchLookup {
const lookup: ReferenceSearchLookup = new ReferenceSearchLookup();
lookup.page = { size: 100, offset: 0 };
lookup.project = {
fields: [
nameof<Reference>(x => x.id),
nameof<Reference>(x => x.label),
nameof<Reference>(x => x.type),
nameof<Reference>(x => x.description),
[nameof<Reference>(x => x.definition), nameof<Definition>(x => x.fields), nameof<Field>(x => x.code)].join('.'),
[nameof<Reference>(x => x.definition), nameof<Definition>(x => x.fields), nameof<Field>(x => x.dataType)].join('.'),
[nameof<Reference>(x => x.definition), nameof<Definition>(x => x.fields), nameof<Field>(x => x.value)].join('.'),
nameof<Reference>(x => x.reference),
nameof<Reference>(x => x.abbreviation),
nameof<Reference>(x => x.source),
nameof<Reference>(x => x.sourceType),
]
};
lookup.type = type;
lookup.order = { items: [nameof<Reference>(x => x.label)] };
if (like) { lookup.like = this.filterService.transformLike(like); }
return lookup;
}
// //
// //
// UI Helpers // UI Helpers

View File

@ -32,6 +32,7 @@ import { NotificationContactType } from '@app/core/common/enum/notification-cont
import { NotificationNotifyState } from '@app/core/common/enum/notification-notify-state'; import { NotificationNotifyState } from '@app/core/common/enum/notification-notify-state';
import { NotificationTrackingState } from '@app/core/common/enum/notification-tracking-state'; import { NotificationTrackingState } from '@app/core/common/enum/notification-tracking-state';
import { NotificationTrackingProcess } from '@app/core/common/enum/notification-tracking-process'; import { NotificationTrackingProcess } from '@app/core/common/enum/notification-tracking-process';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
@Injectable() @Injectable()
export class EnumUtils { export class EnumUtils {
@ -386,4 +387,12 @@ export class EnumUtils {
case NotificationTrackingProcess.OMITTED: return this.language.instant('TYPES.NOTIFICATION-TRACKING-PROCESS.OMITTED'); case NotificationTrackingProcess.OMITTED: return this.language.instant('TYPES.NOTIFICATION-TRACKING-PROCESS.OMITTED');
} }
} }
public toDmpAccessTypeString(value: DmpAccessType): string {
switch (value) {
case DmpAccessType.Public: return this.language.instant('TYPES.DMP-ACCESS-TYPE.PUBLIC');
case DmpAccessType.Restricted: return this.language.instant('TYPES.DMP-ACCESS-TYPE.RESTRICTED');
}
}
} }

View File

@ -16,7 +16,7 @@
</div> </div>
<div class="col-auto"> <div class="col-auto">
<button mat-raised-button class="create-btn" *ngIf="authService.hasPermission(authService.permissionEnum.EditDescriptionTemplateType)" [routerLink]="['/dmp-blueprint/new']"> <button mat-raised-button class="create-btn" *ngIf="authService.hasPermission(authService.permissionEnum.EditDescriptionTemplateType)" [routerLink]="['/dmp-blueprints/new']">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
{{'DMP-BLUEPRINT-LISTING.CREATE-DMP-BLUEPRINT' | translate}} {{'DMP-BLUEPRINT-LISTING.CREATE-DMP-BLUEPRINT' | translate}}
</button> </button>

View File

@ -33,8 +33,8 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label> <mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
<input matInput type="text" name="code" [formControl]="formGroup.get('code')" required> <input matInput type="text" name="code" [formControl]="formGroup.get('code')" required>
<mat-error *ngIf="formGroup.get('code').hasError('required')"> <mat-error *ngIf="formGroup.get('code').hasError('backendError')">{{formGroup.get('code').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-4" *ngIf="!isNew"> <div class="col-4" *ngIf="!isNew">
@ -45,24 +45,24 @@
{{languageCode}} {{languageCode}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('code').hasError('required')"> <mat-error *ngIf="formGroup.get('code').hasError('backendError')">{{formGroup.get('code').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-4"> <div class="col-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.ORDINAL' | translate}}</mat-label> <mat-label>{{'LANGUAGE-EDITOR.FIELDS.ORDINAL' | translate}}</mat-label>
<input matInput type="text" name="ordinal" [formControl]="formGroup.get('ordinal')" required> <input matInput type="text" name="ordinal" [formControl]="formGroup.get('ordinal')" required>
<mat-error *ngIf="formGroup.get('ordinal').hasError('required')"> <mat-error *ngIf="formGroup.get('ordinal').hasError('backendError')">{{formGroup.get('ordinal').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('ordinal').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12"> <div class="col-12">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.PAYLOAD' | translate}}</mat-label> <mat-label>{{'LANGUAGE-EDITOR.FIELDS.PAYLOAD' | translate}}</mat-label>
<input matInput type="text" name="payload" [formControl]="formGroup.get('payload')"> <input matInput type="text" name="payload" [formControl]="formGroup.get('payload')">
<mat-error *ngIf="formGroup.get('payload').hasError('required')"> <mat-error *ngIf="formGroup.get('payload').hasError('backendError')">{{formGroup.get('payload').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('payload').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<h9 class="col-12">{{'LANGUAGE-EDITOR.FIELDS.OVERRIDE' | translate}} <h9 class="col-12">{{'LANGUAGE-EDITOR.FIELDS.OVERRIDE' | translate}}
<mat-checkbox (change)="overrideFromFile($event, formGroup.get('code').value)"></mat-checkbox> <mat-checkbox (change)="overrideFromFile($event, formGroup.get('code').value)"></mat-checkbox>

View File

@ -33,8 +33,8 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'REFERENCE-EDITOR.FIELDS.LABEL' | translate}}</mat-label> <mat-label>{{'REFERENCE-EDITOR.FIELDS.LABEL' | translate}}</mat-label>
<input matInput type="text" name="label" [formControl]="formGroup.get('label')" required> <input matInput type="text" name="label" [formControl]="formGroup.get('label')" required>
<mat-error *ngIf="formGroup.get('label').hasError('required')"> <mat-error *ngIf="formGroup.get('label').hasError('backendError')">{{formGroup.get('label').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('label').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-4"> <div class="col-4">
@ -44,9 +44,9 @@
<mat-option *ngFor="let type of referenceTypeEnum" [value]="type"> <mat-option *ngFor="let type of referenceTypeEnum" [value]="type">
{{enumUtils.toReferenceTypeString(type)}} {{enumUtils.toReferenceTypeString(type)}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('type').hasError('required')"> <mat-error *ngIf="formGroup.get('type').hasError('backendError')">{{formGroup.get('type').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12"> <div class="col-12">
@ -55,9 +55,8 @@
<rich-text-editor-component [parentFormGroup]="formGroup" [controlName]="'description'" [placeholder]="'REFERENCE-EDITOR.FIELDS.DESCRIPTION-PLACEHOLDER'" [wrapperClasses]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'required' : ''" [editable]="formGroup.controls['description'].status !== 'DISABLED'"> <rich-text-editor-component [parentFormGroup]="formGroup" [controlName]="'description'" [placeholder]="'REFERENCE-EDITOR.FIELDS.DESCRIPTION-PLACEHOLDER'" [wrapperClasses]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'required' : ''" [editable]="formGroup.controls['description'].status !== 'DISABLED'">
</rich-text-editor-component> </rich-text-editor-component>
<div [class]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'visible' : 'invisible'" class="mat-form-field formGroup-field-subscript-wrapper"> <div [class]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'visible' : 'invisible'" class="mat-form-field formGroup-field-subscript-wrapper">
<mat-error> <mat-error *ngIf="formGroup.get('description').hasError('backendError')">{{formGroup.get('description').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED'| translate}} <mat-error>{{'GENERAL.VALIDATION.REQUIRED'| translate}}</mat-error>
</mat-error>
</div> </div>
</div> </div>
</div> </div>
@ -65,8 +64,8 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'REFERENCE-EDITOR.FIELDS.SOURCE' | translate}}</mat-label> <mat-label>{{'REFERENCE-EDITOR.FIELDS.SOURCE' | translate}}</mat-label>
<input matInput type="text" name="source" [formControl]="formGroup.get('source')" required> <input matInput type="text" name="source" [formControl]="formGroup.get('source')" required>
<mat-error *ngIf="formGroup.get('source').hasError('required')"> <mat-error *ngIf="formGroup.get('source').hasError('backendError')">{{formGroup.get('source').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('source').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-4"> <div class="col-4">
@ -76,25 +75,25 @@
<mat-option *ngFor="let sourceType of referenceSourceTypeEnum" [value]="sourceType"> <mat-option *ngFor="let sourceType of referenceSourceTypeEnum" [value]="sourceType">
{{enumUtils.toReferenceSourceTypeString(sourceType)}} {{enumUtils.toReferenceSourceTypeString(sourceType)}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('sourceType').hasError('required')"> <mat-error *ngIf="formGroup.get('sourceType').hasError('backendError')">{{formGroup.get('sourceType').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('sourceType').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'REFERENCE-EDITOR.FIELDS.REFERENCE' | translate}}</mat-label> <mat-label>{{'REFERENCE-EDITOR.FIELDS.REFERENCE' | translate}}</mat-label>
<input matInput type="text" name="reference" [formControl]="formGroup.get('reference')" required> <input matInput type="text" name="reference" [formControl]="formGroup.get('reference')" required>
<mat-error *ngIf="formGroup.get('reference').hasError('required')"> <mat-error *ngIf="formGroup.get('reference').hasError('backendError')">{{formGroup.get('reference').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('reference').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-4"> <div class="col-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'REFERENCE-EDITOR.FIELDS.ABBREVIATION' | translate}}</mat-label> <mat-label>{{'REFERENCE-EDITOR.FIELDS.ABBREVIATION' | translate}}</mat-label>
<input matInput type="text" name="abbreviation" [formControl]="formGroup.get('abbreviation')"> <input matInput type="text" name="abbreviation" [formControl]="formGroup.get('abbreviation')">
<mat-error *ngIf="formGroup.get('abbreviation').hasError('required')"> <mat-error *ngIf="formGroup.get('abbreviation').hasError('backendError')">{{formGroup.get('abbreviation').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('abbreviation').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@ -121,8 +120,8 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'REFERENCE-EDITOR.FIELDS.CODE' | translate}}</mat-label> <mat-label>{{'REFERENCE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
<input matInput type="text" name="code" [formControl]="field.get('code')" required> <input matInput type="text" name="code" [formControl]="field.get('code')" required>
<mat-error *ngIf="field.get('code').hasError('required')"> <mat-error *ngIf="field.get('code').hasError('backendError')">{{field.get('code').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="field.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
@ -132,17 +131,17 @@
<mat-option *ngFor="let dataType of referenceFieldDataTypeEnum" [value]="dataType"> <mat-option *ngFor="let dataType of referenceFieldDataTypeEnum" [value]="dataType">
{{enumUtils.toReferenceFieldDataTypeString(dataType)}} {{enumUtils.toReferenceFieldDataTypeString(dataType)}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="field.get('dataType').hasError('required')"> <mat-error *ngIf="field.get('dataType').hasError('backendError')">{{field.get('dataType').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="field.get('dataType').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'REFERENCE-EDITOR.FIELDS.VALUE' | translate}}</mat-label> <mat-label>{{'REFERENCE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput type="text" name="value" [formControl]="field.get('value')" required> <input matInput type="text" name="value" [formControl]="field.get('value')" required>
<mat-error *ngIf="field.get('value').hasError('required')"> <mat-error *ngIf="field.get('value').hasError('backendError')">{{field.get('value').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="field.get('value').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>

View File

@ -190,12 +190,19 @@ export class ReferenceEditorComponent extends BaseEditor<ReferenceEditorModel, R
// fields // fields
// //
addField(): void { addField(): void {
const field: FieldEditorModel = new FieldEditorModel(); (this.formGroup.get('definition').get('fields') as FormArray).push(this.editorModel.createChildField((this.formGroup.get('definition').get('fields') as FormArray).length));
(this.formGroup.get('definition').get('fields') as FormArray).push(field.buildForm());
} }
removeField(fieldIndex: number): void { removeField(fieldIndex: number): void {
(this.formGroup.get('definition').get('fields') as FormArray).removeAt(fieldIndex); (this.formGroup.get('definition').get('fields') as FormArray).removeAt(fieldIndex);
ReferenceEditorModel.reApplyDefinitionFieldsValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
)
this.formGroup.get('definition').get('fields').markAsDirty();
} }

View File

@ -1,4 +1,4 @@
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
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";
@ -76,6 +76,25 @@ export class ReferenceEditorModel extends BaseEditorModel implements ReferencePe
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
createChildField(index: number): UntypedFormGroup {
const field: FieldEditorModel = new FieldEditorModel(this.validationErrorModel);
return field.buildForm({ rootPath: 'definition.fields[' + index + '].' });
}
static reApplyDefinitionFieldsValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
}): void {
const { formGroup, validationErrorModel } = params;
const control = formGroup?.get('definition');
DefinitionEditorModel.reapplyFieldsValidators({
formArray: control.get('fields') as UntypedFormArray,
rootPath: `definition.`,
validationErrorModel: validationErrorModel
});
}
} }
export class DefinitionEditorModel implements DefinitionPersist { export class DefinitionEditorModel implements DefinitionPersist {
@ -133,6 +152,21 @@ export class DefinitionEditorModel implements DefinitionPersist {
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
static reapplyFieldsValidators(params: {
formArray: UntypedFormArray,
validationErrorModel: ValidationErrorModel,
rootPath: string
}): void {
const { validationErrorModel, rootPath, formArray } = params;
formArray?.controls?.forEach(
(control, index) => FieldEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
rootPath: `${rootPath}fields[${index}].`,
validationErrorModel: validationErrorModel
})
);
}
} }
export class FieldEditorModel implements FieldPersist { export class FieldEditorModel implements FieldPersist {
@ -191,5 +225,24 @@ export class FieldEditorModel implements FieldPersist {
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
static reapplyValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
rootPath: string
}): void {
const { formGroup, rootPath, validationErrorModel } = params;
const context = FieldEditorModel.createValidationContext({
rootPath,
validationErrorModel
});
['code', 'dataType', 'value'].forEach(keyField => {
const control = formGroup?.get(keyField);
control?.clearValidators();
control?.addValidators(context.getValidation(keyField).validators);
})
}
} }

View File

@ -32,16 +32,16 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.NAME' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.NAME' | translate}}</mat-label>
<input matInput type="text" name="name" [formControl]="formGroup.get('name')" required> <input matInput type="text" name="name" [formControl]="formGroup.get('name')" required>
<mat-error *ngIf="formGroup.get('name').hasError('required')"> <mat-error *ngIf="formGroup.get('name').hasError('backendError')">{{formGroup.get('name').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('name').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-4"> <div class="col-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CODE' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.CODE' | translate}}</mat-label>
<input matInput type="text" name="code" [formControl]="formGroup.get('code')" required> <input matInput type="text" name="code" [formControl]="formGroup.get('code')" required>
<mat-error *ngIf="formGroup.get('code').hasError('required')"> <mat-error *ngIf="formGroup.get('code').hasError('backendError')">{{formGroup.get('code').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12"> <div class="col-12">
@ -50,9 +50,8 @@
<rich-text-editor-component [parentFormGroup]="formGroup" [controlName]="'description'" [placeholder]="'TENANT-EDITOR.FIELDS.DESCRIPTION-PLACEHOLDER'" [wrapperClasses]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'required' : ''" [editable]="formGroup.controls['description'].status !== 'DISABLED'"> <rich-text-editor-component [parentFormGroup]="formGroup" [controlName]="'description'" [placeholder]="'TENANT-EDITOR.FIELDS.DESCRIPTION-PLACEHOLDER'" [wrapperClasses]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'required' : ''" [editable]="formGroup.controls['description'].status !== 'DISABLED'">
</rich-text-editor-component> </rich-text-editor-component>
<div [class]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'visible' : 'invisible'" class="mat-form-field formGroup-field-subscript-wrapper"> <div [class]="(formGroup.get('description').touched && formGroup.get('description').hasError('required')) ? 'visible' : 'invisible'" class="mat-form-field formGroup-field-subscript-wrapper">
<mat-error> <mat-error *ngIf="formGroup.get('description').hasError('backendError')">{{formGroup.get('description').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED'| translate}} <mat-error>{{'GENERAL.VALIDATION.REQUIRED'| translate}}</mat-error>
</mat-error>
</div> </div>
</div> </div>
</div> </div>
@ -79,40 +78,40 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label>
<input matInput type="text" name="url" [formControl]="source.get('url')" required> <input matInput type="text" name="url" [formControl]="source.get('url')" required>
<mat-error *ngIf="source.get('url').hasError('required')"> <mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required> <input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
<mat-error *ngIf="source.get('issuerUrl').hasError('required')"> <mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required> <input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
<mat-error *ngIf="source.get('clientId').hasError('required')"> <mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required> <input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
<mat-error *ngIf="source.get('clientSecret').hasError('required')"> <mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required> <input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
<mat-error *ngIf="source.get('scope').hasError('required')"> <mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
@ -134,6 +133,8 @@
[matChipInputAddOnBlur]="true" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addDepositCode($event)"/> (matChipInputTokenEnd)="addDepositCode($event)"/>
</mat-chip-grid> </mat-chip-grid>
<mat-error *ngIf="source.get('codes').hasError('backendError')">{{source.get('codes').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('codes').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
@ -162,40 +163,40 @@
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label>
<input matInput type="text" name="url" [formControl]="source.get('url')" required> <input matInput type="text" name="url" [formControl]="source.get('url')" required>
<mat-error *ngIf="source.get('url').hasError('required')"> <mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required> <input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
<mat-error *ngIf="source.get('issuerUrl').hasError('required')"> <mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required> <input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
<mat-error *ngIf="source.get('clientId').hasError('required')"> <mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required> <input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
<mat-error *ngIf="source.get('clientSecret').hasError('required')"> <mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label> <mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required> <input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
<mat-error *ngIf="source.get('scope').hasError('required')"> <mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-6">
@ -217,6 +218,8 @@
[matChipInputAddOnBlur]="true" [matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addFileCode($event)"/> (matChipInputTokenEnd)="addFileCode($event)"/>
</mat-chip-grid> </mat-chip-grid>
<mat-error *ngIf="source.get('codes').hasError('backendError')">{{source.get('codes').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('codes').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>

View File

@ -192,12 +192,20 @@ export class TenantEditorComponent extends BaseEditor<TenantEditorModel, Tenant>
// deposit source // deposit source
// //
addDepositSource(): void { addDepositSource(): void {
const source: TenantSourceEditorModel = new TenantSourceEditorModel(); (this.formGroup.get('config').get('deposit').get('sources') as FormArray).push(this.editorModel.createChildDeposit((this.formGroup.get('config').get('deposit').get('sources') as FormArray).length));
(this.formGroup.get('config').get('deposit').get('sources') as FormArray).push(source.buildForm());
} }
removeDepositSource(sourceIndex: number): void { removeDepositSource(sourceIndex: number): void {
(this.formGroup.get('config').get('deposit').get('sources') as FormArray).removeAt(sourceIndex); (this.formGroup.get('config').get('deposit').get('sources') as FormArray).removeAt(sourceIndex);
//Reapply validators
TenantEditorModel.reApplyDepositSourcesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
)
this.formGroup.get('config').get('deposit').get('sources').markAsDirty();
} }
// deposit source codes // deposit source codes
@ -232,12 +240,20 @@ export class TenantEditorComponent extends BaseEditor<TenantEditorModel, Tenant>
// fileTransformers source // fileTransformers source
// //
addFileSource(): void { addFileSource(): void {
const source: TenantSourceEditorModel = new TenantSourceEditorModel(); (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).push(this.editorModel.createChildFileTransformer((this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).length));
(this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).push(source.buildForm());
} }
removeFileSource(sourceIndex: number): void { removeFileSource(sourceIndex: number): void {
(this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).removeAt(sourceIndex); (this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).removeAt(sourceIndex);
//Reapply validators
TenantEditorModel.reApplyFileTransformerSourcesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
)
this.formGroup.get('config').get('fileTransformers').get('sources').markAsDirty();
} }
// fileTransformers source codes // fileTransformers source codes

View File

@ -1,4 +1,4 @@
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { Tenant, TenantConfig, TenantConfigPersist, TenantDepositConfig, TenantDepositConfigPersist, TenantFileTransformersConfig, TenantFileTransformersConfigPersist, TenantPersist, TenantSource, TenantSourcePersist } from "@app/core/model/tenant/tenant"; import { Tenant, TenantConfig, TenantConfigPersist, TenantDepositConfig, TenantDepositConfigPersist, TenantFileTransformersConfig, TenantFileTransformersConfigPersist, TenantPersist, TenantSource, TenantSourcePersist } from "@app/core/model/tenant/tenant";
import { BaseEditorModel } from "@common/base/base-form-editor-model"; import { BaseEditorModel } from "@common/base/base-form-editor-model";
import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
@ -55,6 +55,44 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
createChildDeposit(index: number): UntypedFormGroup {
const deposit: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel);
return deposit.buildForm({ rootPath: 'config.deposit.sources[' + index + '].' });
}
static reApplyDepositSourcesValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
}): void {
const { formGroup, validationErrorModel } = params;
const control = formGroup?.get('config').get('deposit');
TenantDepositConfigEditorModel.reapplySourcesFieldsValidators({
formArray: control.get('sources') as UntypedFormArray,
rootPath: `config.deposit.`,
validationErrorModel: validationErrorModel
});
}
createChildFileTransformer(index: number): UntypedFormGroup {
const deposit: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel);
return deposit.buildForm({ rootPath: 'config.fileTransformers.sources[' + index + '].' });
}
static reApplyFileTransformerSourcesValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
}): void {
const { formGroup, validationErrorModel } = params;
const control = formGroup?.get('config').get('fileTransformers');
TenantDepositConfigEditorModel.reapplySourcesFieldsValidators({
formArray: control.get('sources') as UntypedFormArray,
rootPath: `config.fileTransformers.`,
validationErrorModel: validationErrorModel
});
}
} }
export class TenantConfigEditorModel implements TenantConfigPersist { export class TenantConfigEditorModel implements TenantConfigPersist {
@ -167,6 +205,21 @@ export class TenantDepositConfigEditorModel implements TenantDepositConfigPersis
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
static reapplySourcesFieldsValidators(params: {
formArray: UntypedFormArray,
validationErrorModel: ValidationErrorModel,
rootPath: string
}): void {
const { validationErrorModel, rootPath, formArray } = params;
formArray?.controls?.forEach(
(control, index) => TenantSourceEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
rootPath: `${rootPath}sources[${index}].`,
validationErrorModel: validationErrorModel
})
);
}
} }
export class TenantFileTransformersConfigEditorModel implements TenantFileTransformersConfigPersist { export class TenantFileTransformersConfigEditorModel implements TenantFileTransformersConfigPersist {
@ -294,4 +347,23 @@ export class TenantSourceEditorModel implements TenantSourcePersist {
baseContext.validation = baseValidationArray; baseContext.validation = baseValidationArray;
return baseContext; return baseContext;
} }
static reapplyValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
rootPath: string
}): void {
const { formGroup, rootPath, validationErrorModel } = params;
const context = TenantSourceEditorModel.createValidationContext({
rootPath,
validationErrorModel
});
['url', 'codes', 'issuerUrl', 'clientId', 'clientSecret', 'scope'].forEach(keyField => {
const control = formGroup?.get(keyField);
control?.clearValidators();
control?.addValidators(context.getValidation(keyField).validators);
})
}
} }

View File

@ -150,48 +150,35 @@
</rich-text-editor-component> </rich-text-editor-component>
</div> </div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.RESEARCHERS"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.RESEARCHERS">
<mat-form-field class="w-100"> <app-reference-field-component [form]="formGroup.get('properties').get(field.id).get('references')" [referenceType]="referenceTypeEnum.Researcher"></app-reference-field-component>
<mat-label>{{'DMP-EDITOR.PLACEHOLDER.RESEARCHERS' | translate}}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('researchers')" [configuration]="researchersAutoCompleteConfiguration">
</app-multiple-auto-complete>
<mat-error *ngIf="formGroup.get('researchers').hasError('backendError')">
{{formGroup.get('researchers').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('researchers').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="mb-4">
<span *ngIf="!formGroup.get('researchers').disabled" class="not-found">{{'DMP-EDITOR.FUNDING-INFO.FIND' | translate}}</span>
<span *ngIf="!formGroup.get('researchers').disabled" class="insert-manually" (click)="addResearcher($event)">{{'DMP-EDITOR.ACTIONS.INSERT-MANUALLY' | translate}}</span>
</div>
</div> </div>
<!-- <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.ORGANIZATIONS"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.ORGANIZATIONS">
<mat-form-field class="w-100"> <app-reference-field-component [form]="formGroup.get('properties').get(field.id).get('references')" [referenceType]="referenceTypeEnum.Organizations"></app-reference-field-component>
<mat-label>{{'DMP-EDITOR.PLACEHOLDER.ORGANIZATION' | translate}}</mat-label> </div>
<app-multiple-auto-complete [formControl]="formGroup.get('organisations')" [configuration]="organisationsAutoCompleteConfiguration"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.FUNDER">
</app-multiple-auto-complete> <app-reference-field-component [form]="formGroup.get('properties').get(field.id).get('references')" [referenceType]="referenceTypeEnum.Funder"></app-reference-field-component>
<mat-error *ngIf="formGroup.get('organisations').hasError('backendError')"> </div>
{{formGroup.get('organisations').getError('backendError').message}}</mat-error> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.GRANT">
<mat-error *ngIf="formGroup.get('organisations').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <app-reference-field-component [form]="formGroup.get('properties').get(field.id).get('references')" [referenceType]="referenceTypeEnum.Grants"></app-reference-field-component>
</mat-form-field> </div>
<div *ngIf="showOrganizationCreator()" class="mb-4"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.PROJECT">
<span *ngIf="!cantAddOrganizations()" class="not-found">{{'DMP-EDITOR.FUNDING-INFO.FIND' | translate}}</span> <app-reference-field-component [form]="formGroup.get('properties').get(field.id).get('references')" [referenceType]="referenceTypeEnum.Project"></app-reference-field-component>
<span *ngIf="!cantAddOrganizations()" class="insert-manually" (click)="addOrganization($event)">{{'DMP-EDITOR.ACTIONS.INSERT-MANUALLY' | translate}}</span> </div>
</div> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.LICENSE">
<app-reference-field-component [form]="formGroup.get('properties').get(field.id).get('references')" [referenceType]="referenceTypeEnum.Licenses"></app-reference-field-component>
</div> </div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.LANGUAGE"> <div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.LANGUAGE">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-select [formControl]="formGroup.get('extraProperties').get('language')" placeholder="{{'DMP-EDITOR.FIELDS.LANGUAGE' | translate}}" required> <mat-select [formControl]="formGroup.get('language')" placeholder="{{'DMP-EDITOR.FIELDS.LANGUAGE' | translate}}" required>
<mat-option *ngFor="let lang of getLanguageInfos()" [value]="lang.code"> <mat-option *ngFor="let lang of getLanguageInfos()" [value]="lang.code">
{{ lang.name }} {{ lang.name }}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('extraProperties').get('language').hasError('backendError')"> <mat-error *ngIf="formGroup.get('language').hasError('backendError')">{{formGroup.get('language').getError('backendError').message}}</mat-error>
{{formGroup.get('extraProperties').get('language').getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('language').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('language').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</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="contact-form">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-select [formControl]="formGroup.get('extraProperties').get('contact')" placeholder="{{'DMP-EDITOR.FIELDS.CONTACT' | translate}}"> <mat-select [formControl]="formGroup.get('extraProperties').get('contact')" placeholder="{{'DMP-EDITOR.FIELDS.CONTACT' | translate}}">
@ -205,39 +192,18 @@
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> {{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div> -->
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.FUNDER">
<funding-info [formGroup]="formGroup" [grantformGroup]="formGroup.get('grant')" [projectFormGroup]="formGroup.get('project')" [funderFormGroup]="formGroup.get('funder')" [isFinalized]="isFinalized" [isNew]="isNew" [isUserOwner]="isUserOwner" [isRequired]="field.required" [type]="1" (onFormChanged)="formChanged()"></funding-info>
</div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.GRANT">
<funding-info [formGroup]="formGroup" [grantformGroup]="formGroup.get('grant')" [projectFormGroup]="formGroup.get('project')" [funderFormGroup]="formGroup.get('funder')" [isFinalized]="isFinalized" [isNew]="isNew" [isUserOwner]="isUserOwner" [isRequired]="field.required" [type]="2" (onFormChanged)="formChanged()"></funding-info>
</div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.PROJECT">
<funding-info [formGroup]="formGroup" [grantformGroup]="formGroup.get('grant')" [projectFormGroup]="formGroup.get('project')" [funderFormGroup]="formGroup.get('funder')" [isFinalized]="isFinalized" [isNew]="isNew" [isUserOwner]="isUserOwner" [isRequired]="field.required" [type]="3" (onFormChanged)="formChanged()"></funding-info>
</div>
<div *ngIf="field.systemFieldType == dmpBlueprintSystemFieldTypeEnum.LICENSE">
<mat-form-field class="w-100">
<app-single-auto-complete [formControl]="formGroup.get('extraProperties').get('license')" placeholder="{{'DMP-EDITOR.FIELDS.LICENSE' | translate}}" [configuration]="licenseAutoCompleteConfiguration">
</app-single-auto-complete>
<mat-error *ngIf="formGroup.get('extraProperties').get('license').hasError('backendError')">
{{formGroup.get('extraProperties').get('license').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('license').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</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">
<mat-select [formControl]="formGroup.get('extraProperties').get('visible')" placeholder="{{'DMP-EDITOR.FIELDS.ACCESS-TYPE' | translate}}"> <mat-select [formControl]="formGroup.get('accessType')" placeholder="{{'DMP-EDITOR.FIELDS.ACCESS-TYPE' | translate}}">
<mat-option *ngFor="let vis of visibles" [value]="vis.value"> <mat-option *ngFor="let dmpAccessType of dmpAccessTypeEnumValues" [value]="dmpAccessType">
{{vis.name | translate}} {{enumUtils.toDmpAccessTypeString(dmpAccessType)}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('extraProperties').get('visible').hasError('backendError')"> <mat-error *ngIf="formGroup.get('accessType').hasError('backendError')">{{formGroup.get('accessType').getError('backendError').message}}</mat-error>
{{formGroup.get('extraProperties').get('visible').getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('accessType').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('visible').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> --> </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">
@ -274,18 +240,17 @@
</div> </div>
</div> </div>
<div class="col-12 card" *ngIf="section.hasTemplates"> <div class="col-12 card" *ngIf="section.hasTemplates">
{{formGroup.get('descriptionTemplates').value | json}}
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="input-form"> <div class="input-form">
<div class="heading">{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}</div> <div class="heading">{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES' | translate}}</div>
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}}</mat-label> <mat-label>{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}}</mat-label>
<app-multiple-auto-complete placeholder="{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}}" [hidePlaceholder]="true" required='true' [value]="sectionTemplates[section.ordinal - 1]" [configuration]="profilesAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event, i)" (optionActionClicked)="onPreviewTemplate($event, i)" (optionSelected)="onOptionSelected($event, i)"> <app-multiple-auto-complete placeholder="{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}}" [hidePlaceholder]="true" required='true' [formControl]="formGroup.get('descriptionTemplates').get(section.id).get('descriptionTemplateGroupId')" [configuration]="descriptionTemplateService.descriptionTempalteGroupMultipleAutocompleteConfiguration" (optionRemoved)="onRemoveDescriptionTemplate($event, i)" (optionActionClicked)="onPreviewDescriptionTemplate($event, i)" (optionSelected)="onDescriptionTemplateSelected($event, i)">
</app-multiple-auto-complete> </app-multiple-auto-complete>
<mat-error *ngIf="formGroup.get('profiles').hasError('backendError')"> <mat-error *ngIf="formGroup.get('descriptionTemplates').get(section.id).get('descriptionTemplateGroupId').hasError('backendError')">{{formGroup.get('descriptionTemplates').get(section.id).get('descriptionTemplateGroupId').getError('backendError').message}}</mat-error>
{{formGroup.get('profiles').getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('descriptionTemplates').get(section.id).get('descriptionTemplateGroupId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('profiles').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<div class="col pl-0 pt-0 pb-0 d-flex"> <div class="col pl-0 pt-0 pb-0 d-flex">

View File

@ -366,21 +366,6 @@ a:hover {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.insert-manually {
text-decoration: underline;
color: var(--primary-color-3);
cursor: pointer;
font-size: 1rem;
font-weight: 400;
}
.not-found {
// cursor: pointer;
font-size: 1rem;
font-weight: 400;
padding: 0rem 0.5rem 0rem 0rem;
}
.not-found-template { .not-found-template {
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 400; font-weight: 400;

View File

@ -33,6 +33,13 @@ import { map, takeUntil } from 'rxjs/operators';
import { DmpEditorModel } from './dmp-editor.model'; import { DmpEditorModel } from './dmp-editor.model';
import { DmpEditorResolver } from './dmp-editor.resolver'; import { DmpEditorResolver } from './dmp-editor.resolver';
import { DmpEditorService } from './dmp-editor.service'; import { DmpEditorService } from './dmp-editor.service';
import { ReferenceType } from '@app/core/common/enum/reference-type';
import { LanguageInfo } from '@app/core/model/language-info';
import { LanguageInfoService } from '@app/core/services/culture/language-info-service';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { UntypedFormArray } from '@angular/forms';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
@Component({ @Component({
selector: 'app-dmp-editor', selector: 'app-dmp-editor',
@ -52,6 +59,9 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
dmpBlueprintSectionFieldCategoryEnum = DmpBlueprintSectionFieldCategory; dmpBlueprintSectionFieldCategoryEnum = DmpBlueprintSectionFieldCategory;
dmpBlueprintSystemFieldTypeEnum = DmpBlueprintSystemFieldType; dmpBlueprintSystemFieldTypeEnum = DmpBlueprintSystemFieldType;
dmpBlueprintExtraFieldDataTypeEnum = DmpBlueprintExtraFieldDataType; dmpBlueprintExtraFieldDataTypeEnum = DmpBlueprintExtraFieldDataType;
referenceTypeEnum = ReferenceType;
dmpAccessTypeEnum = DmpAccessType;
dmpAccessTypeEnumValues = this.enumUtils.getEnumValues<DmpAccessType>(DmpAccessType);
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);
@ -84,7 +94,6 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
protected queryParamsService: QueryParamsService, protected queryParamsService: QueryParamsService,
// Rest dependencies. Inject any other needed deps here: // Rest dependencies. Inject any other needed deps here:
public authService: AuthService, public authService: AuthService,
// public enumUtils: EnumUtils,
private dmpService: DmpService, private dmpService: DmpService,
private logger: LoggingService, private logger: LoggingService,
// private descriptionEditorService: DescriptionEditorService, // private descriptionEditorService: DescriptionEditorService,
@ -92,7 +101,10 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
private matomoService: MatomoService, private matomoService: MatomoService,
private lockService: LockService, private lockService: LockService,
private configurationService: ConfigurationService, private configurationService: ConfigurationService,
// public visibilityRulesService: VisibilityRulesService // public visibilityRulesService: VisibilityRulesService,
private languageInfoService: LanguageInfoService,
private enumUtils: EnumUtils,
public descriptionTemplateService: DescriptionTemplateService
) { ) {
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService); super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService);
@ -343,6 +355,81 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
// this.onSubmit(true, false); // this.onSubmit(true, false);
} }
//
//
// Description Template
//
//
onRemoveDescriptionTemplate(event, sectionIndex: number) {
let found = false;
const section = this.selectedBlueprint.definition.sections[sectionIndex];
let sectionDescriptionTemplates = (this.formGroup.get('descriptionTemplates') as UntypedFormArray).controls.find(x => x.get('sectionId').value === event.id);
}
onPreviewDescriptionTemplate(event, sectionIndex: number) {
// const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, {
// width: '590px',
// minHeight: '200px',
// restoreFocus: false,
// data: {
// template: event
// },
// panelClass: 'custom-modalbox'
// });
// dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
// if (result) {
// this.addProfile(event, sectionIndex);
// this.profilesAutoCompleteConfiguration = {
// filterFn: this.filterProfiles.bind(this),
// initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
// displayFn: (item) => item['label'],
// titleFn: (item) => item['label'],
// subtitleFn: (item) => item['description'],
// popupItemActionIcon: 'visibility'
// };
// }
// });
}
onDescriptionTemplateSelected(event, sectionIndex: number) {
try {
this.addProfile(event, sectionIndex);
// const profileCounts: Map<String, number> = new Map<String, number>();
// profiles.forEach((value) => profileCounts.set(value.id, (profileCounts.get(value.id) !== undefined ? profileCounts.get(value.id): 0 ) + 1));
// const duplicateProfiles = profiles.filter((value) => {
// let isOk = profileCounts.get(value.id) > 1;
// if (isOk) {
// profileCounts.set(value.id, 0);
// }
// return isOk;
// });
// duplicateProfiles.forEach((value) => profiles.splice(profiles.lastIndexOf(value), 1));
// profiles.sort((a,b)=> a.label.localeCompare(b.label));
} catch {
console.info('Could not sort Dataset Templates')
}
}
addProfile(event, sectionIndex: number) {
// const profiles = this.formGroup.get('profiles').value as DmpDatasetProfile[];
// let found = profiles.find((value) => value.id === event.id);
// if (found !== undefined) {
// if (found.data.dmpSectionIndex.indexOf(sectionIndex) === -1) {
// found.data.dmpSectionIndex.push(sectionIndex);
// }
// else {
// this.sectionTemplates[sectionIndex].pop();
// }
// }
// else {
// let dmpDatasetProfileSection: DmpDatasetProfileSectionsFormModel = new DmpDatasetProfileSectionsFormModel();
// dmpDatasetProfileSection.dmpSectionIndex = [sectionIndex];
// profiles.push({ id: null, descriptionTemplateId: event.id, label: event.label, data: dmpDatasetProfileSection });
// }
// this.formGroup.get('profiles').setValue(profiles);
}
// //
// //
// Misc // Misc
@ -352,6 +439,9 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
return this.formGroup && this.formGroup.dirty; //&& this.hasChanges; return this.formGroup && this.formGroup.dirty; //&& this.hasChanges;
} }
getLanguageInfos(): LanguageInfo[] {
return this.languageInfoService.getLanguageInfoValues();
}
// canDeactivate(): boolean { // canDeactivate(): boolean {
// return !this.isDirty(); // return !this.isDirty();
@ -1470,7 +1560,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
// this.formGroup.get('profiles').setValue(profiles); // this.formGroup.get('profiles').setValue(profiles);
// } // }
// onPreviewTemplate(event, sectionIndex: number) { // onPreviewDescriptionTemplate(event, sectionIndex: number) {
// const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, { // const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, {
// width: '590px', // width: '590px',
// minHeight: '200px', // minHeight: '200px',

View File

@ -1,7 +1,8 @@
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; import { 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 { DmpStatus } from "@app/core/common/enum/dmp-status"; import { DmpStatus } from "@app/core/common/enum/dmp-status";
import { Dmp, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpReferencePersist } from "@app/core/model/dmp/dmp"; import { DmpBlueprint } from "@app/core/model/dmp-blueprint/dmp-blueprint";
import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist } from "@app/core/model/dmp/dmp";
import { DmpReference } from "@app/core/model/dmp/dmp-reference"; import { DmpReference } from "@app/core/model/dmp/dmp-reference";
import { ReferencePersist } from "@app/core/model/reference/reference"; import { ReferencePersist } from "@app/core/model/reference/reference";
import { BaseEditorModel } from "@common/base/base-form-editor-model"; import { BaseEditorModel } from "@common/base/base-form-editor-model";
@ -13,12 +14,12 @@ import { Guid } from "@common/types/guid";
export class DmpEditorModel extends BaseEditorModel implements DmpPersist { export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
label: string; label: string;
status: DmpStatus; status: DmpStatus;
properties: string; properties: DmpPropertiesEditorModel;
description: String; description: String;
language: String; language: String;
blueprint: Guid; blueprint: Guid;
accessType: DmpAccessType; accessType: DmpAccessType;
references: DmpReferenceEditorModel[] = []; // references: DmpReferenceEditorModel[] = [];
descriptionTemplates: DmpDescriptionTemplateEditorModel[] = []; descriptionTemplates: DmpDescriptionTemplateEditorModel[] = [];
permissions: string[]; permissions: string[];
@ -32,12 +33,12 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
super.fromModel(item); super.fromModel(item);
this.label = item.label; this.label = item.label;
this.status = item.status; this.status = item.status;
this.properties = item.properties; this.properties = new DmpPropertiesEditorModel().fromModel(item.properties, item.dmpReferences, item.blueprint);
this.description = item.description; this.description = item.description;
this.language = item.language; this.language = item.language;
this.blueprint = item.blueprint?.id; this.blueprint = item.blueprint?.id;
this.accessType = item.accessType; this.accessType = item.accessType;
if (item.dmpReferences) { item.dmpReferences.map(x => this.references.push(new DmpReferenceEditorModel().fromModel(x))); } //if (item.dmpReferences) { item.dmpReferences.map(x => this.references.push(new DmpReferenceEditorModel().fromModel(x))); }
if (item.dmpDescriptionTemplates) { item.dmpDescriptionTemplates.map(x => this.descriptionTemplates.push(new DmpDescriptionTemplateEditorModel().fromModel(x))); } if (item.dmpDescriptionTemplates) { item.dmpDescriptionTemplates.map(x => this.descriptionTemplates.push(new DmpDescriptionTemplateEditorModel().fromModel(x))); }
} }
return this; return this;
@ -50,7 +51,9 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators],
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators], status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
properties: [{ value: this.properties, disabled: disabled }, context.getValidation('properties').validators], properties: this.properties.buildForm({
rootPath: `properties.`
}),
description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators], description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators], language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators],
blueprint: [{ value: this.blueprint, disabled: disabled }, context.getValidation('blueprint').validators], blueprint: [{ value: this.blueprint, disabled: disabled }, context.getValidation('blueprint').validators],
@ -64,25 +67,24 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
// }), context.getValidation('references') // }), context.getValidation('references')
// ) // )
// ), // ),
// descriptionTemplates: this.formBuilder.array(
// (this.descriptionTemplates ?? []).map(
// (item, index) => new DmpDescriptionTemplateEditorModel(
// this.validationErrorModel
// ).fromModel(item).buildForm({
// rootPath: `descriptionTemplates[${index}].`
// }), context.getValidation('descriptionTemplates')
// )
// ),
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators]
}); });
(this.references ?? []).map(
(item, index) => formGroup.addControl(item.id.toString(), new DmpReferenceEditorModel( // (this.references ?? []).filter(x => x?.data?.blueprintFieldId).map(x => x?.data?.blueprintFieldId).map(
this.validationErrorModel // (item, index) => formGroup.addControl(item.data.blueprintFieldId.toString(), new DmpReferenceEditorModel(
).fromModel(item).buildForm({ // this.validationErrorModel
rootPath: `references[${index}].` // ).fromModel(item).buildForm({
})), context.getValidation('references') // rootPath: `references[${index}].`
// })), context.getValidation('references')
// )
const descriptionTemplatesFormGroup = this.formBuilder.group({});
(this.descriptionTemplates ?? []).filter(x => x?.sectionId).map(x => x.sectionId).map(
(item, index) => descriptionTemplatesFormGroup.addControl(item.toString(), this.descriptionTemplates.find(x => x.sectionId === item).buildForm({
rootPath: `descriptionTemplates[${index}].`
})), context.getValidation('descriptionTemplates')
) )
formGroup.addControl('descriptionTemplates', descriptionTemplatesFormGroup);
return formGroup; return formGroup;
} }
@ -98,7 +100,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] }); baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] });
baseValidationArray.push({ key: 'blueprint', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprint')] }); baseValidationArray.push({ key: 'blueprint', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
baseValidationArray.push({ key: 'accessType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'accessType')] }); baseValidationArray.push({ key: 'accessType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'accessType')] });
baseValidationArray.push({ key: 'references', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'references')] }); //baseValidationArray.push({ key: 'references', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'references')] });
baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] }); baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] });
baseValidationArray.push({ key: 'hash', validators: [] }); baseValidationArray.push({ key: 'hash', validators: [] });
@ -107,10 +109,205 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
} }
} }
export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
dmpBlueprintValues: DmpBlueprintValueEditorModel[] = [];
contacts: DmpContactEditorModel[] = [];
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor(
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
) { }
fromModel(item: DmpProperties, dmpReferences: DmpReference[], dmpBlueprint: DmpBlueprint): DmpPropertiesEditorModel {
dmpBlueprint.definition.sections.forEach(section => {
section.fields.forEach(field => {
this.dmpBlueprintValues.push(new DmpBlueprintValueEditorModel().fromModel(
{
fieldId: field.id,
fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue
}, dmpReferences));
});
});
// if (item?.dmpBlueprintValues) { item.dmpBlueprintValues.map(x => this.dmpBlueprintValues.push(new DmpBlueprintValueEditorModel().fromModel(x, dmpReferences))); }
if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel().fromModel(x))); }
return this;
}
buildForm(params?: {
context?: ValidationContext,
disabled?: boolean,
rootPath?: string
}): UntypedFormGroup {
let { context = null, disabled = false, rootPath } = params ?? {}
if (context == null) {
context = DmpPropertiesEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel,
rootPath
});
}
const formGroup = this.formBuilder.group({
contacts: this.formBuilder.array(
(this.contacts ?? []).map(
(item, index) => new DmpContactEditorModel(
this.validationErrorModel
).fromModel(item).buildForm({
rootPath: `contacts[${index}].`
}), context.getValidation('contacts')
)
),
});
(this.dmpBlueprintValues ?? []).filter(x => x?.fieldId).map(
(item, index) => formGroup.addControl(item.fieldId.toString(), item.buildForm({
rootPath: `dmpBlueprintValues[${index}].`
})), context.getValidation('dmpBlueprintValues')
)
return formGroup;
}
static createValidationContext(params: {
rootPath?: string,
validationErrorModel: ValidationErrorModel
}): ValidationContext {
const { rootPath = '', validationErrorModel } = params;
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'dmpBlueprintValues', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}dmpBlueprintValues`)] });
baseValidationArray.push({ key: 'contacts', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}contacts`)] });
baseContext.validation = baseValidationArray;
return baseContext;
}
}
export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
fieldId: Guid;
fieldValue: string;
references: DmpReferencePersist[];
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor(
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
) { }
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[]): DmpBlueprintValueEditorModel {
this.fieldId = item.fieldId;
this.fieldValue = item.fieldValue;
this.references = dmpReferences.filter(x => x.data?.blueprintFieldId === this.fieldId);
return this;
}
buildForm(params?: {
context?: ValidationContext,
disabled?: boolean,
rootPath?: string
}): UntypedFormGroup {
let { context = null, disabled = false, rootPath } = params ?? {}
if (context == null) {
context = DmpBlueprintValueEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel,
rootPath
});
}
return this.formBuilder.group({
fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators],
fieldValue: [{ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators],
references: [{ value: this.references, disabled: disabled }, context.getValidation('references').validators],
});
}
static createValidationContext(params: {
rootPath?: string,
validationErrorModel: ValidationErrorModel
}): ValidationContext {
const { rootPath = '', validationErrorModel } = params;
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<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`)] });
baseContext.validation = baseValidationArray;
return baseContext;
}
}
export class DmpContactEditorModel implements DmpContactPersist {
userId: Guid;
firstName: string;
lastName: string;
email: string;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor(
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
) { }
fromModel(item: DmpContact): DmpContactEditorModel {
this.userId = item.userId;
this.firstName = item.firstName;
this.lastName = item.lastName;
this.email = item.email;
return this;
}
buildForm(params?: {
context?: ValidationContext,
disabled?: boolean,
rootPath?: string
}): UntypedFormGroup {
let { context = null, disabled = false, rootPath } = params ?? {}
if (context == null) {
context = DmpReferenceEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel,
rootPath
});
}
return this.formBuilder.group({
userId: [{ value: this.userId, disabled: disabled }, context.getValidation('userId').validators],
firstName: [{ value: this.firstName, disabled: disabled }, context.getValidation('firstName').validators],
lastName: [{ value: this.lastName, disabled: disabled }, context.getValidation('lastName').validators],
email: [{ value: this.email, disabled: disabled }, context.getValidation('email').validators],
});
}
static createValidationContext(params: {
rootPath?: string,
validationErrorModel: ValidationErrorModel
}): ValidationContext {
const { rootPath = '', validationErrorModel } = params;
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'userId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}userId`)] });
baseValidationArray.push({ key: 'firstName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}firstName`)] });
baseValidationArray.push({ key: 'lastName', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}lastName`)] });
baseValidationArray.push({ key: 'email', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}email`)] });
baseContext.validation = baseValidationArray;
return baseContext;
}
}
export class DmpReferenceEditorModel implements DmpReferencePersist { export class DmpReferenceEditorModel implements DmpReferencePersist {
id: Guid; id: Guid;
reference: ReferencePersist; reference: ReferencePersist;
data: string; referenceId: Guid;
data: DmpReferenceDataPersist;
hash: string; hash: string;
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
@ -203,7 +400,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
return this.formBuilder.group({ return this.formBuilder.group({
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
reference: [{ value: this.descriptionTemplateGroupId, disabled: disabled }, context.getValidation('descriptionTemplateGroupId').validators], descriptionTemplateGroupId: [{ value: this.descriptionTemplateGroupId, disabled: disabled }, context.getValidation('descriptionTemplateGroupId').validators],
sectionId: [{ value: this.sectionId, disabled: disabled }, context.getValidation('sectionId').validators], sectionId: [{ value: this.sectionId, disabled: disabled }, context.getValidation('sectionId').validators],
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators] hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators]
}); });

View File

@ -7,6 +7,7 @@ import { CommonUiModule } from '@common/ui/common-ui.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 { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
@NgModule({ @NgModule({
imports: [ imports: [
@ -16,7 +17,8 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod
ConfirmationDialogModule, ConfirmationDialogModule,
DmpEditorRoutingModule, DmpEditorRoutingModule,
RichTextEditorModule, RichTextEditorModule,
AutoCompleteModule AutoCompleteModule,
ReferenceFieldModule
], ],
declarations: [ declarations: [
DmpEditorComponent, DmpEditorComponent,

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Description } from '@app/core/model/description/description'; import { Description } from '@app/core/model/description/description';
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp'; import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties } from '@app/core/model/dmp/dmp';
import { DmpReference } from '@app/core/model/dmp/dmp-reference'; import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { Reference } from '@app/core/model/reference/reference'; import { Reference } from '@app/core/model/reference/reference';
import { DmpService } from '@app/core/services/dmp/dmp.service'; import { DmpService } from '@app/core/services/dmp/dmp.service';
@ -33,6 +33,14 @@ export class DmpEditorResolver extends BaseEditorResolver {
nameof<Dmp>(x => x.version), nameof<Dmp>(x => x.version),
nameof<Dmp>(x => x.updatedAt), nameof<Dmp>(x => x.updatedAt),
[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.contacts), nameof<DmpContact>(x => x.userId)].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.email)].join('.'),
// [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'), // [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'),
// [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'), // [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'),
// [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'), // [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),
@ -55,6 +63,10 @@ export class DmpEditorResolver extends BaseEditorResolver {
[nameof<Dmp>(x => x.dmpReferences), nameof<DmpReference>(x => x.reference), nameof<Reference>(x => x.reference)].join('.'), [nameof<Dmp>(x => x.dmpReferences), nameof<DmpReference>(x => x.reference), nameof<Reference>(x => x.reference)].join('.'),
[nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
[nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.descriptionTemplateGroupId)].join('.'),
// nameof<Dmp>(x => x.id), // nameof<Dmp>(x => x.id),
// nameof<Dmp>(x => x.label), // nameof<Dmp>(x => x.label),
// nameof<Dmp>(x => x.status), // nameof<Dmp>(x => x.status),
@ -67,23 +79,23 @@ export class DmpEditorResolver extends BaseEditorResolver {
public static blueprintLookupFields(prefix?: string): string[] { public static blueprintLookupFields(prefix?: string): string[] {
return [ return [
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.id)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.id)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.id)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.id)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.label)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.label)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.ordinal)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.ordinal)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.hasTemplates)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.hasTemplates)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.id)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.id)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.descriptionTemplateId)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.descriptionTemplateId)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.id)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.id)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.category)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.category)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.dataType)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.dataType)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.systemFieldType)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.systemFieldType)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.label)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.label)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.placeholder)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.placeholder)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.description)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.description)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.required)].join('.'), (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.required)].join('.'),
prefix ? prefix + '.' : '' + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.ordinal)].join('.') (prefix ? prefix + '.' : '') + [nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.fields), nameof<FieldInSection>(x => x.ordinal)].join('.')
] ]
} }

View File

@ -1,495 +1,11 @@
<ng-container [ngSwitch]="referenceType" *ngIf="form"> <mat-form-field class="w-100" *ngIf="multipleAutoCompleteSearchConfiguration">
<mat-form-field appearance="outline" *ngSwitchCase="referenceTypeEnum.ComboBox"> <mat-label>{{'REFERENCE-FIELD.PLACEHOLDER' | translate}} {{enumUtils.toReferenceTypeString(referenceType)}}</mat-label>
<app-multiple-auto-complete [configuration]="tagsAutoCompleteConfiguration" [formControl]="formGroup.get('tags')" placeholder="{{'DATASET-EDITOR.FIELDS.TAGS' | translate}}" [separatorKeysCodes]="separatorKeysCodes"></app-multiple-auto-complete> <app-multiple-auto-complete [formControl]="form" [configuration]="multipleAutoCompleteSearchConfiguration">
</mat-form-field> </app-multiple-auto-complete>
</ng-container> <mat-error *ngIf="form.hasError('backendError')">{{form.getError('backendError').message}}</mat-error>
<mat-error *ngIf="form.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<!-- <form *ngIf="formGroup" [formGroup]="formGroup" class="dataset-external-references-editor"> --> </mat-form-field>
<!-- Tags --> <div class="mb-4">
<!-- <div class="pt-2"> <span *ngIf="!form.disabled" class="not-found">{{'REFERENCE-FIELD.COULD-NOT-FIND-MESSAGE' | translate}}</span>
<div class="row"> <span *ngIf="!form.disabled" class="insert-manually" (click)="addResearcher($event)">{{'REFERENCE-FIELD.ACTIONS.INSERT-MANUALLY' | translate}}</span>
<div class="col-12 pl-0 pr-0 pb-2 d-flex flex-row"> </div>
<h4 class="col-auto heading">1.3 {{'DATASET-EDITOR.FIELDS.TAGS' | translate}}</h4>
</div>
</div>
<div class="tags-form">
<mat-form-field appearance="outline">
<app-multiple-auto-complete [configuration]="tagsAutoCompleteConfiguration" [formControl]="formGroup.get('tags')" placeholder="{{'DATASET-EDITOR.FIELDS.TAGS' | translate}}" [separatorKeysCodes]="separatorKeysCodes"></app-multiple-auto-complete>
</mat-form-field>
</div>
</div> -->
<!-- Data Repositories -->
<!-- <div class="pt-2">
<div class="row">
<div class="col-12 pl-0 pr-0 pb-2 d-flex flex-row">
<h4 class="col-auto heading">1.5 {{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES' | translate}}</h4>
<div class="col"></div>
<div class="col-auto d-flex align-items-center" *ngIf='!viewOnly'>
<button class="col-auto" mat-raised-button (click)="addDataRepository()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('dataRepositories') && dataRepositoriesTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.dataRepositories" placeholder="{{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES' | translate}}" [parentTemplate]='dataRepositoriesTemplate' [formArray]="formGroup.get('dataRepositories')" [autoCompleteConfiguration]="dataRepositoriesAutoCompleteConfiguration" (onItemChange)="dataRepositoriesOnItemChange($event)">
</app-external-item-listing>
<ng-template #dataRepositoriesTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<span class="col">
{{i+1}}) {{suggestion.get('name').value}}
</span>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.LABEL' | translate}}" type="text" name="name" [formControl]="suggestion.get('name')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.URI' | translate}}" type="text" name="uri" [formControl]="suggestion.get('uri')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES-INFO' | translate}}" type="text" name="info" [formControl]="suggestion.get('info')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateDataRepository(suggestion)" type="button">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto" *ngIf='!viewOnly'>
<button mat-icon-button (click)="callback(i)">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</div> -->
<!-- External Datasets -->
<!-- <div class="pt-2">
<div class="row">
<div class="col-12 pl-0 pr-0 pb-2 d-flex flex-row">
<h4 class="col-auto heading">1.6 {{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS' | translate}}</h4>
<div class="col"></div>
<div class="col-auto d-flex align-items-center" *ngIf='!viewOnly'><button mat-raised-button (click)="addExternalDataset()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
<div class="col-12">
<span class="hint">{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS-DESCRIPTION' | translate}}</span>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('externalDatasets') && externalDatasetsTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.externalDatasets" placeholder="{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS' | translate}}" [parentTemplate]='externalDatasetsTemplate' [formArray]="formGroup.get('externalDatasets')" [autoCompleteConfiguration]="externalDatasetAutoCompleteConfiguration" (onItemChange)="externalDatasetsOnItemChange($event)">
</app-external-item-listing>
<ng-template #externalDatasetsTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('name').value}}
</p>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.EXTERNAL-DATASET.LABEL' | translate}}" type="text" name="name" [formControl]="suggestion.get('name')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.EXTERNAL-DATASET.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASET-INFO' | translate}}" type="text" name="info" [formControl]="suggestion.get('info')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<mat-select placeholder="{{'DATASET-WIZARD.EDITOR.FIELDS.EXTERNAL-DATASET-TYPE' | translate}}" [formControl]="suggestion.get('type')" required>
<mat-option [value]="0">{{'TYPES.EXTERNAL-DATASET-TYPE.SOURCE' | translate}}</mat-option>
<mat-option [value]="1">{{'TYPES.EXTERNAL-DATASET-TYPE.OUTPUT' | translate}}</mat-option>
</mat-select>
<mat-error *ngIf="suggestion.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateExternalDataset(suggestion)" type="button">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</div> -->
<!-- Registries -->
<!-- <div class="pt-2">
<div class="row">
<div class="col-12 pl-0 pr-0 pb-2 d-flex flex-row">
<h4 class="col-auto heading">1.7 {{'DATASET-EDITOR.FIELDS.REGISTRIES' | translate}}</h4>
<div class="col"></div>
<div class="col-auto d-flex align-items-center" *ngIf='!viewOnly'><button mat-raised-button (click)="addRegistry()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('registries') && registriesTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.registries" placeholder="{{'DATASET-EDITOR.FIELDS.REGISTRIES' | translate}}" [parentTemplate]='registriesTemplate' [formArray]="formGroup.get('registries')" [autoCompleteConfiguration]="registriesAutoCompleteConfiguration" (onItemChange)="registriesOnItemChange($event)">
</app-external-item-listing>
<ng-template #registriesTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('label').value}}
</p>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.REGISTRY.LABEL' | translate}}" type="text" name="label" [formControl]="suggestion.get('label')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.REGISTRY.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.REGISTRY.URI' | translate}}" type="text" name="uri" [formControl]="suggestion.get('uri')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateRegistry(suggestion)" type="button">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</div> -->
<!-- Services -->
<!-- <div class="pt-2">
<div class="row">
<div class="col-12 pl-0 pr-0 pb-2 d-flex flex-row">
<h4 class="col-auto heading">1.8 {{'DATASET-EDITOR.FIELDS.SERVICES' | translate}}</h4>
<div class="col"></div>
<div class="col-auto d-flex align-items-center" *ngIf='!viewOnly'>
<button mat-raised-button (click)="addService()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('services') && servicesTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.services" placeholder="{{'DATASET-EDITOR.FIELDS.SERVICES' | translate}}" [parentTemplate]='servicesTemplate' [formArray]="formGroup.get('services')" [autoCompleteConfiguration]="servicesAutoCompleteConfiguration" (onItemChange)="servicesOnItemChange($event)">
</app-external-item-listing>
<ng-template #servicesTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('label').value}}
</p>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.SERVICES.LABEL' | translate}}" type="text" name="label" [formControl]="suggestion.get('label')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.SERVICES.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.SERVICES.URI' | translate}}" type="text" name="uri" [formControl]="suggestion.get('uri')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateRegistry(suggestion)" type="button">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</div> -->
<!-- </form> -->
<!-- <form *ngIf="formGroup" [formGroup]="formGroup" class="dataset-external-references-editor">
<mat-card class="col-12 external-item-card">
<div class="row">
<div class="col-12 row">
<h4 class="col-auto">{{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES' | translate}}</h4>
<div class="col"></div>
<div class="col-auto" *ngIf='!viewOnly'>
<button class="col-auto" mat-raised-button color="primary" (click)="addDataRepository()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('dataRepositories') && dataRepositoriesTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.dataRepositories" placeholder="{{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES' | translate}}" [parentTemplate]='dataRepositoriesTemplate' [formArray]="formGroup.get('dataRepositories')" [autoCompleteConfiguration]="dataRepositoriesAutoCompleteConfiguration" (onItemChange)="dataRepositoriesOnItemChange($event)">
</app-external-item-listing>
<ng-template #dataRepositoriesTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<span class="col">
{{i+1}}) {{suggestion.get('name').value}}
</span>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.LABEL' | translate}}" type="text" name="name" [formControl]="suggestion.get('name')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.URI' | translate}}" type="text" name="uri" [formControl]="suggestion.get('uri')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES-INFO' | translate}}" type="text" name="info" [formControl]="suggestion.get('info')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateDataRepository(suggestion)" type="button" color="primary">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto" *ngIf='!viewOnly'>
<button mat-icon-button (click)="callback(i)">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</mat-card>
<mat-card class="col-12 external-item-card">
<div class="row">
<div class="col-12 row">
<h4 class="col-auto">{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS' | translate}}</h4>
<div class="col"></div>
<div class="col-auto" *ngIf='!viewOnly'><button mat-raised-button color="primary" (click)="addExternalDataset()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
<div class="col-12">
<span>{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS-DESCRIPTION' | translate}}</span>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('externalDatasets') && externalDatasetsTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.externalDatasets" placeholder="{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS' | translate}}" [parentTemplate]='externalDatasetsTemplate' [formArray]="formGroup.get('externalDatasets')" [autoCompleteConfiguration]="externalDatasetAutoCompleteConfiguration" (onItemChange)="externalDatasetsOnItemChange($event)">
</app-external-item-listing>
<ng-template #externalDatasetsTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('name').value}}
</p>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.EXTERNAL-DATASET.LABEL' | translate}}" type="text" name="name" [formControl]="suggestion.get('name')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.EXTERNAL-DATASET.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASET-INFO' | translate}}" type="text" name="info" [formControl]="suggestion.get('info')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<mat-select placeholder="{{'DATASET-WIZARD.EDITOR.FIELDS.EXTERNAL-DATASET-TYPE' | translate}}" [formControl]="suggestion.get('type')" required>
<mat-option [value]="0">{{'TYPES.EXTERNAL-DATASET-TYPE.SOURCE' | translate}}</mat-option>
<mat-option [value]="1">{{'TYPES.EXTERNAL-DATASET-TYPE.OUTPUT' | translate}}</mat-option>
</mat-select>
<mat-error *ngIf="suggestion.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateExternalDataset(suggestion)" type="button" color="primary">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</mat-card>
<mat-card class="col-12 external-item-card">
<div class="row">
<div class="col-12 row">
<h4 class="col-auto">{{'DATASET-EDITOR.FIELDS.REGISTRIES' | translate}}</h4>
<div class="col"></div>
<div class="col-auto" *ngIf='!viewOnly'><button mat-raised-button color="primary" (click)="addRegistry()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('registries') && registriesTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.registries" placeholder="{{'DATASET-EDITOR.FIELDS.REGISTRIES' | translate}}" [parentTemplate]='registriesTemplate' [formArray]="formGroup.get('registries')" [autoCompleteConfiguration]="registriesAutoCompleteConfiguration" (onItemChange)="registriesOnItemChange($event)">
</app-external-item-listing>
<ng-template #registriesTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('label').value}}
</p>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.REGISTRY.LABEL' | translate}}" type="text" name="label" [formControl]="suggestion.get('label')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.REGISTRY.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.REGISTRY.URI' | translate}}" type="text" name="uri" [formControl]="suggestion.get('uri')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateRegistry(suggestion)" type="button" color="primary">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</mat-card>
<mat-card class="col-12 external-item-card">
<div class="row">
<div class="col-12 row">
<h4 class="col-auto">{{'DATASET-EDITOR.FIELDS.SERVICES' | translate}}</h4>
<div class="col"></div>
<div class="col-auto" *ngIf='!viewOnly'>
<button mat-raised-button color="primary" (click)="addService()">
{{'DATASET-EDITOR.FIELDS.CREATE'|translate}}
</button>
</div>
</div>
</div>
<app-external-item-listing class="col-12" *ngIf="formGroup.get('services') && servicesTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.services" placeholder="{{'DATASET-EDITOR.FIELDS.SERVICES' | translate}}" [parentTemplate]='servicesTemplate' [formArray]="formGroup.get('services')" [autoCompleteConfiguration]="servicesAutoCompleteConfiguration" (onItemChange)="servicesOnItemChange($event)">
</app-external-item-listing>
<ng-template #servicesTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('label').value}}
</p>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.SERVICES.LABEL' | translate}}" type="text" name="label" [formControl]="suggestion.get('label')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.SERVICES.ABBREVIATION' | translate}}" type="text" name="abbreviation" [formControl]="suggestion.get('abbreviation')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf="isInternal(suggestion)">
<mat-form-field>
<input matInput placeholder="{{'DATASET-REFERENCED-MODELS.SERVICES.URI' | translate}}" type="text" name="uri" [formControl]="suggestion.get('uri')">
</mat-form-field>
</div>
<div class="col-auto" *ngIf='!viewOnly && isInternal(suggestion)'>
<button mat-raised-button (click)="updateRegistry(suggestion)" type="button" color="primary">
{{ 'DATASET-EDITOR.ACTIONS.UPDATE' | translate }}
</button>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</mat-card>
<mat-card class="col-12 external-item-card">
<div class="row">
<div class="col-12 row">
<h4 class="col-auto">{{'DATASET-EDITOR.FIELDS.TAGS' | translate}}</h4>
</div>
</div> -->
<!-- <app-external-item-listing *ngIf="formGroup.get('tags') && tagsTemplate && externalSourcesConfiguration" [options]="externalSourcesConfiguration.tags" placeholder="{{'DATASET-EDITOR.FIELDS.TAGS' | translate}}" [parentTemplate]='tagsTemplate' [formArray]="formGroup.get('tags')" [autoCompleteConfiguration]="tagsAutoCompleteConfiguration" (onItemChange)="tagsOnItemChange($event)">
</app-external-item-listing> -->
<!-- <mat-form-field>
<mat-chip-list #chipList [disabled]="viewOnly">
<mat-chip *ngFor="let tag of formGroup.get('tags').value"
[removable]="true" (removed)="removeTag(tag)">
{{tag.name}}
<mat-icon matChipRemove *ngIf="!viewOnly">cancel</mat-icon>
</mat-chip>
<input matInput [disabled]="viewOnly" placeholder="{{'DATASET-EDITOR.FIELDS.TAGS' | translate}}"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addTag($event)">
</mat-chip-list>
</mat-form-field>
<ng-template #tagsTemplate let-suggestion let-i="index" let-callback="function">
<div class="col-12 row align-items-center">
<div class="col">
<p>
{{i+1}}) {{suggestion.get('name').value}}
</p>
</div>
<div class="col-auto">
<button mat-icon-button (click)="callback(i)" *ngIf='!viewOnly'>
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</ng-template>
</mat-card>
</form> -->

View File

@ -1,35 +1,14 @@
.dataset-external-references-editor { .insert-manually {
.heading { text-decoration: underline;
text-align: left; color: var(--primary-color-3);
font-weight: 700; cursor: pointer;
font-size: 18px; font-size: 1rem;
letter-spacing: 0px; font-weight: 400;
color: #212121; }
opacity: 0.81;
margin-top: 1.625rem;
margin-bottom: 0.625rem;
}
.hint { .not-found {
text-align: left; // cursor: pointer;
font-weight: 400; font-size: 1rem;
font-size: 16px; font-weight: 400;
letter-spacing: 0px; padding: 0rem 0.5rem 0rem 0rem;
color: #212121; }
opacity: 0.81;
margin-bottom: 2.125rem;
}
.external-item-card {
margin-bottom: 1em;
}
}
::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-outline {
background: #fafafa !important;
}
::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-infix {
font-size: 1rem;
padding: 0.6em 0 1em 0 !important;
}

View File

@ -1,36 +1,10 @@
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item';
import { ExternalSourcesConfiguration } from '@app/core/model/external-sources/external-sources-configuration';
import { DataRepositoryCriteria } from '@app/core/query/data-repository/data-repository-criteria';
import { ExternalDatasetCriteria } from '@app/core/query/external-dataset/external-dataset-criteria';
import { RegistryCriteria } from '@app/core/query/registry/registry-criteria';
import { RequestItem } from '@app/core/query/request-item';
import { ServiceCriteria } from '@app/core/query/service/service-criteria';
import { TagCriteria } from '@app/core/query/tag/tag-criteria';
import { ExternalSourcesConfigurationService } from '@app/core/services/external-sources/external-sources-configuration.service';
import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { ExternalDataRepositoryEditorModel, ExternalDatasetEditorModel, ExternalRegistryEditorModel, ExternalServiceEditorModel, ExternalTagEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model';
import { DatasetExternalDataRepositoryDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component';
import { DatasetExternalDatasetDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component';
import { DatasetExternalRegistryDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component';
import { DatasetExternalServiceDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component';
import { BaseComponent } from '@common/base/base.component';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { takeUntil, map } from 'rxjs/operators';
import { ENTER, COMMA } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material/chips';
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
import { ExternalDataRepositoryService } from '@app/core/services/external-sources/data-repository/extternal-data-repository.service';
import { ExternalDatasetService } from '@app/core/services/external-sources/dataset/external-dataset.service';
import { ExternalRegistryService } from '@app/core/services/external-sources/registry/external-registry.service';
import { ExternalServiceService } from '@app/core/services/external-sources/service/external-service.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { ReferenceType } from '@app/core/common/enum/reference-type'; import { ReferenceType } from '@app/core/common/enum/reference-type';
import { ReferenceService } from '@app/core/services/reference/reference.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { BaseComponent } from '@common/base/base.component';
@Component({ @Component({
selector: 'app-reference-field-component', selector: 'app-reference-field-component',
@ -42,7 +16,9 @@ export class ReferenceFieldComponent extends BaseComponent implements OnInit {
@Input() referenceType: ReferenceType = null; @Input() referenceType: ReferenceType = null;
@Input() form: UntypedFormGroup = null; @Input() form: UntypedFormGroup = null;
referenceTypeEnum = ReferenceType; multipleAutoCompleteSearchConfiguration: MultipleAutoCompleteConfiguration;
// referenceTypeEnum = ReferenceType;
// @Input() viewOnly = false; // @Input() viewOnly = false;
// @Output() formChanged: EventEmitter<any> = new EventEmitter(); // @Output() formChanged: EventEmitter<any> = new EventEmitter();
@ -104,31 +80,34 @@ export class ReferenceFieldComponent extends BaseComponent implements OnInit {
// private externalDataRepositoryService: ExternalDataRepositoryService, // private externalDataRepositoryService: ExternalDataRepositoryService,
// private externalDatasetService: ExternalDatasetService, // private externalDatasetService: ExternalDatasetService,
// private externalRegistryService: ExternalRegistryService, // private externalRegistryService: ExternalRegistryService,
// private externalServiceService: ExternalServiceService, private referenceService: ReferenceService,
public enumUtils: EnumUtils,
) { super(); } ) { super(); }
ngOnInit() { ngOnInit() {
this.multipleAutoCompleteSearchConfiguration = this.referenceService.getMultipleAutoCompleteSearchConfiguration(this.referenceType);
// this.externalSourcesConfigurationService.getExternalSourcesConfiguration()
// .pipe(takeUntil(this._destroyed))
// .subscribe(result => {
// this.externalSourcesConfiguration = result;
// this.externalSourcesConfiguration.dataRepositories.push({ key: '', label: 'All' });
// this.externalSourcesConfiguration.externalDatasets.push({ key: '', label: 'All' });
// this.externalSourcesConfiguration.registries.push({ key: '', label: 'All' });
// this.externalSourcesConfiguration.services.push({ key: '', label: 'All' });
// if (!isNullOrUndefined(this.externalSourcesConfiguration.tags)) {
// this.externalSourcesConfiguration.tags.push({ key: '', label: 'All' });
// } else {
// this.externalSourcesConfiguration.tags = [{ key: '', label: 'All' }];
// }
// });
// this.formGroup.valueChanges // this.externalSourcesConfigurationService.getExternalSourcesConfiguration()
// .pipe(takeUntil(this._destroyed)) // .pipe(takeUntil(this._destroyed))
// .subscribe(val => { // .subscribe(result => {
// this.formChanged.emit(val); // this.externalSourcesConfiguration = result;
// }); // this.externalSourcesConfiguration.dataRepositories.push({ key: '', label: 'All' });
// this.externalSourcesConfiguration.externalDatasets.push({ key: '', label: 'All' });
// this.externalSourcesConfiguration.registries.push({ key: '', label: 'All' });
// this.externalSourcesConfiguration.services.push({ key: '', label: 'All' });
// if (!isNullOrUndefined(this.externalSourcesConfiguration.tags)) {
// this.externalSourcesConfiguration.tags.push({ key: '', label: 'All' });
// } else {
// this.externalSourcesConfiguration.tags = [{ key: '', label: 'All' }];
// }
// });
// this.formGroup.valueChanges
// .pipe(takeUntil(this._destroyed))
// .subscribe(val => {
// this.formChanged.emit(val);
// });
} }
// public cancel(): void { // public cancel(): void {

View File

@ -1,8 +1,10 @@
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 { DescriptionRoutingModule } from '@app/ui/description/description.routing'; import { DescriptionRoutingModule } from '@app/ui/description/description.routing';
import { CommonFormsModule } from '@common/forms/common-forms.module'; import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module'; import { CommonUiModule } from '@common/ui/common-ui.module';
import { ReferenceFieldComponent } from './reference-field.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -10,10 +12,13 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
CommonFormsModule, CommonFormsModule,
FormattingModule, FormattingModule,
DescriptionRoutingModule, DescriptionRoutingModule,
AutoCompleteModule
], ],
declarations: [ declarations: [
ReferenceFieldComponent
], ],
exports: [ exports: [
ReferenceFieldComponent
] ]
}) })
export class ReferenceFieldModule { } export class ReferenceFieldModule { }

View File

@ -11,8 +11,8 @@
{{enumUtils.toSupportiveMaterialTypeString(type)}} {{enumUtils.toSupportiveMaterialTypeString(type)}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('type').hasError('required')"> <mat-error *ngIf="formGroup.get('type').hasError('backendError')">{{formGroup.get('type').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col"> <div class="col">
@ -23,8 +23,8 @@
{{languageCode}} {{languageCode}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('languageCode').hasError('required')"> <mat-error *ngIf="formGroup.get('languageCode').hasError('backendError')">{{formGroup.get('languageCode').getError('backendError').message}}</mat-error>
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('languageCode').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
@ -50,6 +50,8 @@
alignleft aligncenter alignright alignjustify | \ alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | code codesample | searchreplace | preview | removeformat | help' bullist numlist outdent indent | code codesample | searchreplace | preview | removeformat | help'
}" [formControl]="formGroup.get('payload')"></editor> }" [formControl]="formGroup.get('payload')"></editor>
<mat-error *ngIf="formGroup.get('payload').hasError('backendError')">{{formGroup.get('payload').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('payload').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</div> </div>
</div> </div>
</mat-card> </mat-card>

View File

@ -15,7 +15,7 @@
"flow": "standard", "flow": "standard",
"clientId": null, "clientId": null,
"silentCheckSsoRedirectUri": "http://localhost:4200/assets/silent-check-sso.html", "silentCheckSsoRedirectUri": "http://localhost:4200/assets/silent-check-sso.html",
"scope": "openid profile email address phone dmp_web", "scope": "openid profile email address phone dmp_web dmp_notification identity_provider",
"clientSecret": null, "clientSecret": null,
"grantType": "code" "grantType": "code"
}, },

View File

@ -1677,6 +1677,7 @@
"LICENSE": "License", "LICENSE": "License",
"ACCESS-TYPE": "Access Rights", "ACCESS-TYPE": "Access Rights",
"CONTACT": "Contact", "CONTACT": "Contact",
"LANGUAGE": "Language",
"DESCRIPTION-TEMPLATES": "Description templates", "DESCRIPTION-TEMPLATES": "Description templates",
"DESCRIPTION-TEMPLATES-HINT": "Select a template to describe your descriptions" "DESCRIPTION-TEMPLATES-HINT": "Select a template to describe your descriptions"
}, },
@ -1831,6 +1832,13 @@
"MESSAGE": "Somebody else is modifying the DMP at this moment. You may view the dataset but you cannot make any changes." "MESSAGE": "Somebody else is modifying the DMP at this moment. You may view the dataset but you cannot make any changes."
} }
}, },
"REFERENCE-FIELD": {
"PLACEHOLDER":"Select",
"COULD-NOT-FIND-MESSAGE":"Couldn't find it?",
"ACTIONS": {
"INSERT-MANUALLY":"Insert it manually"
}
},
"DMP-CLONE-DIALOG": { "DMP-CLONE-DIALOG": {
"TITLE": "Clone", "TITLE": "Clone",
"FIELDS": { "FIELDS": {
@ -2391,6 +2399,10 @@
"SUCCESSFUL": "Successful", "SUCCESSFUL": "Successful",
"ERROR":"Error", "ERROR":"Error",
"OMITTED": "Omitted" "OMITTED": "Omitted"
},
"DMP-ACCESS-TYPE": {
"PUBLIC": "Public",
"RESTRICTED": "Restricted Access"
} }
}, },
"ADDRESEARCHERS-EDITOR": { "ADDRESEARCHERS-EDITOR": {