LP1904036 Patron editor continued
authorBill Erickson <berickxx@gmail.com>
Wed, 17 Mar 2021 16:19:10 +0000 (12:19 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:27 +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/share/patron/patron.service.ts

index 8b625b9..6a6f19f 100644 (file)
@@ -1,10 +1,26 @@
 <div class="row pb-2 pt-2">
-  <div class="ml-auto">
-    <button class="btn btn-outline-dark" 
-      (click)="printClicked.emit()" i18n>Print</button>
-    <button class="btn btn-outline-dark ml-3" 
-      (click)="saveClicked.emit()" i18n>Save</button>
-    <button class="btn btn-outline-dark ml-3" 
-      (click)="saveCloneClicked.emit()" i18n>Save &amp; Clone</button>
+
+  <div class="col-lg-6">
+    <span class="font-weight-bold" i18n>Show:</span>
+    <button class="btn btn-sm btn-outline-dark ml-2" 
+      [disabled]="showFields == 'required'"
+      (click)="changeFields('required')" i18n>Required Fields</button>
+    <button class="btn btn-sm btn-outline-dark ml-2" 
+      [disabled]="showFields == 'suggested'"
+      (click)="changeFields('suggested')" i18n>Suggested Fields</button>
+    <button class="btn btn-sm btn-outline-dark ml-2" 
+      [disabled]="showFields == 'all'"
+      (click)="changeFields('all')" i18n>All Fields</button>
+  </div>
+
+  <div class="col-lg-6 d-flex">
+    <div class="ml-auto">
+      <button class="btn btn-outline-dark" 
+        (click)="printClicked.emit()" i18n>Print</button>
+      <button class="btn btn-outline-dark ml-2" 
+        (click)="saveClicked.emit()" i18n>Save</button>
+      <button class="btn btn-outline-dark ml-2" 
+        (click)="saveCloneClicked.emit()" i18n>Save &amp; Clone</button>
+    </div>
   </div>
 </div>
index 15c9d1e..60f6dba 100644 (file)
@@ -6,15 +6,21 @@ import {NetService} from '@eg/core/net.service';
 import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronContextService} from './patron.service';
 
