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

# Conflicts:
#	dmp-backend/core/src/main/java/eu/eudat/model/persist/descriptionproperties/PropertyDefinitionFieldSetPersist.java
This commit is contained in:
Efstratios Giannopoulos 2024-04-25 10:27:06 +03:00
commit c27e98acd8
75 changed files with 559 additions and 749 deletions

View File

@ -228,7 +228,7 @@ public class DescriptionPersist {
.iff(() -> item.getStatus() == DescriptionStatus.Finalized) .iff(() -> item.getStatus() == DescriptionStatus.Finalized)
.on(DescriptionPersist._properties) .on(DescriptionPersist._properties)
.over(item.getProperties()) .over(item.getProperties())
.using(() -> this.validatorFactory.validator(PropertyDefinitionPersist.PropertyDefinitionPersistValidator.class).setStatus(item.getStatus()).withDefinition(definition)) .using(() -> this.validatorFactory.validator(PropertyDefinitionPersist.PropertyDefinitionPersistValidator.class).setStatus(item.getStatus()).withDefinition(definition).withRules(definition))
// this.navSpec() // this.navSpec()
// .iff(() -> !this.isNull(item.getTags())) // .iff(() -> !this.isNull(item.getTags()))
// .on(DescriptionPersist._tags) // .on(DescriptionPersist._tags)

View File

@ -4,6 +4,7 @@ import eu.eudat.commons.enums.DescriptionStatus;
import eu.eudat.commons.enums.FieldType; import eu.eudat.commons.enums.FieldType;
import eu.eudat.commons.enums.FieldValidationType; import eu.eudat.commons.enums.FieldValidationType;
import eu.eudat.commons.types.descriptiontemplate.FieldEntity; import eu.eudat.commons.types.descriptiontemplate.FieldEntity;
import eu.eudat.commons.types.descriptiontemplate.RuleEntity;
import eu.eudat.commons.validation.BaseValidator; import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.model.persist.ReferencePersist; import eu.eudat.model.persist.ReferencePersist;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
@ -95,6 +96,8 @@ public class FieldPersist {
private final MessageSource messageSource; private final MessageSource messageSource;
private FieldEntity fieldEntity; private FieldEntity fieldEntity;
private DescriptionStatus status; private DescriptionStatus status;
private List<List<RuleEntity>> rules;
private Boolean fieldSetIsRuleTarget;
protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) { protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) {
super(conventionService, errors); super(conventionService, errors);
@ -111,33 +114,34 @@ public class FieldPersist {
protected List<Specification> specifications(FieldPersist item) { protected List<Specification> specifications(FieldPersist item) {
FieldType fieldType = this.fieldEntity != null && this.fieldEntity.getData() != null ? this.fieldEntity.getData().getFieldType() : FieldType.FREE_TEXT; FieldType fieldType = this.fieldEntity != null && this.fieldEntity.getData() != null ? this.fieldEntity.getData().getFieldType() : FieldType.FREE_TEXT;
boolean required = this.fieldEntity != null && this.fieldEntity.getValidations() != null ? this.fieldEntity.getValidations().contains(FieldValidationType.Required) : false; boolean required = this.fieldEntity != null && this.fieldEntity.getValidations() != null ? this.fieldEntity.getValidations().contains(FieldValidationType.Required) : false;
boolean isRuleTarget = this.checkIfFieldIsRuleTarget();
return Arrays.asList( return Arrays.asList(
this.spec() this.spec()
.iff(()-> FieldType.isTextType(fieldType) && !fieldType.equals(FieldType.CHECK_BOX) && DescriptionStatus.Finalized.equals(this.status) && required) .iff(()-> FieldType.isTextType(fieldType) && !fieldType.equals(FieldType.CHECK_BOX) && DescriptionStatus.Finalized.equals(this.status) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget && required)
.must(() -> !this.isEmpty(item.getTextValue())) .must(() -> !this.isEmpty(item.getTextValue()))
.failOn(FieldPersist._textValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textValue}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._textValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textValue}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(()-> FieldType.isDateType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && required) .iff(()-> FieldType.isDateType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget && required)
.must(() -> !this.isNull(item.getDateValue())) .must(() -> !this.isNull(item.getDateValue()))
.failOn(FieldPersist._dateValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._dateValue}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._dateValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._dateValue}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(()-> FieldType.isExternalIdentifierType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && required) .iff(()-> FieldType.isExternalIdentifierType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget && required)
.must(() -> !this.isNull(item.getExternalIdentifier())) .must(() -> !this.isNull(item.getExternalIdentifier()))
.failOn(FieldPersist._externalIdentifier).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._externalIdentifier}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._externalIdentifier).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._externalIdentifier}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(()-> FieldType.isTextListType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && required && !fieldType.equals(FieldType.TAGS)) .iff(()-> FieldType.isTextListType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget && required && !fieldType.equals(FieldType.TAGS))
.must(() -> !this.isListNullOrEmpty(item.getTextListValue()) || !this.isEmpty(item.getTextValue())) .must(() -> !this.isListNullOrEmpty(item.getTextListValue()) || !this.isEmpty(item.getTextValue()))
.failOn(FieldPersist._textListValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textListValue}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._textListValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textListValue}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(()-> fieldType.equals(FieldType.TAGS) && DescriptionStatus.Finalized.equals(this.status) && required) .iff(()-> fieldType.equals(FieldType.TAGS) && DescriptionStatus.Finalized.equals(this.status) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget && required)
.must(() -> !this.isListNullOrEmpty(item.getTextListValue())) .must(() -> !this.isListNullOrEmpty(item.getTextListValue()))
.failOn(FieldPersist._textListValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textListValue}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._textListValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textListValue}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(()-> FieldType.isReferenceType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && required) .iff(()-> FieldType.isReferenceType(fieldType) && DescriptionStatus.Finalized.equals(this.status) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget && required)
.must(() -> !this.isListNullOrEmpty(item.getReferences()) || !this.isNull(item.getReference())) .must(() -> !this.isListNullOrEmpty(item.getReferences()) || !this.isNull(item.getReference()))
.failOn(FieldPersist._textListValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textListValue}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._textListValue).failWith(messageSource.getMessage("Validation_Required", new Object[]{FieldPersist._textListValue}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(()-> !this.isEmpty(item.getTextValue()) && (fieldType.equals(FieldType.CHECK_BOX) || fieldType.equals(FieldType.BOOLEAN_DECISION)) ) .iff(()-> !this.isEmpty(item.getTextValue()) && (fieldType.equals(FieldType.CHECK_BOX) || fieldType.equals(FieldType.BOOLEAN_DECISION)) && this.isListNullOrEmpty(fieldEntity.getVisibilityRules()) && !isRuleTarget && !fieldSetIsRuleTarget)
.must(() -> this.isBoolean(item.getTextValue())) .must(() -> this.isBoolean(item.getTextValue()))
.failOn(FieldPersist._textValue).failWith(messageSource.getMessage("Validation_UnexpectedValue", new Object[]{FieldPersist._textValue}, LocaleContextHolder.getLocale())), .failOn(FieldPersist._textValue).failWith(messageSource.getMessage("Validation_UnexpectedValue", new Object[]{FieldPersist._textValue}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
@ -167,10 +171,31 @@ public class FieldPersist {
return this; return this;
} }
public PersistValidator withRules(List<List<RuleEntity>> rules) {
this.rules = rules;
return this;
}
public PersistValidator setStatus(DescriptionStatus status) { public PersistValidator setStatus(DescriptionStatus status) {
this.status = status; this.status = status;
return this; return this;
} }
public PersistValidator ifFieldSetIsRuleTarget(Boolean fieldSetIsRuleTarget){
this.fieldSetIsRuleTarget = fieldSetIsRuleTarget;
return this;
}
private boolean checkIfFieldIsRuleTarget(){
if (this.isListNullOrEmpty(rules)) return false;
for (List<RuleEntity> rulesBySection: rules) {
if (!this.isListNullOrEmpty(rulesBySection)){
for (RuleEntity rule :rulesBySection) {
if (rule.getTarget().equals(this.fieldEntity.getId())) return true;
}
}
}
return false;
}
} }
} }

View File

@ -3,6 +3,7 @@ package eu.eudat.model.persist.descriptionproperties;
import eu.eudat.commons.enums.DescriptionStatus; import eu.eudat.commons.enums.DescriptionStatus;
import eu.eudat.commons.types.descriptiontemplate.FieldEntity; import eu.eudat.commons.types.descriptiontemplate.FieldEntity;
import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity; import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity;
import eu.eudat.commons.types.descriptiontemplate.RuleEntity;
import eu.eudat.commons.validation.BaseValidator; import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.errorcode.ErrorThesaurusProperties;
@ -60,6 +61,7 @@ public class PropertyDefinitionFieldSetItemPersist {
private final MessageSource messageSource; private final MessageSource messageSource;
private FieldSetEntity fieldSetEntity; private FieldSetEntity fieldSetEntity;
private List<List<RuleEntity>> rules;
private DescriptionStatus status; private DescriptionStatus status;
protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) { protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource) {
super(conventionService, errors); super(conventionService, errors);
@ -86,7 +88,7 @@ public class PropertyDefinitionFieldSetItemPersist {
.using((itm) -> .using((itm) ->
{ {
FieldEntity fieldEntity = fieldSetEntity != null ? fieldSetEntity.getFieldById((String)itm.getKey()).stream().findFirst().orElse(null) : null; FieldEntity fieldEntity = fieldSetEntity != null ? fieldSetEntity.getFieldById((String)itm.getKey()).stream().findFirst().orElse(null) : null;
return this.validatorFactory.validator(FieldPersist.PersistValidator.class).withFieldEntity(fieldEntity).setStatus(this.status); return this.validatorFactory.validator(FieldPersist.PersistValidator.class).withFieldEntity(fieldEntity).withRules(rules).ifFieldSetIsRuleTarget(this.checkIfFieldSetIsRuleTarget()).setStatus(this.status);
}) })
); );
@ -97,10 +99,27 @@ public class PropertyDefinitionFieldSetItemPersist {
return this; return this;
} }
public PersistValidator withRules(List<List<RuleEntity>> rules) {
this.rules = rules;
return this;
}
public PersistValidator setStatus(DescriptionStatus status) { public PersistValidator setStatus(DescriptionStatus status) {
this.status = status; this.status = status;
return this; return this;
} }
private Boolean checkIfFieldSetIsRuleTarget(){
if (this.isListNullOrEmpty(rules)) return false;
for (List<RuleEntity> rulesBySection: rules) {
if (!this.isListNullOrEmpty(rulesBySection)){
for (RuleEntity rule :rulesBySection) {
if (rule.getTarget().equals(this.fieldSetEntity.getId())) return true;
}
}
}
return false;
}
} }
} }

View File

