From 01afdabb21a74fba651c2059bf2d13f959d0ca7d Mon Sep 17 00:00:00 2001 From: Ioannis Kalyvas Date: Thu, 1 Nov 2018 18:02:15 +0200 Subject: [PATCH] Added Navigation to next question by pressing on a button. Introduces MarkForConsideration Component --- .gitignore | 1 + .../eudat/data/dao/entities/DMPDaoImpl.java | 4 +- .../DevelDatabaseConfiguration.java | 1 - .../handlers/PrincipalArgumentResolver.java | 8 +- dmp-frontend/src/app/app.module.ts | 2 - .../dataset-wizard.component.html | 398 +++++++++--------- .../dataset-wizard.component.ts | 129 +++--- .../dynamic-form-field.component.html | 2 + .../dynamic-form-field.component.ts | 4 +- .../dynamic-form-composite-field.html | 15 +- .../dynamic-form-composite-field.ts | 15 +- .../dynamic-form-group.component.html | 34 +- .../dynamic-form-section.html | 7 +- .../dynamic-form-section.ts | 10 +- .../src/app/form/dynamic-form.module.ts | 9 +- .../dynamic-form/dynamic-form.component.ts | 56 ++- ...m-pending-questions-display.component.html | 4 + ...orm-pending-questions-display.component.ts | 41 +- dmp-frontend/src/app/models/helpers/Pair.ts | 9 + .../src/app/utilities/enhancers/flatJoinOn.ts | 9 + .../src/app/utilities/enhancers/flatMap.ts | 9 + .../src/app/utilities/enhancers/groupBy.ts | 11 + .../form-focus-service/form-focus.service.ts | 36 ++ .../mark-for-consideration.service.ts | 22 + .../visibility-rules.service.ts | 12 +- 25 files changed, 519 insertions(+), 329 deletions(-) create mode 100644 dmp-frontend/src/app/models/helpers/Pair.ts create mode 100644 dmp-frontend/src/app/utilities/enhancers/flatJoinOn.ts create mode 100644 dmp-frontend/src/app/utilities/enhancers/flatMap.ts create mode 100644 dmp-frontend/src/app/utilities/enhancers/groupBy.ts create mode 100644 dmp-frontend/src/app/utilities/form-focus-service/form-focus.service.ts create mode 100644 dmp-frontend/src/app/utilities/mark-for-considerations/mark-for-consideration.service.ts diff --git a/.gitignore b/.gitignore index 10071e274..4d5ad0218 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ temp/ *.jar *.lst dmp-frontend/.vscode/ +*.docx diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java index 43dddd194..55f6670cc 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java @@ -40,7 +40,9 @@ public class DMPDaoImpl extends DatabaseAccess implements DMPDao { if (criteria.getProjects() != null && !criteria.getProjects().isEmpty()) query.where(((builder, root) -> root.get("project").in(criteria.getProjects()))); if (!criteria.getAllVersions()) - query.initSubQuery(String.class).where((builder, root) -> builder.equal(root.get("version"), query.subQueryMax((builder1, externalRoot, nestedRoot) -> builder1.equal(externalRoot.get("groupId"), nestedRoot.get("groupId")), Arrays.asList(new SelectionField(FieldSelectionType.FIELD, "version")), String.class))); + query.initSubQuery(String.class).where((builder, root) -> builder.equal(root.get("version"), + query.subQueryMax((builder1, externalRoot, nestedRoot) -> builder1.equal(externalRoot.get("groupId"), + nestedRoot.get("groupId")), Arrays.asList(new SelectionField(FieldSelectionType.FIELD, "version")), String.class))); if (criteria.getGroupIds() != null && !criteria.getGroupIds().isEmpty()) query.where((builder, root) -> root.get("groupId").in(criteria.getGroupIds())); query.where((builder, root) -> builder.notEqual(root.get("status"), DMP.DMPStatus.DELETED.getValue())); diff --git a/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java b/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java index 7227b8a69..aff90c1ff 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java +++ b/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java @@ -72,7 +72,6 @@ public class DevelDatabaseConfiguration { properties.setProperty("hibernate.c3p0.maxPoolSize", "70"); properties.setProperty("hibernate.c3p0.timeout", "5000"); properties.setProperty("hibernate.connection.release_mode", "after_transaction"); - //properties.setProperty("hibernate.connection.provider_class", "org.hibernate.c3p0.internal.C3P0ConnectionProvider"); return properties; } } \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/handlers/PrincipalArgumentResolver.java b/dmp-backend/web/src/main/java/eu/eudat/logic/handlers/PrincipalArgumentResolver.java index 35f85e4bc..44784769f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/handlers/PrincipalArgumentResolver.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/handlers/PrincipalArgumentResolver.java @@ -44,13 +44,7 @@ public final class PrincipalArgumentResolver implements HandlerMethodArgumentRes if (principal == null) throw new UnauthorisedException("Authentication Information Missing"); if (!claimList.contains(Authorities.ANONYMOUS) && !principal.isAuthorized(claimList)) throw new UnauthorisedException("You are not Authorized For this Action"); - /*Principal principal1 = new Principal(); - principal1.setId(UUID.fromString("46366b8a-a712-4e0c-a499-1a3a0f209325")); - principal1.setToken(UUID.fromString("19031e80-6534-4aa5-b68a-78e97042c968")); - principal1.setName("Ioannis Kalyvas"); - principal1.setAvatarUrl("https://lh5.googleusercontent.com/-X65vX1QO_Ew/AAAAAAAAAAI/AAAAAAAAAAA/AAN31DU5lFIOwD_fZiYW96D410pn6v4E-Q/s96-c/photo.jpg"); - principal1.setAuthorities(new HashSet<>(Arrays.asList(Authorities.ADMIN, Authorities.USER))); - principal1.setExpiresAt(addADay(new Date()));*/ + return principal; } diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index 44c26f898..be434de93 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -11,10 +11,8 @@ import { HttpClient, HttpClientModule } from '@angular/common/http'; import { HttpModule } from '@angular/http'; import { HostConfiguration } from './app.constants'; import { BaseHttpModule } from './utilities/cite-http-service-module/cite-http.module'; - import { LoginOptions } from './user-management/utilties/LoginOptions'; import { LoginModule } from './user-management/login.module'; - import { SharedModule } from './shared/shared.module'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; diff --git a/dmp-frontend/src/app/datasets/dataset-wizard/dataset-wizard.component.html b/dmp-frontend/src/app/datasets/dataset-wizard/dataset-wizard.component.html index 8f1cd3d30..5f9130052 100644 --- a/dmp-frontend/src/app/datasets/dataset-wizard/dataset-wizard.component.html +++ b/dmp-frontend/src/app/datasets/dataset-wizard/dataset-wizard.component.html @@ -29,211 +29,229 @@ -
- - - {{'DATASET-WIZARD.FIRST-STEP.TITLE' | translate}} - - - - {{profile.label}} - - - {{baseErrorModel['Criteria.status']}} - - - -
+
+
+ + + {{'DATASET-WIZARD.FIRST-STEP.TITLE' | translate}} + + + + {{profile.label}} + + + {{baseErrorModel['Criteria.status']}} + + + +
+
-
- {{'DATASET-WIZARD.SECOND-STEP.TITLE' | translate}} +
+ + {{'DATASET-WIZARD.SECOND-STEP.TITLE' | translate}} - - - - {{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES' | translate}} - - - - - + + + + {{'DATASET-EDITOR.FIELDS.DATAREPOSITORIES' | translate}} + + + + + - -
-

- {{i+1}}) {{suggestion.get('label').value}} -

-
-
- - - -
-
- -
-
-
- - - - {{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS' | translate}} - - - - - + +
+

+ {{i+1}}) {{suggestion.get('label').value}} +

+
+
+ + + +
+
+ +
+
+
+ + + + {{'DATASET-EDITOR.FIELDS.EXTERNAL-DATASETS' | translate}} + + + + + - -
-

- {{i+1}}) {{suggestion.get('label').value}} -

-
-
- - - -
+ +
+

+ {{i+1}}) {{suggestion.get('label').value}} +

+
+
+ + + +
-
- - - {{'TYPES.EXTERNAL-DATASET-TYPE.SOURCE' | translate}} - {{'TYPES.EXTERNAL-DATASET-TYPE.OUTPUT' | translate}} - - -
-
- -
-
-
+
+ + + {{'TYPES.EXTERNAL-DATASET-TYPE.SOURCE' | translate}} + {{'TYPES.EXTERNAL-DATASET-TYPE.OUTPUT' | translate}} + + +
+
+ +
+ +
- - - - {{'DATASET-EDITOR.FIELDS.REGISTRIES' | translate}} - - - - - + + + + {{'DATASET-EDITOR.FIELDS.REGISTRIES' | translate}} + + + + + - -
-

- {{i+1}}) {{suggestion.get('label').value}} -

-
-
- -
-
-
+ +
+

+ {{i+1}}) {{suggestion.get('label').value}} +

