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

This commit is contained in:
Efstratios Giannopoulos 2024-03-01 10:55:09 +02:00
commit b1ebc6b35a
19 changed files with 479 additions and 624 deletions

View File

@ -18,6 +18,12 @@ export enum AppPermission {
BrowseDmp = "BrowseDmp", BrowseDmp = "BrowseDmp",
EditDmp = "EditDmp", EditDmp = "EditDmp",
DeleteDmp= "DeleteDmp", DeleteDmp= "DeleteDmp",
CloneDmp = "CloneDmp",
CreateNewVersionDmp = "CreateNewVersionDmp",
ExportDmp = "ExportDmp",
FinalizeDmp = "FinalizeDmp",
AssignDmpUsers = "AssignDmpUsers",
InviteDmpUsers = "InviteDmpUsers",
//DescriptionTemplateType //DescriptionTemplateType
BrowseDescriptionTemplate = "BrowseDescriptionTemplate", BrowseDescriptionTemplate = "BrowseDescriptionTemplate",

View File

@ -131,10 +131,10 @@ export class ReferenceTypeService {
} }
public getResearcherReferenceType(): Guid { public getResearcherReferenceType(): Guid {
return null; return Guid.parse('5a2112e7-ea99-4cfe-98a1-68665e26726e');;
} }
public getGrantReferenceType(): Guid { public getGrantReferenceType(): Guid {
return null; return Guid.parse('5b9c284f-f041-4995-96cc-fad7ad13289c');
} }
} }

View File

@ -163,7 +163,7 @@
<div class="col" *ngIf="field.get('category').value != null"> <div class="col" *ngIf="field.get('category').value != null">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SEMANTICS' | translate}}</mat-label> <mat-label>{{'DMP-BLUEPRINT-EDITOR.FIELDS.SEMANTICS' | translate}}</mat-label>
<app-multiple-auto-complete placeholder="{{'DMP-BLUEPRINT-EDITOR.FIELDS.SEMANTICS' | translate}}" [hidePlaceholder]="true" required='false' [separatorKeysCodes]="separatorKeysCodes" [formControl]="field.get('semantics')" [configuration]="semanticsService.multipleAutocompleteConfiguration"> <app-multiple-auto-complete placeholder="{{'DMP-BLUEPRINT-EDITOR.FIELDS.SEMANTICS' | translate}}" [hidePlaceholder]="true" [required]="false" [separatorKeysCodes]="separatorKeysCodes" [formControl]="field.get('semantics')" [configuration]="semanticsService.multipleAutocompleteConfiguration">
</app-multiple-auto-complete> </app-multiple-auto-complete>
<mat-error *ngIf="field.get('semantics').hasError('backendError')">{{field.get('semantics').getError('backendError').message}}</mat-error> <mat-error *ngIf="field.get('semantics').hasError('backendError')">{{field.get('semantics').getError('backendError').message}}</mat-error>
<mat-error *ngIf="field.get('semantics').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="field.get('semantics').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>

View File

