Merge branch 'AdminTemplateRedesign' into WizardDescriptionRefactor

This commit is contained in:
Kristian Ntavidi 2021-03-16 09:12:49 +02:00
commit 720fa60f9b
28 changed files with 1064 additions and 194 deletions

View File

@ -64,12 +64,31 @@ export class AppComponent implements OnInit, AfterViewInit {
ngAfterViewInit(): void { ngAfterViewInit(): void {
setTimeout(() => { setTimeout(() => {
this.sideNavSubscription = this.sidenavService.status().subscribe(isopen=>{ this.sideNavSubscription = this.sidenavService.status().subscribe(isopen=>{
const hamburger = document.getElementById('hamburger');
if(isopen){ if(isopen){
//update value of hamburfer //update value of hamburfer
document.getElementById('hamburger').classList.add('change'); if(!hamburger){//try later
setTimeout(() => {
const hamburger = document.getElementById('hamburger');
if(hamburger){
hamburger.classList.add('change');
}
}, 300);
}else{
hamburger.classList.add('change');
}
this.sidenav.open() this.sidenav.open()
}else{ }else{//closed
document.getElementById('hamburger').classList.remove('change'); if(!hamburger){//try later
setTimeout(() => {
const hamburger = document.getElementById('hamburger');
if(hamburger){
hamburger.classList.remove('change');
}
}, 300);
}else{
hamburger.classList.remove('change');
}
this.sidenav.close(); this.sidenav.close();
} }

View File

@ -388,7 +388,10 @@ export class GuidedTourComponent implements AfterViewInit, OnDestroy {
} }
const scrollAdjustment = this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0; const scrollAdjustment = this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0;
const tourStepHeight = typeof this.tourStep.nativeElement.getBoundingClientRect === 'function' ? this.tourStep.nativeElement.getBoundingClientRect().height : 0; let tourStepHeight = 0;
if (this.tourStep != null && this.tourStep.nativeElement != null && typeof this.tourStep.nativeElement.getBoundingClientRect === 'function') {
tourStepHeight = this.tourStep.nativeElement.getBoundingClientRect().height;
}
const elementHeight = this.selectedElementRect.height + scrollAdjustment + tourStepHeight; const elementHeight = this.selectedElementRect.height + scrollAdjustment + tourStepHeight;
if ((this.windowRef.nativeWindow.innerHeight - this.topOfPageAdjustment) < elementHeight) { if ((this.windowRef.nativeWindow.innerHeight - this.topOfPageAdjustment) < elementHeight) {

View File

@ -67,12 +67,12 @@
</div> </div>
</div> --> </div> -->
<mat-form-field appearance="none" class="numbering-label" [ngStyle]="calculateLabelWidth(numbering)"> <mat-form-field appearance="none" class="numbering-label" [ngStyle]="calculateLabelWidth(numbering)">
<input matInput type="text" [value]="numbering" disabled> <input [ngClass]="{'text-danger':form.get('title').invalid &&form.get('title').touched}" matInput type="text" [value]="numbering" disabled>
</mat-form-field> </mat-form-field>
<!-- [appearance]="titleControl.focused? 'legacy':'none'" floatLabel="never" --> <!-- [appearance]="titleControl.focused? 'legacy':'none'" floatLabel="never" -->
<mat-form-field class="col field-title" [appearance]="'none'"> <mat-form-field class="col field-title" [appearance]="'none'" floatLabel="never">
<input matInput type="text" placeholder="Title" #titleControl="matInput" <input matInput type="text" placeholder="Title" #titleControl="matInput"
[formControl]="this.form.get('title')"> [formControl]="this.form.get('title')" required>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
@ -145,6 +145,7 @@
[form]="form.get('fields').get(''+i)" [showOrdinal]="false" [form]="form.get('fields').get(''+i)" [showOrdinal]="false"
[indexPath]="indexPath + 'f' + i" [viewOnly]="viewOnly" [indexPath]="indexPath + 'f' + i" [viewOnly]="viewOnly"
[expandView]="hasFocus" [expandView]="hasFocus"
[canBeDeleted]="form.get('fields')['controls'].length !=1"
(delete)="deleteField(i)"> (delete)="deleteField(i)">
</app-dataset-profile-editor-field-component> </app-dataset-profile-editor-field-component>
</div> </div>

View File

@ -32,6 +32,7 @@ import { CurrencyDataEditorModel } from '../../../admin/field-data/currency-data
import { ValidationDataEditorModel } from '../../../admin/field-data/validation-data-editor-models'; import { ValidationDataEditorModel } from '../../../admin/field-data/validation-data-editor-models';
import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service'; import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service';
import { OrganizationsDataEditorModel } from '../../../admin/field-data/organizations-data-editor-models'; import { OrganizationsDataEditorModel } from '../../../admin/field-data/organizations-data-editor-models';
import { EditorCustomValidators } from '../../custom-validators/editor-custom-validators';
@Component({ @Component({
selector: 'app-dataset-profile-editor-composite-field-component', selector: 'app-dataset-profile-editor-composite-field-component',
@ -59,6 +60,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
viewStyleEnum = DatasetProfileFieldViewStyle; viewStyleEnum = DatasetProfileFieldViewStyle;
viewTypeEnum = ViewStyleType; viewTypeEnum = ViewStyleType;
private myCustomValidators:EditorCustomValidators = new EditorCustomValidators();
constructor( constructor(
private dialog: MatDialog, private dialog: MatDialog,
@ -358,15 +360,15 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
fieldForm.addControl('data', new WordListFieldDataEditorModel().buildForm()); fieldForm.addControl('data', new WordListFieldDataEditorModel().buildForm());
this.form.get('data').setValidators(this._atLeastOneElementListValidator('options')); fieldForm.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
this.form.get('data').updateValueAndValidity(); fieldForm.get('data').updateValueAndValidity();
break; break;
case this.viewTypeEnum.Other: case this.viewTypeEnum.Other:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
fieldForm.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE fieldForm.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE
fieldForm.get('data').setValidators(this._atLeastOneElementListValidator('autoCompleteSingleDataList')); fieldForm.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList'));
fieldForm.get('data').updateValueAndValidity();
break; break;
case this.viewTypeEnum.InternalDmpEntities: case this.viewTypeEnum.InternalDmpEntities:
@ -380,6 +382,9 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
case this.viewTypeEnum.RadioBox: case this.viewTypeEnum.RadioBox:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox) fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox)
fieldForm.addControl('data', new RadioBoxFieldDataEditorModel().buildForm()); fieldForm.addControl('data', new RadioBoxFieldDataEditorModel().buildForm());
fieldForm.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
fieldForm.get('data').updateValueAndValidity();
break; break;
case this.viewTypeEnum.TextArea: case this.viewTypeEnum.TextArea:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea) fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea)
@ -416,7 +421,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
break; break;
case this.viewTypeEnum.Organizations: case this.viewTypeEnum.Organizations:
fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Organizations) fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Organizations)
this.form.addControl('data', new OrganizationsDataEditorModel().buildForm()); fieldForm.addControl('data', new OrganizationsDataEditorModel().buildForm());
// this.form.addControl('data', new OrganizationsDataEditorModel().buildForm()) // this.form.addControl('data', new OrganizationsDataEditorModel().buildForm())
// fieldForm.addControl('data', new DatasetsAutoCompleteFieldDataEditorModel().buildForm()); //TODO // fieldForm.addControl('data', new DatasetsAutoCompleteFieldDataEditorModel().buildForm()); //TODO
break; break;
@ -436,7 +441,9 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
(<FormArray>this.form.get('fields')).push(fieldForm); (<FormArray>this.form.get('fields')).push(fieldForm);
// fieldForm.updateValueAndValidity(); fieldForm.get('viewStyle').get('renderStyle').updateValueAndValidity();
fieldForm.get('data').updateValueAndValidity();
} }
@ -488,17 +495,17 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh
// } // }
private _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{ // private _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{ // return (control: AbstractControl): ValidationErrors | null=>{
const fa = control.get(arrayToCheck) as FormArray; // const fa = control.get(arrayToCheck) as FormArray;
if(fa.length === 0){ // if(fa.length === 0){
return {emptyArray: true}; // return {emptyArray: true};
} // }
return null; // return null;
} // }
} // }
calculateLabelWidth(numbering: string){ calculateLabelWidth(numbering: string){

View File

@ -12,7 +12,7 @@
<li class="list-inline-item" *ngIf="!viewOnly && viewType"> <li class="list-inline-item" *ngIf="!viewOnly && viewType">
<mat-icon style="cursor: pointer;" (click)="addNewRule()" [matTooltip]="'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.ADD-VISIBILITY-RULE' | translate">visibility</mat-icon> <mat-icon style="cursor: pointer;" (click)="addNewRule()" [matTooltip]="'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.ADD-VISIBILITY-RULE' | translate">visibility</mat-icon>
</li> </li>
<li class="list-inline-item" *ngIf="!viewOnly && viewType"> <li class="list-inline-item" *ngIf="!viewOnly && viewType && canBeDeleted">
<mat-icon style="cursor: pointer;" (click)="onDelete()" [matTooltip]="'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.DELETE-INPUT' | translate">delete</mat-icon> <mat-icon style="cursor: pointer;" (click)="onDelete()" [matTooltip]="'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.DELETE-INPUT' | translate">delete</mat-icon>
</li> </li>

View File

@ -60,7 +60,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
@Input() expandView: boolean = true; @Input() expandView: boolean = true;
@Input() canBeDeleted:boolean = true;
@Output() delete = new EventEmitter<void>(); @Output() delete = new EventEmitter<void>();
@ -425,7 +425,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
this.form.addControl('data', new WordListFieldDataEditorModel().buildForm()); this.form.addControl('data', new WordListFieldDataEditorModel().buildForm());
this.form.get('data').setValidators(this.myCustomValidators._atLeastOneElementListValidator('options')); this.form.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
this.form.get('data').updateValueAndValidity(); this.form.get('data').updateValueAndValidity();
break; break;
@ -433,7 +433,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox)
this.form.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE this.form.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE
this.form.get('data').setValidators(this.myCustomValidators._atLeastOneElementListValidator('autoCompleteSingleDataList')); this.form.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList'));
this.form.get('data').updateValueAndValidity(); this.form.get('data').updateValueAndValidity();
@ -450,7 +450,7 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox) this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox)
this.form.addControl('data', new RadioBoxFieldDataEditorModel().buildForm()); this.form.addControl('data', new RadioBoxFieldDataEditorModel().buildForm());
this.form.get('data').setValidators(this.myCustomValidators._atLeastOneElementListValidator('options')); this.form.get('data').setValidators(this.myCustomValidators.atLeastOneElementListValidator('options'));
this.form.get('data').updateValueAndValidity(); this.form.get('data').updateValueAndValidity();
break; break;
@ -504,6 +504,10 @@ export class DatasetProfileEditorFieldComponent extends BaseComponent implements
this.form.addControl('data', new ValidationDataEditorModel().buildForm()); this.form.addControl('data', new ValidationDataEditorModel().buildForm());
break; break;
} }
this.form.get('data').updateValueAndValidity();
this.form.get('viewStyle').get('renderStyle').updateValueAndValidity();
this.form.updateValueAndValidity();
setTimeout(() => { //TODO setTimeout(() => { //TODO
this.showPreview = true; this.showPreview = true;
}); });

View File

@ -130,6 +130,8 @@ export class DatasetProfileEditorSectionFieldSetComponent extends BaseComponent
if(el){ if(el){
el.scrollIntoView({behavior: "smooth"}); el.scrollIntoView({behavior: "smooth"});
} }
}else{//scroll on top
window.scrollTo({top:0, behavior:'smooth'});
} }
} }