@ -2,6 +2,7 @@ package eu.eudat.model.persist.descriptionproperties;
import eu.eudat.commons.enums.DescriptionStatus; import eu.eudat.commons.enums.DescriptionStatus;
import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity; import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity;
import eu.eudat.commons.types.descriptiontemplate.RuleEntity;
import eu.eudat.commons.validation.BaseValidator; import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.errorcode.ErrorThesaurusProperties;
@ -35,6 +36,7 @@ public class PropertyDefinitionFieldSetPersist {
private final MessageSource messageSource; private final MessageSource messageSource;
private FieldSetEntity fieldSetEntity; private FieldSetEntity fieldSetEntity;
private DescriptionStatus status; private DescriptionStatus status;
private List<List<RuleEntity>> rules;
protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource, MessageSource messageSource1) { protected PersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory, MessageSource messageSource, MessageSource messageSource1) {
super(conventionService, errors); super(conventionService, errors);
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
@ -56,7 +58,7 @@ public class PropertyDefinitionFieldSetPersist {
.iff(() -> !this.isNull(item.getItems())) .iff(() -> !this.isNull(item.getItems()))
.on(PropertyDefinitionFieldSetPersist._items) .on(PropertyDefinitionFieldSetPersist._items)
.over(item.getItems()) .over(item.getItems())
.using((itm) -> this.validatorFactory.validator(PropertyDefinitionFieldSetItemPersist.PersistValidator.class).withFieldSetEntity(this.fieldSetEntity).setStatus(this.status)), .using((itm) -> this.validatorFactory.validator(PropertyDefinitionFieldSetItemPersist.PersistValidator.class).withFieldSetEntity(this.fieldSetEntity).withRules(this.rules).setStatus(this.status)),
this.spec() this.spec()
.iff(() -> DescriptionStatus.Finalized.equals(this.status) && fieldSetEntity.getHasMultiplicity()) .iff(() -> DescriptionStatus.Finalized.equals(this.status) && fieldSetEntity.getHasMultiplicity())
.must(() -> !this.isListNullOrEmpty(item.getItems()) && min <= item.getItems().size()) .must(() -> !this.isListNullOrEmpty(item.getItems()) && min <= item.getItems().size())
@ -73,6 +75,11 @@ public class PropertyDefinitionFieldSetPersist {
return this; return this;
} }
public PersistValidator withRules(List<List<RuleEntity>> rules) {
this.rules = rules;
return this;
}
public PropertyDefinitionFieldSetPersist.PersistValidator setStatus(DescriptionStatus status) { public PropertyDefinitionFieldSetPersist.PersistValidator setStatus(DescriptionStatus status) {
this.status = status; this.status = status;
return this; return this;

View File

@ -5,6 +5,7 @@ import eu.eudat.commons.enums.FieldValidationType;
import eu.eudat.commons.types.descriptiontemplate.DefinitionEntity; import eu.eudat.commons.types.descriptiontemplate.DefinitionEntity;
import eu.eudat.commons.types.descriptiontemplate.FieldEntity; import eu.eudat.commons.types.descriptiontemplate.FieldEntity;
import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity; import eu.eudat.commons.types.descriptiontemplate.FieldSetEntity;
import eu.eudat.commons.types.descriptiontemplate.RuleEntity;
import eu.eudat.commons.validation.BaseValidator; import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties; import eu.eudat.errorcode.ErrorThesaurusProperties;
@ -47,6 +48,8 @@ public class PropertyDefinitionPersist {
private DescriptionStatus status; private DescriptionStatus status;
private DefinitionEntity definition; private DefinitionEntity definition;
private List<List<RuleEntity>> rules;
protected PropertyDefinitionPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { protected PropertyDefinitionPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors); super(conventionService, errors);
this.messageSource = messageSource; this.messageSource = messageSource;
@ -78,7 +81,7 @@ public class PropertyDefinitionPersist {
.mapKey((k) -> ((String)k)) .mapKey((k) -> ((String)k))
.using((itm) -> { .using((itm) -> {
FieldSetEntity fieldSetEntity = definition != null ? definition.getFieldSetById((String)itm.getKey()).stream().findFirst().orElse(null) : null; FieldSetEntity fieldSetEntity = definition != null ? definition.getFieldSetById((String)itm.getKey()).stream().findFirst().orElse(null) : null;
return this.validatorFactory.validator(PropertyDefinitionFieldSetPersist.PersistValidator.class).withFieldSetEntity(fieldSetEntity).setStatus(this.status); return this.validatorFactory.validator(PropertyDefinitionFieldSetPersist.PersistValidator.class).withFieldSetEntity(fieldSetEntity).withRules(rules).setStatus(this.status);
}) })
); );
} }
@ -94,6 +97,12 @@ public class PropertyDefinitionPersist {
return this; return this;
} }
public PropertyDefinitionPersistValidator withRules(DefinitionEntity definition) {
List<FieldEntity> fields = definition.getAllField();
this.rules = fields.stream().filter(x -> x.getVisibilityRules() != null).map(FieldEntity::getVisibilityRules).collect(Collectors.toList());
return this;
}
private List<FieldSetEntity> getMissingFieldSetEntity(PropertyDefinitionPersist item){ private List<FieldSetEntity> getMissingFieldSetEntity(PropertyDefinitionPersist item){
List<FieldSetEntity> missingMultipleFieldSets = new ArrayList<>(); List<FieldSetEntity> missingMultipleFieldSets = new ArrayList<>();

View File

@ -497,24 +497,20 @@ public class DescriptionServiceImpl implements DescriptionService {
if (FieldType.isTextType(fieldType)) { if (FieldType.isTextType(fieldType)) {
if (FieldType.UPLOAD.equals(fieldType)){ if (FieldType.UPLOAD.equals(fieldType)){
UUID newFileId = this.conventionService.isValidUUID(persist.getTextValue()) ? UUID.fromString(persist.getTextValue()) : null; UUID newFileId = this.conventionService.isValidUUID(persist.getTextValue()) ? UUID.fromString(persist.getTextValue()) : null;
UUID existingFileId = this.conventionService.isValidUUID(data.getTextValue()) ? UUID.fromString(data.getTextValue()) : null;
if (newFileId != null){ if (newFileId != null){
if (!newFileId.equals(existingFileId)) { StorageFileEntity existingFile = this.queryFactory.query(StorageFileQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(newFileId).firstAs(new BaseFieldSet().ensure(StorageFile._id));
if (existingFile == null){
StorageFile storageFile = this.storageFileService.copyToStorage(newFileId, StorageType.Main, true, new BaseFieldSet().ensure(StorageFile._id)); StorageFile storageFile = this.storageFileService.copyToStorage(newFileId, StorageType.Main, true, new BaseFieldSet().ensure(StorageFile._id));
this.storageFileService.updatePurgeAt(storageFile.getId(), null); this.storageFileService.updatePurgeAt(storageFile.getId(), null);
if (existingFileId != null){ data.setTextValue(storageFile.getId().toString());
} else {
if (existingFile.getId() != null){
//DO NOT Remove we can not be sure uf the description is copied //DO NOT Remove we can not be sure uf the description is copied
//this.storageFileService.updatePurgeAt(existingFileId, Instant.now().minusSeconds(60)); //this.storageFileService.updatePurgeAt(existingFileId, Instant.now().minusSeconds(60));
} }
data.setTextValue(storageFile.getId().toString());
} else {
data.setTextValue(newFileId.toString()); data.setTextValue(newFileId.toString());
} }
} else { } else {
if (existingFileId != null){
//DO NOT Remove we can not be sure uf the description is copied
//this.storageFileService.updatePurgeAt(existingFileId, Instant.now().minusSeconds(60));
}
data.setTextValue(null); data.setTextValue(null);
} }
} else { } else {

View File

@ -0,0 +1,9 @@
diff a/dmp-backend/web/pom.xml b/dmp-backend/web/pom.xml (rejected hunks)
@@ -13,6 +13,7 @@
<groupId>eu.eudat</groupId>
<artifactId>dmp-backend</artifactId>
<version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<properties>

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,8 @@ const appRoutes: Routes = [
path: 'home', path: 'home',
loadChildren: () => import('./ui/dashboard/dashboard.module').then(m => m.DashboardModule), loadChildren: () => import('./ui/dashboard/dashboard.module').then(m => m.DashboardModule),
data: { data: {
breadcrumb: true breadcrumb: true,
title: 'GENERAL.TITLES.HOME'
} }
}, },
{ {

View File

@ -4,14 +4,14 @@
<div class="col-12"> <div class="col-12">
<mat-chip-grid #chipList ngDefaultControl class="chip-list"> <mat-chip-grid #chipList ngDefaultControl class="chip-list">
<ng-container *ngIf="value as values;"> <ng-container *ngIf="value as values;">
<mat-chip-row *ngFor="let value of values" [disabled]="disabled" [selectable]="selectable" [removable]="true" [ngClass]="computeClass(value)" class="chip-row"> <mat-chip-row *ngFor="let value of values" [disabled]="disabled" [selectable]="selectable" [removable]="true" [ngClass]="computeClass(value)" class="chip-row" [matTooltip]="_canRemoveItemMessage(getSelectedItem(value)) | translate" >
<ng-container *ngIf="getSelectedItem(value) as selectedItem;"> <ng-container *ngIf="getSelectedItem(value) as selectedItem;">
<ng-template #cellTemplate *ngIf="_selectedValueTemplate(selectedItem)" [ngTemplateOutlet]="_selectedValueTemplate(selectedItem)" [ngTemplateOutletContext]="{ <ng-template #cellTemplate *ngIf="_selectedValueTemplate(selectedItem)" [ngTemplateOutlet]="_selectedValueTemplate(selectedItem)" [ngTemplateOutletContext]="{
item: selectedItem item: selectedItem
}" /> }" />
<span *ngIf="!_selectedValueTemplate(selectedItem)" class="chip-text" [title]="_displayFn(selectedItem)">{{_displayFn(selectedItem)}}</span> <span *ngIf="!_selectedValueTemplate(selectedItem)" class="chip-text" [title]="_displayFn(selectedItem)">{{_displayFn(selectedItem)}}</span>
</ng-container> </ng-container>
<button matChipRemove *ngIf="!disabled" [disabled]="!_canRemoveItem(getSelectedItem(value))"[matTooltipDisabled]="_canRemoveItem(getSelectedItem(value))" [matTooltip]="_canRemoveItemMessage(getSelectedItem(value))" (click)="_removeSelectedItem(getSelectedItem(value), $event)"> <button matChipRemove *ngIf="!disabled && _canRemoveItem(getSelectedItem(value))" (click)="_removeSelectedItem(getSelectedItem(value), $event)">
<mat-icon>cancel</mat-icon> <mat-icon>cancel</mat-icon>
</button> </button>
</mat-chip-row> </mat-chip-row>

View File

@ -333,6 +333,7 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
writeValue(value: any): void { writeValue(value: any): void {
this.value = Array.isArray(value) ? value : null; this.value = Array.isArray(value) ? value : null;
// Update chips observable // Update chips observable
this.autocompleteInput.nativeElement.value = '';
this._items = null; this._items = null;
this.getSelectedItems(this.value); this.getSelectedItems(this.value);
} }
@ -434,16 +435,16 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
} }
_canRemoveItem(item: any): boolean { _canRemoveItem(item: any): boolean {
if(this.configuration.canRemoveItem != null) { if(item != null && this.configuration.canRemoveItem != null) {
return this.configuration.canRemoveItem(item).canRemove; return this.configuration.canRemoveItem(item)?.canRemove;
} }
return true; return true;
} }
_canRemoveItemMessage(item: any): string { _canRemoveItemMessage(item: any): string {
if(this.configuration.canRemoveItem != null) { if(item != null && this.configuration.canRemoveItem != null) {
const canRemoveResuslt = this.configuration.canRemoveItem(item); const canRemoveResuslt = this.configuration.canRemoveItem(item);
if (canRemoveResuslt.canRemove) return canRemoveResuslt.message; if (!canRemoveResuslt?.canRemove) return canRemoveResuslt.message;
} }
return null; return null;
} }
@ -491,3 +492,8 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp
} }
} }
export interface MultipleAutoCompleteCanRemoveItem {
canRemove: boolean;
message: string;
}

View File

@ -205,7 +205,7 @@
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button mat-list-item (click)="$event.stopPropagation();" style="font-style: italic;"> <button mat-list-item (click)="$event.stopPropagation();" style="font-style: italic;">
<img src="/assets/images/editor/icons/argos_entities.svg" class="input_icon" alt="Argos Entities icon"> <img src="/assets/images/editor/icons/internal_entities.svg" class="input_icon" alt="Internal Entities icon">
Argos Entities Argos Entities
</button> </button>
<mat-action-list class="ml-4"> <mat-action-list class="ml-4">

View File

@ -3,20 +3,21 @@
<div class="col-12"> <div class="col-12">
<div class="row"> <div class="row">
<mat-form-field class="col-6"> <mat-form-field class="col-12 col-md-6">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PRIMARY-COLOR' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PRIMARY-COLOR' | translate}}</mat-label>
<input matInput [formControl]="formGroup.get('cssColors')?.get('primaryColorInput')" required /> <input matInput [formControl]="formGroup.get('cssColors')?.get('primaryColorInput')" required />
<ngx-colors <ngx-colors
class="suffix" class="suffix"
matSuffix matSuffix
ngx-colors-trigger ngx-colors-trigger
[overlayClassName]="mr-1"
[formControl]="formGroup.get('cssColors')?.get('primaryColor')" [formControl]="formGroup.get('cssColors')?.get('primaryColor')"
></ngx-colors> ></ngx-colors>
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor')?.hasError('backendError')">{{formGroup.get('cssColors')?.get('primaryColor')?.getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor')?.hasError('backendError')">{{formGroup.get('cssColors')?.get('primaryColor')?.getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor')?.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor')?.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor')?.hasError('invalidColor')">{{'GENERAL.VALIDATION.INVALID-COLOR' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor')?.hasError('invalidColor')">{{'GENERAL.VALIDATION.INVALID-COLOR' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="col-6"> <mat-form-field class="col-12 col-md-6">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PRIMARY-COLOR-2' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PRIMARY-COLOR-2' | translate}}</mat-label>
<input matInput [formControl]="formGroup.get('cssColors')?.get('primaryColor2Input')" required /> <input matInput [formControl]="formGroup.get('cssColors')?.get('primaryColor2Input')" required />
<ngx-colors <ngx-colors
@ -29,7 +30,7 @@
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor2')?.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor2')?.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor2')?.hasError('invalidColor')">{{'GENERAL.VALIDATION.INVALID-COLOR' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor2')?.hasError('invalidColor')">{{'GENERAL.VALIDATION.INVALID-COLOR' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="col-6"> <mat-form-field class="col-12 col-md-6">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PRIMARY-COLOR-3' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PRIMARY-COLOR-3' | translate}}</mat-label>
<input matInput [formControl]="formGroup.get('cssColors')?.get('primaryColor3Input')" required /> <input matInput [formControl]="formGroup.get('cssColors')?.get('primaryColor3Input')" required />
<ngx-colors <ngx-colors
@ -42,7 +43,7 @@
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor3')?.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor3')?.hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor3')?.hasError('invalidColor')">{{'GENERAL.VALIDATION.INVALID-COLOR' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('cssColors')?.get('primaryColor3')?.hasError('invalidColor')">{{'GENERAL.VALIDATION.INVALID-COLOR' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="col-6"> <mat-form-field class="col-12 col-md-6">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.SECONDARY-COLOR' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.SECONDARY-COLOR' | translate}}</mat-label>
<input matInput [formControl]="formGroup.get('cssColors')?.get('secondaryColorInput')" required /> <input matInput [formControl]="formGroup.get('cssColors')?.get('secondaryColorInput')" required />
<ngx-colors <ngx-colors
@ -59,12 +60,11 @@
</div> </div>
<div class="col-12"> <div class="col-12">
<div class="row actions-row"> <div class="row actions-row">
<div class="col"></div> <div class="ml-auto col-auto" *ngIf="editorModel.id"><button class="normal-btn-sm" (click)="delete()">
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
</button> </button>
</div> </div>
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()"> <div class="ml-auto col-auto"><button class="normal-btn-sm" (click)="formSubmit()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
</button> </button>
</div> </div>

