LP1904036 Patron editor continued
authorBill Erickson <berickxx@gmail.com>
Wed, 17 Mar 2021 21:07:14 +0000 (17:07 -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
Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts
Open-ILS/src/eg2/src/app/staff/share/patron/profile-select.component.html
Open-ILS/src/eg2/src/app/staff/share/patron/profile-select.component.ts

index b04a12c..6481774 100644 (file)
   </div>
 </div>
 
-<ng-template #fieldLabel  
-  let-cls="cls" let-field="field" let-overrideLabel="overrideLabel">
+<!-- IDL-generated field labels.  Override with args.overrideLabel -->
+<ng-template #fieldLabel let-args="args">
   <div class="col-lg-3 field-label">
-    <label for="{{getClass(cls)}}-{{field}}-input">
-      {{getFieldLabel(getClass(cls), field, overrideLabel)}}
+    <label for="{{getClass(args.cls)}}-{{args.field}}-input">
+      {{getFieldLabel(getClass(args.cls), args.field, args.overrideLabel)}}
     </label>
     <!-- TODO doc links -->
   </div>
 </ng-template>
 
 <!-- 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: 
-      {cls: cls, field: field, overrideLabel: overrideLabel}">
-  </ng-container>
-
+<ng-template #fieldInput let-args="args">
   <div class="col-lg-3">
     <input 
-      type="{{type || 'text'}}"
+      type="{{args.type || 'text'}}"
       class="form-control" 
-      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(getClass(cls), field)"
-      [pattern]="fieldPattern(getClass(cls), field)"
-      [disabled]="disabled"
+      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)="postFieldChange(args.path, args.field)"
+      [required]="fieldRequired(getClass(args.cls), args.field)"
+      [pattern]="fieldPattern(getClass(args.cls), args.field)"
+      [disabled]="args.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>
-
+<ng-template #fieldCheckbox let-args="args">
   <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)"
+      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)="postFieldChange(args.path, args.field)"
+      [required]="fieldRequired(getClass(args.cls), args.field)"
+      [pattern]="fieldPattern(getClass(args.cls), args.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>
-
+<ng-template #fieldCombobox let-args="args">
   <div class="col-lg-3">
-    <eg-combobox [entries]="entries"
-      name="{{getClass(cls)}}-{{field}}-input"
-      domId="{{getClass(cls)}}-{{field}}-input"
-      [startId]="getFieldValue(path, field)"
+    <eg-combobox [entries]="args.entries"
+      name="{{getClass(args.cls)}}-{{args.field}}-input"
+      domId="{{getClass(args.cls)}}-{{args.field}}-input"
+      [startId]="getFieldValue(args.path, args.field)"
       (onChange)="
-        fieldValueChange(path, field, $event ? $event.id : null); 
-        fieldMaybeModified(path, field)"
-      [required]="fieldRequired(getClass(cls), field)"
-      [disabled]="disabled">
+        fieldValueChange(args.path, args.field, $event ? $event.id : null); 
+        postFieldChange(args.path, args.field)"
+      [required]="fieldRequired(getClass(args.cls), args.field)"
+      [disabled]="args.disabled">
     </eg-combobox>
   </div>
 </ng-template>
 
-<div class="mt-3 striped-rows-even patron-edit-container" *ngIf="patron">
+<!-- like fieldRow below, but for user settings checkboxes -->
+<ng-template #userSettingsCheckboxRow let-args="args">
   <div class="row pt-1 pb-1 mt-1">
-    <ng-container 
-      *ngTemplateOutlet="fieldInput; context: 
-        {cls: 'ac', field: 'barcode', path: 'card', disabled: !patron.isnew()}">
-    </ng-container>
+    <div class="col-lg-3 field-label">
+      <label for="cust-{{args.settingName}}-input">
+        {{userSettingTypes[args.settingName].label()}}
+      </label>
+    </div>
+    <div class="col-lg-3">
+      <input 
+        type="checkbox"
+        class="form-check-input ml-0"
+        name="cust-{{args.settingName}}-input"
+        id="cust-{{args.setingName}-input"
+        [ngModel]="userSettings[args.settingName]"
+        (ngModelChange)="userSettingChange(args.settingName, $event)"
+        [disabled]="args.disabled"
+      />
+    </div>
   </div>