View File

@ -13,7 +13,8 @@
<div class="hint col-12">{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-NAME-HINT' | translate}}</div> <div class="hint col-12">{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-NAME-HINT' | translate}}</div>
<mat-form-field class="col-12"> <mat-form-field class="col-12">
<input matInput type="text" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.TITLE' | translate}}" <input matInput type="text" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.TITLE' | translate}}"
formControlName="title"> formControlName="title" required>
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<!-- <mat-form-field class="col-md-3"> <!-- <mat-form-field class="col-md-3">
<mat-select placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.PAGE' | translate}}" formControlName="page" <mat-select placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.PAGE' | translate}}" formControlName="page"
@ -30,7 +31,8 @@
<div class="hint col-12">{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION-HINT' | translate}}</div> <div class="hint col-12">{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION-HINT' | translate}}</div>
<mat-form-field class="col-12"> <mat-form-field class="col-12">
<input matInput type="text" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.DESCRIPTION' | translate}}" <input matInput type="text" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.DESCRIPTION' | translate}}"
formControlName="description"> formControlName="description" required>
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>

View File

@ -4,16 +4,64 @@ import { AbstractControl, FormArray, ValidationErrors, ValidatorFn } from "@angu
export class EditorCustomValidators{ export class EditorCustomValidators{
public _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{ public atLeastOneElementListValidator(arrayToCheck): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{ return (control: AbstractControl): ValidationErrors | null=>{
const fa = control.get(arrayToCheck) as FormArray; const fa = control.get(arrayToCheck) as FormArray;
if(fa.length === 0){ if(fa.length === 0){
return {emptyArray: true}; return {[EditorCustomValidatorsEnum.emptyArray]: true};
} }
return null; return null;
} }
} }
public pagesHaveAtLeastOneSection(pagesArrayName:string,sectionsArrayName:string ): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{
const pages = control.get(pagesArrayName) as FormArray;
const sections = control.get(sectionsArrayName) as FormArray;
const pageIdsArray = pages.controls.map(page=>page.get('id').value);
const sectionsPageIds = sections.controls.map(section=> section.get('page').value);
let invalidMessage = null;
pageIdsArray.forEach(pageId=>{
if(!sectionsPageIds.includes(pageId)){
invalidMessage = {[EditorCustomValidatorsEnum.atLeastOneSectionInPage]:true};
}
})
return invalidMessage;
}
}
public sectionHasAtLeastOneChildOf(fieldsetsArrayName, sectionsArrayName): ValidatorFn{
return (control: AbstractControl): ValidationErrors | null=>{
const fieldsets = control.get(fieldsetsArrayName) as FormArray;
const sections = control.get(sectionsArrayName) as FormArray;
if((fieldsets && fieldsets.length) || (sections && sections.length)){
return null;
}
return {[EditorCustomValidatorsEnum.sectionMustHaveOneChild] : true};
}
}
} }
export enum EditorCustomValidatorsEnum{
sectionMustHaveOneChild = "sectionMustHaveOneChild",
atLeastOneSectionInPage = 'atLeastOneSectionInPage',
emptyArray="emptyArray"
}

View File