@ -0,0 +1,92 @@
<div class="col-6" >
<mat-form-field class="chip-list">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.MANDATORY' | translate}}</mat-label>
<mat-chip-grid #chipGrid [formControl]="form.get('mandatory')">
<mat-chip-row *ngFor="let field of mandatoryFields"
(removed)="removeChipListValues(field)"
[editable]="true"
(edited)="editChipListValues($event, field)">
{{field}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
<input placeholder="{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.MANDATORY-PLACEHOLDER' | translate}}"
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addChipListValues($event)"/>
</mat-chip-grid>
<mat-error *ngIf="form.get('mandatory').hasError('backendError')">{{form.get('mandatory').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
<h4 class="col-md-12">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.OPTIONAL-TITLE' | translate}}</h4>
<div class="col-12">
<div class="row" *ngFor="let options of form.get('optional')['controls']; let i = index">
<div class="col-4">
<mat-form-field class="col-auto">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [formControl]="options.get('key')">
<mat-error *ngIf="options.get('key').hasError('backendError')">{{options.get('key').getError('backendError').message}}</mat-error>
<mat-error *ngIf="options.get('key').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field class="col-auto">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.DATA-TYPE' | translate}}</mat-label>
<mat-select name="channel" [formControl]="options.get('type')">
<mat-option *ngFor="let type of notificationDataTypeEnum" [value]="type">
{{enumUtils.toNotificationTemplateDataTypeString(type)}}
</mat-option>
</mat-select>
<mat-error *ngIf="options.get('type').hasError('backendError')">{{options.get('type').getError('backendError').message}}</mat-error>
<mat-error *ngIf="options.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field class="col">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [formControl]="options.get('value')">
<mat-error *ngIf="options.get('value').hasError('backendError')">{{options.get('value').getError('backendError').message}}</mat-error>
<mat-error *ngIf="options.get('value').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="col-auto">
<button mat-icon-button (click)="removeSubjectOptionalItem(i)" [disabled]="form.disabled">
<mat-icon>remove</mat-icon>
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-auto">
<button mat-icon-button (click)="addOptionalItem()" [disabled]="form.disabled">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
</div>
<h4 class="col-md-12">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.FORMATTING' | translate}}</h4>
<div class="row" *ngFor="let item of form.get('mandatory').value; let i = index">
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [value]="item" [disabled] = "true">
</mat-form-field>
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [value] ="formatting[item]" (change)="formattingValueChange($event, item)">
<mat-error *ngIf="form.get('formatting').hasError('backendError')">{{form.get('formatting').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
<div class="row" *ngFor="let item of form.get('optional')['controls']; let i = index">
<div *ngIf="item.valid">
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [value]="item.value.key" [disabled] = "true">
</mat-form-field>
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [value]="formatting[item]" (change)="formattingValueChange($event, item)">
<mat-error *ngIf="form.get('formatting').hasError('backendError')">{{form.get('formatting').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,127 @@
import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormArray } from '@angular/forms';
import { NotificationDataType } from '@app/core/common/enum/notification-data-type';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { NotificationFieldInfoEditorModel, NotificationFieldOptionsEditorModel } from '../notification-template-editor.model';
import { MatChipEditedEvent, MatChipInputEvent } from '@angular/material/chips';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
@Component({
selector: 'app-notification-template-field-options-component',
templateUrl: 'notification-template-field-options.component.html',
styleUrls: ['./notification-template-field-options.component.scss']
})
export class NotificationTemplateFieldOptionsComponent extends BaseComponent implements OnInit {
@Input() form;
@Input() validationErrorModel: ValidationErrorModel = null;
@Input() validationRootPath: string = null;
@Input() mandatoryFields: string[] = [];
@Input() formatting: { [key: string]: string } = {};
public notificationDataTypeEnum = this.enumUtils.getEnumValues(NotificationDataType);
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
constructor(
public enumUtils: EnumUtils,
) { super(); }
ngOnInit() {
}
addChipListValues(event: MatChipInputEvent){
this.mandatoryFields = this.add(event, this.mandatoryFields);
}
editChipListValues(event: MatChipEditedEvent, field: string){
this.mandatoryFields = this.edit(field, this.mandatoryFields, event);
this.editFormmattingKey(field, event.value.trim());
}
removeChipListValues(field: string){
this.mandatoryFields = this.remove(field, this.mandatoryFields);
this.deleteFormattingItem(field);
}
add(event: MatChipInputEvent, values: string[]) {
const value = (event.value || '').trim();
if (value) values.push(value)
event.chipInput!.clear();
return values;
}
remove(field: string, values: string[]) {
const index = values.indexOf(field);
if (index >= 0) values.splice(index, 1);
return values;
}
edit(field: string, values: string[], event: MatChipEditedEvent) {
const value = event.value.trim();
if (!value) {
values = this.remove(field, values);
return values;
}
const index = values.indexOf(field);
if (index >= 0) values[index] = value
return values;
}
//options fields
addOptionalItem() {
const formArray = (this.form.get('optional') as UntypedFormArray);
const optional: NotificationFieldInfoEditorModel = new NotificationFieldInfoEditorModel(this.validationErrorModel);
formArray.push(optional.buildForm({ rootPath: this.validationRootPath + 'optional[' + formArray.length + '].' }));
}
removeSubjectOptionalItem(optionalIndex: number): void {
const key = (this.form.get('optional') as UntypedFormArray).at(optionalIndex).get("key").value;
this.deleteFormattingItem(key);
(this.form.get('optional') as UntypedFormArray).removeAt(optionalIndex);
//Reapply validators
NotificationFieldOptionsEditorModel.reapplyValidators(
{
formGroup: this.form,
validationErrorModel: this.validationErrorModel,
rootPath: this.validationRootPath
}
);
this.form.get('optional').markAsDirty();
}
// subject formatting
insertFormattingItem(key: string, value: string){
this.formatting[key] = value;
this.form.get('formatting').setValue(this.formatting);
}
formattingValueChange(event: Event, key: string){
this.formatting[key] = (event.target as HTMLInputElement).value;
this.form.get('formatting').setValue(this.formatting);
}
editFormmattingKey(oldKey: string, newKey: string){
this.insertFormattingItem(newKey, this.formatting[oldKey]);
this.deleteFormattingItem(oldKey);
}
deleteFormattingItem(key:string){
if (key in this.formatting) {
delete this.formatting[key];
this.form.get('formatting').setValue(this.formatting);
}
}
}

View File

@ -102,99 +102,14 @@
<mat-checkbox [checked]="subjectFieldOptionsEnabled" (change)="subjectFieldOptionsSelectionChanged($event)"></mat-checkbox> <mat-checkbox [checked]="subjectFieldOptionsEnabled" (change)="subjectFieldOptionsSelectionChanged($event)"></mat-checkbox>
</h4> </h4>
<div *ngIf="subjectFieldOptionsEnabled == true"> <div *ngIf="subjectFieldOptionsEnabled == true">
<div class="col-6" > <app-notification-template-field-options-component
<mat-form-field class="chip-list"> [form]="formGroup.get('value').get('subjectFieldOptions')"
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.MANDATORY' | translate}}</mat-label> [validationErrorModel]="editorModel.validationErrorModel"
<mat-chip-grid #chipGrid [formControl]="formGroup.get('value').get('subjectFieldOptions').get('mandatory')"> [validationRootPath]="'value.subjectFieldOptions.'"
<mat-chip-row *ngFor="let field of subjectMandatoryFields" [mandatoryFields]="subjectMandatoryFields"
(removed)="removeChipListValues('subject', field)" [formatting]="subjectFormatting">
[editable]="true" </app-notification-template-field-options-component>
(edited)="editChipListValues('subject', $event, field)"> </div>
{{field}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
<input placeholder="{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.MANDATORY-PLACEHOLDER' | translate}}"
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addChipListValues('subject', $event)"/>
</mat-chip-grid>
<mat-error *ngIf="formGroup.get('value').get('subjectFieldOptions').get('mandatory').hasError('backendError')">{{formGroup.get('value').get('subjectFieldOptions').get('mandatory').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
<h4 class="col-md-12">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.OPTIONAL-TITLE' | translate}}</h4>
<div class="col-12">
<div class="row" *ngFor="let subjectOptions of formGroup.get('value').get('subjectFieldOptions').get('optional')['controls']; let i = index">
<div class="col-4">
<mat-form-field class="col-auto">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [formControl]="subjectOptions.get('key')">
<mat-error *ngIf="subjectOptions.get('key').hasError('backendError')">{{subjectOptions.get('key').getError('backendError').message}}</mat-error>
<mat-error *ngIf="subjectOptions.get('key').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field class="col-auto">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.DATA-TYPE' | translate}}</mat-label>
<mat-select name="channel" [formControl]="subjectOptions.get('type')">
<mat-option *ngFor="let type of notificationDataTypeEnum" [value]="type">
{{enumUtils.toNotificationTemplateDataTypeString(type)}}
</mat-option>
</mat-select>
<mat-error *ngIf="subjectOptions.get('type').hasError('backendError')">{{subjectOptions.get('type').getError('backendError').message}}</mat-error>
<mat-error *ngIf="subjectOptions.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field class="col">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [formControl]="subjectOptions.get('value')">
<mat-error *ngIf="subjectOptions.get('value').hasError('backendError')">{{subjectOptions.get('value').getError('backendError').message}}</mat-error>
<mat-error *ngIf="subjectOptions.get('value').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="col-auto">
<button mat-icon-button (click)="removeSubjectOptionalItem(i)" [disabled]="formGroup.disabled">
<mat-icon>remove</mat-icon>
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-auto">
<button mat-icon-button (click)="addSubjectOptionalItem()" [disabled]="formGroup.disabled">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
</div>
<h4 class="col-md-12">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.FORMATTING' | translate}}</h4>
<div class="row" *ngFor="let item of formGroup.get('value').get('subjectFieldOptions').get('mandatory').value; let i = index">
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [value]="item" [disabled] = "true">
</mat-form-field>
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [value] ="subjectFormatting[item]" (change)="subjectFormattingValueChange($event, item)">
<mat-error *ngIf="formGroup.get('value').get('subjectFieldOptions').get('formatting').hasError('backendError')">{{formGroup.get('value').get('subjectFieldOptions').get('formatting').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
<div class="row" *ngFor="let item of formGroup.get('value').get('subjectFieldOptions').get('optional')['controls']; let i = index">
<div *ngIf="item.valid">
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [value]="item.value.key" [disabled] = "true">
</mat-form-field>
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [value]="subjectFormatting[item]" (change)="subjectFormattingValueChange($event, item)">
<mat-error *ngIf="formGroup.get('value').get('subjectFieldOptions').get('formatting').hasError('backendError')">{{formGroup.get('value').get('subjectFieldOptions').get('formatting').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
</div>
</div>
<!-- Body --> <!-- Body -->
<div> <div>
@ -236,98 +151,13 @@
<mat-checkbox [checked]="bodyFieldOptionsEnabled" (change)="bodyFieldOptionsSelectionChanged($event)"></mat-checkbox> <mat-checkbox [checked]="bodyFieldOptionsEnabled" (change)="bodyFieldOptionsSelectionChanged($event)"></mat-checkbox>
</h4> </h4>
<div *ngIf="bodyFieldOptionsEnabled == true"> <div *ngIf="bodyFieldOptionsEnabled == true">
<div class="col-6" > <app-notification-template-field-options-component
<mat-form-field class="chip-list"> [form]="formGroup.get('value').get('bodyFieldOptions')"
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.MANDATORY' | translate}}</mat-label> [validationErrorModel]="editorModel.validationErrorModel"
<mat-chip-grid #chipGrid [formControl]="formGroup.get('value').get('bodyFieldOptions').get('mandatory')"> [validationRootPath]="'value.bodyFieldOptions.'"
<mat-chip-row *ngFor="let field of bodyMandatoryFields" [bodyMandatoryFields]="subjectMandatoryFields"
(removed)="removeChipListValues('body', field)" [formatting]="bodyFormatting">
[editable]="true" </app-notification-template-field-options-component>
(edited)="editChipListValues('body', $event, field)">
{{field}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
<input placeholder="{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.MANDATORY-PLACEHOLDER' | translate}}"
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addChipListValues('body',$event)"/>
</mat-chip-grid>
<mat-error *ngIf="formGroup.get('value').get('bodyFieldOptions').get('mandatory').hasError('backendError')">{{formGroup.get('value').get('bodyFieldOptions').get('mandatory').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
<h4 class="col-md-12">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.OPTIONAL-TITLE' | translate}}</h4>
<div class="col-12">
<div class="row" *ngFor="let bodyOptions of formGroup.get('value').get('bodyFieldOptions').get('optional')['controls']; let i = index">
<div class="col-4">
<mat-form-field class="col-auto">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [formControl]="bodyOptions.get('key')">
<mat-error *ngIf="bodyOptions.get('key').hasError('backendError')">{{bodyOptions.get('key').getError('backendError').message}}</mat-error>
<mat-error *ngIf="bodyOptions.get('key').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field class="col-auto">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.DATA-TYPE' | translate}}</mat-label>
<mat-select name="channel" [formControl]="bodyOptions.get('type')">
<mat-option *ngFor="let type of notificationDataTypeEnum" [value]="type">
{{enumUtils.toNotificationTemplateDataTypeString(type)}}
</mat-option>
</mat-select>
<mat-error *ngIf="bodyOptions.get('type').hasError('backendError')">{{bodyOptions.get('type').getError('backendError').message}}</mat-error>
<mat-error *ngIf="bodyOptions.get('type').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-4">
<mat-form-field class="col">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [formControl]="bodyOptions.get('value')">
<mat-error *ngIf="bodyOptions.get('value').hasError('backendError')">{{bodyOptions.get('value').getError('backendError').message}}</mat-error>
<mat-error *ngIf="bodyOptions.get('value').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="col-auto">
<button mat-icon-button (click)="removeBodyOptionalItem(i)" [disabled]="formGroup.disabled">
<mat-icon>remove</mat-icon>
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-auto">
<button mat-icon-button (click)="addBodyOptionalItem()" [disabled]="formGroup.disabled">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
<h4 class="col-md-12">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.FORMATTING' | translate}}</h4>
<div class="row" *ngFor="let item of formGroup.get('value').get('bodyFieldOptions').get('mandatory').value; let i = index">
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [value]="item" [disabled] = "true">
</mat-form-field>
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [value] ="bodyFormatting[item]" (change)="bodyFormattingValueChange($event, item)">
<mat-error *ngIf="formGroup.get('value').get('bodyFieldOptions').get('formatting').hasError('backendError')">{{formGroup.get('value').get('bodyFieldOptions').get('formatting').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
<div class="row" *ngFor="let item of formGroup.get('value').get('bodyFieldOptions').get('optional')['controls']; let i = index">
<div *ngIf="item.valid">
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.KEY' | translate}}</mat-label>
<input matInput [value]="item.value.key" [disabled] = "true">
</mat-form-field>
<mat-form-field class="col-md-4">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.VALUE' | translate}}</mat-label>
<input matInput [value]="bodyFormatting[item]" (change)="bodyFormattingValueChange($event, item)">
<mat-error *ngIf="formGroup.get('value').get('bodyFieldOptions').get('formatting').hasError('backendError')">{{formGroup.get('value').get('bodyFieldOptions').get('formatting').getError('backendError').message}}</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div> </div>
<!--Extra Options --> <!--Extra Options -->
<div class="row"> <div class="row">

View File

@ -252,10 +252,6 @@ export class NotificationTemplateEditorComponent extends BaseEditor<Notification
this.bccValues = this.add(event, this.bccValues); this.bccValues = this.add(event, this.bccValues);
}else if(type == "extraDataKeys"){ }else if(type == "extraDataKeys"){
this.extraDataKeys = this.add(event, this.extraDataKeys); this.extraDataKeys = this.add(event, this.extraDataKeys);
}else if(type == "subject"){
this.subjectMandatoryFields = this.add(event, this.subjectMandatoryFields);
}else if(type == "body"){
this.bodyMandatoryFields = this.add(event, this.bodyMandatoryFields);
} }
} }
@ -266,12 +262,6 @@ export class NotificationTemplateEditorComponent extends BaseEditor<Notification
this.bccValues = this.edit(field, this.bccValues, event); this.bccValues = this.edit(field, this.bccValues, event);
}else if(type == "extraDataKeys"){ }else if(type == "extraDataKeys"){
this.extraDataKeys = this.edit(field, this.extraDataKeys, event); this.extraDataKeys = this.edit(field, this.extraDataKeys, event);
}else if(type == "subject"){
this.subjectMandatoryFields = this.edit(field, this.subjectMandatoryFields, event);
this.editSubjectFormmattingKey(field, event.value.trim());
}else if(type == "body"){
this.bodyMandatoryFields = this.edit(field, this.bodyMandatoryFields, event);
this.editBodyFormmattingKey(field, event.value.trim());
} }
} }
@ -282,12 +272,6 @@ export class NotificationTemplateEditorComponent extends BaseEditor<Notification
this.bccValues = this.remove(field, this.bccValues); this.bccValues = this.remove(field, this.bccValues);
}else if(type == "extraDataKeys"){ }else if(type == "extraDataKeys"){
this.extraDataKeys = this.remove(field, this.extraDataKeys); this.extraDataKeys = this.remove(field, this.extraDataKeys);
}else if(type == "subject"){
this.subjectMandatoryFields = this.remove(field, this.subjectMandatoryFields);
this.deleteSubjectFormattingItem(field);
}else if(type == "body"){
this.bodyMandatoryFields = this.remove(field, this.bodyMandatoryFields);
this.deleteBodyFormattingItem(field);
} }
} }
@ -321,92 +305,4 @@ export class NotificationTemplateEditorComponent extends BaseEditor<Notification
return values; return values;
} }
//options fields
addSubjectOptionalItem() {
(this.formGroup.get('value').get('subjectFieldOptions').get('optional') as FormArray).push(this.editorModel.createSubjectOptionalField((this.formGroup.get('value').get('subjectFieldOptions').get('optional') as FormArray).length));
}
addBodyOptionalItem() {
(this.formGroup.get('value').get('bodyFieldOptions').get('optional') as FormArray).push(this.editorModel.createBodyOptionalField((this.formGroup.get('value').get('bodyFieldOptions').get('optional') as FormArray).length));
}
removeSubjectOptionalItem(optionalIndex: number): void {
const key = (this.formGroup.get('value').get('subjectFieldOptions').get('optional') as FormArray).at(optionalIndex).get("key").value;
this.deleteSubjectFormattingItem(key);
(this.formGroup.get('value').get('subjectFieldOptions').get('optional') as FormArray).removeAt(optionalIndex);
//Reapply validators
NotificationTemplateEditorModel.reApplyValueValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
);
this.formGroup.get('value').get('subjectFieldOptions').get('optional').markAsDirty();
}
removeBodyOptionalItem(optionalIndex: number): void {
const key = (this.formGroup.get('value').get('bodyFieldOptions').get('optional') as FormArray).at(optionalIndex).get("key").value;
this.deleteBodyFormattingItem(key);
(this.formGroup.get('value').get('bodyFieldOptions').get('optional') as FormArray).removeAt(optionalIndex);
//Reapply validators
NotificationTemplateEditorModel.reApplyValueValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
);
this.formGroup.get('value').get('bodyFieldOptions').get('optional').markAsDirty();
}
// subject formatting
insertSubjectFormattingItem(key: string, value: string){
this.subjectFormatting[key] = value;
this.formGroup.get('value').get('subjectFieldOptions').get('formatting').setValue(this.subjectFormatting);
}
subjectFormattingValueChange(event: Event, key: string){
this.bodyFormatting[key] = (event.target as HTMLInputElement).value;
this.formGroup.get('value').get('subjectFieldOptions').get('formatting').setValue(this.bodyFormatting);
}
editSubjectFormmattingKey(oldKey: string, newKey: string){
this.insertSubjectFormattingItem(newKey, this.subjectFormatting[oldKey]);
this.deleteSubjectFormattingItem(oldKey);
}
deleteSubjectFormattingItem(key:string){
if (key in this.subjectFormatting) {
delete this.subjectFormatting[key];
this.formGroup.get('value').get('subjectFieldOptions').get('formatting').setValue(this.subjectFormatting);
}
}
// body formatting
insertBodyFormattingItem(key: string, value: string){
this.bodyFormatting[key] = value;
this.formGroup.get('value').get('bodyFieldOptions').get('formatting').setValue(this.bodyFormatting);
}
bodyFormattingValueChange(event: Event, key: string){
this.bodyFormatting[key] = (event.target as HTMLInputElement).value;
this.formGroup.get('value').get('bodyFieldOptions').get('formatting').setValue(this.bodyFormatting);
}
editBodyFormmattingKey(oldKey: string, newKey: string){
this.insertBodyFormattingItem(newKey, this.bodyFormatting[oldKey]);
this.deleteBodyFormattingItem(oldKey);
}
deleteBodyFormattingItem(key:string){
if (key in this.bodyFormatting) {
delete this.bodyFormatting[key];
this.formGroup.get('value').get('bodyFieldOptions').get('formatting').setValue(this.bodyFormatting);
}
}
} }