-  <div class="row pt-1 pb-1 mt-1">
-    <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {field: 'usrname'}">
+</ng-template>
+
+<!-- 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)">
+    <ng-container *ngTemplateOutlet="fieldLabel; context: {args: args}">
+    </ng-container>
+    <ng-container *ngTemplateOutlet="args.template; context: {args: args}">
     </ng-container>
   </div>
+</ng-template>
+
+
+<!-- The List O' Fields -->
+
+<div class="mt-3 striped-rows-even patron-edit-container" *ngIf="patron">
+  <ng-container *ngTemplateOutlet="fieldRow; context: {args: 
+    {template: fieldInput, field: 'barcode', cls: 'ac', 
+    path: 'card', disabled: !patron.isnew()}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'usrname'}}">
+  </ng-container>
   <div class="row pt-1 pb-1 mt-1">
+    <ng-container *ngTemplateOutlet="fieldLabel; context: {args: {field: 'passwd'}}">
+    </ng-container>
     <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {field: 'passwd'}">
+      *ngTemplateOutlet="fieldInput; context: {args: {field: 'passwd'}}">
     </ng-container>
     <div class="col-lg-3">
       <button class="btn btn-outline-dark" (click)="generatePassword()" i18n>
       </button>
     </div>
   </div>
+
+  <!-- Name / Preferred Name Tabs -->
   <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: {field: 'prefix'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'first_given_name'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'second_given_name'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'family_name'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'suffix'}">
-          </ng-container>
-        </div>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'prefix'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'first_given_name'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'second_given_name'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'family_name'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'suffix'}}">
+        </ng-container>
       </ng-template>
     </li>
     <li ngbNavItem="preferred">
       <a ngbNavLink i18n>Preferred Name</a>
       <ng-template ngbNavContent>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'pref_prefix'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *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: {field: 'pref_second_given_name'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'pref_family_name'}">
-          </ng-container>
-        </div>
-        <div class="row pt-1 pb-1 mt-1">
-          <ng-container 
-            *ngTemplateOutlet="fieldInput; context: {field: 'pref_suffix'}">
-          </ng-container>
-        </div>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'pref_prefix'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'pref_first_given_name'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'pref_second_given_name'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'pref_family_name'}}">
+        </ng-container>
+        <ng-container *ngTemplateOutlet="fieldRow; context: 
+          {args: {template: fieldInput, field: 'pref_suffix'}}">
+        </ng-container>
       </ng-template>
     </li>
   </ul>
+
+  <!-- Container div for name / pref name tabs -->
   <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">
+
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'name_keywords')">
     <ng-container 
-      *ngTemplateOutlet="fieldLabel; context: {field: 'name_keywords'}">
+      *ngTemplateOutlet="fieldLabel; context: {args: {field: 'name_keywords'}}">
     </ng-container>
     <div class="col-lg-3">
       <textarea
         id="au-name_keywords-input"
         [ngModel]="objectFromPath(null)['name_keywords']()"
         (ngModelChange)="fieldValueChange(null, 'name_keywords', $event)"
-        (change)="fieldMaybeModified(null, 'name_keywords')"
+        (change)="postFieldChange(null, 'name_keywords')"
         [required]="fieldRequired('au', 'name_keywords')"
         [pattern]="fieldPattern('au', 'name_keywords')">
       </textarea>
     </div>
   </div>