@ -40,7 +40,7 @@
<span (click)="stepper.selectedIndex=idx" <span (click)="stepper.selectedIndex=idx"
class="stepper-title-label" class="stepper-title-label"
[ngClass]="{'stepper-title-label-locked': !isStepUnlocked(idx),'stepper-title-label-completed':idx < stepper.selectedIndex} "> [ngClass]="{'stepper-title-label-locked': !isStepUnlocked(idx),'stepper-title-label-completed':idx < stepper.selectedIndex} ">
<ng-container *ngIf="isStepCompleted(idx) else numberLabel"> <ng-container *ngIf="(step.completed &&(idx!=steps.length-1)) else numberLabel">
<mat-icon style="font-size:0.7em; height: 0px;">done</mat-icon> <mat-icon style="font-size:0.7em; height: 0px;">done</mat-icon>
</ng-container> </ng-container>
<ng-template #numberLabel> <ng-template #numberLabel>
@ -119,7 +119,8 @@
<mat-horizontal-stepper [linear]="true" #stepper class="stepper" (selectionChange)="onMatStepperSelectionChange($event)" style="padding-left: 8px; padding-right: 15px;"> <mat-horizontal-stepper [linear]="true" #stepper class="stepper" (selectionChange)="onMatStepperSelectionChange($event)" style="padding-left: 8px; padding-right: 15px;">
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate" [stepControl]="basicInfo"> <!-- IMPORTANT TO BE !INVALID (WHEN THE TEMPLATE IS FINALIZED THE CONTORLS ARE DISABLED) -->
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate" [completed]="(!form.get('label').invalid && !form.get('description').invalid && !form.get('language').invalid)" >
<!-- <ng-template matStepLabel>{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate}} <!-- <ng-template matStepLabel>{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TITLE' | translate}}
</ng-template> --> </ng-template> -->
<div class="row"> <div class="row">
@ -127,7 +128,7 @@
<div class="heading">1.1 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME'| translate}} *</div> <div class="heading">1.1 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME'| translate}} *</div>
<div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME-HINT'| translate}}</div> <div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME-HINT'| translate}}</div>
<mat-form-field class="full-width basic-info-input"> <mat-form-field class="full-width basic-info-input">
<input matInput formControlName="label" <input matInput [formControl]="form.get('label')"
placeholder="{{'DATASET-PROFILE-EDITOR.FIELDS.DATASET-TITLE' | translate}}" required> placeholder="{{'DATASET-PROFILE-EDITOR.FIELDS.DATASET-TITLE' | translate}}" required>
<mat-error *ngIf="form.get('label').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | <mat-error *ngIf="form.get('label').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' |
translate}} translate}}
@ -139,7 +140,7 @@
<!-- <div class="hint">{{'DMP-EDITOR.MAIN-INFO.HINT' | translate}}</div> --> <!-- <div class="hint">{{'DMP-EDITOR.MAIN-INFO.HINT' | translate}}</div> -->
<div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-HINT'| translate}}</div> <div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-HINT'| translate}}</div>
<mat-form-field class="full-width basic-info-input"> <mat-form-field class="full-width basic-info-input">
<textarea matInput formControlName="description" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="5" <textarea matInput [formControl]="form.get('description')" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="5"
placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER'| translate}}" required> placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER'| translate}}" required>
</textarea> </textarea>
<mat-error *ngIf="form.get('description').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' <mat-error *ngIf="form.get('description').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED'
@ -153,7 +154,7 @@
<div class="heading">1.3 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-LANGUAGE'| translate}} *</div> <div class="heading">1.3 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-LANGUAGE'| translate}} *</div>
<mat-form-field class="full-width basic-info-input"> <mat-form-field class="full-width basic-info-input">
<!-- <input matInput formControlName="description" placeholder="{{'DATASET-PROFILE-EDITOR.FIELDS.DATASET-DESCRIPTION' | translate}}" required> --> <!-- <input matInput formControlName="description" placeholder="{{'DATASET-PROFILE-EDITOR.FIELDS.DATASET-DESCRIPTION' | translate}}" required> -->
<mat-select formControlName="language" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-SELECT-LANGUAGE'| translate}}"> <mat-select [formControl]="form.get('language')" placeholder="{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-SELECT-LANGUAGE'| translate}}">
<mat-option *ngFor="let lang of getLanguageInfos()" [value]="lang.code"> <mat-option *ngFor="let lang of getLanguageInfos()" [value]="lang.code">
{{ lang.name }} {{ lang.name }}
</mat-option> </mat-option>
@ -181,7 +182,7 @@
</div> </div>
</div> </div>
</mat-step> --> </mat-step> -->
<mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate" [stepControl]="form"> <mat-step [label]="'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate" [completed]="form.valid">
<!-- <ng-template matStepLabel>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate}}</ng-template> --> <!-- <ng-template matStepLabel>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate}}</ng-template> -->
<div class="row"> <div class="row">
@ -200,7 +201,8 @@
(removeEntry)="onRemoveEntry($event)" (removeEntry)="onRemoveEntry($event)"
[itemSelected]="selectedTocEntry" [itemSelected]="selectedTocEntry"
[viewOnly]="viewOnly" [viewOnly]="viewOnly"
(dataNeedsRefresh)="onDataNeedsRefresh()"> (dataNeedsRefresh)="onDataNeedsRefresh()"
[colorizeInvalid]="colorizeInvalid">
</dataset-profile-table-of-contents> </dataset-profile-table-of-contents>
</div> </div>
</div> </div>
@ -224,6 +226,7 @@
<div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.PAGE-NAME-HINT' | translate}}</div> <div class="hint">{{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.PAGE-NAME-HINT' | translate}}</div>
<mat-form-field> <mat-form-field>
<input type="text" matInput formControlName="title"> <input type="text" matInput formControlName="title">
<mat-error >{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@ -402,7 +405,7 @@
<!-- SAVE BUTTON --> <!-- SAVE BUTTON -->
<div class="col-6 d-flex" *ngIf="!viewOnly"> <div class="col-6 d-flex" *ngIf="!viewOnly">
<div class="row mt-4"> <div class="row mt-4">
<ng-container *ngIf="false"> <ng-container *ngIf="true">
<button mat-raised-button class="col-auto mr-2" color="primary" type="button col-auto" <button mat-raised-button class="col-auto mr-2" color="primary" type="button col-auto"
(click)='checkFormValidation()' (click)='checkFormValidation()'
[disabled]="form.valid">{{'DATASET-PROFILE-EDITOR.ACTIONS.VALIDATE' | translate}}</button> [disabled]="form.valid">{{'DATASET-PROFILE-EDITOR.ACTIONS.VALIDATE' | translate}}</button>
@ -440,12 +443,20 @@
</div> </div>
</ng-container> </ng-container>
<!--
<div class="row"> <!-- <div class="row">
<button (click)="printForm()"> <button (click)="printForm()">
console form console form
</button> </button>
</div> --> </div> -->
<!--
<div *ngIf="form">{{form.value | json}}</div>
<br>
<br>
<div class="row">
{{form.controls?.sections.value |json}}
</div> -->
<!-- {{form.value | json}} --> <!-- {{form.value | json}} -->
<!-- <div class="row"> <!-- <div class="row">

View File

@ -41,7 +41,7 @@ import { Rule } from '@app/core/model/dataset-profile-definition/rule';
import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style'; import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style';
import { invalid } from '@angular/compiler/src/render3/view/util'; import { invalid } from '@angular/compiler/src/render3/view/util';
import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice'; import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice';
import { EditorCustomValidators } from './custom-validators/editor-custom-validators'; import { EditorCustomValidators, EditorCustomValidatorsEnum } from './custom-validators/editor-custom-validators';
import { CustomErrorValidator } from '@common/forms/validation/custom-validator'; import { CustomErrorValidator } from '@common/forms/validation/custom-validator';
import { STEPPER_ANIMATIONS } from './animations/animations'; import { STEPPER_ANIMATIONS } from './animations/animations';
const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json'); const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json');
@ -72,6 +72,8 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
errorMessages: string[] = []; errorMessages: string[] = [];
tocEntryEnumValues = ToCEntryType; tocEntryEnumValues = ToCEntryType;
colorizeInvalid:boolean = false;
customEditorValidators = new EditorCustomValidators(); customEditorValidators = new EditorCustomValidators();
@ -187,7 +189,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
} else { } else {
this.dataModel = new DatasetProfileEditorModel(); this.dataModel = new DatasetProfileEditorModel();
this.form = this.dataModel.buildForm(); this.form = this.dataModel.buildForm();
this.form.setValidators([this.customEditorValidators._atLeastOneElementListValidator('pages'), this.customEditorValidators._atLeastOneElementListValidator('sections')]) this.form.setValidators([this.customEditorValidators.atLeastOneElementListValidator('pages'), this.customEditorValidators.pagesHaveAtLeastOneSection('pages', 'sections')])
if (this.dataModel.status === DatasetProfileEnum.FINALIZED) { if (this.dataModel.status === DatasetProfileEnum.FINALIZED) {
this.form.disable(); this.form.disable();
this.viewOnly = true; this.viewOnly = true;
@ -207,10 +209,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
prepareForm() { prepareForm() {
this.visibilityRulesService.buildVisibilityRules([],this.form); this.visibilityRulesService.buildVisibilityRules([],this.form);
this.form.setValidators([this.customEditorValidators._atLeastOneElementListValidator('pages'), this.customEditorValidators._atLeastOneElementListValidator('pages')]); this.form.setValidators([this.customEditorValidators.atLeastOneElementListValidator('pages'),this.customEditorValidators.pagesHaveAtLeastOneSection('pages', 'sections')]);
this.form.valueChanges this.form.valueChanges
.pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
.subscribe(change => { .subscribe(change => {
// this.datasetProfileService.preview(this.form.value) // this.datasetProfileService.preview(this.form.value)
// .pipe(takeUntil(this._destroyed)) // .pipe(takeUntil(this._destroyed))
// .subscribe(dataset => { // .subscribe(dataset => {
@ -230,8 +233,6 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
this.selectedTocEntry = tocentries[0]; this.selectedTocEntry = tocentries[0];
} }
//this.getPreview();
} }
onIsMultiplicityEnabledChange(isMultiplicityEnabled: boolean) { onIsMultiplicityEnabledChange(isMultiplicityEnabled: boolean) {
@ -241,11 +242,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
} }
} }
addSection() { // addSection() {
const section: SectionEditorModel = new SectionEditorModel(); // const section: SectionEditorModel = new SectionEditorModel();
this.dataModel.sections.push(section); // this.dataModel.sections.push(section);
(<FormArray>this.form.get('sections')).push(section.buildForm()); // (<FormArray>this.form.get('sections')).push(section.buildForm());
} // }
addPage() { addPage() {
const page: PageEditorModel = new PageEditorModel(this.dataModel.pages.length); const page: PageEditorModel = new PageEditorModel(this.dataModel.pages.length);
@ -376,6 +377,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
}); });
} }
updateValidity(){
// this.form.updateValueAndValidity();
console.log('updagted');
}
getFilenameFromContentDispositionHeader(header: string): string { getFilenameFromContentDispositionHeader(header: string): string {
const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g);
@ -399,15 +405,19 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
} }
checkFormValidation() { checkFormValidation() {
if (!this.form.valid) { this.colorizeInvalid = true;
this.nestedIndex = -1; this.printMyErrors(this.form);
this.form.markAllAsTouched();
this.printErrors(this.form);
this.showValidationErrorsDialog(); // if (!this.form.valid) {
this.nestedCount = []; // this.nestedIndex = -1;
this.nestedIndex = 0; // this.form.markAllAsTouched();
this.errorMessages = []; // this.printErrors(this.form);
} // this.showValidationErrorsDialog();
// this.nestedCount = [];
// this.nestedIndex = 0;
// this.errorMessages = [];
// }
} }
printErrors(rootform: FormGroup) { printErrors(rootform: FormGroup) {
@ -740,7 +750,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
// this.dataModel.pages.push(page); // this.dataModel.pages.push(page);
pagesArray.push(pageForm); pagesArray.push(pageForm);
// this.form.updateValueAndValidity();
this.refreshToCEntries(); this.refreshToCEntries();
this.selectedTocEntry = this._findTocEntryById(pageForm.get('id').value, this.toCEntries); this.selectedTocEntry = this._findTocEntryById(pageForm.get('id').value, this.toCEntries);
@ -757,6 +767,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
section.page = parent.id; section.page = parent.id;
section.ordinal = sectionsArray.length; section.ordinal = sectionsArray.length;
sectionsArray.push(section.buildForm()); sectionsArray.push(section.buildForm());
// this.form.updateValueAndValidity();
} else if( parent.type == ToCEntryType.Section) { //SUBSECTION OF SECTION } else if( parent.type == ToCEntryType.Section) { //SUBSECTION OF SECTION
sectionsArray = parent.form.get('sections') as FormArray; sectionsArray = parent.form.get('sections') as FormArray;
@ -774,6 +785,8 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
const sectionAdded = sectionsArray.at(sectionsArray.length -1) as FormGroup; const sectionAdded = sectionsArray.at(sectionsArray.length -1) as FormGroup;
sectionAdded.setValidators(this.customEditorValidators.sectionHasAtLeastOneChildOf('fieldSets','sections'));
sectionAdded.updateValueAndValidity();
this.refreshToCEntries(); this.refreshToCEntries();
@ -817,7 +830,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
break; break;
} }
// this.refreshToCEntries(); this.form.updateValueAndValidity();
} }
@ -912,27 +925,29 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
pages.removeAt(pageIndex); pages.removeAt(pageIndex);
//clean up sections of removed page //clean up sections of removed page
const sections = (this.form.get('sections') as FormArray).controls; const sections = (this.form.get('sections') as FormArray);
const updatedSections = new FormArray([]); const sectionsIndexToBeRemoved = [];
for (let i = 0; i < sections.length; i++) { sections.controls.forEach((section,idx)=>{
let section = sections[i]; if(section.get('page').value === tce.id){
sectionsIndexToBeRemoved.push(idx);
if (section.get('page').value != tce.id) {
updatedSections.push(section);
} }
} });
//replace sections value
this.form.controls.sections = updatedSections;
//update page orders if(sectionsIndexToBeRemoved.length){
sectionsIndexToBeRemoved.reverse().forEach(index=>{
sections.removeAt(index);
});
}
//update page ordinals
for(let i=0; i<pages.length; i++){ for(let i=0; i<pages.length; i++){
pages.at(i).get('ordinal').patchValue(i); pages.at(i).get('ordinal').patchValue(i);
} }
//update validity //update validity
this.form.controls.sections.updateValueAndValidity(); // this.form.controls.sections.updateValueAndValidity();
} }
break; break;
@ -1028,6 +1043,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
//in case selectedtocentrhy is child of the removed element //in case selectedtocentrhy is child of the removed element
this.refreshToCEntries(); this.refreshToCEntries();
this.form.updateValueAndValidity();
} }
@ -1339,22 +1355,22 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
get basicInfo(){ // get basicInfo(){
const label = this.form.get('label'); // const label = this.form.get('label');
const description = this.form.get('description'); // const description = this.form.get('description');
const language = this.form.get('language'); // const language = this.form.get('language');
const fg = new FormGroup({ // const fg = new FormGroup({
label: label, // label: label,
description: description, // description: description,
language: language // language: language
}) // })
return fg; // return fg;
} // }
@ -1421,14 +1437,12 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
} }
isStepCompleted(stepIndex: number){ isStepCompleted(stepIndex: number){ //TODO NA TO DOUME MIPOS DEN XREIAZETAI
let stepCompleted = false; let stepCompleted = false;
this.steps.forEach((step,index)=>{ this.steps.forEach((step,index)=>{
if(stepIndex === index){ if(stepIndex === index){
if(step.stepControl){ stepCompleted = step.completed;
stepCompleted = step.stepControl.valid;
}
} }
}); });
@ -1437,13 +1451,17 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
isStepUnlocked(stepIndex: number): boolean{ isStepUnlocked(stepIndex: number): boolean{
if(stepIndex === 0) return true; if(stepIndex === 0) return true;
if(stepIndex <0 ) return false;
//if previous step is valid then unlock //if previous step is valid then unlock
let stepUnlocked: boolean = false; let stepUnlocked: boolean = false;
if(!this.isStepUnlocked(stepIndex -1)) return false;
this.steps.forEach((step,index)=>{ this.steps.forEach((step,index)=>{
if(index+1 == stepIndex){//previous step if(index+1 == stepIndex){//previous step
if(step.stepControl.valid){
if(step.completed){
stepUnlocked = true; stepUnlocked = true;
} }
} }
@ -1453,27 +1471,27 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
} }
validateStep(step: CdkStep){ validateStep(step: CdkStep){
if(step.hasError){ // if(step.hasError){
this.printMyErrors(this.form); // this.printMyErrors(this.form);
} // }
} }
getFormValidationErrors() { // getFormValidationErrors() {
Object.keys(this.form.controls).forEach(key => { // Object.keys(this.form.controls).forEach(key => {
const controlErrors: ValidationErrors = this.form.get(key).errors; // const controlErrors: ValidationErrors = this.form.get(key).errors;
if (controlErrors != null) { // if (controlErrors != null) {
Object.keys(controlErrors).forEach(keyError => { // Object.keys(controlErrors).forEach(keyError => {
console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]); // console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]);
}); // });
} // }
}); // });
if(this.form.invalid){ // if(this.form.invalid){
console.log('this form is invalid!'); // console.log('this form is invalid!');
console.log(this.form.errors); // console.log(this.form.errors);
} // }
} // }
@ -1481,11 +1499,26 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
const errmess: string[] = []; const errmess: string[] = [];
Object.keys(errors).forEach(keyError => { Object.keys(errors).forEach(keyError => {
if (typeof errors[keyError] === 'boolean') {
errmess.push(numbering + ' ' + key + ' is ' + keyError); switch(keyError){
} else { case EditorCustomValidatorsEnum.atLeastOneSectionInPage:
errmess.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError])); errmess.push('Each section must have at least one subsection');
break;
case EditorCustomValidatorsEnum.emptyArray:
errmess.push(numbering+" needs more information.")
break;
case EditorCustomValidatorsEnum.sectionMustHaveOneChild:
errmess.push(numbering+" must have either subsection or input sets.")
break;
default:
if (typeof errors[keyError] === 'boolean') {
errmess.push(numbering + ' ' + key + ' is ' + keyError);
} else {
errmess.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError]));
}
} }
}); });
return errmess; return errmess;
@ -1495,59 +1528,67 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
printMyErrors(form: AbstractControl){ printMyErrors(form: AbstractControl){
// this._printToCentriesErrrors(this.toCEntries); // this._printToCentriesErrrors(this.toCEntries);
const result = this._getErrors(form); const result = this._getErrors(form);
// console.log('result', result);
// console.log('got errors ');
// console.log(result); // console.log(result);
if(result){ if(result && form.invalid){
form.markAllAsTouched();
const errmess:string[] = []; const errmess:string[] = [];
if(result.length){
form.markAllAsTouched();
let indexes:number[] = []; let indexes:number[] = [];
///search in pages,sections and fieldsets for the id ///search in pages,sections and fieldsets for the id
result.forEach((err,i)=>{ result.forEach((err,i)=>{
const entry = this._findTocEntryById(err.id, this.toCEntries); const entry = this._findTocEntryById(err.id, this.toCEntries);
if(entry){ if(entry){
// errmess.push(`Error on ${entry.numbering} ${entry.label} . ${err.key}`); // errmess.push(`Error on ${entry.numbering} ${entry.label} . ${err.key}`);
errmess.push(...this._buildErrorMessage(err.errors, entry.numbering, err.key)); errmess.push(...this._buildErrorMessage(err.errors, entry.numbering, err.key));
indexes.push(i); indexes.push(i);
} }
}); });
indexes.reverse().forEach(index=>{ indexes.reverse().forEach(index=>{
result.splice(index,1); result.splice(index,1);
});
indexes = [];
//searching in fields
const fieldsets = this._getAllFieldSets(this.toCEntries);
result.forEach((err,i)=>{
fieldsets.filter(fs=>{
let fieldFound = false;
(fs.form.get('fields') as FormArray).controls.forEach(field=>{
if(field.get('id').value === err.id){
fieldFound = true;
indexes.push(i);
}
});
return fieldFound;
})
//printing fieldsets that the field missing
.forEach(fs=>{
// errmess.push(`Missing input in ${fs.numbering} ${fs.label} . ${err.key}`);
errmess.push(...this._buildErrorMessage(err.errors, fs.numbering, err.key));
}); });
});
indexes.reverse().forEach(index=>{ indexes = [];
result.splice(index,1); //searching in fields
}); const fieldsets = this._getAllFieldSets(this.toCEntries);
result.forEach((err,i)=>{
fieldsets.filter(fs=>{
let fieldFound = false;
(fs.form.get('fields') as FormArray).controls.forEach(field=>{
if(field.get('id').value === err.id){
fieldFound = true;
indexes.push(i);
}
});
return fieldFound;
})
//printing fieldsets that the field missing
.forEach(fs=>{
// errmess.push(`Missing input in ${fs.numbering} ${fs.label} . ${err.key}`);
errmess.push(...this._buildErrorMessage(err.errors, fs.numbering, err.key));
});
});
result.forEach(err=>{ indexes.reverse().forEach(index=>{
errmess.push(`Missing ${err.key}` ); result.splice(index,1);
}) });
result.forEach(err=>{
// console.log(err);
if(err.key){
errmess.push(`Missing ${err.key}` );
}else{
errmess.push('Make sure you provide a section and a subsection');
}
// errmess.push(...this._buildErrorMessage(err.errors,"", err.key) );
})
}
// console.log(result); // console.log(result);
@ -1627,11 +1668,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
private _getErrors(aControl: AbstractControl):InvalidControl[]{ private _getErrors(aControl: AbstractControl):InvalidControl[]{
// if(aControl instanceof FormGroup) console.log('is Formgroup');
// if(aControl instanceof FormControl)console.log( aControl.errors);
// if(aControl instanceof FormArray) console.log('isForm array');
// console
if(aControl.valid) return; if(aControl.valid) return;
let controlType = 'control'; let controlType = 'control';
@ -1650,14 +1687,11 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
const keys = Object.keys(controls); const keys = Object.keys(controls);
keys.forEach(key=>{ keys.forEach(key=>{
const control = controls[key]; const control = controls[key];
if(control.valid) return; if(!control.invalid) return; //// !!!!! Important to be !invalid. (In case the template is finalized)
if(control instanceof FormControl){ if(control instanceof FormControl){
const ctrl = control as FormControl; const ctrl = control as FormControl;
// console.log('Control ', key,' of Formgroup ', aControl.get('id'), ' has errors', ctrl.errors);
// console.log(aControl)
invalidControls.push({ invalidControls.push({
errors:ctrl.errors, errors:ctrl.errors,
id: ctrl.get('id')? ctrl.get('id').value: null, id: ctrl.get('id')? ctrl.get('id').value: null,
@ -1674,26 +1708,38 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn
// invalidSubControls:[]//TODO TO CHECK // invalidSubControls:[]//TODO TO CHECK
// }); // });
// } // }
if(control.errors){
// invalidControls.push({
// id: aControl.get('id')? aControl.get('id').value: null,
// errors:aControl.errors,
// key: aControl.get('title')? aControl.get('title').value: 'unspecified',
// invalidSubControls:[]//TODO TO CHECK
// });
invalidControls.push({
errors:control.errors, //THE ONE WE REMOVED
id: control.get('id')? control.get('id').value: null, // if(control.errors){
invalidSubControls: [], // // invalidControls.push({
key: key // // id: aControl.get('id')? aControl.get('id').value: null,
}); // // errors:aControl.errors,
} // // key: aControl.get('title')? aControl.get('title').value: 'unspecified',
// // invalidSubControls:[]//TODO TO CHECK
// // });
// invalidControls.push({
// errors:control.errors,
// id: control.get('id')? control.get('id').value: null,
// invalidSubControls: [],
// key: key
// });
// }
invalidControls.push(...this._getErrors(control)) ; invalidControls.push(...this._getErrors(control)) ;
} }
}); });
/**In case there is an error in a formgroup then the validator probably is custom */
if(aControl.errors){
invalidControls.push({
errors:aControl.errors,
id: aControl.get('id')? aControl.get('id').value: null,
invalidSubControls: [],
key: aControl.get('title')?aControl.get('title').value: null
});
}
break; break;
case 'fa': case 'fa':

View File

@ -22,7 +22,7 @@
[ngStyle]="{'font-size' : (parentLink.type == tocEntryType.FieldSet? '.9rem':'1rem')}" [ngStyle]="{'font-size' : (parentLink.type == tocEntryType.FieldSet? '.9rem':'1rem')}"
> --> > -->
<span class="table-label-element" [ngClass]="{'table-label-element-active': itemSelected?.id == parentLink?.id}" (click)="itemClicked(parentLink)" <span class="table-label-element" [ngClass]="{'table-label-element-active': itemSelected?.id == parentLink?.id, 'text-danger': colorError()}" (click)="itemClicked(parentLink)"
[ngStyle]="{'font-size' : (parentLink.type == tocEntryType.FieldSet? '.9rem':'1rem')}" [ngStyle]="{'font-size' : (parentLink.type == tocEntryType.FieldSet? '.9rem':'1rem')}"
> >
<!-- {{parentLink?.numbering}} {{parentLink?.label? parentLink?.label : 'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.UNTITLED' | translate}} --> <!-- {{parentLink?.numbering}} {{parentLink?.label? parentLink?.label : 'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.UNTITLED' | translate}} -->
@ -33,10 +33,14 @@
</div> </div>
<div class="col-auto d-flex align-items-center" > <div class="col-auto d-flex align-items-center" >
<span class="badge-ball"
*ngIf="!(!((parentLink?.subEntriesType == tocEntryType.FieldSet) && !selectedItemInLinks) || itemSelected?.id == parentLink.id ||isDragging)"> <ng-container *ngIf="!(!((parentLink?.subEntriesType == tocEntryType.FieldSet) && !selectedItemInLinks) || itemSelected?.id == parentLink.id ||isDragging)">
{{parentLink.subEntries?.length}} <mat-icon class="text-danger" style="font-size: 1.4em;" *ngIf="!colorError() && parentLink?.form.invalid && colorizeInvalid && allFieldsAreTouched(parentLink?.form)">priority_high</mat-icon>
</span> <span class="badge-ball"
>
{{parentLink.subEntries?.length}}
</span>
</ng-container>
<span style="cursor: pointer;" (click)="deleteEntry(parentLink)" <span style="cursor: pointer;" (click)="deleteEntry(parentLink)"
@ -109,7 +113,8 @@
[overContainerId]="overContainerId" [overContainerId]="overContainerId"
[isDragging]="isDragging" [isDragging]="isDragging"
[draggingItemId]="draggingItemId" [draggingItemId]="draggingItemId"
[parentRootId]="parentRootId"> [parentRootId]="parentRootId"
[colorizeInvalid]="colorizeInvalid">
</app-dataset-profile-table-of-contents-internal-section> </app-dataset-profile-table-of-contents-internal-section>

View File

@ -1,7 +1,7 @@
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { Component, EventEmitter, Inject, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; import { Component, EventEmitter, Inject, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { FormArray } from '@angular/forms'; import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
import { BaseComponent } from '@common/base/base.component'; import { BaseComponent } from '@common/base/base.component';
import { Foo, ToCEntry, ToCEntryType } from '../table-of-contents-entry'; import { Foo, ToCEntry, ToCEntryType } from '../table-of-contents-entry';
@ -31,6 +31,8 @@ export class DatasetProfileTableOfContentsInternalSection extends BaseComponent
@Input() draggingItemId: string; @Input() draggingItemId: string;
@Input() parentRootId: string; @Input() parentRootId: string;
@Input() colorizeInvalid:boolean = false;
@Input() viewOnly: boolean; @Input() viewOnly: boolean;
// @Input() dropListGroup: Set<string> = new Set<string>(); // @Input() dropListGroup: Set<string> = new Set<string>();
@Input() dropListGroup: string[]; @Input() dropListGroup: string[];
@ -229,4 +231,78 @@ export class DatasetProfileTableOfContentsInternalSection extends BaseComponent
return tocEntryFound? tocEntryFound: null; return tocEntryFound? tocEntryFound: null;
} }
colorError():boolean{
if(!this.colorizeInvalid) return false;
const form = this.parentLink.form;
if(!form || form.valid || !form.touched) return false;
const allFieldsAreTouched = this.allFieldsAreTouched(form);
//fieldset may have errros that are inside its controls and not in the fieldsetFormGroup
if(this.parentLink.type === this.tocEntryType.FieldSet && allFieldsAreTouched) return true;
if(form.errors && allFieldsAreTouched) return true;
//checking form controls if have errors
let hasErrors = false;
if(allFieldsAreTouched){
if(form instanceof FormGroup){
const formGroup = form as FormGroup;
const controls = Object.keys(formGroup.controls);
controls.forEach(control=>{
if(formGroup.get(control).errors){
hasErrors = true;
}
})
}
}
return hasErrors;
}
allFieldsAreTouched(aControl:AbstractControl){//auto na testaroume
if(!aControl|| aControl.untouched) return false;
if(aControl instanceof FormControl){
return aControl.touched;
}else if(aControl instanceof FormGroup){
const controlKeys = Object.keys((aControl as FormGroup).controls);
let areAllTouched = true;
controlKeys.forEach(key=>{
if(!this.allFieldsAreTouched(aControl.get(key))){
areAllTouched = false;
}
})
// const areAllTouched = controlKeys.reduce((acc, key)=>acc && this._allFieldsAreTouched(aControl.get(key)), true);
return areAllTouched;
}else if(aControl instanceof FormArray){
const controls = (aControl as FormArray).controls;
// const areAllTouched = controls.reduce((acc, control)=>acc && this._allFieldsAreTouched(control), true);
let areAllTouched = true;
// controls.reduce((acc, control)=>acc && this._allFieldsAreTouched(control), true);
controls.forEach(control=>{
if(!this.allFieldsAreTouched(control)){
areAllTouched = false;
}
});
return areAllTouched;
}
return false;
}
} }

View File

@ -22,6 +22,7 @@
[draggingItemId]="draggingItemId" [draggingItemId]="draggingItemId"
[parentRootId]="ROOT_ID" [parentRootId]="ROOT_ID"
style="padding-right: 1em;" style="padding-right: 1em;"
[colorizeInvalid]="colorizeInvalid"
></app-dataset-profile-table-of-contents-internal-section> ></app-dataset-profile-table-of-contents-internal-section>
<!-- <span *ngFor="let link of links; let i = index" (click)="toggle(link); goToStep(link);" class="docs-link mt-0"> <!-- <span *ngFor="let link of links; let i = index" (click)="toggle(link); goToStep(link);" class="docs-link mt-0">

View File

@ -50,6 +50,7 @@ export class DatasetProfileTableOfContents extends BaseComponent implements OnIn
@Output() dataNeedsRefresh = new EventEmitter<void>(); @Output() dataNeedsRefresh = new EventEmitter<void>();
@Input() itemSelected: ToCEntry; @Input() itemSelected: ToCEntry;
@Input() colorizeInvalid: boolean = false;
// @Input() set itemSelected(entry:ToCEntry){ // @Input() set itemSelected(entry:ToCEntry){
// this._itemSelected = entry; // this._itemSelected = entry;

View File

@ -76,11 +76,11 @@
} }
} }
::ng-deep .mat-form-field-wrapper { :host ::ng-deep .mat-form-field-wrapper {
background-color: white !important; background-color: white !important;
padding-bottom: 0 !important; padding-bottom: 0 !important;
} }
::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { :host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
padding: 0.3rem 0rem 0.6rem 0rem !important; padding: 0.3rem 0rem 0.6rem 0rem !important;
} }

View File

@ -10,11 +10,11 @@
padding: 0.4rem; padding: 0.4rem;
} }
::ng-deep .mat-form-field-wrapper { :host ::ng-deep .mat-form-field-wrapper {
background-color: white !important; background-color: white !important;
padding-bottom: 0 !important; padding-bottom: 0 !important;
} }
::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { :host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
padding: 0.3rem 0rem 0.6rem 0rem !important; padding: 0.3rem 0rem 0.6rem 0rem !important;
} }