View File

@ -68,16 +68,6 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements
return baseContext; return baseContext;
} }
createSubjectOptionalField(index: number): UntypedFormGroup {
const source: NotificationFieldInfoEditorModel = new NotificationFieldInfoEditorModel(this.validationErrorModel);
return source.buildForm({ rootPath: 'value.subjectFieldOptions.optional[' + index + '].' });
}
createBodyOptionalField(index: number): UntypedFormGroup {
const source: NotificationFieldInfoEditorModel = new NotificationFieldInfoEditorModel(this.validationErrorModel);
return source.buildForm({ rootPath: 'value.bodyFieldOptions.optional[' + index + '].' });
}
static reApplyValueValidators(params: { static reApplyValueValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,

View File

@ -16,6 +16,7 @@ import { NotificationTemplateEditorComponent } from './editor/notification-templ
import { NotificationTemplateListingFiltersComponent } from './listing/filters/notification-template-listing-filters.component'; import { NotificationTemplateListingFiltersComponent } from './listing/filters/notification-template-listing-filters.component';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { EditorModule } from '@tinymce/tinymce-angular'; import { EditorModule } from '@tinymce/tinymce-angular';
import { NotificationTemplateFieldOptionsComponent } from './editor/field-options/notification-template-field-options.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -37,7 +38,8 @@ import { EditorModule } from '@tinymce/tinymce-angular';
declarations: [ declarations: [
NotificationTemplateEditorComponent, NotificationTemplateEditorComponent,
NotificationTemplateListingComponent, NotificationTemplateListingComponent,
NotificationTemplateListingFiltersComponent NotificationTemplateListingFiltersComponent,
NotificationTemplateFieldOptionsComponent
] ]
}) })
export class NotificationTemplateModule { } export class NotificationTemplateModule { }