-  <div class="row pt-1 pb-1 mt-1">
-    <!-- example of overriding a field label -->
-    <eg-string #holdAliasString i18n-text text="Holds Alias"></eg-string>
-    <ng-container 
-      *ngTemplateOutlet="fieldInput; context: 
-        {field: 'alias', overrideLabel: holdAliasString.text}">
-    </ng-container>
-  </div>
-  <div class="row pt-1 pb-1 mt-1">
+
+  <!-- example of overriding a field label -->
+  <eg-string #holdAliasString i18n-text text="Holds Alias"></eg-string>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'alias', overrideLabel: holdAliasString.text}}">
+  </ng-container>
+
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'dob')">
     <ng-container 
-      *ngTemplateOutlet="fieldLabel; context: {field: 'dob'}">
+      *ngTemplateOutlet="fieldLabel; context: {args: {field: 'dob'}}">
     </ng-container>
     <div class="col-lg-3">
       <eg-date-select
         [initialIso]="patron.dob()"
         (onChangeAsIso)="
           fieldValueChange(null, 'dob', $event); 
-          fieldMaybeModified(null, 'dob')"
+          postFieldChange(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 class="row pt-1 pb-1 mt-1">
-    <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {field: 'ident_value'}">
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldCheckbox, field: 'juvenile'}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'guardian'}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldCombobox, field: 'ident_type', entries: identTypes}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'ident_value'}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldCombobox, field: 'ident_type2', entries: identTypes}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'ident_value2'}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'email', type: 'email'}}">
+  </ng-container>
+  <ng-container 
+    *ngIf="userSettingTypes['circ.send_email_checkout_receipts'] && showField('au', 'email')">
+    <ng-container *ngTemplateOutlet="userSettingsCheckboxRow; context: 
+      {args: {settingName: 'circ.send_email_checkout_receipts'}}">
     </ng-container>
-  </div>
-  <div class="row pt-1 pb-1 mt-1">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'day_phone'}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'evening_phone'}}">
+  </ng-container>
+  <ng-container *ngTemplateOutlet="fieldRow; context: 
+    {args: {template: fieldInput, field: 'other_phone'}}">
+  </ng-container>
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'home_ou')">
     <ng-container 
-      *ngTemplateOutlet="fieldCombobox; 
-        context: {field: 'ident_type2', entries: identTypes}">
+      *ngTemplateOutlet="fieldLabel; context: {args: {field: 'home_ou'}}">
     </ng-container>
+    <div class="col-lg-3">
+      <eg-org-select
+        domId="au-home_ou-input"
+        fieldName="au-home_ou-input"
+        [initialOrgId]="patron.home_ou()"
+        [disableOrgs]="cannotHaveUsersOrgs()"
+        (onChange)="
+          fieldValueChange(null, 'home_ou', $event ? $event.id() : null); 
+          postFieldChange(null, 'home_ou')">
+      </eg-org-select>
+    </div>
   </div>
-  <div class="row pt-1 pb-1 mt-1">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'profile')">
     <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {field: 'ident_value2'}">
+      *ngTemplateOutlet="fieldLabel; context: {args: {field: 'profile'}}">
     </ng-container>
+    <div class="col-lg-3">
+      <eg-profile-select #profileSelect 
+        [useDisplayEntries]="true"
+        [initialGroupId]="patron.profile()" 
+        (profileChange)="
+          fieldValueChange(null, 'profile', $event ? $event.id() : null); 
+          postFieldChange(null, 'profile')">
+      </eg-profile-select>
+    </div>
   </div>
-  <div class="row pt-1 pb-1 mt-1">
+  <div class="row pt-1 pb-1 mt-1" *ngIf="showField('au', 'expire_date')">
     <ng-container 
-      *ngTemplateOutlet="fieldInput; context: {field: 'email', type: 'email'}">
+      *ngTemplateOutlet="fieldLabel; context: {args: {field: 'expire_date'}}">
     </ng-container>