View File

@ -122,7 +122,7 @@
background-color: #2e74b649; background-color: #2e74b649;
} }
::ng-deep .mat-form-field-wrapper { :host ::ng-deep .mat-form-field-wrapper {
background-color: white !important; background-color: white !important;
padding-bottom: 0 !important; padding-bottom: 0 !important;
} }

View File

@ -66,7 +66,7 @@
<div class="page-title">About</div> <div class="page-title">About</div>
</div> </div>
<div class="col pt-5 pb-3"> <div class="col pt-5 pb-3">
<div class="row title-4">How it works</div> <div class="row title-4">What it is</div>
</div> </div>
<p class="pt-3"> <p class="pt-3">
ARGOS is the joint effort of OpenAIRE and EUDAT to deliver an open platform for Data Management Planning ARGOS is the joint effort of OpenAIRE and EUDAT to deliver an open platform for Data Management Planning
@ -102,6 +102,322 @@
</p> </p>
</div> </div>
<div class="col pt-0 pb-3">
<div class="row title-4">How it works</div>
</div>
<p class="pt-3">
Research Data Management (RDM) is a vital part of Open Science that lies at the heart of European
research for it increases the quality of produced data and ensures integrity and preservation of
research outputs. RDM refers to activities undertaken throughout a research lifecycle, spanning from
data collection to data process and analysis. Open Access and FAIR principles govern data activities
today to foster data discovery, distribution and exploitation, thus accelerating scientific breakthroughs.
Coronavirus vaccine is the ultimate example of what can be achieved in a short amount of time when
data are openly shared across the global scientific community. RDM urges scientific practices to be
adjusted in ways that enable data understandability and re-usability from both humans and machines;
services and tools are realised or re-designed to support the shift towards Open and FAIR research outputs.
</p>
<p class="pt-2">
Argos is a service for creating and publishing plans that describe data management activities,
commonly known as Data Management Plans (DMPs). The plans are produced as machine-actionable
outputs (ma-DMPs), in the form of rich text documents, following Open and FAIR practices and are
published in Zenodo. Argos supports RDM at the beginning of research for planning data activities to
be performed, as well as throughout and at the end of research for documenting the steps and
processes followed according to institutional or funder RDM requirements.
</p>
<div class="col pt-5 pb-3">
<div class="row title-5">Dmp Lifecycle</div>
</div>
<p class="pt-2">
Argos follows a full DMP publication lifecycle. Users create their DMPs and add descriptions for their
datasets. DMPs and Datasets are by default created in private mode and can be shared with
colleagues to facilitate the writing process. Datasets are added in DMPs per type and / or per scientific
discipline that they concern. That way information is well-organized and it is easier to distinguish
relevant information from a pool of data activities detailed in a single DMP record. Once created, DMPs
and their Datasets are in draft status and are treated as living documents in the Argos environment,
meaning that they can be versioned and updated at any time. When ready, users can validate and
finalize their DMPs, including corresponding Datasets, and change their visibility status from private to
open. Open DMPs in Argos are available and accessible to all from the Public Dashboards. To close the
DMP lifecycle, Argos integrates Zenodo, so users are able to publish their DMPs directly from its environment.
</p>
</div>
<div class="container">
<div class="col-12" style="margin-top: 121px; ">
<div style="position: relative;" id="lifecycle-img-container">
<img id="img-coloured" class="image-coloured img-sharp" src="../assets/img/lifecycle_full_color.png" alt="Dmp Lifecycle">
<img class="image-grey img-sharp" src="../assets/img/lifecycle_full_color.png" alt="Dmp Lifecycle">
<!-- <img class="preface-img" src="../assets/img/lifecycle_full_color.png" alt=""> -->
<!-- EDITORS-DRAFT -->
<div class="red-dot navigate-dot" data-navigate-to="multiple-navigation-1" data-clip-mode="clip-draft" style="top:24.7%; left: 23.5%; cursor: pointer;">
<div class="inner-red-dot"></div>
</div>
<!-- VALIDATED -->
<div class="red-dot navigate-dot" data-navigate-to="navigation-tab-2" data-clip-mode="clip-validated" style="top:19%; left: 56.5% ; cursor: pointer;">
<div class="inner-red-dot"></div>
</div>
<!-- FINALIZED -->
<div class="red-dot navigate-dot" data-navigate-to="navigation-tab-3" data-clip-mode="clip-finalized" style="top:39%; right: 19.7%; cursor: pointer;">
<div class="inner-red-dot"></div>
</div>
<!-- DMP -->
<div class="red-dot navigate-dot" data-navigate-to="navigation-tab-4" data-clip-mode="clip-dmp" style="top:67%; right: -0.3%; cursor: pointer;">
<div class="inner-red-dot"></div>
</div>
</div>
</div>
<div class="col-12" style="margin-top: 121px; ">
<div class="options-menu" id="options-menu">
<div class="row">
<div class="col-7">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" id="multiple-navigation-1" data-img-fragment href="#create_and_add">Create & Add</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" id="navigation-tab-2" data-img-fragment="img-fragment-validated-is" href="#validate">Validate</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" id="navigation-tab-3" data-img-fragment="img-fragment-finalized-is" href="#finalize">Finalize</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" id="navigation-tab-4" data-img-fragment="img-fragment-dmp-out" href="#dmp">Ma-Dmp Outputs</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane show active" id="create_and_add">
<div class="nav-tab-inner-text">
Argos consists of two main editors: the DMP editor and the Dataset editor.
Both editors integrate OpenAIRE and ESOC APIs to ease the writing process and
help create links with other outputs. Also, the editors incorporate a mechanism
to enable compliance with the <span style="color: #23BCBA;">RDA DMP Common Standard</span>.
</div>
<!-- nested naviation -->
<ul class="nav nav-pills" style="padding-top: 2em;">
<li class="nav-item">
<a class="nav-link sub-navigation-button active" data-toggle="tab" data-img-fragment="img-fragment-dmp-editor" href="#dmp_editor_pane">DMP Editor</a>
</li>
<li class="nav-item">
<a class="nav-link sub-navigation-button" data-toggle="tab" data-img-fragment="img-fragment-dataset-editor" href="#dataset_editor_pane">Dataset Editor</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane show active" id="dmp_editor_pane">
<div class="nav-tab-inner-text">
The DMP editor embeds the Dataset editor to provide a holistic view of a project's
scope, beneficiaries and data activities. Therefore, it guides users step-by-step
into adding appropriate administrative information, selecting templates that
correspond to research needs and requirements, and describing data activities.
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="dataset_editor_pane">
<div class="nav-tab-inner-text">
The Dataset editor concers information that is solely for datasets, such as how Open
and FAIR principles have been applied to them throughout data management activities.
It is the place to select RDM templates and start answering data questions. Moreover,
the Dataset editor can be used independently at any time when new data are produced
throughout a project lifetime. That allows users to quickly describe their new data and
link them to existing DMPs in their dashboard.
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="validate">
<div class="nav-tab-inner-text">
Information contained in DMPs should be validated before being published.
Validation at this stage checks for mandatory fields in the DMP and reports back
when necessary information has not been provided by the user. Once the user
add all necessary information, validation is successful.
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="finalize">
<div class="nav-tab-inner-text">
DMPs finalization is the process of locking working drafts of DMPs and publishing
them, either just on the Argos platform or on Zenodo where they alse get DOI.
It is possible for datasets contained in DMPs to be finalized independently. That
is important especially for datasets that can not be shared openly, such as those
of sensitive content. Before publishing DMPs, sensitive datasets can be detached
from its core. That way data descriptions are equally protected form being openly
shared along with the DMP content.
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="dmp">
<div class="nav-tab-inner-text">
Argos ma-DMP outputs carry rich information that exceeds metadata provided in
DMPs whey they are created and deposited as plain texts. Argos applies the RDA
DMP Common Standard to comply with global practices on the machine-actinability.
This is important for two reasons:
</div>
<div class="row pt-2 nav-tab-inner-text">
<div class="col-auto" style="width:2.5em;color: #129D99;">I.</div>
<div class="col">It enables researchers to switch platforms at anytime and seamlessly work</div>
</div>
<div class="row nav-tab-inner-text pt-4">
<div class="col-auto " style="width:2.5em;color: #129D99;">II.</div>
<div class="col">Adds to the Scholarly Communication ecology and pushes for best practices
in neighbour areas to keep up pace, e.g. deposit in repositories and
classification of DMPs as data management plans and not as publications,</div>
</div>
</div>
</div>
</div>
<!-- Images -->
<div class="col">
<img src="../assets/img/3_dmp_editor.png" class="img-fragment-step opacity-1 img-sharp" id="img-fragment-dmp-editor" alt="DMP editor">
<img src="../assets/img/dmp_out.png" class="img-fragment-step img-sharp" id="img-fragment-dmp-out" alt="DMP outputs">
<img src="../assets/img/4_dataset_editor.png" class="img-fragment-step img-sharp" id="img-fragment-dataset-editor" alt="Dataset editor">
<img src="../assets/img/6_validated_is.png" class="img-fragment-step img-sharp" id="img-fragment-validated-is" alt="Validated DMP">
<img src="../assets/img/8_finalized_is.png" class="img-fragment-step img-sharp" id="img-fragment-finalized-is" alt="Finalized DMP">
</div>
</div>
</div>
</div>
</div>
<div class="container-small" style="margin-top: 7em;">
<div class="col pt-5 pb-3" >
<div class="row title-5">Argos in the OpenAIRE ecosystem</div>
</div>
<p class="pt-2">
Argos is hooked on the OpenAIRE ecosystem comprising a diverse set of services and tools for Open
Science and Scholarly Communication, from technical to educational and consultative in nature.
OpenAIRE services and tools are provided for immediate consumption by end-users, i.e. researchers,
research communities, research performing or funding organizations and policymakers. Moreover,
OpenAIRE services are exploitable by software engineers. Argos integrates other OpenAIRE services to
maximise efficiency of operations in Open and FAIR ecosystems and to make connections that add
value in Open Science at global scale. OpenAIRE services that Argos connects to are:
</p>
</div>
<div class="container">
<div class="col" style="margin-top: 121px; position: relative;">
<img src="../assets/img/openaire_ecosystem.png" style="width: 100%; height: auto;" alt="">
<!-- apis -->
<div class="red-dot" style="top:8%; left: 22%"
data-toggle="tooltip" data-placement="bottom"
title='<h2>APIs</h2>
Utilises OpenAIRE pool of resources
and APIs to provide quick input to
users (e.g. CORDIS, Datacite, re3data,
ORCID, etc).'
><div class="inner-red-dot"></div>
</div>
<!-- zenodo -->
<div class="red-dot" style="top:10%; left: 54%"
data-toggle="tooltip" data-placement="bottom"
title='<h2>Zenodo</h2>
Closes the DMP lifecycle by offering a
DMP platform to expose DMP for
their immediate publication in an
open and FAIR manner (DMPs are
assigned DOIs, get licenses, follow
versioning mechanism etc).'
><div class="inner-red-dot"></div>
</div>
<!-- provide -->
<div class="red-dot" style="top:48.2%; left: 2.5%"
data-toggle="tooltip" data-placement="bottom"
title='<h2>Provide</h2>
Sends notifications to repository
managers of the OpenAIRE network
when datasets described in Argos
ma-DMPs are claimed to be
deposited in their repositories.'
><div class="inner-red-dot"></div>
</div>
<!-- monitor -->
<div class="red-dot" style="top:60.2%; right: 2.5%"
data-toggle="tooltip" data-placement="bottom"
title="<h2>Monitor</h2>
Collects contextualised information
about ma-DMPs to understand how
RDM and DMPs evolve in time and
support decision making of funders
and policymakers."
>
<div class="inner-red-dot"></div>
</div>
<!-- graph -->
<div class="red-dot" style="bottom:14.2%; left: 40.5%"
data-toggle="tooltip" data-placement="bottom"
title='<h2>Research Graph</h2>
Enriches the ma-DMP entity in the
OpenAIRE knowledge graph for open
scholarly communication information
and creates relationships with other
research outputs and links, adding in
the OpenAIRE LOD ecology.'
>
<div class="inner-red-dot"></div>
</div>
<!-- explore -->
<div class="red-dot" style="bottom:1%; left: 56%" data-toggle="tooltip" data-placement="bottom"
title='
<h2>Explore</h2>
Adds ma-DMPs in the OpenAIRE
search engine and makes them
findable under a dedicated resource
type category "data managment
plan".'>
<div class="inner-red-dot"></div>
</div>
</div>
</div>
<div class="container-small">
<p class="mt-3 pt-4" style="font-weight: bolder;" >Useful Resources</p>
<div class="resources-list">
<div class="row">
<div class="col-auto" style="padding-top: .5em;">
<div class="blue-dot"></div>
</div>
<div class="col p-0">
Argos RDA output adoption:
<br>
<a href="https://www.rd-alliance.org/implementing-dmp-common-standard-argos-tool-madmps">
https://www.rd-alliance.org/implementing-dmp-common-standard-argos-tool-madmps
</a>
</div>
</div>
<div class="row" >
<div class="col-auto" style="padding-top: .5em;">
<div class="blue-dot"></div>
</div>
<div class="col p-0">
Webinar:
<br>
<a href="https://www.rd-alliance.org/implementing-dmp-common-standard-argos-tool-madmps">
https://www.rd-alliance.org/implementing-dmp-common-standard-argos-tool-madmps
</a>
(starts at 30:15)
</div>
</div>
</div>
<p class="pt-2"> <p class="pt-2">
More details on ARGOS as registered in the OpenAIRE services catalogue is available here: More details on ARGOS as registered in the OpenAIRE services catalogue is available here:
<a href="http://catalogue.openaire.eu/service/openaire.argos"><u>http://catalogue.openaire.eu/service/openaire.argos</u></a>. <a href="http://catalogue.openaire.eu/service/openaire.argos"><u>http://catalogue.openaire.eu/service/openaire.argos</u></a>.
@ -168,6 +484,8 @@
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<!-- Js for this file -->
<script src="../js/how-it-works.js"></script>
</body> </body>
</html> </html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