View File

@ -1,3 +1,7 @@
.css-colors { .css-colors {
} }
::ng-deep .mat-mdc-form-field-icon-suffix {
margin-right: 0.2em;
}

View File

@ -1,6 +1,6 @@
<div *ngIf="formGroup" class="container-fluid default-user-locale"> <div *ngIf="formGroup" class="container-fluid default-user-locale">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12 col-md-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.TIMEZONE' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.TIMEZONE' | translate}}</mat-label>
<mat-select [formControl]="this.formGroup.get('defaultUserLocale')?.get('timezone')" name="culture"> <mat-select [formControl]="this.formGroup.get('defaultUserLocale')?.get('timezone')" name="culture">
@ -11,6 +11,8 @@
<mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('timezone').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('timezone').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('timezone')?.hasError('backendError')">{{formGroup.get('defaultUserLocale')?.get('timezone')?.getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('timezone')?.hasError('backendError')">{{formGroup.get('defaultUserLocale')?.get('timezone')?.getError('backendError').message}}</mat-error>
</mat-form-field> </mat-form-field>
</div>
<div class="col-12 col-md-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CULTURE' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CULTURE' | translate}}</mat-label>
<mat-select [formControl]="this.formGroup.get('defaultUserLocale')?.get('culture')" name="culture"> <mat-select [formControl]="this.formGroup.get('defaultUserLocale')?.get('culture')" name="culture">
@ -21,6 +23,8 @@
<mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('culture').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('culture').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('culture')?.hasError('backendError')">{{formGroup.get('defaultUserLocale')?.get('culture')?.getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('defaultUserLocale')?.get('culture')?.hasError('backendError')">{{formGroup.get('defaultUserLocale')?.get('culture')?.getError('backendError').message}}</mat-error>
</mat-form-field> </mat-form-field>
</div>
<div class="col-12 col-md-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.LANGUAGE' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.LANGUAGE' | translate}}</mat-label>
<mat-select [formControl]="this.formGroup.get('defaultUserLocale')?.get('language')" name="language"> <mat-select [formControl]="this.formGroup.get('defaultUserLocale')?.get('language')" name="language">
@ -34,12 +38,11 @@
</div> </div>
<div class="col-12"> <div class="col-12">
<div class="row actions-row"> <div class="row actions-row">
<div class="col"></div> <div class="ml-auto col-auto" *ngIf="editorModel.id"><button class="normal-btn-sm" (click)="delete()">
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
</button> </button>
</div> </div>
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()"> <div class="ml-auto col-auto"><button class="normal-btn-sm" (click)="formSubmit()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
</button> </button>
</div> </div>

View File

@ -24,7 +24,7 @@
</div> </div>
</div> </div>
<div class="row" > <div class="row" >
<div class="col-6"> <div class="col-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.REPOSITORY-ID' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.REPOSITORY-ID' | translate}}</mat-label>
<input matInput type="text" name="repositoryId" [formControl]="source.get('repositoryId')" required> <input matInput type="text" name="repositoryId" [formControl]="source.get('repositoryId')" required>
@ -32,7 +32,7 @@
<mat-error *ngIf="source.get('repositoryId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('repositoryId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.URL' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -40,7 +40,7 @@
<mat-error *ngIf="source.get('url').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -48,7 +48,7 @@
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -56,7 +56,7 @@
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -64,7 +64,7 @@
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.SCOPE' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -72,7 +72,7 @@
<mat-error *ngIf="source.get('scope').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PDF-TRANSFORMER-ID' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.PDF-TRANSFORMER-ID' | translate}}</mat-label>
<input matInput type="text" name="pdfTransformerId" [formControl]="source.get('pdfTransformerId')" required> <input matInput type="text" name="pdfTransformerId" [formControl]="source.get('pdfTransformerId')" required>
@ -80,7 +80,7 @@
<mat-error *ngIf="source.get('pdfTransformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('pdfTransformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.RDA-TRANSFORMER-ID' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.RDA-TRANSFORMER-ID' | translate}}</mat-label>
<input matInput type="text" name="rdaTransformerId" [formControl]="source.get('rdaTransformerId')" required> <input matInput type="text" name="rdaTransformerId" [formControl]="source.get('rdaTransformerId')" required>
@ -94,12 +94,11 @@
</div> </div>
<div class="col-12"> <div class="col-12">
<div class="row actions-row"> <div class="row actions-row">
<div class="col"></div> <div class="ml-auto col-auto" *ngIf="editorModel.id"><button class="normal-btn-sm" (click)="delete()">
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
</button> </button>
</div> </div>
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()"> <div class="ml-auto col-auto"><button class="normal-btn-sm" (click)="formSubmit()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
</button> </button>
</div> </div>

View File

@ -18,3 +18,7 @@
} }
} }
} }
::ng-deep label {
margin: 0;
}

View File