+type FieldOptions = 'required' | 'suggested' | 'all';
+
 @Component({
   templateUrl: 'edit-toolbar.component.html',
   selector: 'eg-patron-edit-toolbar'
 })
 export class EditToolbarComponent implements OnInit {
 
+    showFields: FieldOptions = 'all';
+
     @Output() saveClicked: EventEmitter<void> = new EventEmitter<void>();
     @Output() saveCloneClicked: EventEmitter<void> = new EventEmitter<void>();
     @Output() printClicked: EventEmitter<void> = new EventEmitter<void>();
+    @Output() showFieldsChanged:
+      EventEmitter<FieldOptions> = new EventEmitter<FieldOptions>();
 
     constructor(
         private org: OrgService,
@@ -25,5 +31,10 @@ export class EditToolbarComponent implements OnInit {
 
     ngOnInit() {
     }
+
+    changeFields(field: FieldOptions) {
+        this.showFields = field;
+        this.showFieldsChanged.emit(field);
+    }
 }
 
index 7d19146..dfd73eb 100644 (file)
@@ -1,21 +1,28 @@
 
-<span class="font-weight-bold" i18n>Show:</span>
-<a class="ml-2" href="javascript:;'" (click)="showFields='required'" i18n>Required Fields</a>
-<a class="ml-2" href="javascript:;'" (click)="showFields='suggested'" i18n>Suggested Fields</a>
-<a class="ml-2" href="javascript:;'" (click)="showFields='all'" i18n>All Fields</a>
+<div class="row" *ngIf="loading">
+  <div class="col-lg-6 offset-lg-3">
+    <eg-progress-inline></eg-progress-inline>
+  </div>
+</div>
 
 <ng-template #fieldLabel  
   let-cls="cls" let-field="field" let-overrideLabel="overrideLabel">
   <div class="col-lg-3 field-label">
-    <label for="{{cls}}-{{field}}-input">
-      {{getFieldLabel(cls, field, overrideLabel)}}
+    <label for="{{getClass(cls)}}-{{field}}-input">
+      {{getFieldLabel(getClass(cls), field, overrideLabel)}}
     </label>
     <!-- TODO doc links -->
   </div>
 </ng-template>
 
-<ng-template #fieldInput let-cls="cls" let-field="field" let-overrideLabel="overrideLabel"
-  let-type="type" let-disabled="disabled" let-path="path">
+<!-- text / number / email inputs -->
+<ng-template #fieldInput 
+  let-cls="cls" 
+  let-path="path"
+  let-field="field" 
+  let-type="type" 
+  let-disabled="disabled" 
+  let-overrideLabel="overrideLabel">
 
   <ng-container 
     *ngTemplateOutlet="fieldLabel; context: 
     <input 
       type="{{type || 'text'}}"
       class="form-control" 
-      name="{{cls}}-{{field}}-input"
-      id="{{cls}}-{{field}}-input"
+      name="{{getClass(cls)}}-{{field}}-input"
+      id="{{getClass(cls)}}-{{field}}-input"
       [ngModel]="objectFromPath(path)[field]()"
       (ngModelChange)="fieldValueChange(path, field, $event)"
       (change)="fieldMaybeModified(path, field)"
-      [required]="fieldRequired(cls, field)"
-      [pattern]="fieldPattern(cls, field)"
+      [required]="fieldRequired(getClass(cls), field)"
+      [pattern]="fieldPattern(getClass(cls), field)"
       [disabled]="disabled"
     />
   </div>
 </ng-template>
 
+<!-- checkbox inputs -->
+<ng-template #fieldCheckbox 
+  let-cls="cls" 
+  let-path="path"
+  let-field="field" 
+  let-type="type" 
+  let-disabled="disabled" 
+  let-overrideLabel="overrideLabel">
+
+  <ng-container 
+    *ngTemplateOutlet="fieldLabel; context: 
+      {cls: cls, field: field, overrideLabel: overrideLabel}">
+  </ng-container>
+
+  <div class="col-lg-3">
+    <input 
+      type="checkbox"
+      class="form-check-input ml-0"
+      name="{{getClass(cls)}}-{{field}}-input"
+      id="{{getClass(cls)}}-{{field}}-input"
+      [ngModel]="objectFromPath(path)[field]() == 't'"
+      (ngModelChange)="fieldValueChange(path, field, $event)"
+      (change)="fieldMaybeModified(path, field)"
+      [required]="fieldRequired(getClass(cls), field)"
+      [pattern]="fieldPattern(getClass(cls), field)"
+      [disabled]="disabled"
+    />
+  </div>
+</ng-template>
+
+<!-- combobox inputs -->
+<ng-template #fieldCombobox
+  let-cls="cls" 
+  let-path="path"
+  let-field="field" 
+  let-disabled="disabled" 
+  let-entries="entries"
+  let-overrideLabel="overrideLabel">
+
+  <ng-container 
+    *ngTemplateOutlet="fieldLabel; context: 
+      {cls: cls, field: field, overrideLabel: overrideLabel}">
+  </ng-container>
+
+  <div class="col-lg-3">
+      <!-- id="{{getClass(cls)}}-{{field}}-input" TODO combobox id-->
+    <eg-combobox [entries]="entries"
+      name="{{getClass(cls)}}-{{field}}-input"
+      [startId]="getFieldValue(path, field)"
+      (onChange)="
+        fieldValueChange(path, field, $event ? $event.id : null); 
+        fieldMaybeModified(path, field)"
+      [required]="fieldRequired(getClass(cls), field)"
+      [disabled]="disabled">
+    </eg-combobox>
+  </div>
+</ng-template>
+
 <div class="mt-3 striped-rows-even patron-edit-container" *ngIf="patron">
   <div class="row pt-1 pb-1 mt-1">
     <ng-container 
   </div>
   <div class="row pt-1 pb-1 mt-1">
     <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'usrname'}">
+      *ngTemplateOutlet="fieldInput; context: {field: 'usrname'}">
     </ng-container>
   </div>
   <div class="row pt-1 pb-1 mt-1">
     <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'passwd'}">
