LP1904036 field visibility
authorBill Erickson <berickxx@gmail.com>
Wed, 24 Mar 2021 19:36:18 +0000 (15:36 -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.service.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html

index 222e4d0..7b6d74b 100644 (file)
@@ -3,14 +3,14 @@
   <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>
+      [disabled]="context.editorFieldVisibilityLevel == 2"
+      (click)="changeFields(2)" i18n>Required Fields</button>
     <button class="btn btn-sm btn-outline-dark ml-2" 
-      [disabled]="showFields == 'suggested'"
-      (click)="changeFields('suggested')" i18n>Suggested Fields</button>
+      [disabled]="context.editorFieldVisibilityLevel == 1"
+      (click)="changeFields(1)" i18n>Suggested Fields</button>
     <button class="btn btn-sm btn-outline-dark ml-2" 
-      [disabled]="showFields == 'all'"
-      (click)="changeFields('all')" i18n>All Fields</button>
+      [disabled]="context.editorFieldVisibilityLevel == 0"
+      (click)="changeFields(0)" i18n>All Fields</button>
   </div>
 
   <div class="col-lg-6 d-flex">
index 0c6d9df..9a68660 100644 (file)
@@ -4,7 +4,7 @@ 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, EditorFieldOptions} from './patron.service';
+import {PatronContextService, FieldVisibilityLevel} from './patron.service';
 
 @Component({
   templateUrl: 'edit-toolbar.component.html',
@@ -12,8 +12,6 @@ import {PatronContextService, EditorFieldOptions} from './patron.service';
 })
 export class EditToolbarComponent implements OnInit {
 
-    showFields: EditorFieldOptions = 'all';
-
     constructor(
         private org: OrgService,
         private net: NetService,
@@ -24,9 +22,8 @@ export class EditToolbarComponent implements OnInit {
     ngOnInit() {
     }
 
-    changeFields(field: EditorFieldOptions) {
-        this.showFields = field;
-        this.context.showFieldsChanged.emit(field);
+    changeFields(v: FieldVisibilityLevel) {
+        this.context.editorFieldVisibilityLevel = v;
     }
 }
 
index 3952dda..fa6d555 100644 (file)
@@ -49,8 +49,8 @@
       [ngModel]="objectFromPath(args.path, args.index)[args.field]()"
       (ngModelChange)="fieldValueChange(args.path, args.index, args.field, $event)"
       (change)="afterFieldChange(args.path, args.index, args.field)"
-      [required]="fieldRequired(getClass(args.cls), args.field)"
-      [pattern]="fieldPattern(getClass(args.cls), args.field)"
+      [required]="fieldRequired(getClass(args.cls) + '.' + args.field)"
+      [pattern]="fieldPattern(getClass(args.cls) + '.' + args.field)"
       [disabled]="args.disabled"
     />
   </div>
@@ -67,8 +67,8 @@
       [ngModel]="objectFromPath(args.path, args.index)[args.field]() == 't'"
       (ngModelChange)="fieldValueChange(args.path, args.index, args.field, $event)"
       (change)="afterFieldChange(args.path, args.index, args.field)"
-      [required]="fieldRequired(getClass(args.cls), args.field)"
-      [pattern]="fieldPattern(getClass(args.cls), args.field)"
+      [required]="fieldRequired(getClass(args.cls) + '.' + args.field)"
+      [pattern]="fieldPattern(getClass(args.cls) + '.' + args.field)"
       [disabled]="disabled"
     />
   </div>
@@ -84,7 +84,7 @@
       (onChange)="
         fieldValueChange(args.path, args.index, args.field, $event ? $event.id : null); 
         afterFieldChange(args.path, args.index, args.field)"
-      [required]="fieldRequired(getClass(args.cls), args.field)"
+      [required]="fieldRequired(getClass(args.cls) + '.' + args.field)"
       [disabled]="args.disabled">
     </eg-combobox>
   </div>
 <!-- One row of label + field.  
     Used when a field requires no additional toggles. -->
 <ng-template #fieldRow let-args="args">
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField(args.cls, args.field)">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField(getClass(args.cls) + '.' + args.field)">
     <ng-container *ngTemplateOutlet="fieldLabel; context: {args: args}">
     </ng-container>
     <ng-container *ngTemplateOutlet="args.template; context: {args: args}">
 
 <!-- The List O' Fields -->
 
-<div class="mt-3 striped-rows-even patron-edit-container" *ngIf="patron && !loading">
-  <ng-container *ngTemplateOutlet="fieldRow; context: {args: 
-    {template: fieldInput, field: 'barcode', cls: 'ac', 
-    path: 'card', disabled: !patron.isnew()}}">
-  </ng-container>
+<div class="mt-3 striped-rows-even patron-edit-container form-validated" *ngIf="patron && !loading">
+
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('ac.barcode')">
+    <ng-container *ngTemplateOutlet="fieldLabel; 
+      context: {args: {cls: 'ac', path: 'card', field: 'barcode'}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldInput; context: {args: 
+      {cls: 'ac', path: 'card', field: 'barcode', disabled: !patron.card().isnew()}}">
+    </ng-container>
+    <div class="col-lg-6">
+      <button class="btn btn-outline-dark" (click)="replaceBarcode()" i18n>
+        Replace Barcode
+      </button>
+      <button class="btn btn-outline-dark ml-2" (click)="showBarcodes()" i18n>
+        See All
+      </button>
+    </div>
+  </div>
+
   <ng-container *ngTemplateOutlet="fieldRow; context: 
     {args: {template: fieldInput, field: 'usrname'}}">
   </ng-container>
     <div [ngbNavOutlet]="nameNav"></div>
   </div>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'name_keywords')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.name_keywords')">
     <ng-container 
       *ngTemplateOutlet="fieldLabel; context: {args: {field: 'name_keywords'}}">
     </ng-container>
         [ngModel]="objectFromPath(null, null)['name_keywords']()"
         (ngModelChange)="fieldValueChange(null, null, 'name_keywords', $event)"
         (change)="afterFieldChange(null, args.index, 'name_keywords')"
-        [required]="fieldRequired('au', 'name_keywords')"
-        [pattern]="fieldPattern('au', 'name_keywords')">
+        [required]="fieldRequired('au.name_keywords')"
+        [pattern]="fieldPattern('au.name_keywords')">
       </textarea>
     </div>
   </div>
     {args: {template: fieldInput, field: 'alias', overrideLabel: holdAliasString.text}}">
   </ng-container>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'dob')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.dob')">
     <ng-container 
       *ngTemplateOutlet="fieldLabel; context: {args: {field: 'dob'}}">
     </ng-container>
         (onChangeAsIso)="
           fieldValueChange(null, null, 'dob', $event); 
           afterFieldChange(null, null, 'dob')"
-        [required]="fieldRequired('au', 'dob')">
+        [required]="fieldRequired('au.dob')">
       </eg-date-select>
     </div>
   </div>
   </div>
 
   <ng-container 
-    *ngIf="userSettingTypes['circ.send_email_checkout_receipts'] && showField('au', 'email')">
+    *ngIf="userSettingTypes['circ.send_email_checkout_receipts'] && showField('au.email')">
     <ng-container *ngTemplateOutlet="userSettingsCheckboxRow; context: 
       {args: {settingName: 'circ.send_email_checkout_receipts'}}">
     </ng-container>
     {args: {template: fieldInput, field: 'other_phone'}}">
   </ng-container>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'home_ou')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.home_ou')">
     <ng-container 
       *ngTemplateOutlet="fieldLabel; context: {args: {field: 'home_ou'}}">
     </ng-container>
     </div>
   </div>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'profile')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.profile')">
     <ng-container 
       *ngTemplateOutlet="fieldLabel; context: {args: {field: 'profile'}}">
     </ng-container>
     </div>
   </div>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'expire_date')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.expire_date')">
     <ng-container 
       *ngTemplateOutlet="fieldLabel; context: {args: {field: 'expire_date'}}">
     </ng-container>
         domId="au-expire_date-input"
         fieldName="au-expire_date-input"
         [noMaxWidth]="true"
