From 27ce73938290b0e58d6962167aa5f31e316307ea Mon Sep 17 00:00:00 2001 From: apapachristou Date: Tue, 24 Sep 2019 17:55:03 +0300 Subject: [PATCH] Adds dialog to ask user for confirmation before leaving. (Issue #166) --- .../confirmation-dialog.component.html | 24 +++++++---- .../confirmation-dialog.component.scss | 4 ++ .../deactivate/can-deactivate.guard.ts | 39 +++++++++++++++++ .../deactivate/deactivate.component.ts | 16 +++++++ .../dataset-create-wizard.component.ts | 16 +++++-- .../dataset-create-wizard.module.ts | 6 ++- .../dataset-create-wizard.routing.ts | 2 + .../quick-wizard-editor.component.ts | 14 +++++-- .../ui/quick-wizard/quick-wizard.module.ts | 7 +++- .../ui/quick-wizard/quick-wizard.rooting.ts | 42 ------------------- .../ui/quick-wizard/quick-wizard.routing.ts | 42 +++++++++++++++++++ dmp-frontend/src/assets/i18n/en.json | 5 ++- 12 files changed, 157 insertions(+), 60 deletions(-) create mode 100644 dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts create mode 100644 dmp-frontend/src/app/library/deactivate/deactivate.component.ts delete mode 100644 dmp-frontend/src/app/ui/quick-wizard/quick-wizard.rooting.ts create mode 100644 dmp-frontend/src/app/ui/quick-wizard/quick-wizard.routing.ts diff --git a/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.html b/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.html index 233425676..9acd1822d 100644 --- a/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.html +++ b/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.html @@ -1,17 +1,27 @@
+
+
+ {{ data.icon }} +
+
+ {{ data.warning }} +
+
+ close +
+
{{ data.message }}
-
close
+
+ close +
-
-
-
+
+
+
diff --git a/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.scss b/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.scss index 98a0908bc..565cad324 100644 --- a/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.scss +++ b/dmp-frontend/src/app/library/confirmation-dialog/confirmation-dialog.component.scss @@ -8,6 +8,10 @@ cursor: pointer; } + .warn-text { + color: #f44336; + } + .cancel { background-color: #aaaaaa; color: #ffffff; diff --git a/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts b/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts new file mode 100644 index 000000000..aac88736e --- /dev/null +++ b/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { CanDeactivate } from '@angular/router'; +import { Observable } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import { map } from 'rxjs/operators'; +import { BaseComponent } from '../../core/common/base/base.component'; +import { CheckDeactivateBaseComponent } from './deactivate.component'; +import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component'; + +@Injectable() +export class CanDeactivateGuard extends BaseComponent implements CanDeactivate { + + constructor( + private dialog: MatDialog, + public language: TranslateService + ) { + super(); + } + + canDeactivate(component: CheckDeactivateBaseComponent): boolean | Observable { + + if (component.canDeactivate()) { + return true; + } else { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '700px', + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.LEAVE-PAGE'), + warning: this.language.instant('GENERAL.CONFIRMATION-DIALOG.LEAVE-WARNING'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.LEAVE'), + icon: 'error_outline' + } + }); + return dialogRef.afterClosed().pipe(map(x => x ? true : false)); + } + } +} diff --git a/dmp-frontend/src/app/library/deactivate/deactivate.component.ts b/dmp-frontend/src/app/library/deactivate/deactivate.component.ts new file mode 100644 index 000000000..ce05a6632 --- /dev/null +++ b/dmp-frontend/src/app/library/deactivate/deactivate.component.ts @@ -0,0 +1,16 @@ +import { BaseComponent } from '../../core/common/base/base.component'; +import { HostListener } from '@angular/core'; + +export abstract class CheckDeactivateBaseComponent extends BaseComponent { + + protected constructor() { super(); } + + abstract canDeactivate(): boolean; + + @HostListener('window:beforeunload', ['$event']) + unloadNotification($event: any) { + if (!this.canDeactivate()) { + $event.returnValue = true; + } + } +} diff --git a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts index 573e0ef20..fb2d1dd29 100644 --- a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts +++ b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts @@ -1,5 +1,5 @@ -import {of as observableOf, Observable } from 'rxjs'; +import { of as observableOf, Observable } from 'rxjs'; import { Component, OnInit, ViewChild } from '@angular/core'; import { FormBuilder, FormGroup, FormArray } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; @@ -16,17 +16,19 @@ import { TranslateService } from '@ngx-translate/core'; import { DatasetEditorWizardComponent } from '../quick-wizard/dataset-editor/dataset-editor-wizard.component'; import { DatasetStatus } from '../../core/common/enum/dataset-status'; import { ConfirmationDialogComponent } from '../../library/confirmation-dialog/confirmation-dialog.component'; +import { CheckDeactivateBaseComponent } from '../../library/deactivate/deactivate.component'; @Component({ selector: 'dataset-create-wizard.component', templateUrl: 'dataset-create-wizard.component.html', styleUrls: ['./dataset-create-wizard.component.scss'], }) -export class DatasetCreateWizard extends BaseComponent implements OnInit, IBreadCrumbComponent { +export class DatasetCreateWizard extends CheckDeactivateBaseComponent implements OnInit, IBreadCrumbComponent { breadCrumbs: Observable; @ViewChild(DatasetEditorWizardComponent, { static: false }) datasetEditorWizardComponent: DatasetEditorWizardComponent; isLinear = false; isNew = true; + isSubmitted = false; formGroup: FormGroup; @@ -70,7 +72,8 @@ export class DatasetCreateWizard extends BaseComponent implements OnInit, IBread .pipe(takeUntil(this._destroyed)) .subscribe( complete => this.onCallbackSuccess(dmpId) - ) + ); + this.isSubmitted = true; } else { return; } @@ -128,7 +131,8 @@ export class DatasetCreateWizard extends BaseComponent implements OnInit, IBread .pipe(takeUntil(this._destroyed)) .subscribe( complete => this.onCallbackSuccess(dmpId) - ) + ); + this.isSubmitted = true; } hasDatasets() { @@ -156,4 +160,8 @@ export class DatasetCreateWizard extends BaseComponent implements OnInit, IBread return this.stepper.selectedIndex == 1; } } + + canDeactivate(): boolean { + return this.isSubmitted || !this.formGroup.dirty; + } } diff --git a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.module.ts b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.module.ts index dbb004455..8c5d790ec 100644 --- a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.module.ts +++ b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.module.ts @@ -7,10 +7,11 @@ import { ConfirmationDialogModule } from '../../library/confirmation-dialog/conf import { UrlListingModule } from '../../library/url-listing/url-listing.module'; import { DatasetDescriptionFormModule } from '../misc/dataset-description-form/dataset-description-form.module'; import { OuickWizardModule } from '../quick-wizard/quick-wizard.module'; -import { QuickWizardRoutingModule } from '../quick-wizard/quick-wizard.rooting'; import { DatasetCreateWizard } from './dataset-create-wizard.component'; import { DatasetCreateWizardRoutingModule } from './dataset-create-wizard.routing'; import { DatasetDmpSelector } from './dmp-selector/dataset-dmp-selector.component'; +import { QuickWizardRoutingModule } from '../quick-wizard/quick-wizard.routing'; +import { CanDeactivateGuard } from '../../library/deactivate/can-deactivate.guard'; @NgModule({ @@ -32,6 +33,9 @@ import { DatasetDmpSelector } from './dmp-selector/dataset-dmp-selector.componen DatasetDmpSelector, ], entryComponents: [ + ], + providers: [ + CanDeactivateGuard ] }) export class DatasetCreateWizardModule { } diff --git a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.routing.ts b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.routing.ts index 63689111c..0eb34454b 100644 --- a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.routing.ts +++ b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.routing.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { DatasetCreateWizard } from './dataset-create-wizard.component'; import { DatasetDmpSelector } from './dmp-selector/dataset-dmp-selector.component'; +import { CanDeactivateGuard } from '../../library/deactivate/can-deactivate.guard'; const routes: Routes = [ { @@ -10,6 +11,7 @@ const routes: Routes = [ data: { breadcrumb: true }, + canDeactivate: [CanDeactivateGuard] }, { path: '', diff --git a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts index 2edef3209..4481729a3 100644 --- a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts +++ b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts @@ -1,6 +1,6 @@ -import {of as observableOf, Observable } from 'rxjs'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { of as observableOf, Observable } from 'rxjs'; +import { Component, OnInit, ViewChild, HostListener } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -24,18 +24,20 @@ import { GrantEditorWizardModel } from '../grant-editor/grant-editor-wizard-mode import { QuickWizardEditorWizardModel } from './quick-wizard-editor.model'; import { FunderFormModel } from '../../dmp/editor/grant-tab/funder-form-model'; import { ProjectFormModel } from '../../dmp/editor/grant-tab/project-form-model'; +import { CheckDeactivateBaseComponent } from '../../../library/deactivate/deactivate.component'; @Component({ selector: 'app-quick-wizard-editor-component', templateUrl: 'quick-wizard-editor.component.html', styleUrls: ['./quick-wizard-editor.component.scss'] }) -export class QuickWizardEditorComponent extends BaseComponent implements OnInit, IBreadCrumbComponent { +export class QuickWizardEditorComponent extends CheckDeactivateBaseComponent implements OnInit, IBreadCrumbComponent { breadCrumbs: Observable = observableOf([]); @ViewChild('stepper', { static: true }) stepper: MatStepper; @ViewChild(DatasetEditorWizardComponent, { static: false }) datasetEditorWizardComponent: DatasetEditorWizardComponent; isNew = true; + isSubmitted = false; quickWizard: QuickWizardEditorWizardModel; allDatasets: DmpFinalizeDialogDataset[] = []; formGroup: FormGroup = null; @@ -155,6 +157,7 @@ export class QuickWizardEditorComponent extends BaseComponent implements OnInit, complete => this.onCallbackSuccess(), error => this.onCallbackError(error) ); + this.isSubmitted = true; } onSubmitSave(): void { @@ -175,6 +178,7 @@ export class QuickWizardEditorComponent extends BaseComponent implements OnInit, .subscribe( complete => this.onCallbackSuccess() ) + this.isSubmitted = true; } }); } @@ -217,4 +221,8 @@ export class QuickWizardEditorComponent extends BaseComponent implements OnInit, return this.formGroup.get('grant').get('label').value; } } + + canDeactivate(): boolean { + return this.isSubmitted || !this.formGroup.dirty; + } } diff --git a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.module.ts b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.module.ts index e6925ddd6..d0f5980af 100644 --- a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.module.ts +++ b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.module.ts @@ -3,7 +3,7 @@ import { CommonFormsModule } from '../../common/forms/common-forms.module'; import { CommonUiModule } from '../../common/ui/common-ui.module'; import { ConfirmationDialogModule } from '../../library/confirmation-dialog/confirmation-dialog.module'; import { UrlListingModule } from '../../library/url-listing/url-listing.module'; -import { QuickWizardRoutingModule } from './quick-wizard.rooting'; +import { QuickWizardRoutingModule } from './quick-wizard.routing'; import { GrantEditorWizardComponent } from './grant-editor/grant-editor-wizard.component'; import { DmpEditorWizardComponent } from './dmp-editor/dmp-editor-wizard.component'; import { QuickWizardEditorComponent } from './quick-wizard-editor/quick-wizard-editor.component'; @@ -13,7 +13,7 @@ import { DatasetDescriptionFormModule } from '../misc/dataset-description-form/d import { DmpModule } from '../dmp/dmp.module'; import { FunderEditorWizardComponent } from './funder-editor/funder-editor-wizard.component'; import { ProjectEditorWizardComponent } from './project-editor/project-editor-wizard.component'; - +import { CanDeactivateGuard } from '../../library/deactivate/can-deactivate.guard'; @NgModule({ imports: [ @@ -36,6 +36,9 @@ import { ProjectEditorWizardComponent } from './project-editor/project-editor-wi ], exports: [ DatasetEditorWizardComponent, + ], + providers: [ + CanDeactivateGuard ] }) export class OuickWizardModule { } diff --git a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.rooting.ts b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.rooting.ts deleted file mode 100644 index fd9248ede..000000000 --- a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.rooting.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { DmpEditorWizardComponent } from './dmp-editor/dmp-editor-wizard.component'; -import { GrantEditorWizardComponent } from './grant-editor/grant-editor-wizard.component'; -import { QuickWizardEditorComponent } from './quick-wizard-editor/quick-wizard-editor.component'; -import { DatasetEditorWizardComponent } from './dataset-editor/dataset-editor-wizard.component'; - -const routes: Routes = [ - { - path: '', - component: QuickWizardEditorComponent, - data: { - breadcrumb: true - }, - },{ - path: 'grant', - component: GrantEditorWizardComponent, - data: { - breadcrumb: true - }, - }, - { - path: 'dmp', - component: DmpEditorWizardComponent, - data: { - breadcrumb: true - }, - }, - { - path: 'dataset', - component: DatasetEditorWizardComponent, - data: { - breadcrumb: true - }, - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class QuickWizardRoutingModule { } diff --git a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.routing.ts b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.routing.ts new file mode 100644 index 000000000..31456ea00 --- /dev/null +++ b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard.routing.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { QuickWizardEditorComponent } from './quick-wizard-editor/quick-wizard-editor.component'; +import { CanDeactivateGuard } from '../../library/deactivate/can-deactivate.guard'; + +const routes: Routes = [ + { + path: '', + component: QuickWizardEditorComponent, + data: { + breadcrumb: true + }, + canDeactivate: [CanDeactivateGuard] + }, + // { + // path: 'grant', + // component: GrantEditorWizardComponent, + // data: { + // breadcrumb: true + // }, + // }, + // { + // path: 'dmp', + // component: DmpEditorWizardComponent, + // data: { + // breadcrumb: true + // }, + // }, + // { + // path: 'dataset', + // component: DatasetEditorWizardComponent, + // data: { + // breadcrumb: true + // }, + // } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class QuickWizardRoutingModule { } diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 4125c87d7..2836e9b1e 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -41,12 +41,15 @@ "PUBLISH-ITEM": "Publish this item?", "ADD-DATASET": "Do you want to continue by adding a Dataset Description to your DMP? You can always add more Dataset Descriptions using \"Add Dataset Description (Wizard)\" menu.", "ZENODO-DOI": "Would you like to create digital object identifier (DOI) for the DMP?", + "LEAVE-PAGE": "If you leave, your changes will be lost.", + "LEAVE-WARNING": "You have unsaved changes!", "ACTIONS": { "CONFIRM": "Yes", "NO": "No", "DELETE": "Delete", "REMOVE": "Remove", - "CANCEL": "Cancel" + "CANCEL": "Cancel", + "LEAVE": "Leave" } }, "ACTIONS": {