LP1821382 Add Items to Bucket menu action
authorBill Erickson <berickxx@gmail.com>
Tue, 26 Mar 2019 15:17:35 +0000 (11:17 -0400)
committerDan Wells <dbw2@calvin.edu>
Wed, 29 May 2019 19:30:50 +0000 (15:30 -0400)
Includes changes to the existing record bucket dialog to support all
bucket types.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Dan Wells <dbw2@calvin.edu>

Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.html
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/record/actions.component.html
Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.html
Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html
Open-ILS/src/eg2/src/app/staff/common.module.ts
Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.html [moved from Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.html with 72% similarity]
Open-ILS/src/eg2/src/app/staff/share/buckets/bucket-dialog.component.ts [moved from Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket-dialog.component.ts with 56% similarity]

index e4ef845..2c1b3c3 100644 (file)
@@ -75,8 +75,8 @@
           </li>
           <li class="list-group-item">
             <div class="d-flex">
-              <eg-record-bucket-dialog #bucketDialog [queueId]="queueId">
-              </eg-record-bucket-dialog>
+              <eg-bucket-dialog #bucketDialog bucketClass="biblio" [fromBibQueue]="queueId">
+              </eg-bucket-dialog>
               <div class="flex-1">
                 <a [routerLink]="" (click)="bucketDialog.open({size:'lg'})" i18n>
                   Copy Queue To Bucket
index 5837ace..4857db8 100644 (file)
@@ -1,5 +1,5 @@
-<eg-record-bucket-dialog #addBasketToBucketDialog>
-</eg-record-bucket-dialog>
+<eg-bucket-dialog #addBasketToBucketDialog>
+</eg-bucket-dialog>
 
 <div class="row">
   <div class="col-lg-4 pr-1">