-        [required]="fieldRequired('au', 'expire_date')"
+        [required]="fieldRequired('au.expire_date')"
         [(ngModel)]="expireDate">
       </eg-date-select>
     </div>
     {args: {template: fieldInput, field: 'claims_never_checked_out_count', type: 'number'}}">
   </ng-container>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'alert_message')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.alert_message')">
     <ng-container 
       *ngTemplateOutlet="fieldLabel; context: {args: {field: 'alert_message'}}">
     </ng-container>
         [ngModel]="objectFromPath(null, null)['alert_message']()"
         (ngModelChange)="fieldValueChange(null, null, 'alert_message', $event)"
         (change)="afterFieldChange(null, null, 'alert_message')"
-        [required]="fieldRequired('au', 'alert_message')"
-        [pattern]="fieldPattern('au', 'alert_message')">
+        [required]="fieldRequired('au.alert_message')"
+        [pattern]="fieldPattern('au.alert_message')">
       </textarea>
     </div>
   </div>
     {args: {settingName: 'opac.default_phone'}}">
   </ng-container>
 
-  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'home_ou')">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au.home_ou')">
     <ng-container *ngTemplateOutlet="userSettingLabel; 
       context: {args: {settingName: 'opac.default_pickup_location'}}">
     </ng-container>
     </ng-container>
     <div class="col-lg-3">
       <div class="form-check form-check-inline mr-2">