@ -23,7 +23,7 @@
</div> </div>
</div> </div>
<div class="row" > <div class="row" >
<div class="col-6"> <div class="col-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.TRANSFORMER-ID' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.TRANSFORMER-ID' | translate}}</mat-label>
<input matInput type="text" name="transformerId" [formControl]="source.get('transformerId')" required> <input matInput type="text" name="transformerId" [formControl]="source.get('transformerId')" required>
@ -31,7 +31,7 @@
<mat-error *ngIf="source.get('transformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="source.get('transformerId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-6"> <div class="col-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.URL' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -39,7 +39,7 @@
<mat-error *ngIf="source.get('url').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -47,7 +47,7 @@
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -55,7 +55,7 @@
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -63,7 +63,7 @@
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'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-12 col-md-6">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'TENANT-CONFIGURATION-EDITOR.FIELDS.SCOPE' | translate}}</mat-label> <mat-label>{{'TENANT-CONFIGURATION-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>
@ -78,11 +78,11 @@
<div class="col-12"> <div class="col-12">
<div class="row actions-row"> <div class="row actions-row">
<div class="col"></div> <div class="col"></div>
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()"> <div class="col-auto" *ngIf="editorModel.id"><button class="normal-btn-sm" (click)="delete()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
</button> </button>
</div> </div>
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()"> <div class="col-auto"><button class="normal-btn-sm" (click)="formSubmit()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
</button> </button>
</div> </div>

View File

@ -7,25 +7,25 @@
</ngx-dropzone-preview> </ngx-dropzone-preview>
</ngx-dropzone> </ngx-dropzone>
<div class="col-12 d-flex justify-content-center attach-btn"> <div class="col-12 d-flex justify-content-center attach-btn">
<button *ngIf="!formGroup.get('logo')?.get('storageFileId')?.value" mat-button (click)="drop.showFileSelector()" type="button" class="attach-file-btn" [disabled]="formGroup.get('logo')?.get('storageFileId')?.disabled"> <button *ngIf="!formGroup.get('logo')?.get('storageFileId')?.value" (click)="drop.showFileSelector()" mat-button type="button" class="attach-file" [disabled]="formGroup.get('logo')?.get('storageFileId')?.disabled">
<mat-icon class="mr-2">upload</mat-icon> <mat-icon class="mr-2">upload</mat-icon>
<mat-label>{{ "TENANT-CONFIGURATION-EDITOR.ACTIONS.UPLOAD" | translate }}</mat-label> <span>{{ "TENANT-CONFIGURATION-EDITOR.ACTIONS.UPLOAD" | translate }}</span>
</button> </button>
<button *ngIf="formGroup.get('logo')?.get('storageFileId')?.value" mat-button (click)="download(formGroup.get('logo')?.get('storageFileId')?.value)" type="button" class="attach-file-btn"> <button *ngIf="formGroup.get('logo')?.get('storageFileId')?.value" (click)="download(formGroup.get('logo')?.get('storageFileId')?.value)" mat-button type="button" class="attach-file">
<mat-icon class="mr-2">download</mat-icon> <mat-icon class="mr-2">download</mat-icon>
<mat-label>{{ "TENANT-CONFIGURATION-EDITOR.ACTIONS.DOWNLOAD" | translate }}</mat-label> <span>{{ "TENANT-CONFIGURATION-EDITOR.ACTIONS.DOWNLOAD" | translate }}</span>
</button> </button>
</div> </div>
</div> </div>
<div class="col-12"> <div class="col-12">
<div class="row actions-row"> <div class="row actions-row">
<div class="col"></div> <div class="col"></div>
<div class="col-auto" *ngIf="editorModel.id"><button mat-raised-button color="primary" (click)="delete()"> <div class="col-auto" *ngIf="editorModel.id"><button class="normal-btn-sm" (click)="delete()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.RESET-TO-DEFAULT' | translate}}
</button> </button>
</div> </div>
<div class="col-auto"><button mat-raised-button color="primary" (click)="formSubmit()"> <div class="col-auto"><button class="normal-btn-sm" (click)="formSubmit()">
{{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}} {{'TENANT-CONFIGURATION-EDITOR.ACTIONS.SAVE' | translate}}
</button> </button>
</div> </div>

View File

@ -1,3 +1,19 @@
.logo {
.attach-btn {
top: -20px;
}
.attach-file {
width: 156px;
height: 44px;
color: #ffffff;
background: var(--primary-color) 0% 0% no-repeat padding-box;
box-shadow: 0px 3px 6px #1e202029;
border-radius: 30px;
}
.attach-file:hover {
background-color: #ffffff;
border: 1px solid var(--primary-color);
color: var(--primary-color);
} }

View File

@ -8,20 +8,25 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col-12">
<mat-card appearance="outlined"> <mat-card appearance="outlined">
<mat-card-header> <mat-card-header>
<mat-card-title>{{'TENANT-CONFIGURATION-EDITOR.TITLE' | translate}}</mat-card-title> <mat-card-title>{{'TENANT-CONFIGURATION-EDITOR.TITLE' | translate}}</mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<div class="row"> <div class="row mt-3">
<mat-accordion class="col-12 headers-align"> <div class="col-12">
<mat-accordion class="headers-align">
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title>{{'TENANT-CONFIGURATION-EDITOR.DEFAULT-USER-LOCALE.TITLE' | translate}}</mat-panel-title> <mat-panel-title>{{'TENANT-CONFIGURATION-EDITOR.DEFAULT-USER-LOCALE.TITLE' | translate}}</mat-panel-title>
<mat-panel-description>{{'TENANT-CONFIGURATION-EDITOR.DEFAULT-USER-LOCALE.HINT' | translate}}</mat-panel-description> <mat-panel-description>{{'TENANT-CONFIGURATION-EDITOR.DEFAULT-USER-LOCALE.HINT' | translate}}</mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div class="mt-3">
<app-tenant-configuration-default-user-locale-editor></app-tenant-configuration-default-user-locale-editor> <app-tenant-configuration-default-user-locale-editor></app-tenant-configuration-default-user-locale-editor>
</div>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel> <mat-expansion-panel>
@ -63,8 +68,11 @@
</mat-accordion> </mat-accordion>
</div> </div>
</div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>

View File

@ -1,81 +1,3 @@
<div class="login-bg d-flex flex-column align-items-center justify-content-center" [ngClass]="{'login-screen': !mergeUsers}"> <div>
</div> </div>
<!-- <div class="login-screen login-bg d-flex flex-column align-items-center justify-content-center">
<div class="login-logo"></div>
<div class="card login-card">
<div class="container card-header">
<div *ngIf="hasGoogleOauth()" class="row social-btns">
<div class="col-auto">
<button mat-icon-button id="googleSignInButton" class="login-social-button">
<i class="fa fa-google"></i>
</button>
</div>
<div *ngIf="hasLinkedInOauth()" class="col-auto">
<button mat-icon-button class="login-social-button">
<i class="fa fa-linkedin" (click)="linkedInLogin()"></i>
</button>
</div>
<div *ngIf="hasFacebookOauth()" class="col-auto">
<button mat-icon-button (click)="facebookLogin()" class="login-social-button">
<i class="fa fa-facebook-square"></i>
</button>
</div>
<div *ngIf="hasTwitterOauth()" class="col-auto">
<button mat-icon-button (click)="twitterLogin()" class="login-social-button">
<i class="fa fa-twitter"></i>
</button>
</div>
</div>
<div class="w-100"></div>
<div class="row pt-2 mb-4 accesss-methods"> -->
<!-- <div class="col justify-content-center"> -->
<!-- <div *ngIf="hasB2AccessOauth()" class="col-auto logo">
<button class="b2access-button" mat-icon-button (click)="b2AccessLogin()" class="login-social-button">
<span class="iconmedium"></span> -->
<!-- <span></span> -->
<!-- </button>
</div>
<div *ngIf="hasOrcidOauth()" class="col-auto orcid-logo">
<button class="orcid-button" mat-icon-button (click)="orcidLogin()" class="login-social-button">
<span class="orcidIconMedium"></span> -->
<!-- <span></span> -->
<!-- </button>
</div>
<div *ngIf="hasOpenAireOauth()" class="col-auto openaire-logo">
<button class="openaire-button" mat-icon-button (click)="openaireLogin()" class="login-social-button">
<span class="openaireIcon"></span> -->
<!-- <span></span> -->
<!-- </button>
</div> -->
<!-- </div> -->
<!-- </div>
<div *ngIf="hasConfigurableProviders()" class="row pt-2 mb-4 accesss-methods">
<div *ngFor="let provider of this.configurableProviderService.providers" class="col-auto configurable-logo">
<button mat-icon-button class="configurable-button" (click)="configurableLogin(provider)" class="login-social-button">
<span class="configurableIcon">{{provider.name}}</span>
</button>
</div>
</div>
<div *ngIf="hasZenodoOauth()" class="col-auto zenodo-logo">
<button class="zenodo-button" mat-icon-button (click)="zenodoLogin()" class="login-social-button">
<span class="zenodoIcon"></span> -->
<!-- <span></span> -->
<!-- </button>
</div>
</div>
<div class="card-footer">
<h4 class="text-uppercase">
<strong>{{ 'HOME.LOGIN.TITLE' | translate }}</strong>
</h4>
<br />
<h5>{{ 'HOME.LOGIN.TEXT' | translate }}</h5>
</div>
</div>
</div> -->

View File

@ -1,401 +0,0 @@
.login-screen {
// margin-top: 70px;
min-height: calc(100vh - 10px);
}
.login-card {
// width: auto;
width: 510.92px;
height: 517.44px;
margin-top: 0;
}
.card {
// box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.14);
box-shadow: 0px 1px 3px #00000038;
border-radius: 6px;
color: rgba(0, 0, 0, 0.87);
background: #fff;
}
.card-raised {
box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.42), 0 4px 25px 0px rgba(0, 0, 0, 0.12),
0 8px 10px -5px rgba(0, 0, 0, 0.2);
}
.page-title {
margin-top: 40px;
}
.title {
text-align: center;
font: Bold 38px/82px Roboto;
height: 60px;
letter-spacing: -0.95px;
color: var(--primary-color-2);
margin-top: 11px;
margin-bottom: 21px;
}
.line {
border-top: 5px solid var(--primary-color-2);
width: 116px;
margin-bottom: 97px;
}
.container {
height: 100%;
position: relative;
z-index: 1;
padding: 15px 30px;
}
@-webkit-keyframes card {
from {
top: -40px;
}
to {
top: 0;
}
}
@keyframes card {
from {
top: -40px;
}
to {
top: 0;
}
}
.card {
position: relative;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 50px;
top: -50px;
-webkit-animation-name: card;
-moz-animation-name: card;
-o-animation-name: card;
animation-name: card;
-webkit-animation-duration: 600ms;
-moz-animation-duration: 600ms;
-o-animation-duration: 600ms;
animation-duration: 600ms;
-webkit-animation-fill-mode: forwards;
-moz-animation-fill-mode: forwards;
-o-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
.card-header {
position: relative;
overflow: hidden;
top: -50px;
width: 100%;
min-height: 200px;
padding: 25px;
border-radius: 3px;
background: #0c7489;
// background: rgb(0, 112, 192);
display: flex;
flex-direction: column;
align-items: center;
}
.card-header h4 {
font-weight: 400;
color: #fff;
margin-bottom: 25px;
margin-top: 5px;
}
// .social-btns {
// height: 6em;
// justify-content: center;
// }
// .social-btns i {
// font-size: 2.5em;
// padding: 0.8em;
// color: #fff;
// }
// .social-btns .col {
// flex-grow: 0;
// }
// .social-btns i:hover {
// background-color: #f5f5f51c;
// border-radius: 60%;
// }
.accesss-methods {
height: 5em;
justify-content: center;
}
.accesss-methods .col-auto:hover {
background-color: #f5f5f51c;
border-radius: 100%;
}
.accesss-methods .logo {
flex-grow: 0;
padding-top: 21px;
padding-bottom: 21px;
}
.accesss-methods .openaire-logo {
flex-grow: 0;
padding-top: 21px;
padding-bottom: 21px;
margin-top: 5px;
height: 90px;
}
.accesss-methods .orcid-logo {
height: 75px;
padding-top: 8px;
margin-top: 13px;
}
.accesss-methods .configurable-logo {
flex-grow: 0;
padding-top: 21px;
padding-bottom: 21px;
margin-top: 5px;
height: 90px;
}
.accesss-methods .zenodo-logo {
height: 75px;
padding-top: 8px;
margin-top: 13px;
}
.tip {
margin-top: -20px;
}
.form-row,
.card-form,
.mat-form-field {
width: 100%;
}
.card-form {
padding: 5px;
}
.form-row {
position: relative;
display: flex;
align-items: center;
margin-top: 13px;
}
.form-row i {
position: relative;
top: -5px;
margin-right: 15px;
color: #555;
}
.card-footer {
padding: 0;
border-radius: 0;
align-items: start;
flex-direction: column;
}
.card-footer button {
color: #e91e63;
}
.btn span.icon {
background: url(img/b2access_small.png) no-repeat;
float: left;
width: 45px;
height: 25px;
}
span.googleIcon {
background: url("../../../../assets/images/argos-login/NoPath\ -\ Copy\ \(2\).png") no-repeat;
}
span.facebookIcon {
background: url("../../../../assets/images/argos-login/NoPath\ -\ Copy\ \(4\).png") no-repeat;
}
span.twitterIcon {
background: url("../../../../assets/images/argos-login/NoPath\ -\ Copy\ \(5\).png") no-repeat;
}
span.googleIcon,
span.facebookIcon,
span.twitterIcon {
float: left;
transform: scale(0.8);
width: 45px;
height: 45px;
}
#googleSignInButton,
.facebookIconButton,
.twitterIconButton,
.openaireIconButton,
.orcidIconMediumButton,
.iconmediumButton {
width: 60px !important;
height: 60px !important;
}
#googleSignInButton:hover,
.facebookIconButton:hover,
.twitterIconButton:hover,
.openaireIconButton:hover,
.orcidIconMediumButton:hover,
.iconmediumButton:hover {
// background-color: var(--primary-color)3b;
background-color: #ececec;
border-radius: 60%;
}
span.openaireIcon {
background: url("../../../../assets/images/argos-login/NoPath\ -\ Copy\ \(6\).png") no-repeat;
background-position: center;
float: right;
transform: scale(0.8);
width: 50px;
height: 45px;
}
span.orcidIconMedium {
background: url("../../../../assets/images/argos-login/NoPath\ -\ Copy.png") no-repeat;
background-position: center;
float: left;
transform: scale(0.8);
width: 50px;
height: 45px;
}
span.iconmedium {
background: url("../../../../assets/images/argos-login/NoPath\ -\ Copy\ \(7\).png") no-repeat;
float: left;
transform: scale(0.85);
width: 67px;
height: 40px;
}
span.configurableIcon {
float: right;
transform: scale(0.85);
width: 50px;
height: 50px;
}
span.zenodoIcon {
background: url("../../../../assets/images/argos-login/zenodo-gradient-200.png") no-repeat;
background-position: center;
float: right;
transform: scale(0.7);
width: 200px;
height: 80px;
}
.b2access-button {
margin-top: 10px;
width: fit-content;
}
.orcid-button {
margin-top: 10px;
width: fit-content;
}
.openaire-button {
margin-top: 10px;
width: fit-content;
}
.configurable-button {
margin-top: 10px;
width: fit-content;
}
.zenodo-button {
margin-top: 10px;
width: fit-content;
}
// .login-logo {
// background: url(img/open-dmp.png) no-repeat;
// width: 273px;
// height: 300px;
// background-size: cover;
// }
.laptop-img {
background: url("../../../../assets/images/login-bottom-image.png") no-repeat;
width: 164px;
height: 200px;
position: relative;
top: -140px;
margin-bottom: -140px;
left: 243px;
border-top: none;
}
.login-bg {
// background: url(img/login_bg.png) no-repeat;
background-size: cover;
}
.login-social-button {
width: auto;
height: auto;
}
@media (min-width: 1200px) {
.container {
width: 100%;
}
}
@media (min-width: 992px) {
.container {
width: 100%;
}
}
@media (min-width: 768px) {
.container {
width: 100%;
}
}
@media (min-width: 401px) and (max-width: 560px) {
.social-btns i {
padding: 0.4em !important;
}
.accesss-methods {
padding-top: 1em;
}
}
@media (min-width: 0px) and (max-width: 400px) {
.social-btns i {
padding: 0.4em !important;
}
.card-header {
height: 350px !important;
}
.accesss-methods {
padding-top: 1em;
}
}

View File

@ -18,7 +18,6 @@ export class LoginComponent extends BaseComponent implements OnInit {
public auth2: any; public auth2: any;
private returnUrl: string; private returnUrl: string;
//public cofigurableProviders: ConfigurableProvider[];
constructor( constructor(
private zone: NgZone, private zone: NgZone,

View File

@ -112,7 +112,7 @@
<span class="material-icons">chevron_right</span> <span class="material-icons">chevron_right</span>
</div> </div>
<button [disabled]="saving" (click)="saveAndClose()" *ngIf="(step === maxStep) && !isLocked && formGroup.get('descriptionTemplateId').value && !viewOnly" mat-raised-button type="button" class="col-auto stepper-btn add-description-btn ml-auto"> <button [disabled]="saving" (click)="saveAndClose()" *ngIf="(step === maxStep) && !isLocked && formGroup.get('descriptionTemplateId').value && !viewOnly" mat-raised-button type="button" class="col-auto stepper-btn add-description-btn ml-auto">
{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE' | translate }} {{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CLOSE' | translate }}
</button> </button>
</div> </div>
<div class="col-auto pr-0"> <div class="col-auto pr-0">

View File

@ -49,6 +49,7 @@ import { DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type'; import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
import { nameof } from 'ts-simple-nameof'; import { nameof } from 'ts-simple-nameof';
import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
import { TableOfContentsValidationService } from './table-of-contents/services/table-of-contents-validation-service';
@Component({ @Component({
selector: 'app-description-editor-component', selector: 'app-description-editor-component',
@ -60,6 +61,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
isNew = true; isNew = true;
isDeleted = false; isDeleted = false;
isCopy = false;
item: Description; item: Description;
fieldsetIdWithFocus: string; fieldsetIdWithFocus: string;
fileTransformerEntityTypeEnum = FileTransformerEntityType; fileTransformerEntityTypeEnum = FileTransformerEntityType;
@ -99,6 +101,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
private dmpService: DmpService, private dmpService: DmpService,
public visibilityRulesService: VisibilityRulesService, public visibilityRulesService: VisibilityRulesService,
public fileTransformerService: FileTransformerService, public fileTransformerService: FileTransformerService,
public tocValidationService: TableOfContentsValidationService
) { ) {
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService, lockService, authService, configurationService); super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService, lockService, authService, configurationService);
@ -124,10 +127,12 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
const itemId = params['id']; const itemId = params['id'];
const dmpId = params['dmpId']; const dmpId = params['dmpId'];
const copyDmpId = params['copyDmpId'];
const dmpSectionId = params['dmpSectionId']; const dmpSectionId = params['dmpSectionId'];
const isPublicDescription = params['public']; const isPublicDescription = params['public'];
const newDmpId = params['newDmpId']; const newDmpId = params['newDmpId'];
if(copyDmpId && !dmpId && dmpSectionId) this.isCopy = true;
@ -623,12 +628,13 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
refreshData(): void { refreshData(): void {
this.getItem(this.editorModel.id, (data: Description) => this.prepareForm(data)); this.getItem(this.editorModel.id, (data: Description) => this.prepareForm(data));
this.tocValidationService.validateForm();
} }
refreshOnNavigateToData(id?: Guid): void { refreshOnNavigateToData(id?: Guid): void {
this.formGroup.markAsPristine(); this.formGroup.markAsPristine();
if (this.isNew) { if (this.isNew || this.isCopy) {
let route = []; let route = [];
route.push('/descriptions/edit/' + id); route.push('/descriptions/edit/' + id);
this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route }); this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route });
@ -841,7 +847,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
Object.keys(formControl.errors).forEach(key => { Object.keys(formControl.errors).forEach(key => {
if (key === 'required') { if (key === 'required') {
// errors.push(this.language.instant(name + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED'))); // errors.push(this.language.instant(name + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED')));
if(name == 'label') errors.push(this.language.instant(this.language.instant('DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION') + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED'))); if(name == 'label') errors.push(this.language.instant(this.language.instant('DESCRIPTION-EDITOR.BASE-INFO.FIELDS.TITLE') + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED')));
else if(name == 'descriptionTemplateId') errors.push(this.language.instant(this.language.instant('DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION-TEMPLATE') + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED'))); else if(name == 'descriptionTemplateId') errors.push(this.language.instant(this.language.instant('DESCRIPTION-EDITOR.BASE-INFO.FIELDS.DESCRIPTION-TEMPLATE') + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED')));
} }
else if (key === 'email') { else if (key === 'email') {
@ -1016,6 +1022,10 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
} }
}); });
this.formGroup.get('properties').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(next => this.tocValidationService.validateForm());
// // const labelSubscription = // // const labelSubscription =
// this.formGroup.get('label').valueChanges // this.formGroup.get('label').valueChanges
// .pipe(takeUntil(this._destroyed)) // .pipe(takeUntil(this._destroyed))
@ -1261,6 +1271,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
finalize() { finalize() {
this.formService.removeAllBackEndErrors(this.formGroup); this.formService.removeAllBackEndErrors(this.formGroup);
this.formService.touchAllFormFields(this.formGroup); this.formService.touchAllFormFields(this.formGroup);
this.tocValidationService.validateForm();
if (!this.isFormValid()) { if (!this.isFormValid()) {
this.dialog.open(FormValidationErrorsDialogComponent, { this.dialog.open(FormValidationErrorsDialogComponent, {
data: { data: {

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { AppPermission } from '@app/core/common/enum/permission.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplate, DescriptionTemplateBaseFieldData, DescriptionTemplateDefinition, DescriptionTemplateExternalDatasetData, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateMultiplicity, DescriptionTemplatePage, DescriptionTemplateReferenceTypeData, DescriptionTemplateRule, DescriptionTemplateSection, DescriptionTemplateSelectData, DescriptionTemplateSelectOption, DescriptionTemplateUploadData, DescriptionTemplateUploadOption } from '@app/core/model/description-template/description-template'; import { DescriptionTemplate, DescriptionTemplateBaseFieldData, DescriptionTemplateDefinition, DescriptionTemplateExternalDatasetData, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateMultiplicity, DescriptionTemplatePage, DescriptionTemplateReferenceTypeData, DescriptionTemplateRule, DescriptionTemplateSection, DescriptionTemplateSelectData, DescriptionTemplateSelectOption, DescriptionTemplateUploadData, DescriptionTemplateUploadOption } from '@app/core/model/description-template/description-template';
import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionReferenceData, DescriptionTag } from '@app/core/model/description/description'; import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionReferenceData, DescriptionTag } from '@app/core/model/description/description';
@ -204,8 +205,12 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
return this.dmpService.getSingle(Guid.parse(copyDmpId), DescriptionEditorResolver.dmpLookupFields()).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed), concatMap(dmp => { return this.dmpService.getSingle(Guid.parse(copyDmpId), DescriptionEditorResolver.dmpLookupFields()).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed), concatMap(dmp => {
return this.descriptionService.getSingle(Guid.parse(id), DescriptionEditorResolver.cloneLookupFields()).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed), map(description => { return this.descriptionService.getSingle(Guid.parse(id), DescriptionEditorResolver.cloneLookupFields()).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed), map(description => {
description.id = null;
description.hash = null;
description.status = DescriptionStatus.Draft;
description.dmp = dmp; description.dmp = dmp;
description.dmpDescriptionTemplate = { description.dmpDescriptionTemplate = {
id: dmp.dmpDescriptionTemplates.filter(x => x.sectionId == Guid.parse(dmpSectionId) && x.descriptionTemplateGroupId == description.descriptionTemplate.groupId)[0].id,
sectionId: Guid.parse(dmpSectionId) sectionId: Guid.parse(dmpSectionId)
} }
return description; return description;

View File

@ -21,7 +21,8 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}) }),
title: 'DESCRIPTION-EDITOR.TITLE-EDIT-DESCRIPTION'
// , // ,
// authContext: { // authContext: {
// permissions: [AppPermission.EditDescription] // permissions: [AppPermission.EditDescription]
@ -39,7 +40,8 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}) }),
title: 'DESCRIPTION-EDITOR.TITLE-EDIT-DESCRIPTION'
// , // ,
// authContext: { // authContext: {
// permissions: [AppPermission.EditDescription] // permissions: [AppPermission.EditDescription]
@ -57,7 +59,8 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}) }),
title: 'DESCRIPTION-EDITOR.TITLE-NEW'
// , // ,
// authContext: { // authContext: {
// permissions: [AppPermission.EditDescription] // permissions: [AppPermission.EditDescription]
@ -75,7 +78,8 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}) }),
title: 'DESCRIPTION-EDITOR.TITLE-NEW'
// , // ,
// authContext: { // authContext: {
// permissions: [AppPermission.EditDescription] // permissions: [AppPermission.EditDescription]