+
+
+ +
+
+
- - - - {{'DATASET-EDITOR.FIELDS.SERVICES' | translate}} - - - - - + + + + {{'DATASET-EDITOR.FIELDS.SERVICES' | translate}} + + + + + - -
-

- {{i+1}}) {{suggestion.get('label').value}} -

-
-
- -
-
-
+ +
+

+ {{i+1}}) {{suggestion.get('label').value}} +

+
+
+ +
+
+
- - - - {{'DATASET-EDITOR.FIELDS.TAGS' | translate}} - - - - + + + + {{'DATASET-EDITOR.FIELDS.TAGS' | translate}} + + + + - -
-

- {{i+1}}) {{suggestion.get('name').value}} -

-
-
- -
-
-
+ +
+

+ {{i+1}}) {{suggestion.get('name').value}} +

+
+
+ +
+
+
- - + + +
- {{'DATASET-WIZARD.THIRD-STEP.TITLE' | translate}} - - - +
- +
+ diff --git a/dmp-frontend/src/app/form/dynamic-form-composite-field/dynamic-form-composite-field.ts b/dmp-frontend/src/app/form/dynamic-form-composite-field/dynamic-form-composite-field.ts index 3a8745dcf..4e05c643b 100644 --- a/dmp-frontend/src/app/form/dynamic-form-composite-field/dynamic-form-composite-field.ts +++ b/dmp-frontend/src/app/form/dynamic-form-composite-field/dynamic-form-composite-field.ts @@ -3,6 +3,8 @@ import { CompositeField } from '../../models/CompositeField'; import { FormGroup, FormArray } from '@angular/forms'; import { Component, Input, OnInit, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core'; import { Field } from '../../models/Field'; +import { MarkForConsiderationService } from '../../utilities/mark-for-considerations/mark-for-consideration.service'; +import { FormFocusService } from '../../utilities/form-focus-service/form-focus.service'; @Component({ selector: 'app-df-composite-field', templateUrl: './dynamic-form-composite-field.html', @@ -15,11 +17,13 @@ export class DynamicFormCompositeFieldComponent implements OnInit { @Input() compositeField: CompositeField; form: FormGroup; - @Input() pathName: string; - @Input() path: string; trackByFn = (index, item) => item ? item['id'] : null; - constructor(private visibilityRulesService: VisibilityRulesService) { + constructor( + private visibilityRulesService: VisibilityRulesService, + private markForConsiderationService: MarkForConsiderationService, + private formFocusService: FormFocusService + ) { } ngOnInit() { @@ -28,10 +32,13 @@ export class DynamicFormCompositeFieldComponent implements OnInit { } } - addMultipleField(fieldIndex: number) { const field: Field = this.compositeField.fields[fieldIndex].cloneForMultiplicity(fieldIndex, ''); this.compositeField.fields[fieldIndex].multiplicityItems.push(field); ((this.form.get('fields').get('' + fieldIndex).get('multiplicityItems'))).push(field.buildForm()); } + + markForConsideration() { + this.markForConsiderationService.markForConsideration(this.compositeField); + } } diff --git a/dmp-frontend/src/app/form/dynamic-form-group/dynamic-form-group.component.html b/dmp-frontend/src/app/form/dynamic-form-group/dynamic-form-group.component.html index fa169e6ec..81afb7efb 100644 --- a/dmp-frontend/src/app/form/dynamic-form-group/dynamic-form-group.component.html +++ b/dmp-frontend/src/app/form/dynamic-form-group/dynamic-form-group.component.html @@ -1,20 +1,20 @@
-
+
-
- - -
-
- -
-
-
-
+
+ + +
+
+ +
+
+
+
diff --git a/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.html b/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.html index 2fe0d2b74..c7d0ad184 100644 --- a/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.html +++ b/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.html @@ -18,16 +18,19 @@ Add one more fieldset + - +
- +
+
diff --git a/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.ts b/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.ts index b310216fc..d943d9101 100644 --- a/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.ts +++ b/dmp-frontend/src/app/form/dynamic-form-section/dynamic-form-section.ts @@ -3,6 +3,7 @@ import { FormGroup, Form, FormArray } from '@angular/forms'; import { Component, Input, OnInit, ViewEncapsulation, ChangeDetectionStrategy, AfterViewInit } from '@angular/core'; import { CompositeField } from '../../models/CompositeField'; import { Section } from '../../models/Section'; +import { FormFocusService } from '../../utilities/form-focus-service/form-focus.service'; @Component({ @@ -20,7 +21,10 @@ export class DynamicFormSectionComponent implements OnInit, AfterViewInit { @Input() pathName: string; @Input() path: string; trackByFn = (index, item) => item ? item['id'] : null; - constructor(public visibilityRulesService: VisibilityRulesService) { } + constructor( + public visibilityRulesService: VisibilityRulesService, + private formFocusService: FormFocusService + ) { } ngOnInit() { if (this.section) { @@ -47,4 +51,8 @@ export class DynamicFormSectionComponent implements OnInit, AfterViewInit { } return false; } + + next(compositeField: CompositeField) { + this.formFocusService.focusNext(compositeField); + } } diff --git a/dmp-frontend/src/app/form/dynamic-form.module.ts b/dmp-frontend/src/app/form/dynamic-form.module.ts index 890146a3b..342fd7ef3 100644 --- a/dmp-frontend/src/app/form/dynamic-form.module.ts +++ b/dmp-frontend/src/app/form/dynamic-form.module.ts @@ -32,6 +32,9 @@ import { NgModule } from '@angular/core'; import { DatasetProfileAdmin } from '../services/datasetProfileAdmin/datasetProfileAfmin.service'; import { DatasetProfileService } from '../services/dataset-profile.service'; import { DatasetWizardService } from '../services/dataset-wizard/dataset-wizard.service'; +import { DynamicFormPendingQuestionsDisplayComponent } from './helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component'; +import { MarkForConsiderationService } from '../utilities/mark-for-considerations/mark-for-consideration.service'; +import { FormFocusService } from '../utilities/form-focus-service/form-focus.service'; @NgModule({ @@ -57,6 +60,7 @@ import { DatasetWizardService } from '../services/dataset-wizard/dataset-wizard. DynamicFormCompositeFieldComponent, DynamicFieldBooleanDecisionComponent, DynamicFieldRadioBoxComponent, + DynamicFormPendingQuestionsDisplayComponent, TableOfContentsComponent, TableOfContentsFieldSetComponent, TableOfContentsGroupComponent, @@ -76,6 +80,7 @@ import { DatasetWizardService } from '../services/dataset-wizard/dataset-wizard. DynamicFormCompositeFieldComponent, DynamicFieldBooleanDecisionComponent, DynamicFieldRadioBoxComponent, + DynamicFormPendingQuestionsDisplayComponent, TableOfContentsComponent, TableOfContentsFieldSetComponent, TableOfContentsGroupComponent, @@ -92,7 +97,9 @@ import { DatasetWizardService } from '../services/dataset-wizard/dataset-wizard. PaginationService, DatasetProfileService, DatasetProfileAdmin, - DatasetWizardService + DatasetWizardService, + MarkForConsiderationService, + FormFocusService ] }) diff --git a/dmp-frontend/src/app/form/dynamic-form/dynamic-form.component.ts b/dmp-frontend/src/app/form/dynamic-form/dynamic-form.component.ts index fe13f31c5..0d97302b0 100644 --- a/dmp-frontend/src/app/form/dynamic-form/dynamic-form.component.ts +++ b/dmp-frontend/src/app/form/dynamic-form/dynamic-form.component.ts @@ -6,11 +6,17 @@ import { BaseHttpService } from '../../utilities/cite-http-service-module/base-h import { VisibilityRulesService } from '../../utilities/visibility-rules/visibility-rules.service'; import { DatasetProfileDefinitionModel } from '../../models/DatasetProfileDefinitionModel'; import { DatasetWizardModel } from '../../models/datasets/DatasetWizardModel'; -import { Component, Input, OnInit, ViewEncapsulation, AfterViewInit } from '@angular/core'; +import { Component, Input, OnInit, ViewEncapsulation, AfterViewInit, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; import 'rxjs/add/operator/switchMap'; +import '../../utilities/enhancers/flatJoinOn'; import { Location } from '@angular/common'; +import { MarkForConsiderationService } from '../../utilities/mark-for-considerations/mark-for-consideration.service'; +import { FormFocusService } from '../../utilities/form-focus-service/form-focus.service'; +import { CompositeField } from '../../models/CompositeField'; +import { Pair } from '../../models/helpers/Pair'; +import { MatStepper } from '@angular/material'; @Component({ selector: 'app-dynamic-form', @@ -19,6 +25,7 @@ import { Location } from '@angular/common'; './dynamic-form.component.scss' ], providers: [ + FormFocusService ], encapsulation: ViewEncapsulation.None, }) @@ -31,6 +38,7 @@ export class DynamicFormComponent implements OnInit, AfterViewInit { datasetProfileDefinitionModel: DatasetProfileDefinitionModel; private progressbar = false; private currentPageIndex = 0; + @ViewChild('stepper') stepper: MatStepper; @Input() dataModel: DatasetWizardModel = new DatasetWizardModel(); @Input() path: string; @@ -43,11 +51,9 @@ export class DynamicFormComponent implements OnInit, AfterViewInit { constructor( private router: Router, - private _location: Location, private route: ActivatedRoute, private visibilityRulesService: VisibilityRulesService, - private http: BaseHttpService, - private datasetWizardService: DatasetWizardService, + private formFocusService: FormFocusService ) { //this.datasetId = route.snapshot.params['id']; } @@ -64,14 +70,26 @@ export class DynamicFormComponent implements OnInit, AfterViewInit { this.visibilityRulesService.buildVisibilityRules(rules); this.datasetProfileDefinitionModel = this.dataModel.datasetProfileDefinition; this.visibilityRulesService.setModel(this.datasetProfileDefinitionModel); + this.createPagination(); this.progressbar = true; + const sections: Pair[] = this.datasetProfileDefinitionModel.pages.map(page => new Pair(page.sections, page.ordinal)).filter(x => x); + const compositeFields: Pair[] = sections.map(section => new Pair(section.left.flatMap(sec => sec.compositeFields), section.right)).filter(x => x); + const nestedSections: Pair[] = sections.map(section => new Pair(section.left.flatMap(x => x.sections), section.right)).filter(x => x); + const nestedCompositeFields: Pair[] = nestedSections.map(section => new Pair(section.left.flatMap(x => x.compositeFields), section.right)).filter(x => x); + const compositeFieldsUnion: Pair[] = compositeFields.concat(nestedCompositeFields); + + //const fields = compositeFieldsUnion.flatJoinOn(x => x.right); + this.formFocusService.setFields(compositeFieldsUnion); this.route.fragment.subscribe((fragment: string) => { const self = this; setTimeout(function () { self.scrollTo(fragment); }); }); + } + ngAfterViewInit() { + this.visibilityRulesService.triggerVisibilityEvaluation(); this.route.queryParams.subscribe((params) => { if (params && 'page' in params) { this.changeCurrentPage(params['page']); @@ -79,35 +97,17 @@ export class DynamicFormComponent implements OnInit, AfterViewInit { }); } - ngAfterViewInit() { - this.visibilityRulesService.triggerVisibilityEvaluation(); - } - toggleSidebar() { this.visibleSidebar = !this.visibleSidebar; } - // getPages(model: DatasetProfileDefinitionModel): Array { - // let pageSet = new Set(); - - // model.sections.forEach(section => { - // pageSet.add(section.page); - // }); - - // return Array.from(pageSet).sort((a, b) => a - b); - // } - shouldDisplaySection(section: Section): Boolean { return (section.page) === this.currentPageIndex; } createPagination() { - /*this.pages.forEach(item => { - this.stepperItems.push({ - label: '', - }) - });*/ + } changePageIndex(index: any) { @@ -120,16 +120,14 @@ export class DynamicFormComponent implements OnInit, AfterViewInit { if (!element) { return; } element.scrollIntoView(); this.visibleSidebar = true; - const scrollElement = document.querySelector('.scrollableContent'); - //scrollElement.scrollTop = topElement.offsetTop; } changeCurrentPage(pageString: string) { - if (!pageString) { return; } + //if (!pageString) { return; } const page = parseInt(pageString); - if (isNaN(page)) { return; } + /*if (isNaN(page)) { return; } const pageIndex = this.pages.indexOf(page); - if (pageIndex === -1) { return; } - this.currentPageIndex = page; + if (pageIndex === -1) { return; }*/ + this.stepper.selectedIndex = page; } } diff --git a/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.html b/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.html index e69de29bb..56c01029f 100644 --- a/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.html +++ b/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.ts b/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.ts index d20f2ffcd..60a3d2d9e 100644 --- a/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.ts +++ b/dmp-frontend/src/app/form/helpers/dynamic-form-pending-questions/dynamic-form-pending-questions-display.component.ts @@ -1,5 +1,15 @@ -import { Component, ViewEncapsulation, OnInit } from '@angular/core'; - +import { Component, ViewEncapsulation, OnInit, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { VisibilityRulesService } from '../../../utilities/visibility-rules/visibility-rules.service'; +import { JsonSerializer } from '../../../utilities/JsonSerializer'; +import { Rule } from '../../../models/Rule'; +import { DatasetWizardModel } from '../../../models/datasets/DatasetWizardModel'; +import { DatasetProfileDefinitionModel } from '../../../models/DatasetProfileDefinitionModel'; +import { Section } from '../../../models/Section'; +import { CompositeField } from '../../../models/CompositeField'; +import '../../../utilities/enhancers/flatMap'; +import { Field } from '../../../models/Field'; +import { MarkForConsiderationService } from '../../../utilities/mark-for-considerations/mark-for-consideration.service'; @Component({ selector: 'app-dynamic-form-pending-questions-display', templateUrl: './dynamic-form-pending-questions-display.component.html', @@ -11,7 +21,32 @@ import { Component, ViewEncapsulation, OnInit } from '@angular/core'; encapsulation: ViewEncapsulation.None, }) export class DynamicFormPendingQuestionsDisplayComponent implements OnInit { + + constructor( + private visibilityRulesService: VisibilityRulesService, + private markForConsideration: MarkForConsiderationService) { + } + + datasetProfileDefinitionModel: DatasetProfileDefinitionModel; + + @Input() + form: FormGroup; + + @Input() dataModel: DatasetWizardModel = new DatasetWizardModel(); + + fields: CompositeField[]; + ngOnInit(): void { - throw new Error('Method not implemented.'); + const rules: Rule[] = JsonSerializer.fromJSONArray(this.dataModel.datasetProfileDefinition.rules, Rule); + this.datasetProfileDefinitionModel = this.dataModel.datasetProfileDefinition; + const sections: Section[] = this.datasetProfileDefinitionModel.pages.flatMap(page => page.sections).filter(x => x); + const compositeFields: CompositeField[] = sections.flatMap(section => section.compositeFields).filter(x => x); + const nestedSections: Section[] = sections.flatMap(section => section.sections).filter(x => x); + const nestedCompositeFiels: CompositeField[] = nestedSections.flatMap(section => section.compositeFields).filter(x => x); + const compositeFieldsUnion = compositeFields.concat(nestedCompositeFiels); + //const fields: Field[] = compositeFields.flatMap(composite => composite.fields).concat(nestedCompositeFiels.flatMap(composite => composite.fields)); + const fields = compositeFieldsUnion.filter(compositeField => this.visibilityRulesService.checkElementVisibility(compositeField.id)) + .filter(compositeField => compositeField.fields.filter(x => x && x.validationRequired && this.visibilityRulesService.getFormGroup(x.id).value == null).length > 0); + fields.forEach(x => this.markForConsideration.markForConsideration(x)); } } diff --git a/dmp-frontend/src/app/models/helpers/Pair.ts b/dmp-frontend/src/app/models/helpers/Pair.ts new file mode 100644 index 000000000..02bffbc32 --- /dev/null +++ b/dmp-frontend/src/app/models/helpers/Pair.ts @@ -0,0 +1,9 @@ +export class Pair { + public readonly left: L; + public readonly right: R; + + constructor(left: L, right: R) { + this.left = left; + this.right = right; + } +} diff --git a/dmp-frontend/src/app/utilities/enhancers/flatJoinOn.ts b/dmp-frontend/src/app/utilities/enhancers/flatJoinOn.ts new file mode 100644 index 000000000..a1e42cb3e --- /dev/null +++ b/dmp-frontend/src/app/utilities/enhancers/flatJoinOn.ts @@ -0,0 +1,9 @@ +interface Array { + flatJoinOn(by: (item: T) => J): Array; +} + +Array.prototype.flatJoinOn = function (f: Function) { + return this.groupBy(f).reduce((ys: any, x: any) => { + return ys.concat(f.call(this, x)); + }, []); +}; diff --git a/dmp-frontend/src/app/utilities/enhancers/flatMap.ts b/dmp-frontend/src/app/utilities/enhancers/flatMap.ts new file mode 100644 index 000000000..6d5e324e8 --- /dev/null +++ b/dmp-frontend/src/app/utilities/enhancers/flatMap.ts @@ -0,0 +1,9 @@ +interface Array { + flatMap(callback: (t: T) => Array): Array; +} + +Array.prototype.flatMap = function (f: Function) { + return this.reduce((ys: any, x: any) => { + return ys.concat(f.call(this, x)); + }, []); +}; diff --git a/dmp-frontend/src/app/utilities/enhancers/groupBy.ts b/dmp-frontend/src/app/utilities/enhancers/groupBy.ts new file mode 100644 index 000000000..ed47ba088 --- /dev/null +++ b/dmp-frontend/src/app/utilities/enhancers/groupBy.ts @@ -0,0 +1,11 @@ +interface Array { + groupBy(by: (item: T) => J): Array>; +} + +Array.prototype.groupBy = function (f: Function) { + return this.reduce((ys: any, x: any) => { + ys[f.call(this, x)] = ys[f.call(this, x)] || []; + ys[f.call(this, x)].push(x); + return ys; + }, []); +}; diff --git a/dmp-frontend/src/app/utilities/form-focus-service/form-focus.service.ts b/dmp-frontend/src/app/utilities/form-focus-service/form-focus.service.ts new file mode 100644 index 000000000..76c4f7ff4 --- /dev/null +++ b/dmp-frontend/src/app/utilities/form-focus-service/form-focus.service.ts @@ -0,0 +1,36 @@ +import { Injectable } from '@angular/core'; +import { CompositeField } from '../../models/CompositeField'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Pair } from '../../models/helpers/Pair'; +import '../../utilities/enhancers/groupBy'; +import { VisibilityRulesService } from '../visibility-rules/visibility-rules.service'; + +@Injectable() +export class FormFocusService { + + private compositeFields: Pair[] = []; + + constructor( + public router: Router, + public route: ActivatedRoute, + public visibilityService: VisibilityRulesService + ) { + + } + + setFields(compositeFields: Pair[]) { + this.compositeFields = compositeFields; + } + + focusNext(field: CompositeField) { + const flattenedCompositeFields = this.compositeFields.groupBy(x => x.right) + .map(x => x.reduce((first: Pair, second: Pair) => + (new Pair(first.left.concat(second.left), first.right)))); + const page = flattenedCompositeFields.filter(x => x['left'].map(y => y.id).indexOf(field.id) !== -1)[0]; + let pageIndex = page['right']; + const currentFields = page['left'].filter(x => this.visibilityService.checkElementVisibility(x.id)).map(x => x.id); + const fieldIndex = currentFields.indexOf(field.id); + if (fieldIndex === currentFields.length - 1) { pageIndex = pageIndex + 1; } + this.router.navigate(['datasets/' + this.route.snapshot.url[0] + '/' + this.route.snapshot.url[1]], { fragment: page['left'].filter(x => this.visibilityService.checkElementVisibility(x.id))[fieldIndex].id, queryParams: { page: pageIndex } }); + } +} diff --git a/dmp-frontend/src/app/utilities/mark-for-considerations/mark-for-consideration.service.ts b/dmp-frontend/src/app/utilities/mark-for-considerations/mark-for-consideration.service.ts new file mode 100644 index 000000000..948ec952f --- /dev/null +++ b/dmp-frontend/src/app/utilities/mark-for-considerations/mark-for-consideration.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { CompositeField } from '../../models/CompositeField'; + +@Injectable() +export class MarkForConsiderationService { + + private compositeFields: CompositeField[] = []; + + markForConsideration(field: CompositeField) { + if (this.exists(field)) { + this.compositeFields.push(field); + } + } + + getFields() { + return this.compositeFields; + } + + exists(field: CompositeField) { + return this.compositeFields.map(x => x.id).indexOf(field.id) === -1; + } +} diff --git a/dmp-frontend/src/app/utilities/visibility-rules/visibility-rules.service.ts b/dmp-frontend/src/app/utilities/visibility-rules/visibility-rules.service.ts index c49cddf4a..8f51cbe1c 100644 --- a/dmp-frontend/src/app/utilities/visibility-rules/visibility-rules.service.ts +++ b/dmp-frontend/src/app/utilities/visibility-rules/visibility-rules.service.ts @@ -39,7 +39,7 @@ export class VisibilityRulesService { } public checkElementVisibility(id: string): boolean { - if (!this.elementVisibilityMap.has(id) || this.elementVisibilityMap.get(id)) { return true; } else { return false; } + return !this.elementVisibilityMap.has(id) || this.elementVisibilityMap.get(id); } public buildVisibilityRules(item: Array) { @@ -90,7 +90,7 @@ export class VisibilityRulesService { parseValue(value: any) { if (typeof value === 'string') { - if (value === 'true') { return true; } else if (value === 'false') { return false; } else { return value; } + if (value === 'true') { return true; } else if (value === 'false') { return false; } else { return this.translate(value); } } else { return value; } } @@ -137,4 +137,12 @@ export class VisibilityRulesService { } } } + + private translate(item: any) { + try { + return JSON.parse(item).value; + } catch (error) { + return item; + } + } }