+    <div class="col-lg-3">
+      <eg-date-select
+        domId="au-expire_date-input"
+        fieldName="au-expire_date-input"
+        [required]="fieldRequired('au', 'expire_date')"
+        [(ngModel)]="expireDate">
+      </eg-date-select>
+    </div>
+    <div class="col-lg-3">
+      <button class="btn btn-outline-dark" (click)="setExpireDate()" i18n>
+        Update Expire Date
+      </button>
+    </div>
   </div>
 </div>
 
index c6c3a55..884ed01 100644 (file)
@@ -1,4 +1,4 @@
-import {Component, OnInit, Input} from '@angular/core';
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
 import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {OrgService} from '@eg/core/org.service';
@@ -9,6 +9,8 @@ import {PcrudService} from '@eg/core/pcrud.service';
 import {PatronService} from '@eg/staff/share/patron/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';
 
 const COMMON_USER_SETTING_TYPES = [
   'circ.holds_behind_desk',
@@ -23,7 +25,7 @@ const COMMON_USER_SETTING_TYPES = [
 const FLESH_PATRON_FIELDS = {
   flesh: 1,
   flesh_fields: {
-    au: ['card', 'mailing_address', 'billing_address', 'addresses']
+    au: ['card', 'mailing_address', 'billing_address', 'addresses', 'settings']
   }
 };
 
@@ -38,14 +40,19 @@ export class EditComponent implements OnInit {
     @Input() cloneId: number;
     @Input() stageUsername: string;
 
+    @ViewChild('profileSelect') private profileSelect: ProfileSelectComponent;
+
     patron: IdlObject;
     changeHandlerNeeded = false;
     nameTab = 'primary';
     loading = false;
 
     identTypes: ComboboxEntry[];
+    profileGroups: ComboboxEntry[];
+    userSettings: {[name: string]: any} = {};
     userSettingTypes: {[name: string]: IdlObject} = {};
     optInSettingTypes: {[name: string]: IdlObject} = {};
+    expireDate: Date;
 
     constructor(
         private org: OrgService,
@@ -105,18 +112,31 @@ export class EditComponent implements OnInit {
                 }
             });
         });
-    };
-
+    }
 
     loadPatron(): Promise<any> {
         if (this.patronId) {
             return this.patronService.getById(this.patronId, FLESH_PATRON_FIELDS)
-            .then(patron => this.patron = patron);
+            .then(patron => {
+                this.patron = patron;
+                this.absorbPatronData();
+            });
         } else {
             return Promise.resolve(this.createNewPatron());
         }
     }
 