View File

@ -0,0 +1,83 @@
<h3>
{{label}}
<button mat-button class="action-btn" type="button" (click)="addSource()" [disabled]="form.disabled">{{'TENANT-EDITOR.ACTIONS.ADD-SOURCE' | translate}}</button>
</h3>
<div *ngFor="let source of form.get('sources').controls; let sourceIndex=index;" class="row mb-3">
<div class="col-12">
<div class="row mb-3 d-flex align-items-center">
<div class="col-auto d-flex">
<mat-card-title>{{'TENANT-EDITOR.FIELDS.SOURCE' | translate}} {{sourceIndex + 1}}</mat-card-title>
</div>
<div class="col-auto d-flex">
<button mat-icon-button class="action-list-icon" matTooltip="{{'TENANT-EDITOR.ACTIONS.REMOVE-SOURCE' | translate}}" (click)="removeSource(sourceIndex)" [disabled]="form.disabled">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
<div class="row" >
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label>
<input matInput type="text" name="url" [formControl]="source.get('url')" required>
<mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
<mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
<mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
<mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
<mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="chip-list">
<mat-label>{{'TENANT-EDITOR.FIELDS.CODES' | translate}}</mat-label>
<mat-chip-grid #chipGrid [formControl]="source.get('codes')">
<mat-chip-row *ngFor="let code of codes.get(sourceIndex)"
(removed)="removeCode(code, sourceIndex)"
[editable]="true"
(edited)="editCode(code, $event, sourceIndex)">
{{code}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
<input placeholder="{{'TENANT-EDITOR.FIELDS.CODES-PLACEHOLDER' | translate}}"
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addCode($event, sourceIndex)"/>
</mat-chip-grid>
<mat-error *ngIf="source.get('codes').hasError('backendError')">{{source.get('codes').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('codes').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,18 @@
.action-btn {
border-radius: 30px;
background-color: var(--secondary-color);
border: 1px solid transparent;
padding-left: 2em;
padding-right: 2em;
box-shadow: 0px 3px 6px #1E202029;
transition-property: background-color, color;
transition-duration: 200ms;
transition-delay: 50ms;
transition-timing-function: ease-in-out;
&:disabled{
background-color: #CBCBCB;
color: #FFF;
border: 0px;
}
}

View File

@ -0,0 +1,99 @@
import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormArray } from '@angular/forms';
import { MatChipEditedEvent, MatChipInputEvent } from '@angular/material/chips';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { TenantDepositConfigEditorModel, TenantEditorModel, TenantSourceEditorModel } from '../tenant-editor.model';
@Component({
selector: 'app-tenant-source-component',
templateUrl: 'tenant-source.component.html',
styleUrls: ['./tenant-source.component.scss']
})
export class TenantSourceComponent extends BaseComponent implements OnInit {
@Input() form;
@Input() validationErrorModel: ValidationErrorModel = null;
@Input() validationRootPath: string = null;
@Input() codes: Map<number, string[]>;
@Input() label: string = null;
constructor(
public enumUtils: EnumUtils,
) { super(); }
ngOnInit() {
}
//
// source
//
addSource(): void {
const formArray = this.form.get('sources') as UntypedFormArray;
this.codes.set(formArray.length, []);
const source: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel);
formArray.push(source.buildForm({ rootPath: this.validationRootPath + 'sources[' + formArray.length + '].' }));
}
removeSource(sourceIndex: number): void {
this.codes.delete((this.form.get('sources') as UntypedFormArray).length);
(this.form.get('sources') as UntypedFormArray).removeAt(sourceIndex);
// Reapply validators
TenantDepositConfigEditorModel.reapplySourcesFieldsValidators(
{
formArray: this.form.get('sources') as UntypedFormArray,
validationErrorModel: this.validationErrorModel,
rootPath: this.validationRootPath
}
)
this.form.get('sources').markAsDirty();
}
// source codes
addCode(event: MatChipInputEvent, key: number): void {
const value = (event.value || '').trim();
if (value){
const values = this.codes.get(key);
values.push(value);
this.codes.set(key, values);
}
event.chipInput!.clear();
}
removeCode(code: string, key: number): void {
const values = this.codes.get(key);
if (values){
const index = values.indexOf(code);
if (index >= 0) {
values.splice(index, 1);
this.codes.set(key, values);
}
}
}
editCode(code: string, event: MatChipEditedEvent, key: number) {
const values = this.codes.get(key);
if (values){
const value = event.value.trim();
// Remove code if it no longer has a value
if (!value) {
this.removeCode(code, key);
return;
}
const index = values.indexOf(code);
if (index >= 0) {
values[index] = value;
this.codes.set(key, values);
}
}
}
}

View File

@ -57,174 +57,22 @@
</div> </div>
<!-- Deposit --> <!-- Deposit -->
<div class="col-12"> <div class="col-12">
<h3> <app-tenant-source-component
{{'TENANT-EDITOR.FIELDS.DEPOSIT' | translate}} [form]="formGroup.get('config').get('deposit')"
<button mat-button class="action-btn" type="button" (click)="addDepositSource()" [disabled]="formGroup.disabled">{{'TENANT-EDITOR.ACTIONS.ADD-SOURCE' | translate}}</button> [validationErrorModel]="editorModel.validationErrorModel"
</h3> [validationRootPath]="'config.deposit.'"
<div *ngFor="let source of formGroup.get('config').get('deposit').get('sources').controls; let sourceIndex=index;" class="row mb-3"> [codes]="depositCodes"
<div class="col-12"> [label]="'TENANT-EDITOR.FIELDS.DEPOSIT' | translate">
<div class="row mb-3 d-flex align-items-center"> </app-tenant-source-component>
<div class="col-auto d-flex">
<mat-card-title>{{'TENANT-EDITOR.FIELDS.SOURCE' | translate}} {{sourceIndex + 1}}</mat-card-title>
</div>
<div class="col-auto d-flex">
<button mat-icon-button class="action-list-icon" matTooltip="{{'TENANT-EDITOR.ACTIONS.REMOVE-SOURCE' | translate}}" (click)="removeDepositSource(sourceIndex)" [disabled]="formGroup.disabled">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
<div class="row" >
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label>
<input matInput type="text" name="url" [formControl]="source.get('url')" required>
<mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
<mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
<mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
<mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
<mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="chip-list">
<mat-label>{{'TENANT-EDITOR.FIELDS.CODES' | translate}}</mat-label>
<mat-chip-grid #chipGrid [formControl]="source.get('codes')">
<mat-chip-row *ngFor="let code of depositCodes.get(sourceIndex)"
(removed)="removeDepositCode(code, sourceIndex)"
[editable]="true"
(edited)="editDepositCode(code, $event, sourceIndex)">
{{code}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
<input placeholder="{{'TENANT-EDITOR.FIELDS.CODES-PLACEHOLDER' | translate}}"
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addDepositCode($event, sourceIndex)"/>
</mat-chip-grid>
<mat-error *ngIf="source.get('codes').hasError('backendError')">{{source.get('codes').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('codes').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div>
</div> </div>
<div class="col-12"> <div class="col-12">
<h3> <app-tenant-source-component
{{'TENANT-EDITOR.FIELDS.FILE-TRANSFORMERS' | translate}} [form]="formGroup.get('config').get('fileTransformers')"
<button mat-button class="action-btn" type="button" (click)="addFileSource()" [disabled]="formGroup.disabled">{{'TENANT-EDITOR.ACTIONS.ADD-SOURCE' | translate}}</button> [validationErrorModel]="editorModel.validationErrorModel"
</h3> [validationRootPath]="'config.fileTransformers.'"
<div *ngFor="let source of formGroup.get('config').get('fileTransformers').get('sources').controls; let sourceIndex=index;" class="row mb-3"> [codes]="fileTransformersCodes"
<div class="col-12"> [label]="'TENANT-EDITOR.FIELDS.FILE-TRANSFORMERS' | translate">
<div class="row mb-3 d-flex align-items-center"> </app-tenant-source-component>
<div class="col-auto d-flex">
<mat-card-title>{{'TENANT-EDITOR.FIELDS.SOURCE' | translate}} {{sourceIndex + 1}}</mat-card-title>
</div>
<div class="col-auto d-flex">
<button mat-icon-button class="action-list-icon" matTooltip="{{'TENANT-EDITOR.ACTIONS.REMOVE-SOURCE' | translate}}" (click)="removeFileSource(sourceIndex)" [disabled]="formGroup.disabled">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
<div class="row" >
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.URL' | translate}}</mat-label>
<input matInput type="text" name="url" [formControl]="source.get('url')" required>
<mat-error *ngIf="source.get('url').hasError('backendError')">{{source.get('url').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.ISSUER-URL' | translate}}</mat-label>
<input matInput type="text" name="issuerUrl" [formControl]="source.get('issuerUrl')" required>
<mat-error *ngIf="source.get('issuerUrl').hasError('backendError')">{{source.get('issuerUrl').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('issuerUrl').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-ID' | translate}}</mat-label>
<input matInput type="text" name="clientId" [formControl]="source.get('clientId')" required>
<mat-error *ngIf="source.get('clientId').hasError('backendError')">{{source.get('clientId').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('clientId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.CLIENT-SECRET' | translate}}</mat-label>
<input matInput type="text" name="clientSecret" [formControl]="source.get('clientSecret')" required>
<mat-error *ngIf="source.get('clientSecret').hasError('backendError')">{{source.get('clientSecret').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('clientSecret').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="w-100">
<mat-label>{{'TENANT-EDITOR.FIELDS.SCOPE' | translate}}</mat-label>
<input matInput type="text" name="scope" [formControl]="source.get('scope')" required>
<mat-error *ngIf="source.get('scope').hasError('backendError')">{{source.get('scope').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('scope').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-6">
<mat-form-field class="chip-list">
<mat-label>{{'TENANT-EDITOR.FIELDS.CODES' | translate}}</mat-label>
<mat-chip-grid #chipGrid [formControl]="source.get('codes')">
<mat-chip-row *ngFor="let code of fileTransformersCodes.get(sourceIndex)"
(removed)="removeFileCode(code, sourceIndex)"
[editable]="true"
(edited)="editFileCode(code, $event, sourceIndex)">
{{code}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
<input placeholder="{{'TENANT-EDITOR.FIELDS.CODES-PLACEHOLDER' | translate}}"
[matChipInputFor]="chipGrid"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addFileCode($event, sourceIndex)"/>
</mat-chip-grid>
<mat-error *ngIf="source.get('codes').hasError('backendError')">{{source.get('codes').getError('backendError').message}}</mat-error>
<mat-error *ngIf="source.get('codes').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</mat-card-content> </mat-card-content>

View File

@ -27,8 +27,7 @@ import { TranslateService } from '@ngx-translate/core';
import { map, takeUntil } from 'rxjs/operators'; import { map, takeUntil } from 'rxjs/operators';
import { TenantEditorResolver } from './tenant-editor.resolver'; import { TenantEditorResolver } from './tenant-editor.resolver';
import { TenantEditorService } from './tenant-editor.service'; import { TenantEditorService } from './tenant-editor.service';
import { TenantEditorModel, TenantSourceEditorModel } from './tenant-editor.model'; import { TenantEditorModel } from './tenant-editor.model';
import { MatChipEditedEvent, MatChipInputEvent } from '@angular/material/chips';
@Component({ @Component({
@ -188,137 +187,5 @@ export class TenantEditorComponent extends BaseEditor<TenantEditorModel, Tenant>
this.formService.validateAllFormFields(this.formGroup); this.formService.validateAllFormFields(this.formGroup);
} }
//
// deposit source
//
addDepositSource(): void {
this.depositCodes.set((this.formGroup.get('config').get('deposit').get('sources') as FormArray).length, []);
(this.formGroup.get('config').get('deposit').get('sources') as FormArray).push(this.editorModel.createChildDeposit((this.formGroup.get('config').get('deposit').get('sources') as FormArray).length));
}
removeDepositSource(sourceIndex: number): void {
this.depositCodes.delete((this.formGroup.get('config').get('deposit').get('sources') as FormArray).length);
(this.formGroup.get('config').get('deposit').get('sources') as FormArray).removeAt(sourceIndex);
//Reapply validators
TenantEditorModel.reApplyDepositSourcesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
)
this.formGroup.get('config').get('deposit').get('sources').markAsDirty();
}
// deposit source codes
addDepositCode(event: MatChipInputEvent, key: number): void {
const value = (event.value || '').trim();
if (value){
const values = this.depositCodes.get(key);
values.push(value);
this.depositCodes.set(key, values);
}
event.chipInput!.clear();
}
removeDepositCode(code: string, key: number): void {
const values = this.depositCodes.get(key);
if (values){
const index = values.indexOf(code);
if (index >= 0) {
values.splice(index, 1);
this.depositCodes.set(key, values);
}
}
}
editDepositCode(code: string, event: MatChipEditedEvent, key: number) {
const values = this.depositCodes.get(key);
if (values){
const value = event.value.trim();
// Remove code if it no longer has a value
if (!value) {
this.removeDepositCode(code, key);
return;
}
const index = values.indexOf(code);
if (index >= 0) {
values[index] = value;
this.depositCodes.set(key, values);
}
}
}
//
// fileTransformers source
//
addFileSource(): void {
this.fileTransformersCodes.set((this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).length, []);
(this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).push(this.editorModel.createChildFileTransformer((this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).length));
}
removeFileSource(sourceIndex: number): void {
this.fileTransformersCodes.delete((this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).length);
(this.formGroup.get('config').get('fileTransformers').get('sources') as FormArray).removeAt(sourceIndex);
//Reapply validators
TenantEditorModel.reApplyFileTransformerSourcesValidators(
{
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
)
this.formGroup.get('config').get('fileTransformers').get('sources').markAsDirty();
}
// fileTransformers source codes
addFileCode(event: MatChipInputEvent, key: number): void {
const value = (event.value || '').trim();
if (value){
const values = this.fileTransformersCodes.get(key);
values.push(value);
this.fileTransformersCodes.set(key, values);
}
event.chipInput!.clear();
}
removeFileCode(code: string, key: number): void {
const values = this.fileTransformersCodes.get(key);
if (values){
const index = values.indexOf(code);
if (index >= 0) {
values.splice(index, 1);
this.fileTransformersCodes.set(key, values);
}
}
}
editFileCode(code: string, event: MatChipEditedEvent, key:number) {
const values = this.fileTransformersCodes.get(key);
if (values){
const value = event.value.trim();
// Remove code if it no longer has a value
if (!value) {
this.removeFileCode(code, key);
return;
}
const index = values.indexOf(code);
if (index >= 0) {
values[index] = value;
this.fileTransformersCodes.set(key, values);
}
}
}
} }

View File

@ -56,11 +56,6 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist
return baseContext; return baseContext;
} }
createChildDeposit(index: number): UntypedFormGroup {
const deposit: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel);
return deposit.buildForm({ rootPath: 'config.deposit.sources[' + index + '].' });
}
static reApplyDepositSourcesValidators(params: { static reApplyDepositSourcesValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,
@ -75,11 +70,6 @@ export class TenantEditorModel extends BaseEditorModel implements TenantPersist
}); });
} }
createChildFileTransformer(index: number): UntypedFormGroup {
const deposit: TenantSourceEditorModel = new TenantSourceEditorModel(this.validationErrorModel);
return deposit.buildForm({ rootPath: 'config.fileTransformers.sources[' + index + '].' });
}
static reApplyFileTransformerSourcesValidators(params: { static reApplyFileTransformerSourcesValidators(params: {
formGroup: UntypedFormGroup, formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel, validationErrorModel: ValidationErrorModel,
@ -186,8 +176,8 @@ export class TenantDepositConfigEditorModel implements TenantDepositConfigPersis
this.validationErrorModel this.validationErrorModel
).fromModel(item).buildForm({ ).fromModel(item).buildForm({
rootPath: `${rootPath}sources[${index}].` rootPath: `${rootPath}sources[${index}].`
}), context.getValidation('sources') })
) ), context.getValidation('sources')
), ),
}); });
} }
@ -258,8 +248,8 @@ export class TenantFileTransformersConfigEditorModel implements TenantFileTransf
this.validationErrorModel this.validationErrorModel
).fromModel(item).buildForm({ ).fromModel(item).buildForm({
rootPath: `${rootPath}sources[${index}].` rootPath: `${rootPath}sources[${index}].`
}), context.getValidation('sources') })
) ), context.getValidation('sources')
), ),
}); });
} }

