LP1885179 Staff catalog add results to basket
authorBill Erickson <berickxx@gmail.com>
Thu, 25 Jun 2020 19:40:08 +0000 (15:40 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Wed, 10 Mar 2021 20:32:47 +0000 (15:32 -0500)
Adds a new staff catalog option to add all search results to the basket.
prior to this, results could only be added one page at a time to the
basket.

Note there is currently a 1k limit on the number of items added from a
search result set.  This could be modified.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Mike Risher <mrisher@catalyte.io>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.html
Open-ILS/src/eg2/src/app/staff/catalog/basket-actions.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts

index af996c2..36c3ead 100644 (file)
@@ -99,11 +99,14 @@ export class CatalogService {
 
         return this.basket.getRecordIds().then(ids => {
 
+            const pageIds =
+                ids.slice(ctx.pager.offset, ctx.pager.limit + ctx.pager.offset);
+
             // Map our list of IDs into a search results object
             // the search context can understand.
             const result = {
                 count: ids.length,
-                ids: ids.map(id => [id])
+                ids: pageIds.map(id => [id])
             };
 
             this.applyResultData(ctx, result);
index 752557c..7014944 100644 (file)
@@ -1,6 +1,8 @@
 <eg-bucket-dialog #addBasketToBucketDialog>
 </eg-bucket-dialog>
 
+<eg-progress-dialog #addAllProgress></eg-progress-dialog>
+
 <div class="d-flex justify-content-end">
   <div class="pr-1">
     <div class="float-right">
   <div class="pr-1">
     <div ngbDropdown placement="bottom-right">
       <button class="btn btn-light" id="basketActions"
-        [disabled]="!basketCount()"
         ngbDropdownToggle i18n>Basket Actions</button>
       <div ngbDropdownMenu aria-labelledby="basketActions">
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="isMetarecordSearch()"
+          (click)="applyAction('add_all')" i18n>Add All Search Results</button>
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('view')" i18n>View Basket</button>
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('hold')" i18n>Place Hold</button>
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('print')" i18n>Print Title Details</button>
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('email')" i18n>Email Title Details</button>
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('bucket')" i18n>Add Basket to Bucket</button>
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('export_marc')" i18n>Export Records</button>
-        <button class="dropdown-item"
+        <button class="dropdown-item" [disabled]="!basketCount()"
           (click)="applyAction('clear')" i18n>Clear Basket</button>
       </div>
     </div>
index b5f3572..50f8611 100644 (file)
@@ -1,11 +1,17 @@
-import {Component, OnInit, ViewChild} from '@angular/core';
+import {Component, Input, OnInit, ViewChild} from '@angular/core';
 import {BasketService} from '@eg/share/catalog/basket.service';
 import {Router} from '@angular/router';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PrintService} from '@eg/share/print/print.service';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
+import {StaffCatalogService} from './catalog.service';
 import {BucketDialogComponent
     } from '@eg/staff/share/buckets/bucket-dialog.component';
+import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
+
+const MAX_FROM_SEARCH_RESULTS = 1000;
 
 @Component({
   selector: 'eg-catalog-basket-actions',
@@ -18,12 +24,17 @@ export class BasketActionsComponent implements OnInit {
     @ViewChild('addBasketToBucketDialog', { static: true })
         addToBucketDialog: BucketDialogComponent;
 
+    @ViewChild('addAllProgress', {static: true})
+        addAllProgress: ProgressDialogComponent;
+
     constructor(
         private router: Router,
         private net: NetService,
         private auth: AuthService,
         private printer: PrintService,
-        private basket: BasketService
+        private basket: BasketService,
+        private cat: CatalogService,
+        private staffCat: StaffCatalogService
     ) {
         this.basketAction = '';
     }
@@ -35,13 +46,33 @@ export class BasketActionsComponent implements OnInit {
         return this.basket.recordCount();
     }
 
+    isMetarecordSearch(): boolean {
+        return this.staffCat.searchContext &&
+            this.staffCat.searchContext.termSearch.isMetarecordSearch();
+    }
+
     // TODO: confirmation dialogs?
 
     applyAction(action: string) {
         this.basketAction = action;
-        console.debug('Performing basket action', this.basketAction);
 
         switch (this.basketAction) {
+
+            case 'add_all':
+                // Add all search results to basket.
+
+                this.addAllProgress.open();
+
+                const ctx = this.staffCat.cloneContext(this.staffCat.searchContext);
+                ctx.pager.offset = 0;
+                ctx.pager.limit = MAX_FROM_SEARCH_RESULTS;
+
+                this.cat.search(ctx)
+                .then(_ => this.basket.addRecordIds(ctx.currentResultIds()))
+                .then(_ => this.addAllProgress.close());
+
+                break;
+
             case 'view':
                 // This does not propagate search params -- unclear if needed.
                 this.router.navigate(['/staff/catalog/search'],
index 63c5c4e..5ed26d4 100644 (file)
@@ -89,7 +89,9 @@ export class StaffCatalogService {
 
     cloneContext(context: CatalogSearchContext): CatalogSearchContext {
         const params: any = this.catUrl.toUrlParams(context);
-        return this.catUrl.fromUrlHash(params);
+        const ctx = this.catUrl.fromUrlHash(params);
+        ctx.isStaff = true; // not carried in the URL
+        return ctx;
     }
 
     applySearchDefaults(): void {