View File

@ -82,7 +82,7 @@
</div> </div>
</div> </div>
<div *ngSwitchCase="descriptionTemplateFieldTypeEnum.CHECK_BOX" class="col-12"> <div *ngSwitchCase="descriptionTemplateFieldTypeEnum.CHECK_BOX" class="col-12">
<mat-checkbox [formControl]="propertiesFormGroup?.get(field.id).get('textValue')" [required]="isRequired"> <mat-checkbox [formControl]="propertiesFormGroup?.get(field.id).get('textValue')">
{{field.data.label}}</mat-checkbox> {{field.data.label}}</mat-checkbox>
<mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error> <mat-error *ngIf="propertiesFormGroup?.get(field.id).get('textValue').hasError('backendError')">{{propertiesFormGroup?.get(field.id).get('textValue').getError('backendError').message}}</mat-error>
</div> </div>

View File

@ -164,7 +164,7 @@ export class DescriptionFormFieldComponent extends BaseComponent implements OnIn
// } // }
break; break;
case DescriptionTemplateFieldType.CHECK_BOX: case DescriptionTemplateFieldType.CHECK_BOX:
if (this.propertiesFormGroup?.get(this.field.id).get('textValue').value == "false") this.propertiesFormGroup?.get(this.field.id).get('textValue').setValue(undefined); this.propertiesFormGroup?.get(this.field.id).get('textValue').setValue(this.propertiesFormGroup?.get(this.field.id).get('textValue').value === 'true');
break; break;
} }