+    absorbPatronData() {
+        this.patron.settings().forEach(setting => {
+            const value = setting.value();
+            if (value !== '' && value !== null) {
+                this.userSettings[setting.name()] = JSON.parse(value);
+            }
+        });
+
+        this.expireDate = new Date(this.patron.expire_date());
+    }
+
     createNewPatron() {
         const patron = this.idl.create('au');
         patron.isnew(true);
@@ -148,26 +168,45 @@ export class EditComponent implements OnInit {
         return this.objectFromPath(path)[field]();
     }
 
+    userSettingChange(name: string, value: any) {
+        // TODO: set dirty
+        this.userSettings[name] = value;
+    }
+
+    // Called as the model changes.
+    // This may be called many times before the final value is applied,
+    // so avoid any heavy lifting here.  See postFieldChange();
     fieldValueChange(path: string, field: string, value: any) {
         if (typeof value === 'boolean') { value = value ? 't' : 'f'; }
-
         this.changeHandlerNeeded = true;
         this.objectFromPath(path)[field](value);
     }
 
-    fieldMaybeModified(path: string, field: string) {
+    // Called after a change operation has completed (e.g. on blur)
+    postFieldChange(path: string, field: string) {
         if (!this.changeHandlerNeeded) { return; } // no changes applied
-
-        // TODO: set dirty = true
-
         this.changeHandlerNeeded = false;
 
-        // check stuff here..
+        // TODO: set dirty
+
 
         const obj = path ? this.patron[path]() : this.patron;
         const value = obj[field]();
 
         console.debug(`Modifying field path=${path} field=${field} value=${value}`);
+
+        switch (field) {
+            // TODO: do many more
+
+            case 'profile':
+                this.setExpireDate();
+                break;
+        }
+    }
+
+    showField(idlClass: string, field: string): boolean {
+      // TODO
+      return true;
     }
 
     fieldRequired(idlClass: string, field: string): boolean {
@@ -175,6 +214,7 @@ export class EditComponent implements OnInit {
         return false;
     }
 
+
     fieldPattern(idlClass: string, field: string): string {
         // TODO
         return null;
@@ -186,7 +226,26 @@ export class EditComponent implements OnInit {
 
         // Normally this is called on (blur), but the input is not
         // focused when using the generate button.
-        this.fieldMaybeModified(null, 'passwd');
+        this.postFieldChange(null, 'passwd');
+    }
+
+
+    cannotHaveUsersOrgs(): number[] {
+        return this.org.list()
+          .filter(org => org.ou_type().can_have_users() === 'f')
+          .map(org => org.id());
+    }
+
+    setExpireDate() {
+        const profile = this.profileSelect.profiles[this.patron.profile()];
+        if (!profile) { return; }
+
+        const seconds = DateUtil.intervalToSeconds(profile.perm_interval());
+        const nowEpoch = new Date().getTime();
+        const newDate = new Date(nowEpoch + (seconds * 1000 /* millis */));
+        this.expireDate = newDate;
+        this.fieldValueChange(null, 'profile', newDate.toISOString());
+        this.postFieldChange(null, 'profile');
     }
 }
 
index ee9cf7d..6d8f57a 100644 (file)
@@ -13,6 +13,7 @@ import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.co
 export class PatronService {
 
     identTypes: IdlObject[];
+    profileGroups: IdlObject[];
 
     constructor(
         private net: NetService,
@@ -99,5 +100,15 @@ export class PatronService {
             {order_by: {cit: ['name']}}, {atomic: true})
         .toPromise().then(types => this.identTypes = types);
     }
+
+    getProfileGroups(): Promise<IdlObject[]> {
+        if (this.profileGroups) {
+            return Promise.resolve(this.profileGroups);
+        }
+
+        return this.pcrud.retrieveAll('pgt',
+            {order_by: {cit: ['name']}}, {atomic: true})
+        .toPromise().then(types => this.profileGroups = types);
+    }
 }
 
index d5a3666..7784aa4 100644 (file)
@@ -1,6 +1,6 @@
 
 <eg-combobox #combobox 
-  [startId]="initialValue" [entries]="cboxEntries"
+  [startId]="initialGroupId" [entries]="cboxEntries"
   (onChange)="propagateCboxChange($event)"
   i18n-placeholder placeholder="Profile Group">
 </eg-combobox>
index 25ef1cb..82fc890 100644 (file)
@@ -43,7 +43,9 @@ export class ProfileSelectComponent implements ControlValueAccessor, OnInit {
 
     @ViewChild('combobox', {static: false}) cbox: ComboboxComponent;
 
-    initialValue: number;
+    // Set the initial value by ID
+    @Input() initialGroupId: number;
+
     cboxEntries: ComboboxEntry[] = [];
     profiles: {[id: number]: IdlObject} = {};
 
@@ -59,7 +61,8 @@ export class ProfileSelectComponent implements ControlValueAccessor, OnInit {
     }
 
     ngOnInit() {
-        this.collectGroups().then(grps => this.sortGroups(grps));
+        this.collectGroups().then(grps => this.sortGroups(grps))
+        .then(_ => this.cbox.selectedId = this.initialGroupId);
     }
 
     collectGroups(): Promise<IdlObject[]> {
@@ -152,7 +155,7 @@ export class ProfileSelectComponent implements ControlValueAccessor, OnInit {
             this.cbox.selectedId = id;
         } else {
             // Will propagate to cbox after its instantiated.
-            this.initialValue = id;
+            this.initialGroupId = id;
         }
     }