LP1821382 Link as conjoined items menu action
authorBill Erickson <berickxx@gmail.com>
Tue, 26 Mar 2019 17:30:54 +0000 (13:30 -0400)
committerDan Wells <dbw2@calvin.edu>
Wed, 29 May 2019 19:30:50 +0000 (15:30 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
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/share/holdings/conjoined-items-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.module.ts

index dddba6e..8957345 100644 (file)
@@ -50,6 +50,7 @@
 <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>
 <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>
+<eg-conjoined-items-dialog #conjoinedDialog></eg-conjoined-items-dialog>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
 
 <!-- holdings grid -->
 <div class='eg-copies w-100 mt-3'>
       i18n-label label="Request Items" (onClick)="requestItems($event)">
     </eg-grid-toolbar-action>
 
       i18n-label label="Request Items" (onClick)="requestItems($event)">
     </eg-grid-toolbar-action>
 
+    <eg-grid-toolbar-action
+      i18n-label label="Link as Conjoined to Marked Bib Record"
+      (onClick)="openConjoinedDialog($event)">
+    </eg-grid-toolbar-action>
+
     <!-- row actions : Add -->
 
     <eg-grid-toolbar-action
     <!-- row actions : Add -->
 
     <eg-grid-toolbar-action
index 75efc7a..6405b0d 100644 (file)
@@ -28,6 +28,8 @@ import {DeleteVolcopyDialogComponent
     } from '@eg/staff/share/holdings/delete-volcopy-dialog.component';
 import {BucketDialogComponent
     } from '@eg/staff/share/buckets/bucket-dialog.component';
     } from '@eg/staff/share/holdings/delete-volcopy-dialog.component';
 import {BucketDialogComponent
     } from '@eg/staff/share/buckets/bucket-dialog.component';
+import {ConjoinedItemsDialogComponent
+    } from '@eg/staff/share/holdings/conjoined-items-dialog.component';
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
 
 // The holdings grid models a single HoldingsTree, composed of HoldingsTreeNodes
 // flattened on-demand into a list of HoldingEntry objects.