-        <input class="form-check-input" type="radio" name="hold-notify-phone" 
+        <input class="form-check-input" type="checkbox" name="hold-notify-phone" 
           id="hold-notify-phone" [(ngModel)]="holdNotifyTypes.phone"/>
         <label class="form-check-label" for="hold-notify-phone" i18n>Phone</label>
       </div>
       <div class="form-check form-check-inline mr-2">
-        <input class="form-check-input" type="radio" name="hold-notify-email" 
+        <input class="form-check-input" type="checkbox" name="hold-notify-email" 
           id="hold-notify-email" [(ngModel)]="holdNotifyTypes.email"/>
         <label class="form-check-label" for="hold-notify-email" i18n>Email</label>
       </div>
-      <div class="form-check form-check-inline mr-2" *ngIf="orgSettings['sms.enable']">
-        <input class="form-check-input" type="radio" name="hold-notify-sms" 
+      <div class="form-check form-check-inline mr-2" 
+        *ngIf="context.settingsCache['sms.enable']">
+        <input class="form-check-input" type="checkbox" name="hold-notify-sms" 
           id="hold-notify-sms" [(ngModel)]="holdNotifyTypes.sms"/>
         <label class="form-check-label" for="hold-notify-sms" i18n>SMS</label>
       </div>
     </div>
   </div>
 
-  <ng-container *ngIf="orgSettings['sms.enable']">
+  <ng-container *ngIf="context.settingsCache['sms.enable']">
 
     <div class="row pt-1 pb-1 mt-1">
       <ng-container *ngTemplateOutlet="userSettingLabel; 
 
   <button class="btn btn-success" (click)="newAddr()" i18n>New Address</button>
 
-  <div class="alert alert-success pt-2 pb-2 mt-3 mb-3 d-flex">
-    <div class="m-auto font-weight-bold" i18n>Statistical Categories</div>
-  </div>
+  <ng-container *ngIf="showField('stat_cats')">
+    <div class="alert alert-success pt-2 pb-2 mt-3 mb-3 d-flex">
+      <div class="m-auto font-weight-bold" i18n>Statistical Categories</div>
+    </div>
 
-  <div class="row pt-1 pb-1 mt-1" *ngFor="let stat of statCats">
-   <div class="col-lg-3 field-label">
-    <label for="asc-{{stat.cat.id()}}-input">{{stat.cat.name()}}</label>
-   </div>
-   <div class="col-lg-3">
-    <eg-combobox
-      domId="asc-{{stat.cat.id()}}-input"
-      name="asc-{{stat.cat.id()}}-input"
-      [entries]="stat.entries"
-      [allowFreeText]="stat.cat.allow_freetext() == 1"
-      [(ngModel)]="userStatCats[stat.cat.id()]"
-      (onChange)="userStatCatChange(stat.cat, $event)">
-    </eg-combobox>
-   </div>
-  </div>
+    <div class="row pt-1 pb-1 mt-1" *ngFor="let stat of statCats">
+     <div class="col-lg-3 field-label">
+      <label for="asc-{{stat.cat.id()}}-input">{{stat.cat.name()}}</label>
+     </div>
+     <div class="col-lg-3">
+      <eg-combobox
+        domId="asc-{{stat.cat.id()}}-input"
+        name="asc-{{stat.cat.id()}}-input"
+        [entries]="stat.entries"
+        [required]="stat.cat.required() == 1"
+        [allowFreeText]="stat.cat.allow_freetext() == 1"
+        [(ngModel)]="userStatCats[stat.cat.id()]"
+        (onChange)="userStatCatChange(stat.cat, $event)">
+      </eg-combobox>
+     </div>
+    </div>
+  </ng-container>
 
