LP1904036 Patron editor: addresses
authorBill Erickson <berickxx@gmail.com>
Fri, 19 Mar 2021 22:00:20 +0000 (18:00 -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.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts

index 53d01f4..86d522d 100644 (file)
@@ -46,9 +46,9 @@
       class="form-control" 
       name="{{getClass(args.cls)}}-{{args.field}}-input"
       id="{{getClass(args.cls)}}-{{args.field}}-input"
-      [ngModel]="objectFromPath(args.path)[args.field]()"
-      (ngModelChange)="fieldValueChange(args.path, args.field, $event)"
-      (change)="afterFieldChange(args.path, args.field)"
+      [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)"
       [disabled]="args.disabled"
@@ -64,9 +64,9 @@
       class="form-check-input ml-0"
       name="{{getClass(args.cls)}}-{{args.field}}-input"
       id="{{getClass(args.cls)}}-{{args.field}}-input"
-      [ngModel]="objectFromPath(args.path)[args.field]() == 't'"
-      (ngModelChange)="fieldValueChange(args.path, args.field, $event)"
-      (change)="afterFieldChange(args.path, args.field)"
+      [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)"
       [disabled]="disabled"
     <eg-combobox [entries]="args.entries"
       name="{{getClass(args.cls)}}-{{args.field}}-input"
       domId="{{getClass(args.cls)}}-{{args.field}}-input"
-      [selectedId]="getFieldValue(args.path, args.field)"
+      [selectedId]="getFieldValue(args.path, args.index, args.field)"
       (onChange)="
-        fieldValueChange(args.path, args.field, $event ? $event.id : null); 
-        afterFieldChange(args.path, args.field)"
+        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)"
       [disabled]="args.disabled">
     </eg-combobox>
         class="form-control" 
         name="au-name_keywords-input"
         id="au-name_keywords-input"
-        [ngModel]="objectFromPath(null)['name_keywords']()"
-        (ngModelChange)="fieldValueChange(null, 'name_keywords', $event)"
-        (change)="afterFieldChange(null, 'name_keywords')"
+        [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')">
       </textarea>
         [noMaxWidth]="true"
         [initialIso]="patron.dob()"
         (onChangeAsIso)="
-          fieldValueChange(null, 'dob', $event); 
-          afterFieldChange(null, 'dob')"
+          fieldValueChange(null, null, 'dob', $event); 
+          afterFieldChange(null, null, 'dob')"
         [required]="fieldRequired('au', 'dob')">
       </eg-date-select>
     </div>
         [initialOrgId]="patron.home_ou()"
         [disableOrgs]="cannotHaveUsersOrgs()"
         (onChange)="
-          fieldValueChange(null, 'home_ou', $event ? $event.id() : null); 
-          afterFieldChange(null, 'home_ou')">
+          fieldValueChange(null, null, 'home_ou', $event ? $event.id() : null); 
+          afterFieldChange(null, null, 'home_ou')">
       </eg-org-select>
     </div>
   </div>
         [useDisplayEntries]="true"
         [initialGroupId]="patron.profile()" 
         (profileChange)="
-          fieldValueChange(null, 'profile', $event ? $event.id() : null); 
-          afterFieldChange(null, 'profile')">
+          fieldValueChange(null, null, 'profile', $event ? $event.id() : null); 
+          afterFieldChange(null, null, 'profile')">
       </eg-profile-select>
     </div>
     <div class="col-lg-6">
         class="form-control" 
         name="au-alert_message-input"
         id="au-alert_message-input"
-        [ngModel]="objectFromPath(null)['alert_message']()"
-        (ngModelChange)="fieldValueChange(null, 'alert_message', $event)"
-        (change)="afterFieldChange(null, 'alert_message')"
+        [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')">
       </textarea>
     </div>
   </ng-container>
 
+  <ng-container *ngFor="let addr of patron.addresses(); let index = index">
+    <div class="alert alert-success p-2 m-3 d-flex">
+      <div class="col-lg-3" i18n>Address</div>
+      <div class="col-lg-9">
+        <div class="form-check form-check-inline mr-2">
+          <input class="form-check-input" type="checkbox" 
+            name="addr-{{addr.id()}}-mailing" id="addr-{{addr.id()}}-mailing" 
+            [ngModel]="addr.id() == patron.mailing_address().id()"
+            (ngModelChange)="setAddrType('mailing', addr, $event)"/>
+          <label class="form-check-label" 
+            for="addr-{{addr.id()}}-mailing" i18n>Mailing</label>
+        </div>
+        <div class="form-check form-check-inline mr-2">
+          <input class="form-check-input" type="checkbox" 
+            name="addr-{{addr.id()}}-billing" id="addr-{{addr.id()}}-billing" 
+            [ngModel]="addr.id() == patron.billing_address().id()"
+            (ngModelChange)="setAddrType('billing', addr, $event)"/>
+          <label class="form-check-label" 
+            for="addr-{{addr.id()}}-billing" i18n>Physical</label>
+        </div>
+        <button class="btn btn-danger" (click)="deleteAddr(addr)" i18n>Delete</button>
+      </div>
+    </div>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'address_type', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'post_code', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'street1', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'street2', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'city', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'county', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'state', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldInput, field: 'country', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldCheckbox, field: 'valid', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="fieldRow; context: {args: {template: 
+      fieldCheckbox, field: 'within_city_limits', cls: 'aua', path: 'addresses', index: index}}">
+    </ng-container>
+  </ng-container>
 
