From bba32a47823c8169777d303519ae7f2d913d2194 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 19 Mar 2021 16:58:17 -0400 Subject: [PATCH] LP1904036 Patron editor: user settings Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg Signed-off-by: Galen Charlton --- .../src/app/staff/catalog/hold/hold.component.ts | 14 +- .../src/app/staff/circ/patron/edit.component.html | 190 +++++++++++++++++++-- .../src/app/staff/circ/patron/edit.component.ts | 92 ++++++++-- .../src/app/staff/circ/patron/resolver.service.ts | 1 + .../src/app/staff/share/patron/patron.service.ts | 25 +++ 5 files changed, 288 insertions(+), 34 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts index f7c138eac0..ac616d780f 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts @@ -213,14 +213,14 @@ export class HoldComponent implements OnInit { if (this.smsEnabled) { - return this.pcrud.search( - 'csc', {active: 't'}, {order_by: {csc: 'name'}}) - .pipe(tap(carrier => { - this.smsCarriers.push({ - id: carrier.id(), - label: carrier.name() + return this.patron.getSmsCarriers().then(carriers => { + carriers.forEach(carrier => { + this.smsCarriers.push({ + id: carrier.id(), + label: carrier.name() + }); }); - })).toPromise(); + }); } }).then(_ => { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html index 8e562b7277..53d01f4c94 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html @@ -30,6 +30,14 @@ + +
+ +
+
+
@@ -72,7 +80,7 @@
-
- -
+ +
+ +
+ +
+
+ + + +
+ + + + +
+
+ + @@ -118,7 +148,7 @@ -
+
@@ -187,7 +217,6 @@
- {{nameTab}}
@@ -260,12 +289,16 @@ *ngTemplateOutlet="fieldInput; context: {args: {field: 'email', type: 'email'}}">
- - + + + + + +
@@ -284,8 +317,10 @@ *ngTemplateOutlet="fieldInput; context: {args: {field: 'day_phone'}}">
- + + +
@@ -352,5 +387,126 @@
+ + + + + + + + + + + + + +
+ + +
+ +
+
+ +
User Settings
+ + + + +
+ + +
+ + +
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + +
+ + + + +
+ + + +
+
+ +
+ + +
+ + +
+
+
+ + +
Addresses
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts index c7589248c3..357dcb8803 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts @@ -17,6 +17,7 @@ import {StringService} from '@eg/share/string/string.service'; import {EventService} from '@eg/core/event.service'; import {PermService} from '@eg/core/perm.service'; import {SecondaryGroupsDialogComponent} from './secondary-groups.component'; +import {ServerStoreService} from '@eg/core/server-store.service'; const COMMON_USER_SETTING_TYPES = [ 'circ.holds_behind_desk', @@ -28,6 +29,12 @@ 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', @@ -68,7 +75,10 @@ export class EditComponent implements OnInit { nameTab = 'primary'; loading = false; + smsCarriers: ComboboxEntry[]; identTypes: ComboboxEntry[]; + inetLevels: ComboboxEntry[]; + orgSettings: {[name: string]: any} = {}; userSettings: {[name: string]: any} = {}; userSettingTypes: {[name: string]: IdlObject} = {}; optInSettingTypes: {[name: string]: IdlObject} = {}; @@ -82,6 +92,8 @@ export class EditComponent implements OnInit { // patron we are editing. hasPerm: {[name: string]: boolean} = {}; + holdNotifyTypes: {email?: boolean, phone?: boolean, sms?: boolean} = {}; + constructor( private org: OrgService, private net: NetService, @@ -92,6 +104,7 @@ export class EditComponent implements OnInit { private toast: ToastService, private perms: PermService, private evt: EventService, + private serverStore: ServerStoreService, private patronService: PatronService, public context: PatronContextService ) {} @@ -106,21 +119,43 @@ export class EditComponent implements OnInit { .then(_ => this.getSecondaryGroups()) .then(_ => this.applyPerms()) .then(_ => this.setIdentTypes()) + .then(_ => this.setInetLevels()) .then(_ => this.setOptInSettings()) + .then(_ => this.setOrgSettings()) + .then(_ => this.setSmsCarriers()) .finally(() => this.loading = false); } - getSecondaryGroups(): Promise { + setOrgSettings(): Promise { + return this.serverStore.getItemBatch(ORG_SETTING_TYPES) + .then(settings => this.orgSettings = settings); + } - return this.net.request( - 'open-ils.actor', - 'open-ils.actor.user.get_groups', - this.auth.token(), this.patronId + setSmsCarriers(): Promise { + if (!this.orgSettings['sms.enable']) { + return Promise.resolve(); + } - ).pipe(concatMap(maps => this.pcrud.search('pgt', - {id: maps.map(m => m.grp())}, {}, {atomic: true}) + return this.patronService.getSmsCarriers().then(carriers => { + this.smsCarriers = carriers.map(carrier => { + return { + id: carrier.id(), + label: carrier.name() + }; + }); + }); + } - )).pipe(tap(grps => this.secondaryGroups = grps)).toPromise(); + getSecondaryGroups(): Promise { + return this.net.request( + 'open-ils.actor', + 'open-ils.actor.user.get_groups', + this.auth.token(), this.patronId + + ).pipe(concatMap(maps => this.pcrud.search('pgt', + {id: maps.map(m => m.grp())}, {}, {atomic: true}) + + )).pipe(tap(grps => this.secondaryGroups = grps)).toPromise(); } setIdentTypes(): Promise { @@ -130,6 +165,13 @@ export class EditComponent implements OnInit { }); } + setInetLevels(): Promise { + return this.patronService.getInetLevels() + .then(levels => { + this.inetLevels = levels.map(t => ({id: t.id(), label: t.name()})); + }); + } + applyPerms(): Promise { const promise = this.permOrgs ? @@ -196,6 +238,23 @@ export class EditComponent implements OnInit { } }); + const holdNotify = this.userSettings['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; + } + + if (this.userSettings['opac.default_sms_carrier']) { + this.userSettings['opac.default_sms_carrier'] = + Number(this.userSettings['opac.default_sms_carrier']); + } + + if (this.userSettings['opac.default_pickup_location']) { + this.userSettings['opac.default_pickup_location'] = + Number(this.userSettings['opac.default_pickup_location']); + } + this.expireDate = new Date(this.patron.expire_date()); } @@ -242,6 +301,13 @@ export class EditComponent implements OnInit { // so avoid any heavy lifting here. See afterFieldChange(); fieldValueChange(path: string, 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); + if (oldValue === value) { return; } + this.changeHandlerNeeded = true; this.objectFromPath(path)[field](value); } @@ -253,11 +319,11 @@ export class EditComponent implements OnInit { // 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}`); + console.debug( + `Modifying field path=${path || ''} field=${field} value=${value}`); switch (field) { // TODO: do many more @@ -300,6 +366,12 @@ export class EditComponent implements OnInit { .map(org => org.id()); } + cannotHaveVolsOrgs(): number[] { + return this.org.list() + .filter(org => org.ou_type().can_have_vols() === 'f') + .map(org => org.id()); + } + setExpireDate() { const profile = this.profileSelect.profiles[this.patron.profile()]; if (!profile) { return; } diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts index 8c6b2179a5..7f96c44c6f 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts @@ -43,6 +43,7 @@ export class PatronResolver implements Resolve> { 'ui.circ.billing.amount_limit', 'circ.staff_client.do_not_auto_attempt_print', 'circ.disable_patron_credit', + 'sms.enable', 'credit.processor.default' ]).then(settings => { this.context.noTallyClaimsReturned = diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts b/Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts index 6d8f57a0da..459975aa5f 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts @@ -1,4 +1,5 @@ import {Injectable} from '@angular/core'; +import {tap} from 'rxjs/operators'; import {IdlObject} from '@eg/core/idl.service'; import {NetService} from '@eg/core/net.service'; import {OrgService} from '@eg/core/org.service'; @@ -13,7 +14,9 @@ import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.co export class PatronService { identTypes: IdlObject[]; + inetLevels: IdlObject[]; profileGroups: IdlObject[]; + smsCarriers: IdlObject[]; constructor( private net: NetService, @@ -101,6 +104,16 @@ export class PatronService { .toPromise().then(types => this.identTypes = types); } + getInetLevels(): Promise { + if (this.inetLevels) { + return Promise.resolve(this.inetLevels); + } + + return this.pcrud.retrieveAll('cnal', + {order_by: {cit: ['name']}}, {atomic: true}) + .toPromise().then(levels => this.inetLevels = levels); + } + getProfileGroups(): Promise { if (this.profileGroups) { return Promise.resolve(this.profileGroups); @@ -110,5 +123,17 @@ export class PatronService { {order_by: {cit: ['name']}}, {atomic: true}) .toPromise().then(types => this.profileGroups = types); } + + getSmsCarriers(): Promise { + if (this.smsCarriers) { + return Promise.resolve(this.smsCarriers); + } + + this.smsCarriers = []; + return this.pcrud.search( + 'csc', {active: 't'}, {order_by: {csc: 'name'}}) + .pipe(tap(carrier => this.smsCarriers.push(carrier)) + ).toPromise().then(_ => this.smsCarriers); + } } -- 2.11.0