@@ -99,6 +101,8 @@ export class HoldingsMaintenanceComponent implements OnInit {
         private deleteVolcopy: DeleteVolcopyDialogComponent;
     @ViewChild('bucketDialog')
         private bucketDialog: BucketDialogComponent;
         private deleteVolcopy: DeleteVolcopyDialogComponent;
     @ViewChild('bucketDialog')
         private bucketDialog: BucketDialogComponent;
+    @ViewChild('conjoinedDialog')
+        private conjoinedDialog: ConjoinedItemsDialogComponent;
 
     holdingsTree: HoldingsTree;
 
 
     holdingsTree: HoldingsTree;
 
@@ -851,7 +855,18 @@ export class HoldingsMaintenanceComponent implements OnInit {
             this.bucketDialog.itemIds = copyIds;
             this.bucketDialog.open({size: 'lg'}).then(
                 // No need to reload the grid after adding items to buckets.
             this.bucketDialog.itemIds = copyIds;
             this.bucketDialog.open({size: 'lg'}).then(
                 // No need to reload the grid after adding items to buckets.
-                () => {},
+                ok => {},
+                dismissed => {}
+            );
+        }
+    }
+
+    openConjoinedDialog(rows: HoldingsEntry[]) {
+        const copyIds = this.selectedCopyIds(rows);
+        if (copyIds.length > 0) {
+            this.conjoinedDialog.copyIds = copyIds;
+            this.conjoinedDialog.open({size: 'sm'}).then(
+                ok => {}, // No grid reload required
                 dismissed => {}
             );
         }
                 dismissed => {}
             );
         }
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.html
new file mode 100644 (file)
index 0000000..906ce24
--- /dev/null
@@ -0,0 +1,42 @@
+
+
+<eg-string #successMsg
+    text="Successfully Attached Conjoined Item(s)" i18n-text></eg-string>
+<eg-string #errorMsg 
+    text="Failed To Attach Conjoined Item(s)" i18n-text></eg-string>
+
+<ng-template #dialogContent>
+    <div class="modal-header bg-info">
+      <h4 class="modal-title">
+        <span i18n>Attach {{copyIds.length}} Conjoined Item(s)</span>
+      </h4>
+      <button type="button" class="close" 
+        i18n-aria-label aria-label="Close" (click)="dismiss('cross_click')">
+        <span aria-hidden="true">&times;</span>
+      </button>
+    </div>
+    <div class="modal-body">
+      <div class="row form-validated">
+        <div class="col-lg-4" i18n>
+          <label for="cbox-peer-types">Peer Type:</label>
+        </div>
+        <div class="col-lg-8">
+          <eg-combobox [entries]="peerTypes" [required]="true"
+            i18n-placeholder placeholder="Peer Type..."
+            id="cbox-peer-types" (onChange)="peerTypeChanged($event)">
+          </eg-combobox>
+        </div>
+      </div>
+    </div>
+    <div class="modal-footer">
+      <ng-container>
+        <button type="button" class="btn btn-warning" 
+          (click)="dismiss('canceled')" i18n>Cancel</button>
+        <button type="button" class="btn btn-success" 
+          (click)="linkCopies()" [disabled]="!peerType" i18n>
+          Attach
+        </button>
+      </ng-container>
+    </div>
+  </ng-template>
+  
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/conjoined-items-dialog.component.ts
new file mode 100644 (file)
index 0000000..51000a7
--- /dev/null
@@ -0,0 +1,120 @@
+import {Component, OnInit, OnDestroy, Input, ViewChild, Renderer2} from '@angular/core';
+import {Subscription} from 'rxjs';
+import {IdlService} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StoreService} from '@eg/core/store.service';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+
+
+/**
+ * Dialog for linking conjoined items.
+ */
+
+@Component({
+  selector: 'eg-conjoined-items-dialog',
+  templateUrl: 'conjoined-items-dialog.component.html'
+})
+
+export class ConjoinedItemsDialogComponent
+    extends DialogComponent implements OnInit, OnDestroy {
+
+    @Input() copyIds: number[];
+    ids: number[]; // copy of list so we can pop()
+
+    peerType: number;
+    numSucceeded: number;
+    numFailed: number;
+    peerTypes: ComboboxEntry[];
+    peerRecord: number;
+
+    onOpenSub: Subscription;
+
+    @ViewChild('successMsg')
+        private successMsg: StringComponent;
+
+    @ViewChild('errorMsg')
+        private errorMsg: StringComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private idl: IdlService,
+        private pcrud: PcrudService,
+        private localStore: StoreService) {
+        super(modal); // required for subclassing
+        this.peerTypes = [];
+    }
+
+    ngOnInit() {
+        this.onOpenSub = this.onOpen$.subscribe(() => {
+            this.ids = [].concat(this.copyIds);
+            this.numSucceeded = 0;
+            this.numFailed = 0;
+            this.peerRecord =
+                this.localStore.getLocalItem('eg.cat.marked_conjoined_record');
+
+            if (!this.peerRecord) {
+                this.close(false);
+            }
+
+            if (this.peerTypes.length === 0) {
+                this.getPeerTypes();
+            }
+        });
+    }
+
+    ngOnDestroy() {
+        this.onOpenSub.unsubscribe();
+    }
+
+    getPeerTypes(): Promise<any> {
+        return this.pcrud.retrieveAll('bpt', {}, {atomic: true}).toPromise()
+        .then(types =>
+            // Map types to ComboboxEntry's
+            this.peerTypes = types.map(t => ({id: t.id(), label: t.name()}))
+        );
+    }
+
+    peerTypeChanged(entry: ComboboxEntry) {
+        if (entry) {
+            this.peerType = entry.id;
+        } else {
+            this.peerType = null;
+        }
+    }
+
+    linkCopies(): Promise<any> {
+
+        if (this.ids.length === 0) {
+            this.close(this.numSucceeded > 0);
+            return Promise.resolve();
+        }
+
+        const id = this.ids.pop();
+        const map = this.idl.create('bpbcm');
+        map.peer_record(this.peerRecord);
+        map.target_copy(id);
+        map.peer_type(this.peerType);
+
+        return this.pcrud.create(map).toPromise().then(
+            ok => {
+                this.successMsg.current().then(msg => this.toast.success(msg));
+                this.numSucceeded++;
+                return this.linkCopies();
+            },
+            err => {
+                this.numFailed++;
+                console.error(err);
+                this.errorMsg.current().then(msg => this.toast.warning(msg));
+                return this.linkCopies();
+            }
+        );
+    }
+}
+
+
+
index 97a65ce..d9ae4fb 100644 (file)
@@ -6,6 +6,7 @@ import {MarkMissingDialogComponent} from './mark-missing-dialog.component';
 import {CopyAlertsDialogComponent} from './copy-alerts-dialog.component';
 import {ReplaceBarcodeDialogComponent} from './replace-barcode-dialog.component';
 import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
 import {CopyAlertsDialogComponent} from './copy-alerts-dialog.component';
 import {ReplaceBarcodeDialogComponent} from './replace-barcode-dialog.component';
 import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
+import {ConjoinedItemsDialogComponent} from './conjoined-items-dialog.component';
 
 @NgModule({
     declarations: [
 
 @NgModule({
     declarations: [
@@ -13,7 +14,8 @@ import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
       ReplaceBarcodeDialogComponent,
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
       ReplaceBarcodeDialogComponent,
-      DeleteVolcopyDialogComponent
+      DeleteVolcopyDialogComponent,
+      ConjoinedItemsDialogComponent
     ],
     imports: [
         StaffCommonModule
     ],
     imports: [
         StaffCommonModule
@@ -23,7 +25,8 @@ import {DeleteVolcopyDialogComponent} from './delete-volcopy-dialog.component';
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
       ReplaceBarcodeDialogComponent,
       MarkMissingDialogComponent,
       CopyAlertsDialogComponent,
       ReplaceBarcodeDialogComponent,
-      DeleteVolcopyDialogComponent
+      DeleteVolcopyDialogComponent,
+      ConjoinedItemsDialogComponent
     ],
     providers: [
         HoldingsService
     ],
     providers: [
         HoldingsService