LP1904036 saved changes warning
authorBill Erickson <berickxx@gmail.com>
Thu, 8 Apr 2021 20:30:11 +0000 (16:30 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:30 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts

index f76e2fe..f224d12 100644 (file)
@@ -1333,9 +1333,9 @@ export class EditComponent implements OnInit, AfterViewInit {
 
     save(clone?: boolean): Promise<any> {
 
-        // TODO clear unload prompt
-
+        this.changesPending = false;
         this.loading = true;
+
         return this.saveUser()
         .then(_ => this.saveUserSettings())
         .then(_ => this.updateHoldPrefs())
index 388c36a..dba472e 100644 (file)
@@ -4,6 +4,11 @@
   </eg-staff-banner>
 </ng-container>
 
+<eg-confirm-dialog #pendingChangesDialog
+  i18n-dialogTitle dialogTitle="Unsaved Changes Confirmation" 
+  i18n-dialogBoby  dialogBody="Unsaved changes will be lost.  Continue navigation?">
+</eg-confirm-dialog>
+
 <ng-container *ngIf="context.summary">
   <eg-staff-banner i18n-bannerText bannerText="Patron: 
     {{context.summary.patron.family_name()}}, 
         <li ngbNavItem="edit" [disabled]="!context.summary">
           <a ngbNavLink i18n>Edit</a>
           <ng-template ngbNavContent>
-            <eg-patron-edit [patronId]="patronId" [toolbar]="editorToolbar">
+            <eg-patron-edit #patronEditor [patronId]="patronId" [toolbar]="editorToolbar">
             </eg-patron-edit> 
           </ng-template>
         </li>
index ac1b084..d87bac0 100644 (file)
@@ -1,4 +1,4 @@
-import {Component, ViewChild, OnInit, AfterViewInit} from '@angular/core';
+import {Component, ViewChild, OnInit, AfterViewInit, HostListener} from '@angular/core';
 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
 import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {NetService} from '@eg/core/net.service';
@@ -9,6 +9,8 @@ import {PatronContextService, BillGridEntry} from './patron.service';
 import {PatronSearch, PatronSearchComponent
     } from '@eg/staff/share/patron/search.component';
 import {EditToolbarComponent} from './edit-toolbar.component';
+import {EditComponent} from './edit.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
 
 const MAIN_TABS =
     ['checkout', 'items_out', 'holds', 'bills', 'messages', 'edit', 'search'];
@@ -29,7 +31,12 @@ export class PatronComponent implements OnInit, AfterViewInit {
     /* eg-patron-edit is unable to find #editorToolbar directly
      * within the template.  Adding a ref here allows it to
      * successfully transfer to the editor */
-    @ViewChild('editorToolbar') editorToolbar: EditToolbarComponent;
+    @ViewChild('editorToolbar') private editorToolbar: EditToolbarComponent;
+
+    @ViewChild('patronEditor') private patronEditor: EditComponent;
+
+    @ViewChild('pendingChangesDialog')
+        private pendingChangesDialog: ConfirmDialogComponent;
 
     constructor(
         private router: Router,
@@ -46,6 +53,29 @@ export class PatronComponent implements OnInit, AfterViewInit {
         this.load();
     }
 
+    @HostListener('window:beforeunload', ['$event'])
+    canDeactivate($event?: Event): Promise<boolean> {
+
+        if (this.patronEditor && this.patronEditor.changesPending) {
+
+            // Each warning dialog clears the current "changes are pending"
+            // flag so the user is not presented with the dialog again
+            // unless new changes are made.
+            this.patronEditor.changesPending = false;
+
+            if ($event) { // window.onbeforeunload
+                $event.preventDefault();
+                $event.returnValue = true;
+
+            } else { // tab OR route change.
+                return this.pendingChangesDialog.open().toPromise();
+            }
+
+        } else {
+            return Promise.resolve(true);
+        }
+    }
+
     load() {
         this.loading = true;
         this.fetchSettings()
@@ -102,8 +132,13 @@ export class PatronComponent implements OnInit, AfterViewInit {
         // tab will change with route navigation.
         evt.preventDefault();
 
-        this.patronTab = evt.nextId;
-        this.routeToTab();
+        // Protect against tab changes with dirty data.
+        this.canDeactivate().then(ok => {
+            if (ok) {
+                this.patronTab = evt.nextId;
+                this.routeToTab();
+            }
+        });
     }
 
     // The bills tab has various sub-interfaces.  If the user is already
index 87fecbe..728b86b 100644 (file)
@@ -5,6 +5,7 @@ import {BcSearchComponent} from './bcsearch.component';
 import {PatronResolver} from './resolver.service';
 import {TestPatronPasswordComponent} from './test-password.component';
 import {RegisterPatronComponent} from './register.component';
+import {CanDeactivateGuard} from '@eg/share/util/can-deactivate.guard';
 
 const routes: Routes = [{
     path: '',
@@ -49,7 +50,8 @@ const routes: Routes = [{
   }, {
     path: ':id/:tab',
     component: PatronComponent,
-    resolve: {resolver : PatronResolver}
+    resolve: {resolver : PatronResolver},
+    canDeactivate: [CanDeactivateGuard]
 }];
 
 @NgModule({