From 645c0493b5cf9f7e67e26804bb9f621093b76f34 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 19 Jun 2020 12:15:57 -0400 Subject: [PATCH] LPXXX Angular Volcopy / TABS Signed-off-by: Bill Erickson --- .../staff/cat/volcopy/copy-attrs.component.html | 18 ++- .../app/staff/cat/volcopy/copy-attrs.component.ts | 125 +++++++-------------- .../src/app/staff/cat/volcopy/routing.module.ts | 11 +- .../app/staff/cat/volcopy/vol-edit.component.css | 6 - .../app/staff/cat/volcopy/vol-edit.component.html | 2 +- .../app/staff/cat/volcopy/volcopy.component.html | 71 +++++++----- .../src/app/staff/cat/volcopy/volcopy.component.ts | 80 +++++++++---- .../src/app/staff/cat/volcopy/volcopy.service.ts | 121 +++++++++++++++++++- Open-ILS/src/eg2/src/styles.css | 12 ++ 9 files changed, 293 insertions(+), 153 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html index ca2e2e2a13..789f6572d2 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html @@ -14,7 +14,21 @@ -
+
+
Templates:
+
+ +
+
+ + + + +
+ + +
@@ -441,7 +455,7 @@

Statistics

-
+
entry - loanDurationLabelMap: {[level: number]: string} = {}; fineLevelLabelMap: {[level: number]: string} = {}; @@ -67,6 +63,9 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { @ViewChild('copyAlertsDialog', {static: false}) private copyAlertsDialog: CopyAlertsDialogComponent; + @ViewChild('copyTemplateCbox', {static: false}) + copyTemplateCbox: ComboboxComponent; + constructor( private router: Router, private route: ActivatedRoute, @@ -79,15 +78,18 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { private pcrud: PcrudService, private holdings: HoldingsService, private volcopy: VolCopyService, - private format: FormatService + private format: FormatService, + private store: StoreService ) { } ngOnInit() { - this.load(); } ngAfterViewInit() { + const tmpl = this.store.getLocalItem('cat.copy.last_template'); + if (tmpl) { this.copyTemplateCbox.selectedId = tmpl; } + this.loanDurationLabelMap[1] = this.loanDurationShort.text; this.loanDurationLabelMap[2] = this.loanDurationNormal.text; this.loanDurationLabelMap[3] = this.loanDurationLong.text; @@ -97,75 +99,11 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { this.fineLevelLabelMap[3] = this.fineLevelHigh.text; } - load() { - - this.pcrud.retrieveAll('crahp') - .pipe(tap(rule => this.ageProtectRules.push(rule))).toPromise() - .then(_ => { - - this.ageProtectRules = this.ageProtectRules.sort( - (a, b) => a.name() < b.name() ? -1 : 1); - - }).then(_ => { - - return this.pcrud.retrieveAll('cfg') - .pipe(tap(rule => this.floatingGroups.push(rule))).toPromise(); - - }).then(_ => { - - this.floatingGroups = this.floatingGroups.sort( - (a, b) => a.name() < b.name() ? -1 : 1); - - }).then(_ => { - - return this.pcrud.retrieveAll('ccm') - .pipe(tap(rule => this.circModifiers.push(rule))).toPromise(); - - }).then(_ => { - - this.circModifiers = this.circModifiers.sort( - (a, b) => a.name() < b.name() ? -1 : 1); - - }).then(_ => { - - return this.pcrud.retrieveAll('citm') - .pipe(tap(itemType => this.itemTypeMaps.push(itemType))).toPromise(); - - }).then(_ => { - - this.itemTypeMaps = this.itemTypeMaps.sort( - (a, b) => a.value() < b.value() ? -1 : 1); - - }).then(_ => { - - return this.net.request('open-ils.circ', - 'open-ils.circ.stat_cat.asset.retrieve.all', - this.auth.token(), this.auth.user().ws_ou() - ).toPromise().then(stats => this.statCats = stats); - - }).then(_ => { - - // Sort most local to the front of the list. - this.statCats = this.statCats.sort((s1, s2) => { - const d1 = this.org.get(s1.owner()).ou_type().depth(); - const d2 = this.org.get(s2.owner()).ou_type().depth(); - - if (d1 > d2) { - return -1; - } else if (d1 < d2) { - return 1; - } else { - return s1.name() < s2.name() ? -1 : 1; - } - }); - - this.statCats.forEach(cat => { - cat.entries().forEach( - entry => this.statCatEntryMap[entry.id()] = entry); - }); - }); + statCats(): IdlObject[] { + return this.volcopy.statCats; } + orgSn(orgId: number): string { return orgId ? this.org.get(orgId).shortname() : ''; } @@ -180,8 +118,8 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { let value = ''; if (entry) { - if (this.statCatEntryMap[entry.id()]) { - value = this.statCatEntryMap[entry.id()].value(); + if (this.volcopy.statCatEntryMap[entry.id()]) { + value = this.volcopy.statCatEntryMap[entry.id()].value(); } else { // Map to a remote stat cat. Ignore. return; @@ -248,12 +186,12 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { return this.org.get(value).shortname(); case 'age_protect': - const rule = this.ageProtectRules.filter( + const rule = this.volcopy.ageProtectRules.filter( r => r.id() === Number(value))[0]; return rule ? rule.name() : ''; case 'floating': - const grp = this.floatingGroups.filter( + const grp = this.volcopy.floatingGroups.filter( g => g.id() === Number(value))[0]; return grp ? grp.name() : ''; @@ -264,12 +202,12 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { return this.fineLevelLabelMap[value]; case 'circ_as_type': - const map = this.itemTypeMaps.filter( + const map = this.volcopy.itemTypeMaps.filter( m => m.code() === value)[0]; return map ? map.value() : ''; case 'circ_modifier': - const mod = this.circModifiers.filter( + const mod = this.volcopy.circModifiers.filter( m => m.code() === value)[0]; return mod ? mod.name() : ''; @@ -332,7 +270,7 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { } entry.id(entryId); - entry.value(this.statCatEntryMap[entryId].value()); + entry.value(this.volcopy.statCatEntryMap[entryId].value()); copy.ischanged(true); }); @@ -350,6 +288,23 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { } ); } + + applyTemplate() { + const entry = this.copyTemplateCbox.selected; + if (!entry) { return; } + + this.store.setLocalItem('cat.copy.last_template', entry.id); + + const template = this.volcopy.templates[entry.id]; + + Object.keys(template).forEach(field => { + const value = template[field]; + + if (value === null || value === undefined) { return; } + + this.applyCopyValue(field, value); + }); + } } diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts index fb4f44d7c5..c5d3f67d2d 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts @@ -3,16 +3,7 @@ import {RouterModule, Routes} from '@angular/router'; import {VolCopyComponent} from './volcopy.component'; const routes: Routes = [{ - path: 'edit/item/:copy_id', - component: VolCopyComponent - }, { - path: 'edit/callnumber/:vol_id', - component: VolCopyComponent - }, { - path: 'edit/record/:record_id', - component: VolCopyComponent - }, { - path: 'edit/session/:session', + path: ':tab/:target/:target_id', component: VolCopyComponent /* }, { diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css index 96a852a5cc..de37f46a49 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css @@ -5,17 +5,11 @@ input[type="number"] { } .vol-row { - /*background-color: #d9edf7;*/ background-color: rgba(0,0,0,.03); border-top: 1px solid #d9edf7; border-bottom: 1px solid #d9edf7; } -.batch-vol-row { - border: 2px solid #d9edf7; -} - - .clear-button { border: none; background-color: rgba(0, 0, 0, 0.0); diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html index b132b0ab0c..f0644477e8 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html @@ -12,7 +12,7 @@ dialogBody="Delete {{deleteCopyCount}} Item(s)?"> -
+
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html index 156540f789..af4ebe396e 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html @@ -1,11 +1,5 @@ -
-
- -
-
-
Holdings Editor Session Expired @@ -15,29 +9,52 @@ - -
- -
- - -
-
- -
- -
-
-
-
- - +
+ + + + + +
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+ +
+
+
+ +
+
+ +
+ + +
+
+
+
+ + +
-
+ diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts index 7d493f12ca..b73777986a 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts @@ -12,6 +12,7 @@ import {VolCopyContext} from './volcopy'; import {ProgressInlineComponent} from '@eg/share/dialog/progress-inline.component'; import {AnonCacheService} from '@eg/share/util/anon-cache.service'; import {VolCopyService} from './volcopy.service'; +import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap'; const COPY_FLESH = { flesh: 1, @@ -48,6 +49,10 @@ export class VolCopyComponent implements OnInit { loading = true; sessionExpired = false; + tab = 'holdings'; // holdings | attrs | config + target: string; // item | callnumber | record | session + targetId: string; // id value or session string + @ViewChild('loadingProgress', {static: false}) loadingProgress: ProgressInlineComponent; @@ -67,35 +72,54 @@ export class VolCopyComponent implements OnInit { ) { } ngOnInit() { - this.context = new VolCopyContext(); - this.context.org = this.org; // inject; - this.route.paramMap.subscribe( (params: ParamMap) => this.negotiateRoute(params)); } negotiateRoute(params: ParamMap) { - this.context.recordId = +params.get('record_id') || null; - this.context.volId = +params.get('vol_id') || null; - this.context.copyId = +params.get('copy_id') || null; - this.context.session = params.get('session') || null; - this.load(); + this.tab = params.get('tab') || 'holdings'; + this.target = params.get('target'); + this.targetId = params.get('target_id'); + + if (this.volcopy.currentContext) { + // Avoid clobbering the context on route change. + this.context = this.volcopy.currentContext; + } else { + this.context = new VolCopyContext(); + this.context.org = this.org; // inject; + } + + switch (this.target) { + case 'item': + this.context.copyId = +this.targetId; + break; + case 'callnumber': + this.context.volId = +this.targetId; + break; + case 'record': + this.context.recordId = +this.targetId; + break; + case 'session': + this.context.session = this.targetId; + break; + } + + if (!this.volcopy.currentContext) { + // Avoid refetching the data during route changes. + this.volcopy.currentContext = this.context; + this.load(); + } } load(copyIds?: number[]) { - this.sessionExpired = false; this.loading = true; this.context.reset(); - this.volcopy.fetchDefaults() - .then(_ => this.volcopy.fetchCopyStats()) + this.volcopy.load() .then(_ => this.fetchHoldings(copyIds)) .then(_ => this.volcopy.applyVolLabels( this.context.volNodes().map(n => n.target))) - .then(_ => this.holdings.fetchCallNumberClasses()) - .then(_ => this.holdings.fetchCallNumberPrefixes()) - .then(_ => this.holdings.fetchCallNumberSuffixes()) .then(_ => this.context.sortHoldings()) .then(_ => this.context.setRecordId()) .then(_ => this.loading = false); @@ -111,20 +135,36 @@ export class VolCopyComponent implements OnInit { this.context.sessionType = 'mixed'; return this.fetchSession(this.context.session); - } else if (this.context.recordId) { - this.context.sessionType = 'record'; - return this.fetchRecords(this.context.recordId); + } else if (this.context.copyId) { + this.context.sessionType = 'copy'; + return this.fetchCopies(this.context.copyId); } else if (this.context.volId) { this.context.sessionType = 'vol'; return this.fetchVols(this.context.volId); - } else if (this.context.copyId) { - this.context.sessionType = 'copy'; - return this.fetchCopies(this.context.copyId); + } else if (this.context.recordId) { + this.context.sessionType = 'record'; + return this.fetchRecords(this.context.recordId); } } + // Changing a tab in the UI means changing the route. + // Changing the route ultimately results in changing the tab. + beforeTabChange(evt: NgbTabChangeEvent) { + evt.preventDefault(); + this.tab = evt.nextId; + this.routeToTab(); + } + + routeToTab() { + const url = + `/staff/cat/volcopy/${this.tab}/${this.target}/${this.targetId}`; + + // Retain search parameters + this.router.navigate([url], {queryParamsHandling: 'merge'}); + } + fetchSession(session: string): Promise { return this.cache.getItem(session, 'edit-these-copies') diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts index abaa8b8460..d78663bfc4 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts @@ -10,6 +10,8 @@ import {AuthService} from '@eg/core/auth.service'; import {VolCopyContext} from './volcopy'; import {HoldingsService, CallNumData} from '@eg/staff/share/holdings/holdings.service'; import {ServerStoreService} from '@eg/core/server-store.service'; +import {StoreService} from '@eg/core/store.service'; +import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component'; /* Managing volcopy data */ @@ -21,6 +23,19 @@ export class VolCopyService { defaultValues: any = null; copyStatuses: {[id: number]: IdlObject} = null; + // Track this here so it can survive route changes. + currentContext: VolCopyContext; + + ageProtectRules: IdlObject[] = []; + floatingGroups: IdlObject[] = []; + itemTypeMaps: IdlObject[] = []; + circModifiers: IdlObject[] = []; + statCats: IdlObject[] = []; + statCatEntryMap: {[id: number]: IdlObject} = {}; // entry id => entry + + templateNames: ComboboxEntry[] = []; + templates: any = {}; + constructor( private evt: EventService, private net: NetService, @@ -29,13 +44,112 @@ export class VolCopyService { private auth: AuthService, private pcrud: PcrudService, private holdings: HoldingsService, - private store: ServerStoreService + private store: StoreService, + private serverStore: ServerStoreService ) {} + + // Fetch the data that is always needed. + load(): Promise { + + if (this.itemTypeMaps.length > 0) { + return Promise.resolve(); + } + + return this.fetchDefaults() + .then(_ => this.holdings.fetchCallNumberClasses()) + .then(_ => this.holdings.fetchCallNumberPrefixes()) + .then(_ => this.holdings.fetchCallNumberSuffixes()) + .then(_ => this.fetchCopyStats()) + .then(_ => this.fetchTemplates()) + .then(_ => { + + return this.pcrud.retrieveAll('crahp') + .pipe(tap(rule => this.ageProtectRules.push(rule))).toPromise() + + }).then(_ => { + + this.ageProtectRules = this.ageProtectRules.sort( + (a, b) => a.name() < b.name() ? -1 : 1); + + }).then(_ => { + + return this.pcrud.retrieveAll('cfg') + .pipe(tap(rule => this.floatingGroups.push(rule))).toPromise(); + + }).then(_ => { + + this.floatingGroups = this.floatingGroups.sort( + (a, b) => a.name() < b.name() ? -1 : 1); + + }).then(_ => { + + return this.pcrud.retrieveAll('ccm') + .pipe(tap(rule => this.circModifiers.push(rule))).toPromise(); + + }).then(_ => { + + this.circModifiers = this.circModifiers.sort( + (a, b) => a.name() < b.name() ? -1 : 1); + + }).then(_ => { + + return this.pcrud.retrieveAll('citm') + .pipe(tap(itemType => this.itemTypeMaps.push(itemType))).toPromise(); + + }).then(_ => { + + this.itemTypeMaps = this.itemTypeMaps.sort( + (a, b) => a.value() < b.value() ? -1 : 1); + + }).then(_ => { + + return this.net.request('open-ils.circ', + 'open-ils.circ.stat_cat.asset.retrieve.all', + this.auth.token(), this.auth.user().ws_ou() + ).toPromise().then(stats => this.statCats = stats); + + }).then(_ => { + + // Sort most local to the front of the list. + this.statCats = this.statCats.sort((s1, s2) => { + const d1 = this.org.get(s1.owner()).ou_type().depth(); + const d2 = this.org.get(s2.owner()).ou_type().depth(); + + if (d1 > d2) { + return -1; + } else if (d1 < d2) { + return 1; + } else { + return s1.name() < s2.name() ? -1 : 1; + } + }); + + this.statCats.forEach(cat => { + cat.entries().forEach( + entry => this.statCatEntryMap[entry.id()] = entry); + }); + }); + } + + fetchTemplates(): Promise { + + // TODO: copy templates should be server settings + const tmpls = this.store.getLocalItem('cat.copy.templates'); + if (!tmpls) { return Promise.resolve(); } + + this.templates = tmpls; + this.templateNames = Object.keys(tmpls) + .sort((n1, n2) => n1 < n2 ? -1 : 1) + .map(name => ({id: name, label: name})); + + return Promise.resolve(); + } + fetchDefaults(): Promise { if (this.defaultValues) { return Promise.resolve(); } - return this.store.getItem('cat.copy.defaults').then( + return this.serverStore.getItem('cat.copy.defaults').then( defaults => { this.defaultValues = defaults || {}; } @@ -254,5 +368,8 @@ export class VolCopyService { return promise; } + + + } diff --git a/Open-ILS/src/eg2/src/styles.css b/Open-ILS/src/eg2/src/styles.css index d1144fd9fb..7780680d2a 100644 --- a/Open-ILS/src/eg2/src/styles.css +++ b/Open-ILS/src/eg2/src/styles.css @@ -225,6 +225,18 @@ body>.dropdown-menu {z-index: 2100;} color: black; } +/* Washed out version of the Bootstrap 'info' background. + * Useful for blocking out sections of a page/form without it + * being so intensely colorful */ +.bg-faint { + /*background-color: rgb(204, 229, 255, 0.3);*/ + + /* d9edf7 */ + /*background-color: rgb(217, 237, 247, 0.5);*/ + + background-color: rgba(0,0,0,.03); +} + /* Allow for larger XL dialogs */ @media (min-width: 1300px) { .modal-xl { max-width: 1200px; } } @media (min-width: 1600px) { .modal-xl { max-width: 1500px; } } -- 2.11.0