</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>
-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';
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',
const FLESH_PATRON_FIELDS = {
flesh: 1,
flesh_fields: {
- au: ['card', 'mailing_address', 'billing_address', 'addresses']
+ au: ['card', 'mailing_address', 'billing_address', 'addresses', 'settings']
}
};
@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,
}
});
});
- };
-
+ }
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);
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 {
return false;
}
+
fieldPattern(idlClass: string, field: string): string {
// TODO
return null;
// 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');
}
}