index e00a396..f96df6f 100644 (file)
@@ -5,8 +5,8 @@ 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 {RecordBucketDialogComponent
-    } from '@eg/staff/share/buckets/record-bucket-dialog.component';
+import {BucketDialogComponent
+    } from '@eg/staff/share/buckets/bucket-dialog.component';
 
 @Component({
   selector: 'eg-catalog-basket-actions',
@@ -17,7 +17,7 @@ export class BasketActionsComponent implements OnInit {
     basketAction: string;
 
     @ViewChild('addBasketToBucketDialog')
-        addToBucketDialog: RecordBucketDialogComponent;
+        addToBucketDialog: BucketDialogComponent;
 
     constructor(
         private router: Router,
@@ -89,8 +89,12 @@ export class BasketActionsComponent implements OnInit {
 
             case 'bucket':
                 this.basket.getRecordIds().then(ids => {
-                    this.addToBucketDialog.recordId = ids;
-                    this.addToBucketDialog.open({size: 'lg'});
+                    this.addToBucketDialog.bucketClass = 'biblio';
+                    this.addToBucketDialog.itemIds = ids;
+                    this.addToBucketDialog.open({size: 'lg'}).then(
+                        ok => {},
+                        dismissed => {}
+                    );
                 });
                 break;
 
index c526099..7634103 100644 (file)
@@ -10,8 +10,8 @@
 <eg-string key="catalog.record.toast.cleared" 
   text="Record Marks Cleared"></eg-string>
 
-<eg-record-bucket-dialog #recordBucketDialog [recordId]="recId">
-</eg-record-bucket-dialog>
+<eg-bucket-dialog #recordBucketDialog bucketClass="biblio" [itemIds]="[recId]">
+</eg-bucket-dialog>
 
 <div class="row ml-0 mr-0">
 
index 42366ed..dddba6e 100644 (file)
@@ -49,6 +49,7 @@
 <eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
 <eg-replace-barcode-dialog #replaceBarcode></eg-replace-barcode-dialog>
 <eg-delete-volcopy-dialog #deleteVolcopy></eg-delete-volcopy-dialog>
+<eg-bucket-dialog #bucketDialog></eg-bucket-dialog>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
       (onClick)="openItemNotes($event, 'create')">
     </eg-grid-toolbar-action>
 
+    <eg-grid-toolbar-action
+      i18n-group group="Add" i18n-label label="Add Items To Bucket"
+      (onClick)="openBucketDialog($event)">
+    Z</eg-grid-toolbar-action>
+
     <!-- row actions: Edit -->
 
     <eg-grid-toolbar-action
index fea6385..75efc7a 100644 (file)
@@ -26,6 +26,8 @@ import {ReplaceBarcodeDialogComponent
     } from '@eg/staff/share/holdings/replace-barcode-dialog.component';
 import {DeleteVolcopyDialogComponent
     } from '@eg/staff/share/holdings/delete-volcopy-dialog.component';
+import {BucketDialogComponent
+    } from '@eg/staff/share/buckets/bucket-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -95,6 +97,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
         private replaceBarcode: ReplaceBarcodeDialogComponent;
     @ViewChild('deleteVolcopy')
         private deleteVolcopy: DeleteVolcopyDialogComponent;
+    @ViewChild('bucketDialog')
+        private bucketDialog: BucketDialogComponent;
 
     holdingsTree: HoldingsTree;
 
@@ -839,4 +843,17 @@ export class HoldingsMaintenanceComponent implements OnInit {
         const params = {target: copyIds, holdFor: 'staff'};
         this.router.navigate(['/staff/catalog/hold/C'], {queryParams: params});
     }
+
+    openBucketDialog(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length > 0) {
+            this.bucketDialog.bucketClass = 'copy';
+            this.bucketDialog.itemIds = copyIds;
+            this.bucketDialog.open({size: 'lg'}).then(
+                // No need to reload the grid after adding items to buckets.
+                () => {},
+                dismissed => {}
+            );
+        }
+    }
 }
index 90f066b..9a65daf 100644 (file)
@@ -3,8 +3,6 @@
   routerLink's
   egDateFilter's
 -->
-<eg-record-bucket-dialog #addToListDialog>
-</eg-record-bucket-dialog>
 
 <div class="col-lg-12 card tight-card mb-2 bg-light">
   <div class="card-body">
                   <span i18n>Place Hold</span>
                 </button>
               </span>
-              <!--
-              <span class="pl-1">
-                <button 
-                  (click)="addToListDialog.recordId=summary.record.id(); addToListDialog.open({size: 'lg'})"
-                  class="btn btn-sm btn-info label-with-material-icon small-text-1">
-                  <span class="material-icons">playlist_add_check</span>
-                  <span i18n>Add to List</span>
-                </button>
-              </span>
-              -->
             </div>
           </div>
         </div>
index 5a83f8a..9c822a2 100644 (file)
@@ -17,7 +17,7 @@ import {StringService} from '@eg/share/string/string.service';
 import {TitleComponent} from '@eg/share/title/title.component';
 import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
 import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
-import {RecordBucketDialogComponent} from '@eg/staff/share/buckets/record-bucket-dialog.component';
+import {BucketDialogComponent} from '@eg/staff/share/buckets/bucket-dialog.component';
 import {BibSummaryComponent} from '@eg/staff/share/bib-summary/bib-summary.component';
 import {TranslateComponent} from '@eg/staff/share/translate/translate.component';
 import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component';
@@ -40,7 +40,7 @@ import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.componen
     OpChangeComponent,
     FmRecordEditorComponent,
     DateSelectComponent,
-    RecordBucketDialogComponent,
+    BucketDialogComponent,
     BibSummaryComponent,
     TranslateComponent,
     AdminPageComponent
@@ -64,7 +64,7 @@ import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.componen
     OpChangeComponent,
     FmRecordEditorComponent,
     DateSelectComponent,
-    RecordBucketDialogComponent,
+    BucketDialogComponent,
     BibSummaryComponent,
     TranslateComponent,
     AdminPageComponent
@@ -1,13 +1,13 @@
 <ng-template #dialogContent>
   <div class="modal-header bg-info">
     <h4 class="modal-title">
-      <ng-container *ngIf="recIds.length > 0">
-        <span *ngIf="recIds.length == 1" i18n>
-          Add Record #{{recIds[0]}} to Bucket</span>
-        <span *ngIf="recIds.length > 1" i18n>
-          Add {{recIds.length}} Record(s) to Bucket</span>
+      <ng-container *ngIf="itemIds.length > 0">
+        <span *ngIf="itemIds.length == 1" i18n>
+          Add Item #{{itemIds[0]}} to Bucket</span>
+        <span *ngIf="itemIds.length > 1" i18n>
+          Add {{itemIds.length}} Items to Bucket</span>
       </ng-container>
-      <span *ngIf="qId" i18n>Add Records from queue #{{qId}} to Bucket</span>
+      <span *ngIf="fromBibQueue" i18n>Add Records from queue #{{fromBibQueue}} to Bucket</span>
     </h4>
     <button type="button" class="close" 
       i18n-aria-label aria-label="Close" 
     <div class="row">
       <div class="col-lg-3 font-weight-bold" i18n>Name of existing bucket</div>
       <div class="col-lg-5">
-         <select 
-          class="form-control"
-          placeholder="Existing Bucket..."
-          i18n-placeholder
-          [(ngModel)]="selectedBucket">
-          <option *ngFor="let bkt of buckets" 
-            value="{{bkt.id()}}">{{bkt.name()}}</option>
-        </select>
+        <eg-combobox [entries]="formatBucketEntries()" 
+          (onChange)="bucketChanged($event)"
+          placeholder="Existing Bucket..." i18n-placeholder>
+        </eg-combobox>
       </div>
       <div class="col-lg-4">
         <button class="btn btn-info" (click)="addToSelected()" i18n 
@@ -6,37 +6,36 @@ import {ToastService} from '@eg/share/toast/toast.service';
 import {AuthService} from '@eg/core/auth.service';
 import {DialogComponent} from '@eg/share/dialog/dialog.component';
 import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 
 /**
  * Dialog for adding bib records to new and existing record buckets.
  */
 
 @Component({
-  selector: 'eg-record-bucket-dialog',
-  templateUrl: 'record-bucket-dialog.component.html'
+  selector: 'eg-bucket-dialog',
+  templateUrl: 'bucket-dialog.component.html'
 })
 
-export class RecordBucketDialogComponent
-    extends DialogComponent implements OnInit {
+export class BucketDialogComponent extends DialogComponent implements OnInit {
 
     selectedBucket: number;
     newBucketName: string;
     newBucketDesc: string;
     buckets: any[];
 
-    @Input() bucketType: string;
+    @Input() bucketClass: 'biblio' | 'user' | 'callnumber' | 'copy';
+    @Input() bucketType: string; // e.g. staff_client
 
-    // Add one or more bib records to bucket by ID.
-    recIds: number[];
-    @Input() set recordId(id: number | number[]) {
-        this.recIds = [].concat(id);
-    }
+    // ID's of items to add to the bucket
+    @Input() itemIds: number[];
 
-    // Add items from a (vandelay) bib queue to a bucket
-    qId: number;
-    @Input() set queueId(id: number) {
-        this.qId = id;
-    }
+    // If set, itemIds will be derived from the records in a bib queue
+    @Input() fromBibQueue: number;
+
+    // bucket item classes are these plus a following 'i'.
+    bucketFmClass: 'ccb' | 'ccnb' | 'cbreb' | 'cub';
+    targetField: string;
 
     constructor(
         private modal: NgbModal, // required for passing to parent
@@ -47,40 +46,77 @@ export class RecordBucketDialogComponent
         private evt: EventService,
         private auth: AuthService) {
         super(modal); // required for subclassing
-        this.recIds = [];
+        this.buckets = [];
+        this.itemIds = [];
+        this.fromBibQueue = null;
     }
 
     ngOnInit() {
-
-        if (this.qId) {
-            this.bucketType = 'vandelay_queue';
-        } else {
-            this.bucketType = 'staff_client';
-        }
-
         this.onOpen$.subscribe(ok => {
-            // Reset data on dialog open
-
-            this.selectedBucket = null;
-            this.newBucketName = '';
-            this.newBucketDesc = '';
-
+            this.reset(); // Reset data on dialog open
             this.net.request(
                 'open-ils.actor',
                 'open-ils.actor.container.retrieve_by_class.authoritative',
                 this.auth.token(), this.auth.user().id(),
-                'biblio', this.bucketType
+                this.bucketClass, this.bucketType
             ).subscribe(buckets => this.buckets = buckets);
         });
     }
 
+    reset() {
+        this.selectedBucket = null;
+        this.newBucketName = '';
+        this.newBucketDesc = '';
+
+        if (!this.bucketClass) {
+            this.bucketClass = 'biblio';
+        }
+
+        switch (this.bucketClass) {
+            case 'biblio':
+                if (this.fromBibQueue) {
+                    this.bucketType = 'vandelay_queue';
+                }
+                this.bucketFmClass = 'cbreb';
+                this.targetField = 'target_biblio_record_entry';
+                break;
+            case 'copy':
+                this.bucketFmClass = 'ccb';
+                this.targetField = 'target_copy';
+                break;
+            case 'callnumber':
+                this.bucketFmClass = 'ccnb';
+                this.targetField = 'target_call_number';
+                break;
+            case 'user':
+                this.bucketFmClass = 'cub';
+                this.targetField = 'target_user';
+        }
+
+        if (!this.bucketType) {
+            this.bucketType = 'staff_client';
+        }
+    }
+
     addToSelected() {
         this.addToBucket(this.selectedBucket);
     }
 
+    bucketChanged(entry: ComboboxEntry) {
+        if (entry) {
+            this.selectedBucket = entry.id;
+        } else {
+            this.selectedBucket = null;
+        }
+    }
+
+    formatBucketEntries(): ComboboxEntry[] {
+        return this.buckets.map(b => ({id: b.id(), label: b.name()}));
+    }
+
     // Create a new bucket then add the record
     addToNew() {
-        const bucket = this.idl.create('cbreb');
+        const bucket = this.idl.create(this.bucketFmClass);
 
         bucket.owner(this.auth.user().id());
         bucket.name(this.newBucketName);
@@ -90,7 +126,7 @@ export class RecordBucketDialogComponent
         this.net.request(
             'open-ils.actor',
             'open-ils.actor.container.create',
-            this.auth.token(), 'biblio', bucket
+            this.auth.token(), this.bucketClass, bucket
         ).subscribe(bktId => {
             const evt = this.evt.parse(bktId);
             if (evt) {
@@ -106,27 +142,27 @@ export class RecordBucketDialogComponent
     }
 
     addToBucket(id: number) {
-        if (this.recIds.length > 0) {
+        if (this.itemIds.length > 0) {
             this.addRecordToBucket(id);
-        } else if (this.qId) {
-            this.addQueueToBucket(id);
+        } else if (this.fromBibQueue) {
+            this.addBibQueueToBucket(id);
         }
     }
 
     // Add the record(s) to the bucket with provided ID.
     addRecordToBucket(bucketId: number) {
         const items = [];
-        this.recIds.forEach(recId => {
-            const item = this.idl.create('cbrebi');
+        this.itemIds.forEach(itemId => {
+            const item = this.idl.create(this.bucketFmClass + 'i');
             item.bucket(bucketId);
-            item.target_biblio_record_entry(recId);
+            item[this.targetField](itemId);
             items.push(item);
         });
 
         this.net.request(
             'open-ils.actor',
             'open-ils.actor.container.item.create',
-            this.auth.token(), 'biblio', items
+            this.auth.token(), this.bucketClass, items
         ).subscribe(resp => {
             const evt = this.evt.parse(resp);
             if (evt) {
@@ -137,14 +173,14 @@ export class RecordBucketDialogComponent
         });
     }
 
-    addQueueToBucket(bucketId: number) {
+    addBibQueueToBucket(bucketId: number) {
         const bucket = this.buckets.filter(b => b.id() === bucketId)[0];
         if (!bucket) { return; }
 
         this.net.request(
             'open-ils.vandelay',
             'open-ils.vandelay.bib_queue.to_bucket',
-            this.auth.token(), this.qId, bucket.name()
+            this.auth.token(), this.fromBibQueue, bucket.name()
         ).toPromise().then(resp => {
             const evt = this.evt.parse(resp);
             if (evt) {