View File

@ -14,6 +14,7 @@ import { TenantEditorComponent } from './editor/tenant-editor.component';
import { TenantListingComponent } from './listing/tenant-listing.component'; import { TenantListingComponent } from './listing/tenant-listing.component';
import { TenantListingFiltersComponent } from "./listing/filters/tenant-listing-filters.component"; import { TenantListingFiltersComponent } from "./listing/filters/tenant-listing-filters.component";
import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module'; import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module';
import { TenantSourceComponent } from './editor/source/tenant-source.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -33,7 +34,8 @@ import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-ed
declarations: [ declarations: [
TenantEditorComponent, TenantEditorComponent,
TenantListingComponent, TenantListingComponent,
TenantListingFiltersComponent TenantListingFiltersComponent,
TenantSourceComponent
] ]
}) })
export class TenantModule { } export class TenantModule { }

View File

@ -30,11 +30,11 @@
<div class="dmp-card-actions"> <div class="dmp-card-actions">
<a class="col-auto border-right pointer" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DMP-LISTING.ACTIONS.EXPORT' | translate}}</a> <a class="col-auto border-right pointer" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DMP-LISTING.ACTIONS.EXPORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isDraftDmp(dmp)" [routerLink]="['/plans/edit/' + dmp.id]" target="_blank"><span class="material-icons icon-align">add</span>{{'DMP-LISTING.ACTIONS.ADD-DESCRIPTION-SHORT' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="isDraftDmp(dmp)" [routerLink]="['/plans/edit/' + dmp.id]" target="_blank"><span class="material-icons icon-align">add</span>{{'DMP-LISTING.ACTIONS.ADD-DESCRIPTION-SHORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isUserOwner(dmp)" (click)="inviteToDmp()"><span class="material-icons icon-align pr-2">group_add</span>{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="isDmpOwner(dmp)" (click)="inviteToDmp()"><span class="material-icons icon-align pr-2">group_add</span>{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="cloneClicked()"><span class="material-icons icon-align pr-2">filter_none</span>{{'DMP-LISTING.ACTIONS.CLONE' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="cloneClicked()"><span class="material-icons icon-align pr-2">filter_none</span>{{'DMP-LISTING.ACTIONS.CLONE' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="!isAuthenticated()" (click)="viewVersions(dmp)"><span class="material-icons icon-align pr-2">library_books</span>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="!isAuthenticated()" (click)="viewVersions(dmp)"><span class="material-icons icon-align pr-2">library_books</span>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="!isDraftDmp(dmp) && isUserOwner(dmp)" (click)="deleteClicked(dmp.id)"><span class="material-icons icon-align pr-2">delete</span>{{ 'DMP-LISTING.ACTIONS.DELETE' | translate }}</a> <a class="col-auto border-right pointer" *ngIf="!isDraftDmp(dmp) && isDmpOwner(dmp)" (click)="deleteClicked(dmp.id)"><span class="material-icons icon-align pr-2">delete</span>{{ 'DMP-LISTING.ACTIONS.DELETE' | translate }}</a>
<a class="col-auto pointer" *ngIf="isAuthenticated()" [matMenuTriggerFor]="actionsMenu"><span class="material-icons icon-align pl-2">more_horiz</span></a> <a class="col-auto pointer" *ngIf="isAuthenticated()" [matMenuTriggerFor]="actionsMenu"><span class="material-icons icon-align pl-2">more_horiz</span></a>
</div> </div>
<mat-menu #exportMenu="matMenu" xPosition="before"> <mat-menu #exportMenu="matMenu" xPosition="before">
@ -44,13 +44,13 @@
</button> </button>
</mat-menu> </mat-menu>
<mat-menu #actionsMenu="matMenu" xPosition="before"> <mat-menu #actionsMenu="matMenu" xPosition="before">
<button *ngIf="isUserOwner(dmp)" mat-menu-item (click)="newVersionClicked()"> <button *ngIf="canCreateNewVersion(dmp)" mat-menu-item (click)="newVersionClicked()">
<mat-icon>queue</mat-icon>{{'DMP-LISTING.ACTIONS.NEW-VERSION' | translate}} <mat-icon>queue</mat-icon>{{'DMP-LISTING.ACTIONS.NEW-VERSION' | translate}}
</button> </button>
<button mat-menu-item (click)="viewVersions(dmp)"> <button mat-menu-item (click)="viewVersions(dmp)">
<mat-icon>library_books</mat-icon>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}} <mat-icon>library_books</mat-icon>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}
</button> </button>
<button mat-menu-item *ngIf="isDraftDmp(dmp) && isUserOwner(dmp)" (click)="deleteClicked(dmp.id)" class="menu-item"> <button mat-menu-item *ngIf="isDraftDmp(dmp) && isDmpOwner(dmp)" (click)="deleteClicked(dmp.id)" class="menu-item">
<mat-icon>delete</mat-icon>{{ 'DMP-LISTING.ACTIONS.DELETE' | translate }} <mat-icon>delete</mat-icon>{{ 'DMP-LISTING.ACTIONS.DELETE' | translate }}
</button> </button>
</mat-menu> </mat-menu>