+      *ngTemplateOutlet="fieldInput; context: {field: 'passwd'}">
     </ng-container>
     <div class="col-lg-3">
       <button class="btn btn-outline-dark" (click)="generatePassword()" i18n>
       </button>
     </div>
   </div>
-  <ul ngbNav #nameNav="ngbNav" class="nav-tabs" [activeId]="nameTab">
+  <ul ngbNav #nameNav="ngbNav" class="nav-tabs" [(activeId)]="nameTab">
     <li ngbNavItem="primary">
       <a ngbNavLink i18n>Primary Name</a>
       <ng-template ngbNavContent>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'prefix'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'prefix'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'first_given_name'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'first_given_name'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'second_given_name'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'second_given_name'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'family_name'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'family_name'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'suffix'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'suffix'}">
           </ng-container>
         </div>
       </ng-template>
       <ng-template ngbNavContent>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'pref_prefix'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'pref_prefix'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'pref_first_given_name'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'pref_first_given_name'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'pref_second_given_name'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'pref_second_given_name'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'pref_family_name'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'pref_family_name'}">
           </ng-container>
         </div>
         <div class="row pt-1 pb-1 mt-1">
           <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {cls: 'au', field: 'pref_suffix'}">
+            *ngTemplateOutlet="fieldInput; context: {field: 'pref_suffix'}">
           </ng-container>
         </div>
       </ng-template>
     </li>
   </ul>
-  <div [ngbNavOutlet]="nameNav"></div>
+  <div class="border rounded p-2" [ngClass]="{
+    'border-primary': nameTab == 'primary', 
+    'border-success': nameTab == 'preferred'}">
+    <b>{{nameTab}}</b>
+    <div [ngbNavOutlet]="nameNav"></div>
+  </div>
   <div class="row pt-1 pb-1 mt-1">
     <ng-container 
-      *ngTemplateOutlet="fieldLabel; context: {cls: 'au', field: 'name_keywords'}">
+      *ngTemplateOutlet="fieldLabel; context: {field: 'name_keywords'}">
     </ng-container>
     <div class="col-lg-3">
       <textarea
         [ngModel]="objectFromPath(null)['name_keywords']()"
         (ngModelChange)="fieldValueChange(null, 'name_keywords', $event)"
         (change)="fieldMaybeModified(null, 'name_keywords')"
-        [required]="fieldRequired(cls, 'name_keywords')"
-        [pattern]="fieldPattern(cls, 'name_keywords')">
+        [required]="fieldRequired('au', 'name_keywords')"
+        [pattern]="fieldPattern('au', 'name_keywords')">
       </textarea>
     </div>
   </div>
     <eg-string #holdAliasString i18n-text text="Holds Alias"></eg-string>
     <ng-container 
       *ngTemplateOutlet="fieldInput; context: 
-        {cls: 'au', field: 'alias', overrideLabel: holdAliasString.text}">
+        {field: 'alias', overrideLabel: holdAliasString.text}">
+    </ng-container>
+  </div>
+  <div class="row pt-1 pb-1 mt-1">
+    <ng-container 
+      *ngTemplateOutlet="fieldLabel; context: {field: 'dob'}">
+    </ng-container>
+    <div class="col-lg-3">
+      <eg-date-select
+        domId="au-dob-input"
+        fieldName="au-dob-input"
+        [initialIso]="patron.dob()"
+        (onChangeAsIso)="
+          fieldValueChange(null, 'dob', $event); 
+          fieldMaybeModified(null, 'dob')"
+        [required]="fieldRequired('au', 'dob')">
+      </eg-date-select>
+    </div>
+  </div>
+  <div class="row pt-1 pb-1 mt-1">
+    <ng-container 
+      *ngTemplateOutlet="fieldCheckbox; context: {field: 'juvenile'}">
+    </ng-container>
+  </div>
+  <div class="row pt-1 pb-1 mt-1">
+    <ng-container 
+      *ngTemplateOutlet="fieldInput; context: {field: 'guardian'}">
+    </ng-container>
+  </div>
+  <div class="row pt-1 pb-1 mt-1">
+    <ng-container 
+      *ngTemplateOutlet="fieldCombobox; 
+        context: {field: 'ident_type', entries: identTypes}">
     </ng-container>
   </div>
 </div>