View File

@ -0,0 +1,13 @@
import { EventEmitter, Injectable } from "@angular/core";
@Injectable()
export class TableOfContentsValidationService {
private _validateFormEvent: EventEmitter<any> = new EventEmitter<any>();
get validateFormEvent(): EventEmitter<any> {
return this._validateFormEvent;
}
validateForm(): void {
this._validateFormEvent.emit();
}
}

View File

@ -6,6 +6,7 @@ import { ToCEntry } from '../models/toc-entry';
import { ToCEntryType } from '../models/toc-entry-type.enum'; import { ToCEntryType } from '../models/toc-entry-type.enum';
import { DescriptionFieldIndicator } from '../../description-editor.model'; import { DescriptionFieldIndicator } from '../../description-editor.model';
import { Observable, Subscription, map } from 'rxjs'; import { Observable, Subscription, map } from 'rxjs';
import { TableOfContentsValidationService } from '../services/table-of-contents-validation-service';
@Component({ @Component({
selector: 'table-of-contents-internal', selector: 'table-of-contents-internal',
@ -34,8 +35,7 @@ export class TableOfContentsInternal implements OnInit, OnDestroy {
tocEntriesStateSubscriptions: Subscription[] = []; tocEntriesStateSubscriptions: Subscription[] = [];
tocEntriesStateMap: Map<string, boolean> = new Map<string, boolean>(); tocEntriesStateMap: Map<string, boolean> = new Map<string, boolean>();
constructor() { constructor(private tocValidationService: TableOfContentsValidationService) { }
}
ngOnInit(): void { ngOnInit(): void {
// console.log('component created' + JSON.stringify(this.tocentries)); // console.log('component created' + JSON.stringify(this.tocentries));
@ -90,10 +90,10 @@ export class TableOfContentsInternal implements OnInit, OnDestroy {
refreshErrorIndicators(): void { refreshErrorIndicators(): void {
this.updatedMap = this.updateMap(this.tocentries, this.parentMap); this.updatedMap = this.updateMap(this.tocentries, this.parentMap);
for (let entry of this.tocentries) { for (let entry of this.tocentries) {
this.tocEntriesStateMap.set(entry.id, false); this.tocEntriesStateMap.set(entry.id, this.hasErrors(entry.id));
this.tocEntriesStateSubscriptions.push( this.tocEntriesStateSubscriptions.push(
this.propertiesFormGroup.statusChanges this.tocValidationService.validateFormEvent
.pipe(map(() => this.hasErrors(entry.id))) .pipe(map(() => this.hasErrors(entry.id)))
.subscribe(next => { .subscribe(next => {
this.tocEntriesStateMap.set(entry.id, next); this.tocEntriesStateMap.set(entry.id, next);

View File

@ -5,11 +5,12 @@ import { TableOfContentsInternal } from './table-of-contents-internal/table-of-c
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service'; import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
import { TableOfContentsComponent } from './table-of-contents.component'; import { TableOfContentsComponent } from './table-of-contents.component';
import { TableOfContentsValidationService } from './services/table-of-contents-validation-service';
@NgModule({ @NgModule({
imports: [CommonModule, RouterModule, MatIconModule], imports: [CommonModule, RouterModule, MatIconModule],
declarations: [TableOfContentsComponent, TableOfContentsInternal], declarations: [TableOfContentsComponent, TableOfContentsInternal],
exports: [TableOfContentsComponent], exports: [TableOfContentsComponent],
providers: [VisibilityRulesService] providers: [VisibilityRulesService, TableOfContentsValidationService]
}) })
export class TableOfContentsModule { } export class TableOfContentsModule { }

View File

@ -1,12 +1,3 @@
.header-image {
background: url("/assets/images/new-dashboard-bg.png") no-repeat;
background-size: cover;
margin-top: 70px;
min-height: 15em;
position: relative;
}
.header-title { .header-title {
text-align: left; text-align: left;
font-size: 1.25rem; font-size: 1.25rem;

View File

@ -52,7 +52,7 @@
} }
.id-btn { .id-btn {
background: url("../../../../assets/images/NoPath.png") no-repeat center; background: url("../../../../assets/images/orcid.png") no-repeat center;
width: 1em; width: 1em;
margin-right: 0.3em; margin-right: 0.3em;
align-self: center; align-self: center;

View File

@ -78,19 +78,26 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-auto dmp-stepper" *ngIf="this.step != 0"> <!-- <div class="col-auto dmp-stepper" *ngIf="this.step != 0"> -->
<div class="stepper-title">{{'DMP-EDITOR.TITLE' | translate}}</div> <div class="col-12 col-md-3 pr-md-0" *ngIf="this.step != 0">
<div class="stepper-options"> <div class="row stepper-title">
<div class="col-12 pl-0">
<span>{{'DMP-EDITOR.TITLE' | translate}}</span>
</div>
</div>
<div class="row stepper-options">
<!-- try col-12 too -->
<div class="col-auto">
<ol class="stepper-list" start="1"> <ol class="stepper-list" start="1">
<div *ngIf="selectedBlueprint?.definition && this.step !== 0"> <div *ngIf="selectedBlueprint?.definition && this.step !== 0">
<!-- <div *ngIf="selectedBlueprint?.definition"> --> <!-- <div *ngFor="let section of selectedBlueprint?.definition?.sections; let i=index"> -->
<div *ngFor="let section of selectedBlueprint?.definition?.sections; let i=index"> <ng-container *ngFor="let section of selectedBlueprint?.definition?.sections; let i=index">
<li (click)="changeStep(i + 1)" [ngClass]="{'active': this.step === (i + 1), 'text-danger': hasErrors(section.id) }">{{section.label}}</li> <li (click)="changeStep(i + 1)" [ngClass]="{'active': this.step === (i + 1), 'text-danger': hasErrors(section.id) }">{{section.label}}</li>
<ol class="descriptionsInSection"> <ol class="descriptionsInSection">
<li *ngFor="let description of descriptionsInSection(section.id); let descriptionIndex = index" (click)="editDescription(description.id, false)" class="active-description"> <li *ngFor="let description of descriptionsInSection(section.id); let descriptionIndex = index" (click)="editDescription(description.id, false)" class="active-description">
<div class="d-flex flex-direction-row"> <div class="d-flex flex-direction-row">
<div class="label" matTooltip="{{description.label}}">{{'DMP-EDITOR.DESCRIPTION' | translate}}: {{ description.label }}</div> <div class="label" matTooltip="{{description.label}}">{{'DMP-EDITOR.DESCRIPTION' | translate}}: {{ description.label }}</div>
<mat-icon *ngIf="description.status !== descriptionStatusEnum.Finalized && canDeleteSection(section.id) && !formGroup.disabled" [ngClass]="{'drag-handle-disabled': formGroup.disabled}" class="ml-2 mr-2 remove-description size-16" matTooltip="{{'DMP-EDITOR.ACTIONS.DELETE' | translate}}" (click)="$event.stopPropagation(); removeDescription(description.id)">close</mat-icon> <div (click)="$event.stopPropagation(); removeDescription(description.id)"><mat-icon *ngIf="description.status !== descriptionStatusEnum.Finalized && canDeleteSection(section.id) && !formGroup.disabled" [ngClass]="{'drag-handle-disabled': formGroup.disabled}" class="ml-2 mr-2 remove-description size-16" matTooltip="{{'DMP-EDITOR.ACTIONS.DELETE' | translate}}">close</mat-icon></div>
<mat-icon *ngIf="description.status === descriptionStatusEnum.Finalized" class="ml-2 mr-2 status-icon check-icon size-16" matTooltip="{{'TYPES.DESCRIPTION-STATUS.FINALIZED' | translate}}">check</mat-icon> <mat-icon *ngIf="description.status === descriptionStatusEnum.Finalized" class="ml-2 mr-2 status-icon check-icon size-16" matTooltip="{{'TYPES.DESCRIPTION-STATUS.FINALIZED' | translate}}">check</mat-icon>
</div> </div>
</li> </li>
@ -102,26 +109,35 @@
</a> </a>
</li> </li>
</ul> </ul>
</div> </ng-container>
</div> </div>
</ol> </ol>
</div> </div>
<div class="stepper-actions" *ngIf="this.step !== 0"> </div>
<div mat-raised-button type="button" class="col-auto previous stepper-btn mr-2 ml-auto" [ngClass]="{'previous-disabled': this.step === 1}" (click)="previousStep()"> <!-- TODO -->
<div class="row justify-content-around" *ngIf="this.step !== 0">
<div class="col-auto mb-1">
<div mat-raised-button type="button" class="previous stepper-btn mr-2" [ngClass]="{'previous-disabled': this.step === 1}" (click)="previousStep()">
<span class="material-icons">chevron_left</span> <span class="material-icons">chevron_left</span>
<div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div> <div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div>
</div> </div>
</div>
<div class="col-auto mb-1">
<div *ngIf="this.step < this.maxSteps" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxSteps, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()"> <div *ngIf="this.step < this.maxSteps" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxSteps, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()">
<div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div> <div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div>
<span class="material-icons">chevron_right</span> <span class="material-icons">chevron_right</span>
</div> </div>
</div> </div>
<div class="col-auto pr-0" *ngIf="this.step !== 0"> </div>
<!-- TODO -->
<div class="row" *ngIf="this.step !== 0">
<div class="col-12">
<app-dmp-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup"></app-dmp-form-progress-indication> <app-dmp-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup"></app-dmp-form-progress-indication>
</div> </div>
</div> </div>
</div>
<div class="col-auto form" id="editor-form" *ngIf="this.step !== 0"> <div class="col-12 col-md form" id="editor-form" *ngIf="this.step !== 0">
<div *ngIf="selectedBlueprint?.definition"> <div *ngIf="selectedBlueprint?.definition">
<div *ngFor="let section of selectedBlueprint?.definition?.sections; let i=index"> <div *ngFor="let section of selectedBlueprint?.definition?.sections; let i=index">
<div class="section-info" [hidden]="this.step !== (i + 1)"> <div class="section-info" [hidden]="this.step !== (i + 1)">
@ -299,8 +315,7 @@
<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' [formControl]="formGroup.get('descriptionTemplates').get(section.id)" [configuration]="getDescriptionTemplateMultipleAutoCompleteConfiguration(section.id)" (optionActionClicked)="onPreviewDescriptionTemplate($event, section.id)"> --> <app-multiple-auto-complete placeholder="{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}}" [hidePlaceholder]="true" required='true' [formControl]="formGroup.get('descriptionTemplates').get(section.id)" [configuration]="getDescriptionTemplateMultipleAutoCompleteConfiguration(section.id)" (optionActionClicked)="onPreviewDescriptionTemplate($event, section.id)" (optionRemoved)="onRemoveDescriptionTemplate($event, section.id)">
<app-multiple-auto-complete placeholder="{{'DMP-EDITOR.FIELDS.DESCRIPTION-TEMPLATES-HINT' | translate}}" [hidePlaceholder]="true" required='true' [formControl]="formGroup.get('descriptionTemplates').get(section.id)" [configuration]="descriptionTemplateService.descriptionTempalteGroupMultipleAutocompleteConfiguration" (optionActionClicked)="onPreviewDescriptionTemplate($event, section.id)" (optionRemoved)="onRemoveDescriptionTemplate($event, section.id)">
</app-multiple-auto-complete> </app-multiple-auto-complete>
<mat-error *ngIf="formGroup.get('descriptionTemplates').get(section.id).hasError('backendError')">{{formGroup.get('descriptionTemplates').get(section.id).getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('descriptionTemplates').get(section.id).hasError('backendError')">{{formGroup.get('descriptionTemplates').get(section.id).getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('descriptionTemplates').get(section.id).hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('descriptionTemplates').get(section.id).hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>

View File

@ -243,7 +243,7 @@ mat-icon.size-16 {
// width: calc(100% - 366px); // width: calc(100% - 366px);
position: relative; position: relative;
left: 362px; // left: 362px;
width: calc(100% - 366px); width: calc(100% - 366px);
overflow-y: auto; overflow-y: auto;
height: calc(100vh - 218px); height: calc(100vh - 218px);

View File

@ -1,7 +1,7 @@
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { Component, EventEmitter, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormArray, UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { FormArray, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status'; import { DescriptionStatus } from '@app/core/common/enum/description-status';
@ -17,6 +17,7 @@ import { DmpUserType } from '@app/core/common/enum/dmp-user-type';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type'; import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { AppPermission } from '@app/core/common/enum/permission.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { DescriptionSectionPermissionResolver } from '@app/core/model/description/description'; import { DescriptionSectionPermissionResolver } from '@app/core/model/description/description';
import { DmpBlueprint, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { DmpBlueprint, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpPersist } from '@app/core/model/dmp/dmp'; import { Dmp, DmpPersist } from '@app/core/model/dmp/dmp';
@ -35,9 +36,10 @@ import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/serv
import { UserService } from '@app/core/services/user/user.service'; import { UserService } from '@app/core/services/user/user.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { MultipleAutoCompleteCanRemoveItem } from '@app/library/auto-complete/multiple/multiple-auto-complete.component';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; import { DescriptionTemplatePreviewDialogComponent } from '@app/ui/admin/description-template/description-template-preview/description-template-preview-dialog.component';
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
import { BaseEditor } from '@common/base/base-editor'; import { BaseEditor } from '@common/base/base-editor';
import { FormService } from '@common/forms/form-service'; import { FormService } from '@common/forms/form-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
@ -45,14 +47,10 @@ import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/
import { FilterService } from '@common/modules/text-filter/filter-service'; import { FilterService } from '@common/modules/text-filter/filter-service';
import { Guid } from '@common/types/guid'; import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable, interval, of } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map, takeUntil } from 'rxjs/operators';
import { DmpEditorModel, DmpFieldIndicator } from './dmp-editor.model'; import { DmpEditorModel, DmpFieldIndicator } 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 { DescriptionTemplatePreviewDialogComponent } from '@app/ui/admin/description-template/description-template-preview/description-template-preview-dialog.component';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
@Component({ @Component({
selector: 'app-dmp-editor', selector: 'app-dmp-editor',
@ -260,7 +258,10 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
} }
refreshData(): void { refreshData(): void {
this.getItem(this.editorModel.id, (data: Dmp) => this.prepareForm(data)); this.getItem(this.editorModel.id, (data: Dmp) => {
this.prepareForm(data)
if (this.isNew === false) this.nextStep();
});
} }
refreshOnNavigateToData(id?: Guid): void { refreshOnNavigateToData(id?: Guid): void {
@ -636,7 +637,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
} }
} }
canRemoveDescriptionTemplate(item: DescriptionTemplate, sectionId){ canRemoveDescriptionTemplate(item: DescriptionTemplate, sectionId): MultipleAutoCompleteCanRemoveItem {
if (item) { if (item) {
const descriptionsInSection = this.descriptionsInSection(sectionId); const descriptionsInSection = this.descriptionsInSection(sectionId);
@ -644,13 +645,17 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
for (let index = 0; index < descriptionsInSection.length; index++) { for (let index = 0; index < descriptionsInSection.length; index++) {
const description = descriptionsInSection[index]; const description = descriptionsInSection[index];
if (description.dmpDescriptionTemplate?.descriptionTemplateGroupId === item.groupId) { if (description.dmpDescriptionTemplate?.descriptionTemplateGroupId === item.groupId) {
return {canRemove: false, return {
canRemove: false,
message: 'DMP-EDITOR.UNSUCCESSFUL-REMOVE-TEMPLATE' message: 'DMP-EDITOR.UNSUCCESSFUL-REMOVE-TEMPLATE'
} as CanRemoveDescriptionTemplate } as MultipleAutoCompleteCanRemoveItem
} }
} }
} }
} }
return {
canRemove: true,
} as MultipleAutoCompleteCanRemoveItem;
} }
// //
@ -666,8 +671,3 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
return this.languageInfoService.getLanguageInfoValues(); return this.languageInfoService.getLanguageInfoValues();
} }
} }
export interface CanRemoveDescriptionTemplate {
canRemove: boolean;
message: string;
}

View File

@ -13,14 +13,16 @@ const routes: Routes = [
path: 'new', path: 'new',
loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule), loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule),
data: { data: {
breadcrumb: true breadcrumb: true,
title: 'DMP-EDITOR.TITLE-NEW'
} }
}, },
{ {
path: 'edit', path: 'edit',
loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule), loadChildren: () => import('./dmp-editor-blueprint/dmp-editor.module').then(m => m.DmpEditorModule),
data: { data: {
breadcrumb: true breadcrumb: true,
title: 'DMP-EDITOR.TITLE-EDIT'
} }
}, },
{ {

View File

@ -60,14 +60,6 @@
color: var(--primary-color-3); color: var(--primary-color-3);
} }
.header-image {
background: url("/assets/images/new-dashboard-bg.png") no-repeat;
background-size: cover;
margin-top: 70px;
min-height: 15em;
position: relative;
}
.header-title { .header-title {
text-align: left; text-align: left;
font-size: 1.25rem; font-size: 1.25rem;

View File

@ -36,7 +36,7 @@
// ********BUTTONS******** // ********BUTTONS********
.id-btn { .id-btn {
background: url("../../../../assets/images/NoPath.png") no-repeat center; background: url("../../../../assets/images/orcid.png") no-repeat center;
width: 1em; width: 1em;
margin-right: 0.3em; margin-right: 0.3em;
align-self: center; align-self: center;

View File

@ -8,7 +8,7 @@ const routes: Routes = [
component: DmpOverviewComponent, component: DmpOverviewComponent,
data: { data: {
breadcrumb: true, breadcrumb: true,
title: 'GENERAL.TITLES.DATASET-OVERVIEW' title: 'GENERAL.TITLES.PLAN-OVERVIEW'
}, },
}, },
{ {
@ -16,7 +16,7 @@ const routes: Routes = [
component: DmpOverviewComponent, component: DmpOverviewComponent,
data: { data: {
breadcrumb: true, breadcrumb: true,
title: 'GENERAL.TITLES.DATASET-OVERVIEW' title: 'GENERAL.TITLES.PLAN-OVERVIEW'
}, },
} }
]; ];

View File

@ -3,7 +3,8 @@
<mat-list class="nav mat-list" *ngFor="let groupMenuItem of groupMenuItems; let firstGroup = first; let i = index" [class.nav-list-item]="showItem(groupMenuItem)" [class.nav-list-item-hidden]="!showItem(groupMenuItem)" [ngClass]="{'flex-grow-1': i == groupMenuItems.length - 2}"> <mat-list class="nav mat-list" *ngFor="let groupMenuItem of groupMenuItems; let firstGroup = first; let i = index" [class.nav-list-item]="showItem(groupMenuItem)" [class.nav-list-item-hidden]="!showItem(groupMenuItem)" [ngClass]="{'flex-grow-1': i == groupMenuItems.length - 2}">
<div *ngIf="showItem(groupMenuItem);"> <div *ngIf="showItem(groupMenuItem);">
<hr *ngIf="!firstGroup"> <hr *ngIf="!firstGroup">
<mat-list-item routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" *ngFor="let groupMenuRoute of groupMenuItem.routes; let first = first" class="nav-item" [ngClass]="{'mt-4': first && firstGroup}"> <mat-list-item routerLinkActive="active" [isActiveMatchOptions]="{ paths: 'exact', queryParams: 'ignored' }" *ngFor="let groupMenuRoute of groupMenuItem.routes; let first = first" class="nav-item" [ngClass]="{'mt-4': first && firstGroup}">
<!-- {{ groupMenuRoute |json }} -->
<a class="new-dmp nav-link nav-row" *ngIf="groupMenuRoute.path !== '/contact-support' && groupMenuRoute.path !== '/co-branding' && groupMenuRoute.path !== '/feedback' && groupMenuRoute.path !== '/descriptions'" [routerLink]="[groupMenuRoute.path]" [ngClass]="{'dmp-tour': groupMenuRoute.path == '/plans'}"> <a class="new-dmp nav-link nav-row" *ngIf="groupMenuRoute.path !== '/contact-support' && groupMenuRoute.path !== '/co-branding' && groupMenuRoute.path !== '/feedback' && groupMenuRoute.path !== '/descriptions'" [routerLink]="[groupMenuRoute.path]" [ngClass]="{'dmp-tour': groupMenuRoute.path == '/plans'}">
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i> <i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
<i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i> <i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i>

View File

@ -263,16 +263,16 @@
} }
span.googleIcon { span.googleIcon {
background: url("../../../assets/images/argos-login/NoPath\ -\ Copy\ \(2\).png") no-repeat; background: url("../../../assets/images/login/google.png") no-repeat;
margin-left: .2rem; margin-left: .2rem;
} }
span.facebookIcon { span.facebookIcon {
background: url("../../../assets/images/argos-login/NoPath\ -\ Copy\ \(4\).png") no-repeat; background: url("../../../assets/images/login/facebook.png") no-repeat;
} }
span.twitterIcon { span.twitterIcon {
background: url("../../../assets/images/argos-login/NoPath\ -\ Copy\ \(5\).png") no-repeat; background: url("../../../assets/images/login/twitter.png") no-repeat;
} }
span.linkedInIcon { span.linkedInIcon {
@ -290,7 +290,7 @@
} }
span.orcidIconMedium { span.orcidIconMedium {
background: url("../../../assets/images/argos-login/NoPath\ -\ Copy.png") no-repeat; background: url("../../../assets/images/login/orcid.png") no-repeat;
background-position: center; background-position: center;
float: left; float: left;
transform: scale(0.45); transform: scale(0.45);
@ -299,7 +299,7 @@
} }
span.openaireIcon { span.openaireIcon {
background: url("../../../assets/images/argos-login/NoPath\ -\ Copy\ \(6\).png") no-repeat; background: url("../../../assets/images/login/openaire.png") no-repeat;
background-position: center; background-position: center;
float: right; float: right;
transform: scale(0.4); transform: scale(0.4);
@ -308,7 +308,7 @@
} }
span.zenodoIcon { span.zenodoIcon {
background: url("../../../assets/images/argos-login/zenodo-gradient-sm.png") no-repeat; background: url("../../../assets/images/login/zenodo.png") no-repeat;
background-position: center; background-position: center;
float: right; float: right;
transform: scale(0.58); transform: scale(0.58);

View File

@ -1,6 +1,6 @@
{ {
"APP_NAME": "Argos", "APP_NAME": "OpenCDMP",
"APP_NAME_CAPS": "ARGOS", "APP_NAME_CAPS": "OpenCDMP",
"GENERAL": { "GENERAL": {
"VALIDATION": { "VALIDATION": {
"REQUIRED": "Required", "REQUIRED": "Required",
@ -93,8 +93,10 @@
"USERS": "Users", "USERS": "Users",
"PROFILE": "My Profile", "PROFILE": "My Profile",
"LOGIN": "Login", "LOGIN": "Login",
"PLAN-OVERVIEW": "Plan Overview",
"DATASET-OVERVIEW": "Description Overview", "DATASET-OVERVIEW": "Description Overview",
"MAINTENANCE-TASKS": "Maintenance" "MAINTENANCE-TASKS": "Maintenance",
"HOME": "Home"
}, },
"FILE-TRANSFORMER": { "FILE-TRANSFORMER": {
"PDF": "PDF", "PDF": "PDF",
@ -754,6 +756,7 @@
"EMPTY-LIST": "Nothing here yet." "EMPTY-LIST": "Nothing here yet."
}, },
"DESCRIPTION-EDITOR": { "DESCRIPTION-EDITOR": {
"TITLE-NEW": "New Description",
"TITLE-ADD-DESCRIPTION": "Adding Description", "TITLE-ADD-DESCRIPTION": "Adding Description",
"TITLE-EDIT-DESCRIPTION": "Editing Description", "TITLE-EDIT-DESCRIPTION": "Editing Description",
"TITLE-PREVIEW-DESCRIPTION": "Previewing Description", "TITLE-PREVIEW-DESCRIPTION": "Previewing Description",
@ -1446,6 +1449,7 @@
} }
}, },
"DMP-EDITOR": { "DMP-EDITOR": {
"TITLE-NEW": "New Plan",
"TITLE-EDIT": "Editing Plan", "TITLE-EDIT": "Editing Plan",
"TITLE": "Plan Blueprint", "TITLE": "Plan Blueprint",
"UNSAVED-CHANGES": "unsaved changes", "UNSAVED-CHANGES": "unsaved changes",
@ -1453,7 +1457,7 @@
"DESCRIPTION": "Description", "DESCRIPTION": "Description",
"NO-TEMPLATE-MESSAGE": "If you can't find a template or if you want to create a personalized template for your institution, research community or training needs, please contact us.", "NO-TEMPLATE-MESSAGE": "If you can't find a template or if you want to create a personalized template for your institution, research community or training needs, please contact us.",
"DESCRIPTION-TEMPLATES-MAX-MULTIPLICITY": "Description Templates has reached the maximun multiplicity", "DESCRIPTION-TEMPLATES-MAX-MULTIPLICITY": "Description Templates has reached the maximun multiplicity",
"UNSUCCESSFUL-REMOVE-TEMPLATE": "Failed to remove template, one or more Descriptions of this Plan use this template", "UNSUCCESSFUL-REMOVE-TEMPLATE": "Cannot remove template, because it's already being used by one or more Descriptions.",
"FIELDS": { "FIELDS": {
"TITLE": "Title of Plan", "TITLE": "Title of Plan",
"DESCRIPTION": "Description", "DESCRIPTION": "Description",

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 411 B

View File

@ -1,10 +1,12 @@
<div class="form-content-editor-content"> <div class="container-fluid">
<div mat-dialog-title class="d-flex justify-content-between m-0"> <div class="row mt-3">
<span class="title">{{'GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.WARNING' | translate}}</span> <div mat-dialog-title class="col-12 pr-1 d-flex justify-content-between">
<span class="mr-3 title">{{'GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.WARNING' | translate}}</span>
<mat-icon class="close-icon" (click)="onClose()">close</mat-icon> <mat-icon class="close-icon" (click)="onClose()">close</mat-icon>
</div> </div>
<div class="content"> </div>
<div class="col p-0"> <div class="row mt-3 mr-3 mb-3">
<div class="col-12">
<ng-container *ngIf="errorMessages"> <ng-container *ngIf="errorMessages">
<ul class="error-list" *ngIf="errorMessages.length > 1 else singleError"> <ul class="error-list" *ngIf="errorMessages.length > 1 else singleError">
<li *ngFor="let error of errorMessages">{{error}}</li> <li *ngFor="let error of errorMessages">{{error}}</li>
@ -15,8 +17,8 @@
</ng-template> </ng-template>
</div> </div>
</div> </div>
<div> <div class="row mt-3 mb-3">
<div class="col actions"> <div class="col-12 actions">
<button mat-button type="button" class="ml-auto cancel-btn" (click)="onClose()">{{'GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.ACTIONS.CANCEL' | translate}}</button> <button mat-button type="button" class="ml-auto cancel-btn" (click)="onClose()">{{'GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.ACTIONS.CANCEL' | translate}}</button>
</div> </div>
</div> </div>

View File

@ -3,11 +3,18 @@
// Be sure that you only ever include this mixin once! // Be sure that you only ever include this mixin once!
@include mat.core(); @include mat.core();
// :root {
// --primary-color: #129d99;
// --primary-color-2: #23bcba;
// --primary-color-3: #00b29f;
// --secondary-color: #f7dd72;
// }
:root { :root {
--primary-color: #129d99; --primary-color: #18488F;
--primary-color-2: #23bcba; --primary-color-2: #1E59B1;
--primary-color-3: #00b29f; --primary-color-3: #246AD3;
--secondary-color: #f7dd72; --secondary-color: #36900B;
} }
// Define your theme with color palettes, typography and density // Define your theme with color palettes, typography and density

View File

@ -48,7 +48,7 @@ You can also clear any filters already applied, by pressing the `clear all filte
## Edit form ## Edit form
When you try to add new notification templates or edit existing ones, the **notification template editing form** will appear containing the following controls: When you try to add new notification templates or edit existing ones, the **notification template editing form** will appear containing the following sections.
### Main information section ### Main information section

View File

@ -1,5 +1,137 @@
--- ---
sidebar_position: 5 sidebar_position: 5
description: Manage all reference types
--- ---
# Reference Types # Reference Types
In this page, there is a listing where you can view details about all the available reference types.
:::info
A **reference type** is any type of information that is made available to the users when they fill [plan](/docs/category/plans) forms. This information is either static, or coming from an outside source (*an external API*). These sources are extremely configurable as we will see in this section.
:::
The information displayed by default is: the `name`, the `status`, the `identification code` and timestamps for the `creation` and `updates` of the records. At the top right corner of the listing you can also select which columns to display.
:::tip
For reference types, all the columns are visible by default.
:::
You can also create new or edit / remove reference types by clicking to the `+ Create Reference Type` button at the top right of the page or to the three dots at the last column, respectively.
## Authorization
Only users that have the **Admin** role can access this page.
## Pagination
Not all the records are being displayed at once. By default, there is a pagination of 10 records applied to them.
You can control how many records are being displayed at any time, by adjusting the `items per page` control at the bottom left corner of the table.
## Filtering
There is a filtering option available for reference types.
- **Is Active**: By toggling this control you can view only the active or only the disabled reference types.<br/>*By default, this option is set to true.*
In order for the filters to apply, you have to click the `Apply filters` button.
You can also clear any filters already applied, by pressing the `clear all filters` option, located at the top of the popup.
## Edit form
When you try to add new reference types or edit existing ones, the **reference type editing form** will appear containing the following sections.
### Main information section
- **Name**: The label of this reference type.
- **Code**: The identification code which is used internally for this type.
### Fields section
In this section we can add custom fields made available from the source. When the `Add Field` button is pressed, a form appears containing the following controls:
- **Label**: The label of the field.
- **Description**: A short description for the field. <br/>*This is optional*
- **Code**: An identification code for this field, used for the information mappings we will discuss about shortly.
- **Data Type**: The data type of a field information can either be `Text` or `Date`.
:::tip
There is no limit on the fields that can be configured. To remove a field configuration, press the `delete` icon on the right side of the field form.
:::
### Sources section
In this section we can add configuration for the sources of this reference type. The configured sources can be more than one. This is useful when there is a need to 'merge' information coming from multiple sources at the same time. The basic information we can provide for a source is the following:
- **Key**: An identification key for our source.<br/>*Used internally by the system.*
- **Label**: A display name for our source.
- **Ordinal**: This specifies the order in which the source will be called.
- **Dependencies**: A source can be dependent on other reference types. Here we can specify these dependencies.
- **Source Type**: It can either be `API` or `Static`.
:::info
All the options that follow are only applicable if the source type we select is `API`. In case we select `Static` the only thing we can add are the static fields of the source and their values.
:::
- **Url**: The url of our source.
- **Pagination Path**: The path inside the response in which the pagination information is located. This is only needed if the results are coming paginated from the source.
- **Content Type**: The content type of the responses of the source.<br/>*In most cases, this is `application/json`.*
- **First Page**: We can specify the first page to be different than the default 0.
- **HTTP Method**: The http method the source expects. This can either be `GET` or `POST`.
- **Filter Type**: How filtering is handled when this source is used. This can be set as `local` or `remote`.<br/>*In most cases, this is `remote`, meaning that the source handles the filtering. If set to `null`, the filtering (if any) is also handled remotely by the source.*
- **Results**: The path inside the source response where the results are located.
### Mappings section
In this section we can specify how we will be consuming the information coming from our sources by mapping all the source fields we are interested in.
For every field we have to specify the response path on which the information resides.
By default, there are three fields available for mapping from the start.
- **reference_id**: This can be used for identifier fields
- **label**
- **description**
If we have specified more fields for our source, these will also appear by their code in this form for configuring their paths.
### Authentication section
Some APIs require authentication. In this section we can specify the information about how our source API handles authentication. It is optional, and the form can be enabled by checking the `Authentication` checkbox. The options we have are the following:
- **Url**: The url where the API listens for authentication.
- **HTTP Method**: The HTTP method for the authentication request.
- **Token Path**: The path of the token, concatinated after the token type. <br/>*In most cases, it is `null`.*
- **Type**: The token type.<br/>*In most cases, it is `Bearer`.*
- **Request Body**: The body contents of the request, if required.
### Queries section
In this section we can specify query parameters we can apply to our requests.
- Name: The name of the query parameter. <br/>*For example, `like`.*
- Default Value: The default value of the parameter.<br/>*This is optional.*
## End notes
:::tip
The path fields on this form are using the [JsonPath](https://github.com/json-path/JsonPath) format.
:::
:::note
For every source we add, the form gets populated with new `field mapping`, `authentication` and `queries` sections we can configure as we saw above.
:::