-  <div class="alert alert-success pt-2 pb-2 mt-2 mb-2 d-flex">
-    <div class="m-auto font-weight-bold" i18n>Surveys</div>
-  </div>
 
-  <div class="card pt-1 pb-1 mt-1" *ngFor="let survey of surveys">
-    <div class="card-header">{{survey.name()}}</div>
-    <div class="card-body">
-      <div class="row pt-1 pb-1 mt-1" *ngFor="let question of survey.questions()">
-        <div class="col-lg-3 field-label">
-          <label for="asvq-{{question.id()}}-input">{{question.question()}}</label>
-        </div>
-        <div class="col-lg-3">
-          <eg-combobox
-            domId="asvq-{{question.id()}}-input"
-            name="asvq-{{question.id()}}-input"
-            [entries]="surveyQuestionAnswers(question)"
-            (onChange)="applySurveyResponse(question, $event)">
-          </eg-combobox>
+  <ng-container *ngIf="showField('surveys')">
+    <div class="alert alert-success pt-2 pb-2 mt-2 mb-2 d-flex">
+      <div class="m-auto font-weight-bold" i18n>Surveys</div>
+    </div>
+    <div class="card pt-1 pb-1 mt-1" *ngFor="let survey of surveys">
+      <div class="card-header">{{survey.name()}}</div>
+      <div class="card-body">
+        <div class="row pt-1 pb-1 mt-1" *ngFor="let question of survey.questions()">
+          <div class="col-lg-3 field-label">
+            <label for="asvq-{{question.id()}}-input">{{question.question()}}</label>
+          </div>
+          <div class="col-lg-3">
+            <eg-combobox
+              domId="asvq-{{question.id()}}-input"
+              name="asvq-{{question.id()}}-input"
+              [entries]="surveyQuestionAnswers(question)"
+              (onChange)="applySurveyResponse(question, $event)">
+            </eg-combobox>
+          </div>
         </div>
       </div>
     </div>
-  </div>
+  </ng-container>
 
 </div>
 
index ee0676d..4b08ab9 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, EditorFieldOptions} from './patron.service';
+import {PatronContextService, FieldVisibilityLevel} 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';
@@ -29,12 +29,6 @@ const COMMON_USER_SETTING_TYPES = [
   'opac.default_sms_notify'
 ];
 
