2020-08-31 18:08:32 +02:00
import { AfterViewInit , Component , Input , OnChanges , OnInit , SimpleChanges , ViewChild , Output , EventEmitter } from '@angular/core' ;
2021-03-09 09:04:50 +01:00
import { AbstractControl , AbstractControlOptions , FormArray , FormGroup } from '@angular/forms' ;
2021-09-24 20:52:14 +02:00
import { MatExpansionPanel } from '@angular/material/expansion' ;
2020-07-29 17:04:19 +02:00
import { MatHorizontalStepper } from '@angular/material/stepper' ;
2021-09-20 13:34:09 +02:00
import { CompositeField } from '@app/core/model/dataset-profile-definition/composite-field' ;
import { Field } from '@app/core/model/dataset-profile-definition/field' ;
2020-07-29 17:04:19 +02:00
import { Rule } from '@app/core/model/dataset-profile-definition/rule' ;
2021-03-09 09:04:50 +01:00
import { DatasetProfileTableOfContentsInternalSection } from '@app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section' ;
2020-07-29 17:04:19 +02:00
import { LinkToScroll } from '@app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents' ;
import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service' ;
import { BaseComponent } from '@common/base/base.component' ;
2021-04-12 18:08:09 +02:00
import { debounceTime , takeUntil } from 'rxjs/operators' ;
2021-04-13 10:19:09 +02:00
import { VisibilityRuleSource } from './visibility-rules/models/visibility-rule-source' ;
2020-07-29 17:04:19 +02:00
@Component ( {
selector : 'app-dataset-description' ,
templateUrl : './dataset-description.component.html' ,
styleUrls : [ './dataset-description.component.scss' ]
} )
export class DatasetDescriptionComponent extends BaseComponent implements OnInit , AfterViewInit , OnChanges {
2020-09-23 09:21:26 +02:00
// @ViewChild('stepper', { static: false }) stepper: MatHorizontalStepper;
2020-07-29 17:04:19 +02:00
@Input ( ) path : string ;
@Input ( ) visibilityRules : Rule [ ] = [ ] ;
@Input ( ) datasetProfileId : String ;
2021-11-02 15:50:43 +01:00
@Input ( ) datasetDescription : String ;
2020-07-29 17:04:19 +02:00
@Input ( ) linkToScroll : LinkToScroll ;
2020-08-31 18:08:32 +02:00
@Output ( ) formChanged : EventEmitter < any > = new EventEmitter ( ) ;
2021-04-06 08:57:17 +02:00
@Output ( ) fieldsetFocusChange : EventEmitter < string > = new EventEmitter < string > ( ) ;
2021-03-09 09:04:50 +01:00
tocentries : ToCEntry [ ] ;
@Input ( ) form : FormGroup ;
2021-03-18 09:52:18 +01:00
@Input ( ) TOCENTRY_ID_PREFIX = "" ;
2021-04-13 10:19:09 +02:00
@Output ( ) visibilityRulesInstance = new EventEmitter < VisibilityRulesService > ( ) ;
2021-03-09 09:04:50 +01:00
2021-04-12 18:08:09 +02:00
public hiddenEntriesIds :string [ ] = [ ] ;
2020-07-29 17:04:19 +02:00
constructor (
private visibilityRulesService : VisibilityRulesService ,
) {
super ( ) ;
2021-03-09 09:04:50 +01:00
2020-07-29 17:04:19 +02:00
}
ngOnInit() {
2021-09-20 20:34:20 +02:00
this . tocentries = this . getTocEntries ( ) ;
const rules_to_append = this . _enrichWithMultiplicityRules ( this . tocentries ) ;
this . visibilityRulesService . buildVisibilityRules ( [ . . . this . visibilityRules , . . . rules_to_append ] , this . form ) ;
2021-04-13 10:19:09 +02:00
2020-09-24 19:28:03 +02:00
// if (this.form) {
// this.form.valueChanges
// .pipe(takeUntil(this._destroyed))
// .subscribe(val => {
// this.formChanged.emit(val);
// });
// }
2021-04-13 10:19:09 +02:00
this . visibilityRulesInstance . emit ( this . visibilityRulesService ) ;
2021-03-17 10:08:59 +01:00
2021-09-20 13:34:09 +02:00
2021-04-12 18:08:09 +02:00
this . hiddenEntriesIds = this . _findHiddenEntries ( this . tocentries ) ;
this . visibilityRulesService . visibilityChange
. pipe (
takeUntil ( this . _destroyed ) ,
debounceTime ( 100 )
)
. subscribe ( _ = > {
this . hiddenEntriesIds = this . _findHiddenEntries ( this . tocentries ) ;
} )
2020-07-29 17:04:19 +02:00
}
ngOnChanges ( changes : SimpleChanges ) {
// When the form is changed set stepper index to 0.
2020-09-23 09:21:26 +02:00
// if (this.stepper && changes['form'] && !changes['form'].isFirstChange()) {
// this.stepper.selectedIndex = 0;
// } else if (this.stepper && changes['linkToScroll'] && changes['linkToScroll'].currentValue) {
// if (changes['linkToScroll'].currentValue.page >= 0) {
// this.stepper.selectedIndex = changes['linkToScroll'].currentValue.page;
// }
// }
2020-07-29 17:04:19 +02:00
}
ngAfterViewInit() {
}
2021-03-09 09:04:50 +01:00
2021-04-06 08:57:17 +02:00
onAskedToScroll ( panel : MatExpansionPanel , id? :string ) {
2021-03-18 09:52:18 +01:00
panel . open ( ) ;
2021-04-06 08:57:17 +02:00
this . fieldsetFocusChange . emit ( id ) ;
2021-03-18 09:52:18 +01:00
}
2021-09-20 20:34:20 +02:00
private _enrichWithMultiplicityRules ( tocentries : ToCEntry [ ] ) : Rule [ ] {
2021-09-20 13:34:09 +02:00
if ( tocentries ) {
2021-09-20 20:34:20 +02:00
return tocentries . map ( entry = > {
if ( entry . type === ToCEntryType . Field ) return [ ] ; // * TODO Me to tora implementation den tha ftasei pote edo
2021-09-20 13:34:09 +02:00
if ( entry . type === ToCEntryType . FieldSet ) {
// if(multiplicity: )
try {
// TODO OTAN KANW HIDE MULTIPLE PEDIO TOTE STO ON SHOW HANO TA VALUES (AUTO MPOREI NA EINAI KAI LEGIT) ('NA DOUME AN ONTOS DIAGRAFONTAI I APLA DEN TA DEIXNOUME')
// * UPDATE KANEI DESTROY TO COMPONENT H NGIF . PITHANOTATA NA XREIASTEI NA TO KANOUME HIDDEN AN THELOUME KATI ALLO
const multiplicity = entry . form . get ( 'multiplicity' ) . value ;
if ( ( multiplicity . max > 1 ) && ( multiplicity . min > 0 ) && ( multiplicity . max >= multiplicity . min ) ) { // has valid multiplicity
2021-09-20 20:34:20 +02:00
return this . _createAndAppendVisibilityRule ( entry ) ;
2021-09-20 13:34:09 +02:00
}
} catch {
}
2021-09-20 20:34:20 +02:00
return [ ] ;
2021-09-20 13:34:09 +02:00
}
if ( entry . subEntries ) {
2021-09-20 20:34:20 +02:00
return this . _enrichWithMultiplicityRules ( entry . subEntries ) ;
2021-09-20 13:34:09 +02:00
}
} )
2021-09-20 20:34:20 +02:00
. reduce ( ( r , c ) = > { return [ . . . c , . . . r ] } , [ ] ) ;
2021-09-20 13:34:09 +02:00
}
2021-09-20 20:34:20 +02:00
return [ ] ;
2021-09-20 13:34:09 +02:00
}
2021-09-20 20:34:20 +02:00
private _createAndAppendVisibilityRule ( entry : ToCEntry ) : Rule [ ] {
const rules_to_append = [ ] ;
2021-09-20 13:34:09 +02:00
if ( entry && ( entry . type === ToCEntryType . FieldSet ) ) {
//childs that are either target or source
const childIdsWithVisRules = ( entry . form . get ( 'fields' ) as FormArray ) . controls . reduce ( ( all , s ) = >
{
const sval = s . value as Field ;
return this . visibilityRules . find ( x = > ( x . targetField === sval . id ) || ( x . sourceField === sval . id ) ) ? [ . . . all , sval ] : all ;
} , [ ] ) as Field [ ] ;
const innerCompositeFieldOriginalIds = ( entry . form . get ( 'fields' ) as FormArray ) . controls . map ( x = > x . get ( 'id' ) . value ) as string [ ] ;
//multiplicity items
const multiplicityItemsValue = entry . form . get ( 'multiplicityItems' ) . value as CompositeField [ ] ;
// ********* FIELDS OF FIELDSET ARE EITHER TARGETS OR SOURCES *****
if ( childIdsWithVisRules . length && multiplicityItemsValue && multiplicityItemsValue . length ) {
//check each multiplicity item composite field
multiplicityItemsValue . forEach ( mi = > {
const multiplicityCompositeFieldIds = mi . fields . map ( x = > x . id ) ;
const idMappings = multiplicityCompositeFieldIds . map ( x = > {
return {
original : innerCompositeFieldOriginalIds.find ( l = > x . includes ( l ) ) ,
multiplicityIdValue : x
}
} ) as { original : string , multiplicityIdValue : string } [ ] ;
//each field of mutliplicity item
mi . fields . forEach ( field = > {
//get original visibility rules (original field)
//original id
const original_id = childIdsWithVisRules . find ( x = > field . id . includes ( x . id ) ) . id ;
//get vis rules
//as source
const original_as_source = this . visibilityRules . filter ( x = > x . sourceField === original_id ) ;
const original_as_target = this . visibilityRules . filter ( x = > x . targetField === original_id ) ;
if ( original_as_source . length ) {
//inner dependencies
const innerDep = original_as_source . filter ( x = > innerCompositeFieldOriginalIds . includes ( x . targetField ) ) ;
innerDep . forEach ( x = > {
const newRule = { . . . x , sourceField : field.id , targetField : idMappings.find ( l = > l . original === x . targetField ) . multiplicityIdValue } as Rule ;
2021-09-20 20:34:20 +02:00
rules_to_append . push ( newRule ) ;
2021-09-20 13:34:09 +02:00
} )
//outer dependencies
const outerDep = original_as_source . filter ( x = > ! innerCompositeFieldOriginalIds . includes ( x . targetField ) ) ;
outerDep . forEach ( x = > {
const newRule = { . . . x , sourceField : field.id } ;
2021-09-20 20:34:20 +02:00
rules_to_append . push ( newRule ) ;
2021-09-20 13:34:09 +02:00
} )
}
if ( original_as_target . length ) {
//inner dependencies
const innerDep = original_as_target . filter ( x = > innerCompositeFieldOriginalIds . includes ( x . sourceField ) ) ;
innerDep . forEach ( x = > {
const newRule = { . . . x , targetField : field.id , sourceField : idMappings.find ( l = > l . original === x . sourceField ) . multiplicityIdValue } as Rule ;
2021-09-20 20:34:20 +02:00
rules_to_append . push ( newRule ) ;
2021-09-20 13:34:09 +02:00
} )
//outer dependencies
const outerDep = original_as_target . filter ( x = > ! innerCompositeFieldOriginalIds . includes ( x . sourceField ) ) ;
outerDep . forEach ( x = > {
const newRule = { . . . x , targetField : field.id } as Rule ;
2021-09-20 20:34:20 +02:00
rules_to_append . push ( newRule ) ;
2021-09-20 13:34:09 +02:00
} )
}
} )
} ) ;
}
// ** FIELDSET ITSELF IS TARGET
// ** source it can never be
const compositeFieldAsTargetRules = this . visibilityRules . filter ( x = > x . targetField === entry . id ) ;
const idCompositeFieldMappings = multiplicityItemsValue . map ( x = > {
return {
originalValue : entry.id ,
newValue :x.id
}
} ) as { originalValue : string , newValue : string } [ ] ;
if ( compositeFieldAsTargetRules . length ) {
compositeFieldAsTargetRules . forEach ( x = > {
idCompositeFieldMappings . forEach ( l = > {
const newRule = { . . . x , targetField : l.newValue } ;
2021-09-20 20:34:20 +02:00
rules_to_append . push ( newRule ) ;
2021-09-20 13:34:09 +02:00
} ) ;
} ) ;
}
}
2021-09-20 20:34:20 +02:00
return rules_to_append ;
2021-09-20 13:34:09 +02:00
}
2021-03-18 09:52:18 +01:00
2021-03-09 09:04:50 +01:00
private _buildRecursively ( form : FormGroup , whatAmI :ToCEntryType ) : ToCEntry {
if ( ! form ) return null ;
switch ( whatAmI ) {
case ToCEntryType . Section :
const sections = form . get ( 'sections' ) as FormArray ;
const fieldsets = form . get ( 'compositeFields' ) as FormArray ;
const tempResult :ToCEntry [ ] = [ ] ;
if ( sections && sections . length ) {
sections . controls . forEach ( section = > {
tempResult . push ( this . _buildRecursively ( section as FormGroup , ToCEntryType . Section ) ) ;
} ) ;
} else if ( fieldsets && fieldsets . length ) {
fieldsets . controls . forEach ( fieldset = > {
tempResult . push ( this . _buildRecursively ( fieldset as FormGroup , ToCEntryType . FieldSet ) ) ;
} ) ;
}
return {
form : form ,
id : form.get ( 'id' ) . value ,
label : form.get ( 'title' ) . value ,
numbering : '' ,
subEntries :tempResult ,
subEntriesType : sections && sections . length ? ToCEntryType.Section : ToCEntryType.FieldSet ,
type : ToCEntryType . Section ,
ordinal : form.get ( 'ordinal' ) . value
}
case ToCEntryType . FieldSet :
return {
form : form ,
label :form.get ( 'title' ) . value ,
id : form.get ( 'id' ) . value ,
numbering : 's' ,
subEntries : [ ] ,
subEntriesType : ToCEntryType.Field ,
type : ToCEntryType . FieldSet ,
ordinal : form.get ( 'ordinal' ) . value
}
}
}
private _sortByOrdinal ( tocentries : ToCEntry [ ] ) {
if ( ! tocentries || ! tocentries . length ) return ;
tocentries . sort ( this . _customCompare ) ;
tocentries . forEach ( entry = > {
this . _sortByOrdinal ( entry . subEntries ) ;
} ) ;
}
private _customCompare ( a , b ) {
return a . ordinal - b . ordinal ;
}
private _calculateNumbering ( tocentries : ToCEntry [ ] , depth :number [ ] = [ ] ) {
if ( ! tocentries || ! tocentries . length ) {
return ;
}
let prefixNumbering = depth . length ? depth . join ( '.' ) : '' ;
if ( depth . length ) prefixNumbering = prefixNumbering + "." ;
tocentries . forEach ( ( entry , i ) = > {
entry . numbering = prefixNumbering + ( i + 1 ) ;
this . _calculateNumbering ( entry . subEntries , [ . . . depth , i + 1 ] )
} ) ;
}
getTocEntries ( ) : ToCEntry [ ] {
2021-04-12 18:08:09 +02:00
if ( ! this . form ) { return [ ] ; }
2021-03-09 09:04:50 +01:00
const result : ToCEntry [ ] = [ ] ;
//build parent pages
( this . form . get ( 'pages' ) as FormArray ) . controls . forEach ( ( pageElement , i ) = > {
result . push ( {
id : i + 'id' ,
label : pageElement.get ( 'title' ) . value ,
type : ToCEntryType . Page ,
form : pageElement ,
numbering : ( i + 1 ) . toString ( ) ,
subEntriesType : ToCEntryType.Section ,
subEntries : [ ] ,
ordinal : pageElement.get ( 'ordinal' ) . value
} as ToCEntry )
} ) ;
result . forEach ( ( entry , i ) = > {
const sections = entry . form . get ( 'sections' ) as FormArray ;
sections . controls . forEach ( section = > {
const tempResults = this . _buildRecursively ( section as FormGroup , ToCEntryType . Section ) ;
entry . subEntries . push ( tempResults ) ;
} ) ;
} ) ;
this . _sortByOrdinal ( result ) ;
//calculate numbering
this . _calculateNumbering ( result ) ;
return result ;
}
2021-04-12 18:08:09 +02:00
private _findHiddenEntries ( tocentries :ToCEntry [ ] ) : string [ ] {
if ( ! tocentries ) return [ ] ;
const invisibleEntries :string [ ] = [ ]
tocentries . forEach ( entry = > {
if ( entry . type === ToCEntryType . FieldSet ) {
const isVisible = this . visibilityRulesService . checkElementVisibility ( entry . id ) ;
if ( ! isVisible ) {
invisibleEntries . push ( entry . id ) ;
} else {
//check field inputs
const fields = entry . form . get ( 'fields' ) as FormArray ;
const oneFieldAtLeastIsVisible = fields . controls . some ( field = > this . visibilityRulesService . checkElementVisibility ( field . get ( 'id' ) . value ) ) ;
if ( ! oneFieldAtLeastIsVisible ) {
invisibleEntries . push ( entry . id ) ;
}
}
} else {
const hiddenEntries = this . _findHiddenEntries ( entry . subEntries ) ;
if ( entry . subEntries && ( entry . subEntries . every ( e = > hiddenEntries . includes ( e . id ) ) ) ) {
//all children all hidden then hide parent node;
invisibleEntries . push ( entry . id ) ;
} else {
invisibleEntries . push ( . . . hiddenEntries ) ;
}
}
} )
return invisibleEntries ;
}
2021-03-09 09:04:50 +01:00
}
export interface ToCEntry {
id : string ;
label : string ;
subEntriesType : ToCEntryType ;
subEntries : ToCEntry [ ] ;
type : ToCEntryType ;
form : AbstractControl ;
numbering : string ;
ordinal : number ;
2020-07-29 17:04:19 +02:00
}
2021-03-09 09:04:50 +01:00
export enum ToCEntryType {
Page = 0 ,
Section = 1 ,
FieldSet = 2 ,
Field = 3
2021-11-02 15:50:43 +01:00
}