View File

@ -26,6 +26,7 @@ import { AuthService } from '../../../../core/services/auth/auth.service';
import { CloneDmpDialogComponent } from '../../clone-dialog/dmp-clone-dialog.component'; import { CloneDmpDialogComponent } from '../../clone-dialog/dmp-clone-dialog.component';
import { DmpInvitationDialogComponent } from '../../invitation/dialog/dmp-invitation-dialog.component'; import { DmpInvitationDialogComponent } from '../../invitation/dialog/dmp-invitation-dialog.component';
import { NewVersionDmpDialogComponent } from '../../new-version-dialog/dmp-new-version-dialog.component'; import { NewVersionDmpDialogComponent } from '../../new-version-dialog/dmp-new-version-dialog.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@Component({ @Component({
selector: 'app-dmp-listing-item-component', selector: 'app-dmp-listing-item-component',
@ -104,7 +105,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit {
} }
viewVersions(dmp: Dmp) { viewVersions(dmp: Dmp) {
if (dmp.accessType == DmpAccessType.Public && dmp.status == DmpStatus.Finalized && !this.isUserOwner(dmp)) { if (dmp.accessType == DmpAccessType.Public && dmp.status == DmpStatus.Finalized && !this.isDmpOwner(dmp)) {
let url = this.router.createUrlTree(['/explore-plans/versions/', dmp.groupId]); let url = this.router.createUrlTree(['/explore-plans/versions/', dmp.groupId]);
window.open(url.toString(), '_blank'); window.open(url.toString(), '_blank');
} else { } else {
@ -214,8 +215,12 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit {
this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error); this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error);
} }
isUserOwner(dmp: Dmp): boolean { isDmpOwner(dmp: Dmp): boolean {
const principalId: Guid = this.authentication.userId(); const principalId: Guid = this.authentication.userId();
if (principalId) return !!dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id)); if (principalId) return !!dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id));
} }
canCreateNewVersion(dmp: Dmp): boolean {
return this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp);
}
} }