-// Duplicate these settings in resolver.service so they can be
-// fetched/cached with the original batch (fewer net calls).
-const ORG_SETTING_TYPES = [
-    'sms.enable'
-]
-
 const PERMS_NEEDED = [
     'EDIT_SELF_IN_CLIENT',
     'UPDATE_USER',
@@ -47,6 +41,44 @@ const PERMS_NEEDED = [
     'UPDATE_PATRON_PRIMARY_CARD'
 ];
 
+enum FieldVisibility {
+    REQUIRED = 3,
+    VISIBLE = 2,
+    SUGGESTED = 1
+}
+
+// 3 == value universally required
+// 2 == field is visible by default
+// 1 == field is suggested by default
+const DEFAULT_FIELD_VISIBILITY = {
+    'ac.barcode': FieldVisibility.REQUIRED,
+    'au.usrname': FieldVisibility.REQUIRED,
+    'au.passwd': FieldVisibility.REQUIRED,
+    'au.first_given_name': FieldVisibility.REQUIRED,
+    'au.family_name': FieldVisibility.REQUIRED,
+    'au.pref_first_given_name': FieldVisibility.VISIBLE,
+    'au.pref_family_name': FieldVisibility.VISIBLE,
+    'au.ident_type': FieldVisibility.REQUIRED,
+    'au.ident_type2': FieldVisibility.VISIBLE,
+    'au.home_ou': FieldVisibility.REQUIRED,
+    'au.profile': FieldVisibility.REQUIRED,
+    'au.expire_date': FieldVisibility.REQUIRED,
+    'au.net_access_level': FieldVisibility.REQUIRED,
+    'aua.address_type': FieldVisibility.REQUIRED,
+    'aua.post_code': FieldVisibility.REQUIRED,
+    'aua.street1': FieldVisibility.REQUIRED,
+    'aua.street2': FieldVisibility.VISIBLE,
+    'aua.city': FieldVisibility.REQUIRED,
+    'aua.county': FieldVisibility.VISIBLE,
+    'aua.state': FieldVisibility.VISIBLE,
+    'aua.country': FieldVisibility.REQUIRED,
+    'aua.valid': FieldVisibility.VISIBLE,
+    'aua.within_city_limits': FieldVisibility.VISIBLE,
+    'stat_cats': FieldVisibility.SUGGESTED,
+    'surveys': FieldVisibility.SUGGESTED,
+    'au.name_keywords': FieldVisibility.SUGGESTED
+};
+
 interface StatCat {
     cat: IdlObject;
     entries: ComboboxEntry[];
@@ -79,7 +111,6 @@ export class EditComponent implements OnInit {
     smsCarriers: ComboboxEntry[];
     identTypes: ComboboxEntry[];
     inetLevels: ComboboxEntry[];
-    orgSettings: {[name: string]: any} = {};
     statCats: StatCat[] = [];
     userStatCats: {[statId: number]: ComboboxEntry} = {};
     userSettings: {[name: string]: any} = {};
@@ -87,7 +118,8 @@ export class EditComponent implements OnInit {
     optInSettingTypes: {[name: string]: IdlObject} = {};
     secondaryGroups: IdlObject[];
     expireDate: Date;
-    visibleFields: EditorFieldOptions;
+
+    fieldVisibility: {[key: string]: FieldVisibility} = {};
 
     // All locations we have the specified permissions
     permOrgs: {[name: string]: number[]};
@@ -119,7 +151,6 @@ export class EditComponent implements OnInit {
         this.context.saveClicked.subscribe(_ => this.save());
         this.context.saveCloneClicked.subscribe(_ => this.saveClone());
         this.context.printClicked.subscribe(_ => this.printPatron());
-        this.context.showFieldsChanged.subscribe(f => this.visibleFields = f);
 
         this.load();
     }
@@ -134,7 +165,6 @@ export class EditComponent implements OnInit {
         .then(_ => this.setIdentTypes())
         .then(_ => this.setInetLevels())
         .then(_ => this.setOptInSettings())
-        .then(_ => this.setOrgSettings())
         .then(_ => this.setSmsCarriers())
         .then(_ => this.loading = false);
     }
@@ -167,13 +197,8 @@ export class EditComponent implements OnInit {
         });
     }
 
-    setOrgSettings(): Promise<any> {
-        return this.serverStore.getItemBatch(ORG_SETTING_TYPES)
-        .then(settings => this.orgSettings = settings);
-    }
-
     setSmsCarriers(): Promise<any> {
-        if (!this.orgSettings['sms.enable']) {
+        if (!this.context.settingsCache['sms.enable']) {
             return Promise.resolve();
         }
 
@@ -326,12 +351,14 @@ export class EditComponent implements OnInit {
         patron.isnew(true);
         patron.addresses([]);
         patron.settings([]);
+        patron.cards([]);
         patron.waiver_entries([]);
 
         const card = this.idl.create('ac');
         card.isnew(true);
         card.usr(-1);
         patron.card(card);
+        patron.cards().push(card);
 
         this.patron = patron;
     }
@@ -449,18 +476,58 @@ export class EditComponent implements OnInit {
         }
     }
 
-    showField(idlClass: string, field: string): boolean {
-      // TODO
-      return true;
+    showField(field: string): boolean {
+
+        if (this.fieldVisibility[field] === undefined) {
+            // Settings have not yet been applied for this field.
+            // Calculate them now.
+
+            // The preferred name fields use the primary name field settings
+            let settingKey = field;
+            let altName = false;
+            if (field.match(/^au.alt_/)) {
+                altName = true;
+                settingKey = field.replace(/alt_/, '');
+            }
+
+            const required = `ui.patron.edit.${settingKey}.require`;
+            const show = `ui.patron.edit.${settingKey}.show`;
+            const suggest = `ui.patron.edit.${settingKey}.suggest`;
+
+            if (this.context.settingsCache[required]) {
+                if (altName) {
+                    // Preferred name fields are never required.
+                    this.fieldVisibility[field] = FieldVisibility.VISIBLE;
+                } else {
+                    this.fieldVisibility[field] = FieldVisibility.REQUIRED;
+                }
+
+            } else if (this.context.settingsCache[show]) {
+                this.fieldVisibility[field] = FieldVisibility.VISIBLE;
+
+            } else if (this.context.settingsCache[suggest]) {
+                this.fieldVisibility[field] = FieldVisibility.SUGGESTED;
+            }
+        }
+
+        if (this.fieldVisibility[field] == undefined) {
+            // No org settings were applied above.  Use the default
+            // settings if present or assume the field has no
+            // visibility flags applied.
+            this.fieldVisibility[field] = DEFAULT_FIELD_VISIBILITY[field] || 0;
+        }
+
+        return this.fieldVisibility[field] >=
+            this.context.editorFieldVisibilityLevel;
     }
 
-    fieldRequired(idlClass: string, field: string): boolean {
+    fieldRequired(field: string): boolean {
         // TODO
         return false;
     }
 
 
-    fieldPattern(idlClass: string, field: string): string {
+    fieldPattern(field: string): string {
         // TODO
         return null;
     }
@@ -749,6 +816,24 @@ export class EditComponent implements OnInit {
     printPatron() {
         // TODO
     }
+
+    replaceBarcode() {
+        // Disable current card
+        this.patron.card().active('f');
+        this.patron.card().ischanged(true);
+
+        const card = this.idl.create('ac');
+        card.isnew(true);
+        card.id(this.autoId--);
+        card.usr(this.patron.id());
+        card.active('t');
+
+        this.patron.card(card);
+        this.patron.cards().push(card);
+    }
+
+    showBarcodes() {
+    }
 }
 
 
index 7508dc8..49be822 100644 (file)
@@ -8,8 +8,11 @@ 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 type EditorFieldOptions = 'required' | 'suggested' | 'all';
-
+export enum FieldVisibilityLevel {
+    ALL_FIELDS = 0,
+    SUGGESTED_FIELDS = 1,
+    REQUIRED_FIELDS = 2
+}
 
 export interface BillGridEntry extends CircDisplayInfo {
     xact: IdlObject; // mbt
@@ -110,14 +113,17 @@ export class PatronContextService {
     // These should persist tab changes
     checkouts: CircGridEntry[] = [];
 
+    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>();
-    showFieldsChanged: EventEmitter<EditorFieldOptions> =
-        new EventEmitter<EditorFieldOptions>();
+
+    editorFieldVisibilityLevel: FieldVisibilityLevel =
+        FieldVisibilityLevel.ALL_FIELDS;
 
     constructor(
         private store: StoreService,
index 90cf9df..a781d55 100644 (file)
@@ -45,8 +45,91 @@ export class PatronResolver implements Resolve<Promise<any[]>> {
           'circ.disable_patron_credit',
           'sms.enable',
           'ui.patron.registration.require_address',
-          'credit.processor.default'
+          'credit.processor.default',
+          'global.password_regex',
+          'global.juvenile_age_threshold',
+          'patron.password.use_phone',
+          'ui.patron.default_inet_access_level',
+          'ui.patron.default_ident_type',
+          'ui.patron.default_country',
+          'ui.patron.registration.require_address',
+          'circ.holds.behind_desk_pickup_supported',
+          'circ.patron_edit.clone.copy_address',
+          'circ.privacy_waiver',
+          'ui.patron.edit.au.prefix.require',
+          'ui.patron.edit.au.prefix.show',
+          'ui.patron.edit.au.prefix.suggest',
+          'ui.patron.edit.ac.barcode.regex',
+          'ui.patron.edit.au.second_given_name.show',
+          'ui.patron.edit.au.second_given_name.suggest',
+          'ui.patron.edit.au.suffix.show',
+          'ui.patron.edit.au.suffix.suggest',
+          'ui.patron.edit.au.alias.show',
+          'ui.patron.edit.au.alias.suggest',
+          'ui.patron.edit.au.dob.require',
+          'ui.patron.edit.au.dob.show',
+          'ui.patron.edit.au.dob.suggest',
+          'ui.patron.edit.au.dob.calendar',
+          'ui.patron.edit.au.dob.example',
+          'ui.patron.edit.au.juvenile.show',
+          'ui.patron.edit.au.juvenile.suggest',
+          'ui.patron.edit.au.ident_value.show',
+          'ui.patron.edit.au.ident_value.require',
+          'ui.patron.edit.au.ident_value.suggest',
+          'ui.patron.edit.au.ident_value2.show',
+          'ui.patron.edit.au.ident_value2.suggest',
+          'ui.patron.edit.au.email.require',
+          'ui.patron.edit.au.email.show',
+          'ui.patron.edit.au.email.suggest',
+          'ui.patron.edit.au.email.regex',
+          'ui.patron.edit.au.email.example',
+          'ui.patron.edit.au.day_phone.require',
+          'ui.patron.edit.au.day_phone.show',
+          'ui.patron.edit.au.day_phone.suggest',
+          'ui.patron.edit.au.day_phone.regex',
+          'ui.patron.edit.au.day_phone.example',
+          'ui.patron.edit.au.evening_phone.require',
+          'ui.patron.edit.au.evening_phone.show',
+          'ui.patron.edit.au.evening_phone.suggest',
+          'ui.patron.edit.au.evening_phone.regex',
+          'ui.patron.edit.au.evening_phone.example',
+          'ui.patron.edit.au.other_phone.require',
+          'ui.patron.edit.au.other_phone.show',
+          'ui.patron.edit.au.other_phone.suggest',
+          'ui.patron.edit.au.other_phone.regex',
+          'ui.patron.edit.au.other_phone.example',
+          'ui.patron.edit.phone.regex',
+          'ui.patron.edit.phone.example',
+          'ui.patron.edit.au.active.show',
+          'ui.patron.edit.au.active.suggest',
+          'ui.patron.edit.au.barred.show',
+          'ui.patron.edit.au.barred.suggest',
+          'ui.patron.edit.au.master_account.show',
+          'ui.patron.edit.au.master_account.suggest',
+          'ui.patron.edit.au.claims_returned_count.show',
+          'ui.patron.edit.au.claims_returned_count.suggest',
+          'ui.patron.edit.au.claims_never_checked_out_count.show',
+          'ui.patron.edit.au.claims_never_checked_out_count.suggest',
+          'ui.patron.edit.au.alert_message.show',
+          'ui.patron.edit.au.alert_message.suggest',
+          'ui.patron.edit.aua.post_code.regex',
+          'ui.patron.edit.aua.post_code.example',
+          'ui.patron.edit.aua.county.require',
+          'ui.patron.edit.au.guardian.show',
+          'ui.patron.edit.au.guardian.suggest',
+          'ui.patron.edit.guardian_required_for_juv',
+          'format.date',
+          'ui.patron.edit.default_suggested',
+          'opac.barcode_regex',
+          'opac.username_regex',
+          'sms.enable',
+          'ui.patron.edit.aua.state.require',
+          'ui.patron.edit.aua.state.suggest',
+          'ui.patron.edit.aua.state.show',
+          'ui.admin.work_log.max_entries',
+          'ui.admin.patron_log.max_entries'
         ]).then(settings => {
+            this.context.settingsCache = settings;
             this.context.noTallyClaimsReturned =
                 settings['circ.do_not_tally_claims_returned'];
             this.context.tallyLost = settings['circ.tally_lost'];
index c2352c6..0a523db 100644 (file)
@@ -35,7 +35,7 @@
   <div class="row mb-1 alert alert-danger p-0" 
     *ngFor="let pen of context.alerts.alertPenalties">
     <div class="col-lg-9"
-      title="{{pen.standing_penalty().name()">
+      title="{{pen.standing_penalty().name()}}">
       {{pen.note() || pen.standing_penalty().label()}}
     </div>
     <div class="col-lg-3">{{pen.set_date() | date:'shortDate'}}</div>