import {Component, OnInit, AfterViewInit, Input, ViewChild} from '@angular/core';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {empty, from} from 'rxjs';
import {concatMap, tap} from 'rxjs/operators';
import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
import {OrgService} from '@eg/core/org.service';
import {EditToolbarComponent, VisibilityLevel} from './edit-toolbar.component';
import {PatronSearchFieldSet} from '@eg/staff/share/patron/search.component';
import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
+import {HoldNotifyUpdateDialogComponent} from './hold-notify-update.component';
const COMMON_USER_SETTING_TYPES = [
'circ.holds_behind_desk',
'opac.hold_notify',
'opac.default_phone',
'opac.default_pickup_location',
- 'opac.default_sms_carrier',
+ 'opac.default_sms_carrier_id',
'opac.default_sms_notify'
];
private profileSelect: ProfileSelectComponent;
@ViewChild('secondaryGroupsDialog')
private secondaryGroupsDialog: SecondaryGroupsDialogComponent;
+ @ViewChild('holdNotifyUpdateDialog')
+ private holdNotifyUpdateDialog: HoldNotifyUpdateDialogComponent;
@ViewChild('addrAlert') private addrAlert: AlertDialogComponent;
@ViewChild('addrRequiredAlert')
private addrRequiredAlert: AlertDialogComponent;
fieldVisibility: {[key: string]: FieldVisibility} = {};
+ holdNotifyValues = {
+ day_phone: null,
+ other_phone: null,
+ evening_phone: null,
+ default_phone: null,
+ default_sms: null,
+ default_sms_carrier_id: null,
+ phone_notify: false,
+ email_notify: false,
+ sms_notify: false
+ };
+
// All locations we have the specified permissions
permOrgs: {[name: string]: number[]};
this.fieldDoc[doc.fm_class()] = {};
}
this.fieldDoc[doc.fm_class()][doc.field()] = doc.string();
- console.log(this.fieldDoc);
})).toPromise();
}
}
absorbPatronData() {
+
+ const usets = this.userSettings;
+ let setting;
+
+ this.holdNotifyValues.day_phone = this.patron.day_phone();
+ this.holdNotifyValues.other_phone = this.patron.other_phone();
+ this.holdNotifyValues.evening_phone = this.patron.evening_phone();
+
this.patron.settings().forEach(setting => {
const value = setting.value();
if (value !== '' && value !== null) {
- this.userSettings[setting.name()] = JSON.parse(value);
+ usets[setting.name()] = JSON.parse(value);
}
});
- const holdNotify = this.userSettings['opac.hold_notify'];
+ const holdNotify = usets['opac.hold_notify'];
+
if (holdNotify) {
- this.holdNotifyTypes.email = holdNotify.match(/email/) !== null;
- this.holdNotifyTypes.phone = holdNotify.match(/phone/) !== null;
- this.holdNotifyTypes.sms = holdNotify.match(/sms/) !== null;
+ this.holdNotifyTypes.email = this.holdNotifyValues.email_notify
+ = holdNotify.match(/email/) !== null;
+
+ this.holdNotifyTypes.phone = this.holdNotifyValues.phone_notify
+ = holdNotify.match(/phone/) !== null;
+
+ this.holdNotifyTypes.sms = this.holdNotifyValues.sms_notify
+ = holdNotify.match(/sms/) !== null;
+ }
+
+ if (setting = usets['opac.default_sms_carrier_id']) {
+ setting = usets['opac.default_sms_carrier_id'] = Number(setting);
+ this.holdNotifyValues.default_sms_carrier_id = setting;
}
- if (this.userSettings['opac.default_sms_carrier']) {
- this.userSettings['opac.default_sms_carrier'] =
- Number(this.userSettings['opac.default_sms_carrier']);
+ if (setting = usets['opac.default_phone']) {
+ this.holdNotifyValues.default_phone = setting;
}
- if (this.userSettings['opac.default_pickup_location']) {
- this.userSettings['opac.default_pickup_location'] =
- Number(this.userSettings['opac.default_pickup_location']);
+ if (setting = usets['opac.default_sms_notify']) {
+ this.holdNotifyValues.default_sms = setting;
+ }
+
+ if (setting = usets['opac.default_pickup_location']) {
+ usets['opac.default_pickup_location'] = Number(setting);
}
this.expireDate = new Date(this.patron.expire_date());
this.patron = patron;
}
+
+
objectFromPath(path: string, index: number): IdlObject {
const base = path ? this.patron[path]() : this.patron;
if (index === null || index === undefined) {
userSettingChange(name: string, value: any) {
this.userSettings[name] = value;
-
- switch (name) {
- case 'opac.default_phone':
- case 'opac.default_sms_notify':
- case 'opac.default_sms_carrier':
- // TODO hold related contact info updated
- break;
- }
-
this.adjustSaveSate();
}
handlePhoneChange(field: string, value: string) {
this.dupeValueChange(field, value);
- // TODO: hold contact info stuff
const pwUsePhone =
this.context.settingsCache['patron.password.use_phone'];
this.loading = true;
return this.saveUser()
.then(_ => this.saveUserSettings())
+ .then(_ => this.updateHoldPrefs())
.then(_ => this.postSaveRedirect());
}
).toPromise();
}
+
+ updateHoldPrefs(): Promise<any> {
+ if (this.patron.isnew()) { return Promise.resolve(); }
+
+ return this.collectHoldNotifyChange()
+ .then(mods => {
+ if (mods.length === 0) { return Promise.resolve(); }
+
+ this.holdNotifyUpdateDialog.smsCarriers = this.smsCarriers;
+ this.holdNotifyUpdateDialog.mods = mods;
+
+ return this.holdNotifyUpdateDialog.open().toPromise();
+ });
+ }
+
+ // Compare current values with those collected at patron load time.
+ // For any that have changed, ask the server if the original values
+ // are used on active holds.
+ collectHoldNotifyChange(): Promise<any[]> {
+ const mods = [];
+ const holdNotify = this.userSettings['opac.hold_notify'] || '';
+
+ return from(Object.keys(this.holdNotifyValues))
+ .pipe(concatMap(field => {
+
+ let newValue, matches;
+
+ if (field.match(/default_/)) {
+ newValue = this.userSettings[`opac.${field}`] || null;
+
+ } else if (field.match(/_phone/)) {
+ newValue = this.patron[field]();
+
+ } else if (matches = field.match(/(\w+)_notify/)) {
+ newValue = this.userSettings['opac.hold_notify'].match(matches[1]) !== null;
+ }
+
+ const oldValue = this.holdNotifyValues[field];
+
+ // No change to apply?
+ if (newValue === oldValue) { return empty(); }
+
+ return this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.holds.retrieve_by_notify_staff',
+ this.auth.token(), this.patronId, newValue, field
+ ).pipe(tap(holds => {
+ if (holds && holds.length > 0) {
+ mods.push({
+ field: field,
+ newValue: newValue,
+ oldValue: oldValue,
+ holds: holds
+ });
+ };
+ }));
+ })).toPromise().then(_ => mods);
+ }
+
+
printPatron() {
// TODO
}
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" i18n>Update Hold Notification Info?</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="row mt-2" *ngFor="let mod of mods">
+ <div class="col-lg-12">
+
+ <!-- TODO pref change wording -->
+
+ <div class="form-check form-check-inline">
+ <input class="form-check-input" type="checkbox"
+ id="{{mod.field}}-checkbox" [(ngModel)]="selected[mod.field]"/>
+ <label class="form-check-label" for="{{mod.field}}-checkbox">
+ <ng-container *ngIf="isCarrierChange(mod)" i18n>
+ A carrier other than "{{carrierName(mod.oldValue)}}"
+ is currently used in {{mod.holds.length}} hold(s).
+ Update to "{{carrierName(mod.newValue)}}"?
+ </ng-container>
+ <ng-container *ngIf="isBoolChange(mod)" i18n>
+ {{mod.holds.length}} hold(s) have it set to
+ <eg-bool [value]="mod.oldValue"></eg-bool>.
+ Update to <eg-bool [value]="mod.newValue"></eg-bool>.
+ </ng-container>
+ <ng-container *ngIf="isPhoneChange(mod)" i18n>
+ {{mod.oldValue}} is used for {{mod.holds.length}} hold(s).
+ Update to {{mod.newValue}}?
+ </ng-container>
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-success"
+ (click)="applyChanges()" i18n>Apply Changes</button>
+ <button type="button" class="btn btn-warning"
+ (click)="close()" i18n>Cancel</button>
+ </div>
+</ng-template>
--- /dev/null
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {Observable, empty} from 'rxjs';
+import {switchMap, tap} from 'rxjs/operators';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {OrgService} from '@eg/core/org.service';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+
+/* Apply notification changes to affected holds */
+
+export interface HoldNotifyMod {
+ field: string,
+ newValue: any,
+ oldValue: any,
+ holds: any[]
+}
+
+@Component({
+ selector: 'eg-hold-notify-update-dialog',
+ templateUrl: 'hold-notify-update.component.html'
+})
+
+export class HoldNotifyUpdateDialogComponent
+ extends DialogComponent implements OnInit {
+
+ // Values provided directly by our parent component
+ smsCarriers: ComboboxEntry[];
+ mods: HoldNotifyMod[] = [];
+
+ selected: {[field: string]: boolean} = {};
+
+ constructor(
+ private modal: NgbModal,
+ private toast: ToastService,
+ private net: NetService,
+ private idl: IdlService,
+ private evt: EventService,
+ private pcrud: PcrudService,
+ private org: OrgService,
+ private auth: AuthService) {
+ super(modal);
+ }
+
+ applyChanges() {
+ }
+
+ isPhoneChange(mod: HoldNotifyMod): boolean {
+ return mod.field.match(/_phone/) !== null;
+ }
+
+ isBoolChange(mod: HoldNotifyMod): boolean {
+ return mod.field.match(/_notify/) !== null && !this.isCarrierChange(mod);
+ }
+
+ isCarrierChange(mod: HoldNotifyMod): boolean {
+ return mod.field.match(/carrier/) !== null;
+ }
+
+ carrierName(id: number): string {
+ const entry = this.smsCarriers.filter(e => e.id === id)[0];
+ return entry ? entry.label : '';
+ }
+}
+
+