LP1904036 Patron ui checkout tab
authorBill Erickson <berickxx@gmail.com>
Fri, 5 Feb 2021 22:57:42 +0000 (17:57 -0500)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:23 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts
Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts

index 988ea06..d2a7615 100644 (file)
@@ -9,12 +9,12 @@
   dialogBody="Enter the number of {{checkoutNoncat ? checkoutNoncat.name() : ''}} circulating">
 </eg-prompt-dialog>
 
-<div class="row">
+<div class="row mb-2 pb-2 border-bottom">
   <div class="col-lg-12 d-flex">
     <div class="form-inline">
       <div class="input-group">
         <div class="input-group-prepend">
-          <div ngbDropdown class="d-inline-block">
+          <div ngbDropdown>
             <button ngbDropdownToggle class="btn btn-outline-dark" id="checkout-button">
               <ng-container *ngIf="!checkoutNoncat" i18n>Barcode</ng-container>
               <ng-container *ngIf="checkoutNoncat">{{checkoutNoncat.name()}}</ng-container>
         <input type="text" class="form-control" id="barcode-input"
           [placeholder]="checkoutNoncat ? '' : 'Barcode...'" i18n-placeholder
           [(ngModel)]="checkoutBarcode" [disabled]="checkoutNoncat != null"
-          i18n-aria-label aria-label="Barcode Input"/>
+          i18n-aria-label aria-label="Barcode Input" (keyup.enter)="checkout()" />
         <div class="input-group-append">
-          <button class="btn btn-outline-dark" (click)="checkout()" i18n>Submit</button>
+          <button class="btn btn-outline-dark" (keyup.enter)="checkout()" 
+            (click)="checkout()" i18n>Submit</button>
         </div>
       </div>
     </div>
-    <div class="ml-auto">
-      here
+    <div class="flex-1"></div>
+    <div ngbDropdown>
+      <button ngbDropdownToggle class="btn mr-2" [ngClass]="{
+          'btn-outline-dark': context.dueDateOptions == 0,
+          'btn-outline-success': context.dueDateOptions > 0
+        }"
+        i18n>Date Options</button>
+      <div ngbDropdownMenu>
+        <button ngbDropdownItem (click)="toggleDateOptions(1)">
+          <span *ngIf="context.dueDateOptions > 0" 
+            class="badge badge-success">&#x2713;</span>  
+          <span *ngIf="context.dueDateOptions == 0" 
+            class="badge badge-warning">&#x2717;</span> 
+          <span class="pl-2" i18n>Specific Due Date</span>
+        </button>
+        <button ngbDropdownItem (click)="toggleDateOptions(2)">
+          <span *ngIf="context.dueDateOptions == 2" 
+            class="badge badge-success">&#x2713;</span>  
+          <span *ngIf="context.dueDateOptions < 2" 
+            class="badge badge-warning">&#x2717;</span> 
+          <span class="pl-2" i18n>Specific Due Date Until Logout</span>
+        </button>
+      </div>
     </div>
+    <eg-datetime-select (onChangeAsIso)="setDueDate($event)"></eg-datetime-select>
   </div>
 </div>
 
index f1f8c69..096ff1b 100644 (file)
@@ -25,6 +25,8 @@ export class CheckoutComponent implements OnInit {
     checkoutBarcode = '';
     gridDataSource: GridDataSource = new GridDataSource();
     cellTextGenerator: GridCellTextGenerator;
+    dueDate: string;
+    copiesInFlight: {[barcode: string]: boolean} = {};
 
     @ViewChild('nonCatCount') nonCatCount: PromptDialogComponent;
     @ViewChild('checkoutsGrid') checkoutsGrid: GridComponent;
@@ -53,6 +55,10 @@ export class CheckoutComponent implements OnInit {
         this.focusInput();
     }
 
+    setDueDate(iso: string) {
+        this.dueDate = iso;
+    }
+
     focusInput() {
         const input = document.getElementById('barcode-input');
         if (input) { input.focus(); }
@@ -73,8 +79,19 @@ export class CheckoutComponent implements OnInit {
                 params.noncat_type = this.checkoutNoncat.id();
                 return params;
             });
+
         } else if (this.checkoutBarcode) {
+
+            if (this.copiesInFlight[this.checkoutBarcode]) {
+                console.log('Item ' +
+                    this.checkoutBarcode + ' is already mid-checkout');
+                return Promise.resolve(null);
+            }
+
+            this.copiesInFlight[this.checkoutBarcode] = true;
+
             params.copy_barcode = this.checkoutBarcode;
+            if (this.context.dueDateOptions > 0) { params.due_date = this.dueDate; }
             return Promise.resolve(params);
         }
 
@@ -92,6 +109,11 @@ export class CheckoutComponent implements OnInit {
 
         .then((result: CheckoutResult) => {
             if (result) {
+
+                if (result.params.copy_barcode) {
+                    delete this.copiesInFlight[result.params.copy_barcode];
+                }
+
                 if (result.success) {
                     this.gridifyResult(result);
                     this.resetForm();
@@ -101,6 +123,13 @@ export class CheckoutComponent implements OnInit {
     }
 
     resetForm() {
+
+        if (this.context.dueDateOptions < 2) {
+            // Due date is not configured to persist.
+            this.context.dueDateOptions = 0;
+            this.dueDate = null;
+        }
+
         this.checkoutBarcode = '';
         this.checkoutNoncat = null;
         this.focusInput();
@@ -154,5 +183,20 @@ export class CheckoutComponent implements OnInit {
             }
         }));
     }
+
+    // 0: use server due date
+    // 1: use specific due date once
+    // 2: use specific due date until the end of the session.
+    toggleDateOptions(value: 1 | 2) {
+        if (this.context.dueDateOptions > 0) {
+            if (value === 1) {
+                this.context.dueDateOptions = 0;
+            } else if (this.context.dueDateOptions === 1) {
+                this.context.dueDateOptions = 2;
+            }
+        } else {
+            this.context.dueDateOptions = value;
+        }
+    }
 }
 
index cf653c7..7bb36d7 100644 (file)
@@ -70,6 +70,7 @@ export class PatronManagerService {
 
     // These should persist tab changes
     checkouts: CircGridEntry[] = [];
+    dueDateOptions: 0 | 1 | 2 = 0; // auto date; specific date; session date
 
     constructor(
         private net: NetService,
index 1b69e04..10a9bc2 100644 (file)
@@ -13,6 +13,7 @@ import {AudioService} from '@eg/share/util/audio.service';
 
 // API parameter options
 export interface CheckoutParams {
+    due_date?: string;
     patron_id: number;
     copy_id?: number;
     copy_barcode?: string;
@@ -68,8 +69,8 @@ export class CircService {
         return this.net.request(
             'open-ils.circ',
             'open-ils.circ.checkout.full',
-            this.auth.token(), params
-        ).toPromise().then(result => this.processCheckoutResult(params, result))
+            this.auth.token(), params).toPromise()
+        .then(result => this.processCheckoutResult(params, result));
     }
 
     processCheckoutResult(