From 3d16fb2158359ae528f1b2d86e3a4c008326b3bd 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 Signed-off-by: Michele Morgan --- .../staff/catalog/basket-actions.component.html | 5 ++ .../app/staff/catalog/basket-actions.component.ts | 10 ++-- .../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, 115 insertions(+), 17 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 7014944d49..f70c1c5b07 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 @@ -34,6 +34,11 @@ + + 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 50f86113bd..bcd5dfe84a 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 @@ -10,6 +10,7 @@ import {StaffCatalogService} from './catalog.service'; import {BucketDialogComponent } from '@eg/staff/share/buckets/bucket-dialog.component'; import {ProgressDialogComponent} from '@eg/share/dialog/progress.component'; +import {BasketSelectionService} from './basket-selection.service'; const MAX_FROM_SEARCH_RESULTS = 1000; @@ -35,6 +36,7 @@ export class BasketActionsComponent implements OnInit { private basket: BasketService, private cat: CatalogService, private staffCat: StaffCatalogService + private basketSelect: BasketSelectionService ) { this.basketAction = ''; } @@ -84,14 +86,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 @@ -109,7 +111,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', @@ -126,7 +128,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 4346fc044c..8ca7b0f464 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