-  <div class="alert alert-success p-2 m-3" i18n>Addresses</div>
+  <button class="btn btn-success" (click)="newAddr()" i18n>New Address</button>
 
 </div>
 
index 357dcb8..1e2a9f1 100644 (file)
@@ -272,8 +272,14 @@ export class EditComponent implements OnInit {
         this.patron = patron;
     }
 
-    objectFromPath(path: string): IdlObject {
-        return path ? this.patron[path]() : this.patron;
+    objectFromPath(path: string, index: number): IdlObject {
+        const base = path ? this.patron[path]() : this.patron;
+        if (index === null || index === undefined) {
+            return base;
+        } else {
+            // Some paths lead to an array of objects.
+            return base[index];
+        }
     }
 
     getFieldLabel(idlClass: string, field: string, override?: string): string {
@@ -287,8 +293,8 @@ export class EditComponent implements OnInit {
         return cls || 'au';
     }
 
-    getFieldValue(path: string, field: string): any {
-        return this.objectFromPath(path)[field]();
+    getFieldValue(path: string, index: number, field: string): any {
+        return this.objectFromPath(path, index)[field]();
     }
 
     userSettingChange(name: string, value: any) {
@@ -299,28 +305,27 @@ export class EditComponent implements OnInit {
     // Called as the model changes.
     // This may be called many times before the final value is applied,
     // so avoid any heavy lifting here.  See afterFieldChange();
-    fieldValueChange(path: string, field: string, value: any) {
+    fieldValueChange(path: string, index: number, field: string, value: any) {
         if (typeof value === 'boolean') { value = value ? 't' : 'f'; }
 
         // This can be called in cases where components fire up, even
         // though the actual value on the patron has not changed.
         // Exit early in that case so we don't mark the form as dirty.
-        const oldValue = this.getFieldValue(path, field);
+        const oldValue = this.getFieldValue(path, index, field);
         if (oldValue === value) { return; }
 
         this.changeHandlerNeeded = true;
-        this.objectFromPath(path)[field](value);
+        this.objectFromPath(path, index)[field](value);
     }
 
     // Called after a change operation has completed (e.g. on blur)
-    afterFieldChange(path: string, field: string) {
+    afterFieldChange(path: string, index: number, field: string) {
         if (!this.changeHandlerNeeded) { return; } // no changes applied
         this.changeHandlerNeeded = false;
 
         // TODO: set dirty
 
-        const obj = path ? this.patron[path]() : this.patron;
-        const value = obj[field]();
+        const value = this.getFieldValue(path, index, field);
 
         console.debug(
             `Modifying field path=${path || ''} field=${field} value=${value}`);
@@ -351,12 +356,12 @@ export class EditComponent implements OnInit {
     }
 
     generatePassword() {
-        this.fieldValueChange(null,
+        this.fieldValueChange(null, null,
           'passwd', Math.floor(Math.random() * 9000) + 1000);
 
         // Normally this is called on (blur), but the input is not
         // focused when using the generate button.
-        this.afterFieldChange(null, 'passwd');
+        this.afterFieldChange(null, null, 'passwd');
     }
 
 
@@ -380,8 +385,8 @@ export class EditComponent implements OnInit {
         const nowEpoch = new Date().getTime();
         const newDate = new Date(nowEpoch + (seconds * 1000 /* millis */));
         this.expireDate = newDate;
-        this.fieldValueChange(null, 'profile', newDate.toISOString());
-        this.afterFieldChange(null, 'profile');
+        this.fieldValueChange(null, null, 'profile', newDate.toISOString());
+        this.afterFieldChange(null, null, 'profile');
     }
 
     handleBoolResponse(success: boolean,
@@ -474,17 +479,44 @@ export class EditComponent implements OnInit {
         ).toPromise().then(resp => {
 
             if (Number(resp) === 1) {
-
                 return this.handleBoolResponse(
                     true, 'circ.patron.edit.grplink.success');
 
             } else {
-
                 return this.handleBoolResponse(
                     false, 'circ.patron.edit.grplink.fail',
                     'Failed to change group links: ' + resp);
             }
         });
     }
+
+    // Set the mailing or billing address
+    setAddrType(addrType: string, addr: IdlObject, selected: boolean) {
+        if (selected) {
+            this.patron[addrType + '_address'](addr);
+        } else {
+            // Unchecking mailing/billing means we have to randomly
+            // select another address to fill that role.  Select the
+            // first address in the list (that does not match the
+            // modifed address)
+            this.patron.addresses().some(a => {
+                if (a.id() !== addr.id()) {
+                    this.patron[addrType + '_address'](a);
+                    return true;
+                }
+            });
+        }
+    }
+
+    deleteAddr(addr: IdlObject) {
+    }
+
+    newAddr() {
+        const addr = this.idl.create('aua');
+        addr.isnew(true);
+        addr.valid('t');
+        this.patron.addresses().push(addr);
+    }
 }
 
+