LP1904036 Barcodes dialog; toolbar integration
authorBill Erickson <berickxx@gmail.com>
Wed, 24 Mar 2021 21:34:21 +0000 (17:34 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:28 +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-toolbar.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html
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/patron.module.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/register.component.html

index 7b6d74b..1655dd8 100644 (file)
@@ -3,24 +3,24 @@
   <div class="col-lg-6">
     <span class="font-weight-bold" i18n>Show:</span>
     <button class="btn btn-sm btn-outline-dark ml-2" 
-      [disabled]="context.editorFieldVisibilityLevel == 2"
+      [disabled]="visibilityLevel == 2"
       (click)="changeFields(2)" i18n>Required Fields</button>
     <button class="btn btn-sm btn-outline-dark ml-2" 
-      [disabled]="context.editorFieldVisibilityLevel == 1"
+      [disabled]="visibilityLevel == 1"
       (click)="changeFields(1)" i18n>Suggested Fields</button>
     <button class="btn btn-sm btn-outline-dark ml-2" 
-      [disabled]="context.editorFieldVisibilityLevel == 0"
+      [disabled]="visibilityLevel == 0"
       (click)="changeFields(0)" i18n>All Fields</button>
   </div>
 
   <div class="col-lg-6 d-flex">
     <div class="ml-auto">
-      <button class="btn btn-outline-dark" 
-        (click)="context.printClicked.emit()" i18n>Print</button>
-      <button class="btn btn-outline-dark ml-2" 
-        (click)="context.saveClicked.emit()" i18n>Save</button>
-      <button class="btn btn-outline-dark ml-2" 
-        (click)="context.saveCloneClicked.emit()" i18n>Save &amp; Clone</button>
+      <button class="btn btn-outline-dark"
+        (click)="printClicked.emit()" i18n>Print</button>
+      <button class="btn btn-outline-dark ml-2" [disabled]="disableSave"
+        (click)="saveClicked.emit()" i18n>Save</button>
+      <button class="btn btn-outline-dark ml-2" [disabled]="disableSave"
+        (click)="saveCloneClicked.emit()" i18n>Save &amp; Clone</button>
     </div>
   </div>
 </div>
index 9a68660..0569ce3 100644 (file)
@@ -4,7 +4,13 @@ import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {OrgService} from '@eg/core/org.service';
 import {NetService} from '@eg/core/net.service';
 import {PatronService} from '@eg/staff/share/patron/patron.service';
-import {PatronContextService, FieldVisibilityLevel} from './patron.service';
+import {PatronContextService} from './patron.service';
+
+export enum VisibilityLevel {
+    ALL_FIELDS = 0,
+    SUGGESTED_FIELDS = 1,
+    REQUIRED_FIELDS = 2
+}
 
 @Component({
   templateUrl: 'edit-toolbar.component.html',
@@ -12,6 +18,15 @@ import {PatronContextService, FieldVisibilityLevel} from './patron.service';
 })
 export class EditToolbarComponent implements OnInit {
 
+    disableSave = false;
+    visibilityLevel: VisibilityLevel = VisibilityLevel.ALL_FIELDS;
+
+    disableSaveStateChanged: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+    saveClicked: EventEmitter<void> = new EventEmitter<void>();
+    saveCloneClicked: EventEmitter<void> = new EventEmitter<void>();
+    printClicked: EventEmitter<void> = new EventEmitter<void>();
+
     constructor(
         private org: OrgService,
         private net: NetService,
@@ -20,10 +35,12 @@ export class EditToolbarComponent implements OnInit {
     ) {}
 
     ngOnInit() {
+        // Emitted by our editor component.
+        this.disableSaveStateChanged.subscribe(d => this.disableSave = d);
     }
 
-    changeFields(v: FieldVisibilityLevel) {
-        this.context.editorFieldVisibilityLevel = v;
+    changeFields(v: VisibilityLevel) {
+        this.visibilityLevel = v;
     }
 }
 
index fa6d555..fe3d34f 100644 (file)
@@ -13,6 +13,7 @@
 
 <eg-patron-secondary-groups [secondaryGroups]="secondaryGroups" #secondaryGroupsDialog>
 </eg-patron-secondary-groups>
+<eg-patron-barcodes #barcodesDialog [patron]="patron"></eg-patron-barcodes>
 
 <div class="row" *ngIf="loading">
   <div class="col-lg-6 offset-lg-3">
       <button class="btn btn-outline-dark" (click)="replaceBarcode()" i18n>
         Replace Barcode
       </button>
-      <button class="btn btn-outline-dark ml-2" (click)="showBarcodes()" i18n>
+      <button class="btn btn-outline-dark ml-2" (click)="barcodesDialog.open()" i18n>
         See All
       </button>
     </div>
index 4b08ab9..4bdffa9 100644 (file)
@@ -8,7 +8,7 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {PatronService} from '@eg/staff/share/patron/patron.service';
-import {PatronContextService, FieldVisibilityLevel} from './patron.service';
+import {PatronContextService} from './patron.service';
 import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component';
 import {DateUtil} from '@eg/share/util/date';
 import {ProfileSelectComponent} from '@eg/staff/share/patron/profile-select.component';
@@ -18,6 +18,7 @@ import {EventService} from '@eg/core/event.service';
 import {PermService} from '@eg/core/perm.service';
 import {SecondaryGroupsDialogComponent} from './secondary-groups.component';
 import {ServerStoreService} from '@eg/core/server-store.service';
+import {EditToolbarComponent, VisibilityLevel} from './edit-toolbar.component';
 
 const COMMON_USER_SETTING_TYPES = [
   'circ.holds_behind_desk',
@@ -94,6 +95,7 @@ export class EditComponent implements OnInit {
     @Input() patronId: number;
     @Input() cloneId: number;
     @Input() stageUsername: string;
+    @Input() toolbar: EditToolbarComponent;
 
     @ViewChild('profileSelect')
         private profileSelect: ProfileSelectComponent;
@@ -118,6 +120,7 @@ export class EditComponent implements OnInit {
     optInSettingTypes: {[name: string]: IdlObject} = {};
     secondaryGroups: IdlObject[];
     expireDate: Date;
+    changesPending = false;
 
     fieldVisibility: {[key: string]: FieldVisibility} = {};
 
@@ -148,9 +151,9 @@ export class EditComponent implements OnInit {
     ngOnInit() {
 
         // Listen for edit-toolbar events
-        this.context.saveClicked.subscribe(_ => this.save());
-        this.context.saveCloneClicked.subscribe(_ => this.saveClone());
-        this.context.printClicked.subscribe(_ => this.printPatron());
+        this.toolbar.saveClicked.subscribe(_ => this.save());
+        this.toolbar.saveCloneClicked.subscribe(_ => this.saveClone());
+        this.toolbar.printClicked.subscribe(_ => this.printPatron());
 
         this.load();
     }
@@ -169,6 +172,9 @@ export class EditComponent implements OnInit {
         .then(_ => this.loading = false);
     }
 
+    setupToolbar() {
+    }
+
     setSurveys(): Promise<any> {
         return this.patronService.getSurveys()
         .then(surveys => this.surveys = surveys);
@@ -388,10 +394,18 @@ export class EditComponent implements OnInit {
         return this.objectFromPath(path, index)[field]();
     }
 
+    adjustSaveSate() {
+        // Avoid responding to any value changes while we are loading
+        if (this.loading) { return; }
 
-    userStatCatChange(cat: IdlObject, entry: ComboboxEntry) {
-        // TODO: set dirty
+        // TODO other checks
 
+        this.changesPending = true;
+        const canSave = document.querySelector('.ng-invalid') === null;
+        this.toolbar.disableSaveStateChanged.emit(!canSave);
+    }
+
+    userStatCatChange(cat: IdlObject, entry: ComboboxEntry) {
         let map = this.patron.stat_cat_entries()
             .filter(m => m.stat_cat() === cat.id())[0];
 
@@ -411,11 +425,13 @@ export class EditComponent implements OnInit {
             map.target_usr(this.patronId);
             this.patron.stat_cat_entries().push(map);
         }
+
+        this.adjustSaveSate();
     }
 
     userSettingChange(name: string, value: any) {
-        // TODO: set dirty
         this.userSettings[name] = value;
+        this.adjustSaveSate();
     }
 
     applySurveyResponse(question: IdlObject, answer: ComboboxEntry) {
@@ -458,8 +474,6 @@ export class EditComponent implements OnInit {
         if (!this.changeHandlerNeeded) { return; } // no changes applied
         this.changeHandlerNeeded = false;
 
-        // TODO: set dirty
-
         const obj = this.objectFromPath(path, index);
         const value = this.getFieldValue(path, index, field);
         obj.ischanged(true); // isnew() supersedes
@@ -474,6 +488,7 @@ export class EditComponent implements OnInit {
                 this.setExpireDate();
                 break;
         }
+        this.adjustSaveSate();
     }
 
     showField(field: string): boolean {
@@ -517,13 +532,17 @@ export class EditComponent implements OnInit {
             this.fieldVisibility[field] = DEFAULT_FIELD_VISIBILITY[field] || 0;
         }
 
-        return this.fieldVisibility[field] >=
-            this.context.editorFieldVisibilityLevel;
+        return this.fieldVisibility[field] >= this.toolbar.visibilityLevel;
     }
 
     fieldRequired(field: string): boolean {
-        // TODO
-        return false;
+
+        // Password field is not required for existing patrons.
+        if (field === 'au.passwd' && !this.patronId) {
+            return false;
+        }
+
+        return this.fieldVisibility[field] == 3;
     }
 
 
@@ -834,6 +853,10 @@ export class EditComponent implements OnInit {
 
     showBarcodes() {
     }
+
+    canSave(): boolean {
+        return document.querySelector('.ng-invalid') === null;
+    }
 }
 
 
index e830e7a..c67b31e 100644 (file)
         <li ngbNavItem="edit" [disabled]="!context.patron">
           <a ngbNavLink i18n>Edit</a>
           <ng-template ngbNavContent>
-            <eg-patron-edit [patronId]="patronId"></eg-patron-edit> 
+            <eg-patron-edit [patronId]="patronId" [toolbar]="editorToolbar">
+            </eg-patron-edit> 
           </ng-template>
         </li>
 
 
       <ng-container *ngIf="patronTab === 'edit'">
         <!-- put the editor toolbar up here in the sticky section -->
-        <eg-patron-edit-toolbar></eg-patron-edit-toolbar>
+        <eg-patron-edit-toolbar #editorToolbar></eg-patron-edit-toolbar>
       </ng-container>
 
     </div><!-- end of sticky top -->
index d155585..d37e618 100644 (file)
@@ -1,4 +1,4 @@
-import {Component, OnInit, AfterViewInit} from '@angular/core';
+import {Component, ViewChild, OnInit, AfterViewInit} 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';
@@ -8,6 +8,7 @@ import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronContextService, BillGridEntry} from './patron.service';
 import {PatronSearch, PatronSearchComponent
     } from '@eg/staff/share/patron/search.component';
+import {EditToolbarComponent} from './edit-toolbar.component';
 
 const MAIN_TABS =
     ['checkout', 'items_out', 'holds', 'bills', 'messages', 'edit', 'search'];
@@ -25,6 +26,11 @@ export class PatronComponent implements OnInit, AfterViewInit {
     showSummary = true;
     loading = true;
 
+    /* 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;
+
     constructor(
         private router: Router,
         private route: ActivatedRoute,
index e4bed3b..439059f 100644 (file)
@@ -28,6 +28,7 @@ import {PatronStatCatsComponent} from './statcats.component';
 import {PatronGroupComponent} from './group.component';
 import {RegisterPatronComponent} from './register.component';
 import {SecondaryGroupsDialogComponent} from './secondary-groups.component';
+import {PatronBarcodesDialogComponent} from './barcodes.component';
 
 @NgModule({
   declarations: [
@@ -47,6 +48,7 @@ import {SecondaryGroupsDialogComponent} from './secondary-groups.component';
     PatronGroupComponent,
     RegisterPatronComponent,
     PatronStatCatsComponent,
+    PatronBarcodesDialogComponent,
     SecondaryGroupsDialogComponent
   ],
   imports: [
index 49be822..bb703bc 100644 (file)
@@ -8,12 +8,6 @@ import {PatronSearch} from '@eg/staff/share/patron/search.component';
 import {StoreService} from '@eg/core/store.service';
 import {CircService, CircDisplayInfo} from '@eg/staff/share/circ/circ.service';
 
-export enum FieldVisibilityLevel {
-    ALL_FIELDS = 0,
-    SUGGESTED_FIELDS = 1,
-    REQUIRED_FIELDS = 2
-}
-
 export interface BillGridEntry extends CircDisplayInfo {
     xact: IdlObject; // mbt
     billingLocation?: string;
@@ -115,16 +109,6 @@ export class PatronContextService {
 
     settingsCache: {[key: string]: any} = {};
 
-    // Emitted by the patron edit toolbar, which is kept as a
-    // separate component so it can be positioned differently.
-    // We just act as a go-between.
-    saveClicked: EventEmitter<void> = new EventEmitter<void>();
-    saveCloneClicked: EventEmitter<void> = new EventEmitter<void>();
-    printClicked: EventEmitter<void> = new EventEmitter<void>();
-
-    editorFieldVisibilityLevel: FieldVisibilityLevel =
-        FieldVisibilityLevel.ALL_FIELDS;
-
     constructor(
         private store: StoreService,
         private net: NetService,
index fd7f283..94284b6 100644 (file)
@@ -3,7 +3,8 @@
 
 
 <div class="sticky-top-with-nav bg-white">
-  <eg-patron-edit-toolbar></eg-patron-edit-toolbar>
-  <eg-patron-edit [stageUsername]="stageUsername" [cloneId]="cloneId">
+  <eg-patron-edit-toolbar #editorToolbar></eg-patron-edit-toolbar>
+  <eg-patron-edit [stageUsername]="stageUsername" 
+    [cloneId]="cloneId" [toolbar]="editorToolbar">
   </eg-patron-edit>
 </div>