index 2ccd57c..7bf6098 100644 (file)
@@ -6,6 +6,7 @@ import {IdlService, IdlObject} from '@eg/core/idl.service';
 import {NetService} from '@eg/core/net.service';
 import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronContextService} from './patron.service';
+import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component';
 
 const FLESH_PATRON_FIELDS = {
   flesh: 1,
@@ -28,6 +29,9 @@ export class EditComponent implements OnInit {
     patron: IdlObject;
     changeHandlerNeeded = false;
     nameTab = 'primary';
+    loading = false;
+
+    identTypes: ComboboxEntry[];
 
     constructor(
         private org: OrgService,
@@ -38,12 +42,29 @@ export class EditComponent implements OnInit {
     ) {}
 
     ngOnInit() {
+        this.load();
+    }
+
+    load(): Promise<any> {
+        this.loading = true;
+        return this.loadPatron()
+        .then(_ => this.setIdentTypes())
+        .finally(() => this.loading = false);
+    }
 
+    setIdentTypes(): Promise<any> {
+        return this.patronService.getIdentTypes()
+        .then(types => {
+            this.identTypes = types.map(t => ({id: t.id(), label: t.name()}));
+        });
+    }
+
+    loadPatron(): Promise<any> {
         if (this.patronId) {
-            this.patronService.getById(this.patronId, FLESH_PATRON_FIELDS)
+            return this.patronService.getById(this.patronId, FLESH_PATRON_FIELDS)
             .then(patron => this.patron = patron);
         } else {
-            this.createNewPatron();
+            return Promise.resolve(this.createNewPatron());
         }
     }
 
@@ -68,7 +89,19 @@ export class EditComponent implements OnInit {
             this.idl.classes[idlClass].field_map[field].label;
     }
 
+    // With this, the 'cls' specifier is only needed in the template
+    // when it's not 'au', which is the base/common class.
+    getClass(cls: string): string {
+        return cls || 'au';
+    }
+
+    getFieldValue(path: string, field: string): any {
+        return this.objectFromPath(path)[field]();
+    }
+
     fieldValueChange(path: string, field: string, value: any) {
+        if (typeof value === 'boolean') { value = value ? 't' : 'f'; }
+
         this.changeHandlerNeeded = true;
         this.objectFromPath(path)[field](value);
     }
@@ -80,11 +113,12 @@ export class EditComponent implements OnInit {
 
         this.changeHandlerNeeded = false;
 
-        console.debug(`Modifying field path=${path} field=${field}`);
-
         // check stuff here..
 
         const obj = path ? this.patron[path]() : this.patron;
+        const value = obj[field]();
+
+        console.debug(`Modifying field path=${path} field=${field} value=${value}`);
     }
 
     fieldRequired(idlClass: string, field: string): boolean {
index d0c98bf..ee9cf7d 100644 (file)
@@ -11,6 +11,9 @@ import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.co
 
 @Injectable()
 export class PatronService {
+
+    identTypes: IdlObject[];
+
     constructor(
         private net: NetService,
         private org: OrgService,
@@ -86,5 +89,15 @@ export class PatronService {
             return null;
         });
     }
+
+    getIdentTypes(): Promise<IdlObject[]> {
+        if (this.identTypes) {
+            return Promise.resolve(this.identTypes);
+        }
+
+        return this.pcrud.retrieveAll('cit',
+            {order_by: {cit: ['name']}}, {atomic: true})
+        .toPromise().then(types => this.identTypes = types);
+    }
 }