From 5d93451a610d90b86a7ec093913d7a0f5657e764 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 14 Oct 2020 16:53:59 -0400 Subject: [PATCH] LP1899406 Staff catalog view basket selection In the Angular staff catalog, support applying basket actions to selected items within a basket, without selection/desection impacting the contents of the basket. Note that 'Export All Basket Records' applies to the entire basket and is not affected by the selection. All other actions, besides View and Clear Basket, are affected by the selection. Signed-off-by: Bill Erickson --- .../staff/catalog/basket-actions.component.html | 4 +- .../app/staff/catalog/basket-actions.component.ts | 12 ++-- .../app/staff/catalog/basket-selection.service.ts | 68 ++++++++++++++++++++++ .../eg2/src/app/staff/catalog/catalog.module.ts | 4 +- .../app/staff/catalog/result/record.component.ts | 16 ++++- .../staff/catalog/result/results.component.html | 10 ++-- .../app/staff/catalog/result/results.component.ts | 19 ++++-- 7 files changed, 113 insertions(+), 20 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/basket-selection.service.ts diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html index 752557c503..0ffb9d3886 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html +++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html @@ -27,9 +27,9 @@ + (click)="applyAction('bucket')" i18n>Add to Bucket + (click)="applyAction('export_marc')" i18n>Export All Basket Records diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts index b5f35722bb..42f6d5ce8a 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts @@ -6,6 +6,7 @@ import {AuthService} from '@eg/core/auth.service'; import {PrintService} from '@eg/share/print/print.service'; import {BucketDialogComponent } from '@eg/staff/share/buckets/bucket-dialog.component'; +import {BasketSelectionService} from './basket-selection.service'; @Component({ selector: 'eg-catalog-basket-actions', @@ -23,7 +24,8 @@ export class BasketActionsComponent implements OnInit { private net: NetService, private auth: AuthService, private printer: PrintService, - private basket: BasketService + private basket: BasketService, + private basketSelect: BasketSelectionService ) { this.basketAction = ''; } @@ -53,14 +55,14 @@ export class BasketActionsComponent implements OnInit { break; case 'hold': - this.basket.getRecordIds().then(ids => { + this.basketSelect.getRecordIds().then(ids => { this.router.navigate(['/staff/catalog/hold/T'], {queryParams: {target: ids}}); }); break; case 'print': - this.basket.getRecordIds().then(ids => { + this.basketSelect.getRecordIds().then(ids => { this.net.request( 'open-ils.search', 'open-ils.search.biblio.record.print', ids @@ -78,7 +80,7 @@ export class BasketActionsComponent implements OnInit { break; case 'email': - this.basket.getRecordIds().then(ids => { + this.basketSelect.getRecordIds().then(ids => { this.net.request( 'open-ils.search', 'open-ils.search.biblio.record.email', @@ -95,7 +97,7 @@ export class BasketActionsComponent implements OnInit { break; case 'bucket': - this.basket.getRecordIds().then(ids => { + this.basketSelect.getRecordIds().then(ids => { this.addToBucketDialog.bucketClass = 'biblio'; this.addToBucketDialog.itemIds = ids; this.addToBucketDialog.open({size: 'lg'}); diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/basket-selection.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/basket-selection.service.ts new file mode 100644 index 0000000000..892de60e23 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/catalog/basket-selection.service.ts @@ -0,0 +1,68 @@ +import {Injectable, EventEmitter} from '@angular/core'; +import {BasketService} from '@eg/share/catalog/basket.service'; +import {Observable} from 'rxjs'; + +/* + * Provides a way to select / deselect items in the basket view, + * with the same API as the basket service, without affecting the + * contents of the basket. + */ + +@Injectable() +export class BasketSelectionService { + + idList: number[]; + + constructor(private basket: BasketService) { + this.idList = []; + } + + init(): Promise { + return this.basket.getRecordIds().then(ids => this.setRecordIds(ids)); + } + + hasRecordId(id: number): boolean { + return this.idList.indexOf(Number(id)) > -1; + } + + recordCount(): number { + return this.idList.length; + } + + getRecordIds(): Promise { + return Promise.resolve(this.idList); + } + + setRecordIds(ids: number[]): Promise { + this.idList = ids; + return Promise.resolve(this.idList); + } + + addRecordIds(ids: number[]): Promise { + ids = ids.filter(id => !this.hasRecordId(id)); // avoid dupes + + if (ids.length === 0) { + return Promise.resolve(this.idList); + } + return this.setRecordIds( + this.idList.concat(ids.map(id => Number(id)))); + } + + removeRecordIds(ids: number[]): Promise { + + if (this.idList.length === 0) { + return Promise.resolve(this.idList); + } + + const wantedIds = this.idList.filter( + id => ids.indexOf(Number(id)) < 0); + + return this.setRecordIds(wantedIds); // OK if empty + } + + removeAllRecordIds(): Promise { + return this.setRecordIds([]); + } +} + + diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts index 9b7d57acdc..e0bdb10635 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts @@ -31,6 +31,7 @@ import {CnBrowseResultsComponent} from './cnbrowse/results.component'; import {SearchTemplatesComponent} from './search-templates.component'; import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module'; import {PreferencesComponent} from './prefs.component'; +import {BasketSelectionService} from './basket-selection.service'; @NgModule({ declarations: [ @@ -69,7 +70,8 @@ import {PreferencesComponent} from './prefs.component'; MarcEditModule ], providers: [ - StaffCatalogService + StaffCatalogService, + BasketSelectionService ] }) diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts index bd066a72e0..eb22bd8e02 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts @@ -10,6 +10,7 @@ import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service'; import {StaffCatalogService} from '../catalog.service'; import {BasketService} from '@eg/share/catalog/basket.service'; import {CourseService} from '@eg/staff/share/course.service'; +import {BasketSelectionService} from '../basket-selection.service'; @Component({ selector: 'eg-catalog-result-record', @@ -32,6 +33,8 @@ export class ResultRecordComponent implements OnInit, OnDestroy { hasCourse = false; courses: any[] = []; + recordSelector: BasketService | BasketSelectionService; + constructor( private router: Router, private org: OrgService, @@ -39,7 +42,8 @@ export class ResultRecordComponent implements OnInit, OnDestroy { private catUrl: CatalogUrlService, private staffCat: StaffCatalogService, private basket: BasketService, - private course: CourseService + private course: CourseService, + private basketSelect: BasketSelectionService ) {} ngOnInit() { @@ -51,6 +55,12 @@ export class ResultRecordComponent implements OnInit, OnDestroy { this.basketSub = this.basket.onChange.subscribe(() => { this.isRecordSelected = this.basket.hasRecordId(this.summary.id); }); + + if (this.searchContext.showBasket) { + this.recordSelector = this.basketSelect; + } else { + this.recordSelector = this.basket; + } } ngOnDestroy() { @@ -127,9 +137,9 @@ export class ResultRecordComponent implements OnInit, OnDestroy { toggleBasketEntry() { if (this.isRecordSelected) { - return this.basket.addRecordIds([this.summary.id]); + return this.recordSelector.addRecordIds([this.summary.id]); } else { - return this.basket.removeRecordIds([this.summary.id]); + return this.recordSelector.removeRecordIds([this.summary.id]); } } } diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html index 91bc2fde17..ac78bbad21 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html @@ -32,7 +32,7 @@
-
+

Results for browse "{{searchContext.termSearch.browseEntry.value()}}"

@@ -40,11 +40,11 @@

Search Results ({{searchContext.result.count}})

-
+

Basket View

-
-
+
+ [ngClass]="{'col-lg-10': !searchContext.showBasket, 'col-lg-12': searchContext.showBasket}">
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts index edcb381044..2cc6604a00 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts @@ -10,6 +10,7 @@ import {PcrudService} from '@eg/core/pcrud.service'; import {StaffCatalogService} from '../catalog.service'; import {IdlObject} from '@eg/core/idl.service'; import {BasketService} from '@eg/share/catalog/basket.service'; +import {BasketSelectionService} from '../basket-selection.service'; @Component({ selector: 'eg-catalog-results', @@ -29,6 +30,8 @@ export class ResultsComponent implements OnInit, OnDestroy { routeSub: Subscription; basketSub: Subscription; + recordSelector: BasketService | BasketSelectionService; + constructor( private route: ActivatedRoute, private pcrud: PcrudService, @@ -36,7 +39,8 @@ export class ResultsComponent implements OnInit, OnDestroy { private bib: BibRecordService, private catUrl: CatalogUrlService, private staffCat: StaffCatalogService, - private basket: BasketService + private basket: BasketService, + private basketSelect: BasketSelectionService ) {} ngOnInit() { @@ -68,6 +72,13 @@ export class ResultsComponent implements OnInit, OnDestroy { // Watch for basket changes applied by other components. this.basketSub = this.basket.onChange.subscribe( () => this.applyRecordSelection()); + + if (this.searchContext.showBasket) { + this.recordSelector = this.basketSelect; + this.basketSelect.init(); + } else { + this.recordSelector = this.basket; + } } ngOnDestroy() { @@ -84,7 +95,7 @@ export class ResultsComponent implements OnInit, OnDestroy { const ids = this.searchContext.currentResultIds(); let allChecked = true; ids.forEach(id => { - if (!this.basket.hasRecordId(id)) { + if (!this.recordSelector.hasRecordId(id)) { allChecked = false; } }); @@ -121,9 +132,9 @@ export class ResultsComponent implements OnInit, OnDestroy { const ids = this.searchContext.currentResultIds(); if (this.allRecsSelected) { - this.basket.addRecordIds(ids); + this.recordSelector.addRecordIds(ids); } else { - this.basket.removeRecordIds(ids); + this.recordSelector.removeRecordIds(ids); } } } -- 2.11.0