From 83588f6da7ae99489803d756749e777909db75d9 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 6 Nov 2019 12:41:32 -0500 Subject: [PATCH] LP1850555 Angular Item (Copy) Location Select Component Adds a new item location select component which filters the list of displayed locations based on a permission-check org or a specific context org unit. Values in the selector are decorated with the org unit short name in parens after the location name to clarify the owning lib. Sandbox example included. Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg --- .../app/share/combobox/combobox.component.ts | 1 + .../item-location-select.component.html | 13 ++ .../item-location-select.component.ts | 118 ++++++++++++++++++ .../item-location-select.module.ts | 26 ++++ .../app/staff/sandbox/sandbox.component.html | 15 +++ .../app/staff/sandbox/sandbox.component.ts | 3 + .../src/app/staff/sandbox/sandbox.module.ts | 2 + 7 files changed, 178 insertions(+) create mode 100644 Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.html create mode 100644 Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.ts create mode 100644 Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.module.ts diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts index 41d2b30cec..3f5aab8501 100644 --- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts +++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts @@ -18,6 +18,7 @@ export interface ComboboxEntry { // If no label is provided, the 'id' value is used. label?: string; freetext?: boolean; + userdata?: any; // opaque external value; ignored by this component. } @Component({ diff --git a/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.html b/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.html new file mode 100644 index 0000000000..b5ad4da982 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.html @@ -0,0 +1,13 @@ + + + {{r.label}} ({{orgName(r.userdata.owning_lib())}}) + + + + diff --git a/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.ts b/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.ts new file mode 100644 index 0000000000..082aa80ccf --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.component.ts @@ -0,0 +1,118 @@ +import {Component, OnInit, Input, Output, ViewChild, EventEmitter, forwardRef} from '@angular/core'; +import {ControlValueAccessor, FormGroup, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms'; +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {IdlObject} from '@eg/core/idl.service'; +import {OrgService} from '@eg/core/org.service'; +import {AuthService} from '@eg/core/auth.service'; +import {PermService} from '@eg/core/perm.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component'; + +/** + * Item (Copy) Location Selector. + * + * + * + */ + +@Component({ + selector: 'eg-item-location-select', + templateUrl: './item-location-select.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ItemLocationSelectComponent), + multi: true + }] +}) +export class ItemLocationSelectComponent implements OnInit, ControlValueAccessor { + + // Limit copy locations to those owned at or above org units where + // the user has work permissions for the provided permission code. + @Input() permFilter: string; + + // Limit copy locations to those owned at or above this org unit. + @Input() contextOrgId: number; + + @Input() orgUnitLabelField = 'shortname'; + + // Emits an acpl object or null on combobox value change + @Output() valueChange: EventEmitter; + + @ViewChild('comboBox', {static: false}) comboBox: ComboboxComponent; + + startId: number = null; + filterOrgs: number[]; + cache: {[id: number]: IdlObject} = {}; + + propagateChange = (id: number) => {}; + propagateTouch = () => {}; + + constructor( + private org: OrgService, + private auth: AuthService, + private perm: PermService, + private pcrud: PcrudService + ) { + this.valueChange = new EventEmitter(); + } + + ngOnInit() { + this.setFilterOrgs().then(_ => this.getLocations()); + } + + getLocations(): Promise { + const entries: ComboboxEntry[] = []; + const search = {owning_lib: this.filterOrgs, deleted: 'f'}; + + return this.pcrud.search('acpl', search, {order_by: {acpl: 'name'}} + ).pipe(map(loc => { + this.cache[loc.id()] = loc; + entries.push({id: loc.id(), label: loc.name(), userdata: loc}); + })).toPromise().then(_ => { + this.comboBox.entries = entries; + }); + } + + registerOnChange(fn) { + this.propagateChange = fn; + } + + registerOnTouched(fn) { + this.propagateTouch = fn; + } + + cboxChanged(entry: ComboboxEntry) { + const id = entry ? entry.id : null; + this.propagateChange(id); + this.valueChange.emit(id ? this.cache[id] : null); + } + + writeValue(id: number) { + if (this.comboBox) { // May not yet be initialized + this.comboBox.selectedId = id; + } else if (id) { + this.startId = id; + } + } + + setFilterOrgs(): Promise { + if (this.permFilter) { + return this.perm.hasWorkPermAt([this.permFilter], true) + .then(values => this.filterOrgs = values[this.permFilter]); + } + + const org = this.contextOrgId || this.auth.user().ws_ou(); + this.filterOrgs = this.org.ancestors(this.contextOrgId, true); + + return Promise.resolve(this.filterOrgs); + } + + orgName(orgId: number): string { + return this.org.get(orgId)[this.orgUnitLabelField](); + } +} + + + diff --git a/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.module.ts b/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.module.ts new file mode 100644 index 0000000000..f82989a852 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/item-location-select/item-location-select.module.ts @@ -0,0 +1,26 @@ +import {NgModule} from '@angular/core'; +import {EgCommonModule} from '@eg/common.module'; +import {EgCoreModule} from '@eg/core/core.module'; +import {CommonWidgetsModule} from '@eg/share/common-widgets.module'; +import {ItemLocationSelectComponent} from './item-location-select.component'; +import {ReactiveFormsModule} from '@angular/forms'; + +@NgModule({ + declarations: [ + ItemLocationSelectComponent + ], + imports: [ + EgCommonModule, + EgCoreModule, + CommonWidgetsModule, + ReactiveFormsModule + ], + exports: [ + ItemLocationSelectComponent + ], + providers: [ + ] +}) + +export class ItemLocationSelectModule { } + diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index 3b9609b399..7f98f45087 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -416,3 +416,18 @@ + +
+

Item (Copy) Location Selector

+
+
+ + +
+
Selected ID: {{locId}}
+
+ valueChange Handler Produced: {{aLocation ? aLocation.name() : '(none)'}} +
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts index c44a7f7a0b..caf86fa8d2 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts @@ -109,6 +109,9 @@ export class SandboxComponent implements OnInit { myTimeForm: FormGroup; + locId = 1; // Stacks + aLocation: IdlObject; // acpl + constructor( private idl: IdlService, private org: OrgService, diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts index a33deb86ce..15be7f31d5 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts @@ -7,6 +7,7 @@ import {SandboxComponent} from './sandbox.component'; import {ReactiveFormsModule} from '@angular/forms'; import {SampleDataService} from '@eg/share/util/sample-data.service'; import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-select.module'; +import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-location-select.module'; @NgModule({ declarations: [ @@ -17,6 +18,7 @@ import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-sele TranslateModule, FmRecordEditorModule, OrgFamilySelectModule, + ItemLocationSelectModule, SandboxRoutingModule, ReactiveFormsModule ], -- 2.43.2