View File

@ -150,11 +150,15 @@ p a {
color: #212121; color: #212121;
} }
.title-1, .title-2, .title-3, .title-4 { .title-1, .title-2, .title-3, .title-4, .title-5 {
text-align: left; text-align: left;
font-size: 2.37rem; font-size: 2.37rem;
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
} }
.title-5{
color: #129D99;
font: normal normal 300 27px/35px Roboto;
}
.page-title { .page-title {
text-align: center; text-align: center;
@ -379,3 +383,220 @@ hr {
.ext-link-icon { .ext-link-icon {
margin-left: .2rem; margin-left: .2rem;
} }
/* ABOUT HOW-IT-WORKDS PAGE REDESIGN */
.red-dot{
width: 27px;
height: 27px;
z-index: 20;
border: 3px solid #ee254091;
position: absolute;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.inner-red-dot{
background-color: #EE2540;
width: 19px;
height: 19px;
border-radius: 50%;
}
.tooltip >.tooltip-inner {
padding: 2em;
background-color: #FFF;
color: black;
box-shadow: 0px 3px 6px #0000001A;
text-align: start;
max-width: 25em;
}
.tooltip .show{
opacity: 1 !important;
}
.tooltip h2{
color: #23BCBA;
font-size: 1.3rem;
font-weight: bold;
line-height: 2rem;
}
.tooltip.bs-tooltip-top .arrow:before {
border-top-color: #fff
}
.tooltip.bs-tooltip-right .arrow:before {
border-right-color: #fff;
}
.tooltip.bs-tooltip-left .arrow:before {
border-left-color: #fff;
}
.tooltip.bs-tooltip-bottom .arrow:before {
border-bottom-color: #fff;
}
.tooltip.show {
opacity: 1 !important;
}
.image-coloured {
width: 100%;
height: auto;
display: block;
z-index: 1;
position: absolute;
left: 0;
top: 0px;
clip-path: polygon(0% 0%);
opacity: 0;
transition: opacity 200ms 50ms ease-in-out;
}
.image-grey {
width: 100%;
height: auto;
display: block;
z-index: 0;
filter: grayscale(100%);
}
.clip-draft{
clip-path:polygon(41.84% 0.0%, 40.13% 46.41%, 40.90% 100%, 0.0% 100.0%, 0.0% 0.0%);
}
.clip-validated{
clip-path: polygon(41.84% 0%, 40.13% 46.41%, 40.9% 100%, 68.32% 100.0%, 68.32% 0%);
}
.clip-finalized{
clip-path: polygon(58.86% 0.0%, 58.74% 100.0%, 88.65% 100.0%, 88.65% 0.0%);
}
.clip-dmp{
clip-path: polygon(80.26% 0.0%, 80.13% 100.0%, 100.00% 100.0%, 100.0% 0.0%);
}
/* .preface-img{
width: 100%; height: auto; display: block; z-index: 2;
position: absolute;
top:0;
left: 0;
opacity: 1;
filter: grayscale(100%);
transition-property: opacity;
transition-duration: 300ms;
transition-delay: 50ms;
transition-timing-function: ease-in;
} */
.options-menu{
box-shadow: 0px 6px 15px #0000001A;
border-radius: 36px;
padding: 3.2em 4.1em !important;
min-height: 30em;
}
/* First level of tabs */
.options-menu .nav-tabs{
border-color: transparent !important;
color: #212121;
margin-bottom: 1.2em;
}
.options-menu .nav-tabs .nav-item .nav-link{
font-weight: normal;
border-radius: 0px !important;
padding: 0.2rem 0rem !important;
}
.options-menu .nav-tabs .nav-item:first-child .nav-link{
margin-left: 0px;
}
.options-menu .nav-tabs .nav-link.active{
font-weight: bold;
border-left:0px!important;
border-top:0px !important;
border-right:0px !important;
}
.options-menu .nav-tabs .nav-link:hover:not(.active){
border-color: transparent !important;
}
.options-menu .nav-tabs .nav-link{
opacity: 0.5;
transition: opacity 200ms 50ms ease-out;
}
.options-menu .nav-tabs .nav-link:hover{
color: #212121;
}
.options-menu .nav-tabs .nav-link.active{
border-bottom: 3px solid black !important;
opacity: 1;
}
/* nested tabs */
.options-menu .nav-pills{
margin-bottom: 1em;
}
.options-menu .nav-pills .nav-link{
padding: .3rem 1.3rem !important;
font-weight: normal;
border-radius: 40px;
border: 1px solid #1A1A1A;
color: #1A1A1A;
opacity: 0.5;
/* transition: opacity 200ms 50ms ease-out; */
}
.options-menu .nav-pills .nav-link.active{
background-color: #129D99;
font-weight: bold;
color: #F9FBFC;
border: 1px solid transparent;
opacity: 1;
}
.options-menu .nav-pills .nav-item:first-child .nav-link{
margin-left: 0px;
}
.img-fragment-step{
width: auto;
max-width: 100%;
max-height:23em;
position: absolute;
opacity: 0;
transition: opacity 200ms 50ms ease-out;
}
.opacity-0{
opacity: 0;
}
.opacity-1{
opacity: 1;
}
.nav-tab-inner-text{
text-align: left;
font-size: 0.9em;
letter-spacing: 0px;
color: #1A1A1A;
opacity: 1;
}
.resources-list{
margin-bottom: 9em;
}
/* .resources-list li::marker{
font-size: 1.5em;
color: #23BCBA;
} */
.blue-dot{
width: 0.7em;
height: 0.7em;
background-color: #23BCBA;
border-radius: 50%;
}
.img-sharp{
image-rendering: -webkit-optimize-contrast;
image-rendering: -moz-crisp-edges;
}
/* END OF ABOUT HOW-IT-WORKDS PAGE REDESIGN */

View File

@ -0,0 +1,105 @@
jQuery(function(){
const $img_coloured = $('#img-coloured');
const $img_preface = $('.preface-img');
const $action_dots = $('[data-clip-mode]');
const $lifecycle_img_container = $('#lifecycle-img-container');
let current_mode = null;
const mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(mutation=>{
if(mutation.attributeName === 'class'){//class changed
const isActive = mutation.target.className.includes('active');
const target_image_id = mutation.target.attributes['data-img-fragment'].value;
if(target_image_id && target_image_id.length){
//dummy fix clean all opacities
$('.img-fragment-step').each(function(){
$(this).removeClass('opacity-1');
});
if(isActive){
$("#"+target_image_id).addClass('opacity-1');
}
}
if(mutation.target.id == 'multiple-navigation-1' && isActive){ //search children
const $target_nav_links = $('.sub-navigation-button');
$target_nav_links.removeClass('active');
$target_nav_links.first().addClass('active');
}
}
})
});
$(function () {
$('[data-toggle="tooltip"]').tooltip({html:true})
})
$action_dots.on('mouseenter', function(){
const mode = $(this).attr('data-clip-mode');
current_mode = mode;
$img_coloured.addClass(mode);
// $img_preface.addClass('opacity-0');
$img_coloured.addClass('opacity-1');
$action_dots.each(function(){
if($(this).attr('data-clip-mode') != mode){
$(this).addClass('d-none');
};
});
});
$action_dots.on('mouseleave', function(){
});
$lifecycle_img_container.on('mouseleave', function(){
if(current_mode){
const mode = current_mode;
//give animation time to complete
setTimeout(() => {
$img_coloured.removeClass(mode);
$action_dots.each(function(){
$(this).removeClass('d-none');
});
}, 300);
$img_coloured.removeClass('opacity-1');
// $img_preface.removeClass('opacity-0');
}
current_mode = null;
});
$("[data-img-fragment]").each(function(){
const nodeElement = $(this)[0];
mutationObserver.observe(nodeElement ,{attributes:true});
});
$('.navigate-dot').on('click', function(){
const navigate_to = $(this).attr('data-navigate-to');
if(navigate_to){
const $navigate_to = $('#'+navigate_to);
$navigate_to.click();
const offset = $('#options-menu').offset().top - 120;
window.scrollTo({top:offset, behavior:'smooth'})
}
});
});