LP1936233 Item status actions menu
authorBill Erickson <berickxx@gmail.com>
Tue, 20 Jul 2021 21:57:44 +0000 (17:57 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 20 Jul 2021 21:57:44 +0000 (17:57 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/share/holdings/mark-items-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/holdings/mark-items-dialog.component.ts [new file with mode: 0644]

diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/mark-items-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/holdings/mark-items-dialog.component.html
new file mode 100644 (file)
index 0000000..9b70023
--- /dev/null
@@ -0,0 +1,65 @@
+
+
+<eg-string #successMsg
+    text="Successfully Marked Item" i18n-text></eg-string>
+<eg-string #errorMsg 
+    text="Failed To Mark Item" i18n-text></eg-string>
+
+<eg-confirm-dialog #confirmDialog
+  i18n-dialogBody dialogBody="Mark {{copyCount}} item(s} as {{statusName}}?">
+
+<eg-confirm-dialog #confirmDelete i18n-dialogTitle i18n-dialogBody 
+  dialogTitle="Confirm Delete"
+  dialogBody="Item {{currentBarcode}} is in a status with a copy delete warning.">
+</eg-confirm-dialog>
+
+<eg-confirm-dialog #confirmLastHold i18n-dialogTitle i18n-dialogBody 
+  dialogTitle="Confirm Last Hold"
+  dialogBody="Item {{currentBarcode}} is the last item to fill a hold">
+</eg-confirm-dialog>
+
+<eg-confirm-dialog #confirmCheckedOut i18n-dialogTitle i18n-dialogBody 
+  dialogTitle="Confirm Checked Out"
+  dialogBody="Item {{currentBarcode}} is checked out.">
+</eg-confirm-dialog>
+
+<eg-confirm-dialog #confirmInTransit i18n-dialogTitle i18n-dialogBody 
+  dialogTitle="Confirm In Transit"
+  dialogBody="Item {{currentBarcode}} is in transit.">
+</eg-confirm-dialog>
+
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 class="modal-title">
+      <span i18n>Mark Item {{statusName}}</span>
+    </h4>
+    <button type="button" class="close" 
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <div class="row d-flex justify-content-center">
+      <h5>Mark {{copies.length}} Item(s) {{statusName}}?</h5>
+    </div>
+    <div class="row" *ngIf="numSucceeded > 0">
+      <div class="col-lg-12" i18n>
+        {{numSucceeded}} Items(s) Successfully Marked {{statusName}}
+      </div>
+    </div>
+    <div class="row" *ngIf="numFailed > 0">
+      <div class="col-lg-12">
+        <div class="alert alert-warning">
+          {{numFailed}} Items(s) Failed to be Marked {{statusName}}
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-warning" 
+      (click)="close()" i18n>Cancel</button>
+    <button type="button" class="btn btn-success" 
+      (click)="markItems()" i18n>Mark {{statusName}}</button>
+  </div>
+</ng-template>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/mark-items-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/mark-items-dialog.component.ts
new file mode 100644 (file)
index 0000000..42a666d
--- /dev/null
@@ -0,0 +1,161 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {of, empty, from, Observable, throwError} from 'rxjs';
+import {concatMap, map} from 'rxjs/operators';
+import {NetService} from '@eg/core/net.service';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {AuthService} from '@eg/core/auth.service';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {StringComponent} from '@eg/share/string/string.component';
+import {HoldingsService} from './holdings.service';
+
+
+/**
+ * Dialog for marking items various statuses
+ */
+
+
+interface MarkItemArgs {
+    handle_checkin?: boolean;
+    handle_transit?: boolean;
+    handle_last_hold_copy?: boolean;
+    handle_copy_delete_warning?: boolean;
+}
+
+
+@Component({
+  selector: 'eg-mark-items-dialog',
+  templateUrl: 'mark-items-dialog.component.html'
+})
+
+export class MarkItemsDialogComponent
+    extends DialogComponent implements OnInit {
+
+    @Input() markAs: 'missing' | 'discard';
+    @Input() copies: IdlObject[];
+
+    copyCount = 0;
+    method: string;
+    statusName: string;
+    copyStatuses: {[statId: number]: IdlObject} = {};
+    numSucceeded: number;
+    numFailed: number;
+    currentBarcode: string;
+
+    @ViewChild('successMsg') private successMsg: StringComponent;
+    @ViewChild('errorMsg') private errorMsg: StringComponent;
+
+    @ViewChild('confirmDelete') private confirmDelete: ConfirmDialogComponent;
+    @ViewChild('confirmLastHold') private confirmLastHold: ConfirmDialogComponent;
+    @ViewChild('confirmCheckedOut') private confirmCheckedOut: ConfirmDialogComponent;
+    @ViewChild('confirmInTransit') private confirmInTransit: ConfirmDialogComponent;
+
+    constructor(
+        private modal: NgbModal, // required for passing to parent
+        private toast: ToastService,
+        private net: NetService,
+        private evt: EventService,
+        private holdings: HoldingsService,
+        private auth: AuthService) {
+        super(modal); // required for subclassing
+    }
+
+    ngOnInit() {}
+
+    open(args?: NgbModalOptions): Observable<boolean> {
+        this.numSucceeded = 0;
+        this.numFailed = 0;
+        this.currentBarcode = '';
+        let copyStatus: number;
+
+        switch (this.markAs) {
+            case 'missing':
+                this.method = 'open-ils.circ.mark_item_missing';
+                copyStatus = 4;
+                break;
+            case 'discard':
+                this.method = 'open-ils.circ.mark_item_discard';
+                copyStatus = 13;
+                break;
+        }
+
+        return of(
+            this.holdings.getCopyStatuses()
+            .then(stats => {
+                this.copyStatuses = stats;
+                this.statusName = this.copyStatuses[copyStatus].name();
+            })
+        ).pipe(concatMap(_ => super.open(args)));
+    }
+
+    markOneItem(copy: IdlObject, args?: MarkItemArgs): Observable<boolean> {
+        if (!args) { args = {}; }
+        this.currentBarcode = copy.barcode();
+
+        return this.net.request(
+            'open-ils.circ', this.method, this.auth.token(), copy.id(), args)
+        .pipe(concatMap(resp => {
+            return this.handleMarkResponse(copy, args, resp);
+        }));
+    }
+
+    handleMarkResponse(copy: IdlObject, args: MarkItemArgs, resp: any): Observable<boolean> {
+        const evt = this.evt.parse(resp);
+
+        if (!evt) { return of(true); } // success
+
+        let dialog: ConfirmDialogComponent;
+
+        switch (evt.textcode) {
+            case 'ITEM_TO_MARK_CHECKED_OUT':
+                dialog = this.confirmCheckedOut;
+                args.handle_checkin = true;
+                break;
+            case 'ITEM_TO_MARK_IN_TRANSIT':
+                args.handle_transit = true;
+                dialog = this.confirmInTransit;
+                break;
+            case 'ITEM_TO_MARK_LAST_HOLD_COPY':
+                args.handle_last_hold_copy = true;
+                dialog = this.confirmLastHold;
+                break;
+            case 'COPY_DELETE_WARNING':
+                args.handle_copy_delete_warning = true;
+                dialog = this.confirmDelete;
+                break;
+            default:
+                console.error('Marking failed ' + evt);
+                return of(false);
+        }
+
+        return dialog.open().pipe(concatMap(confirmed => {
+            if (confirmed) {
+                return this.markOneItem(copy, args);
+            } else {
+                return of(false);
+            }
+        }));
+    }
+
+    // Returns a stream of copy IDs.  Any non-null ID is a copy that
+    // was successfully modified.
+    markItems() {
+        if (!this.copies || this.copies.length === 0) {
+            this.close();
+            return;
+        }
+
+        this.copyCount = this.copies.length;
+
+        from(this.copies).pipe(concatMap(copy => {
+            return this.markOneItem(copy)
+            .pipe(map(modified => this.respond(modified ? copy.id() : null)));
+        })).toPromise().then(_ => this.close());
+    }
+}
+
+
+