LP1840773 SCKO Angular
authorBill Erickson <berickxx@gmail.com>
Tue, 28 Jun 2022 19:23:31 +0000 (15:23 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 28 Jun 2022 19:23:31 +0000 (15:23 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/scko/banner.component.html
Open-ILS/src/eg2/src/app/scko/banner.component.ts
Open-ILS/src/eg2/src/app/scko/scko.component.css
Open-ILS/src/eg2/src/app/scko/scko.component.html
Open-ILS/src/eg2/src/app/scko/scko.service.ts

index 037360a..cea5bd0 100644 (file)
@@ -74,7 +74,8 @@
       <form (ngSubmit)="submitStaffLogin()" #staffLoginForm="ngForm" class="form-validated">
 
         <div class="form-group row">
-          <label class="col-lg-4 text-right font-weight-bold" for="username" i18n>Username</label>
+          <label class="col-lg-4 text-right font-weight-bold" 
+            for="staff-username" i18n>Username</label>
           <input 
             type="text" 
             class="form-control col-lg-8"
@@ -88,7 +89,8 @@
         </div>
 
         <div class="form-group row">
-          <label class="col-lg-4 text-right font-weight-bold" for="password" i18n>Password</label>
+          <label class="col-lg-4 text-right font-weight-bold" 
+            for="staff-password" i18n>Password</label>
           <input 
             type="password" 
             class="form-control col-lg-8"
         </div>
 
         <div class="form-group row" *ngIf="workstations && workstations.length">
-          <label class="col-lg-4 text-right font-weight-bold" for="workstation" i18n>Workstation</label>
+          <label class="col-lg-4 text-right font-weight-bold" 
+            for="workstation" i18n>Workstation</label>
           <select 
             class="form-control col-lg-8" 
             id="workstation" 
index 9a6a39c..4485f86 100644 (file)
@@ -1,4 +1,4 @@
-import {Component, OnInit, NgZone, HostListener} from '@angular/core';
+import {Component, OnInit, AfterViewInit, NgZone, HostListener} from '@angular/core';
 import {Location} from '@angular/common';
 import {Router, ActivatedRoute, NavigationEnd} from '@angular/router';
 import {AuthService, AuthWsState} from '@eg/core/auth.service';
@@ -13,7 +13,7 @@ import {EventService, EgEvent} from '@eg/core/event.service';
   templateUrl: 'banner.component.html'
 })
 
-export class SckoBannerComponent implements OnInit {
+export class SckoBannerComponent implements OnInit, AfterViewInit {
 
     workstations: any[];
     workstationNotFound = false;
@@ -42,9 +42,6 @@ export class SckoBannerComponent implements OnInit {
 
     ngOnInit() {
 
-        // TODO focus the right thing on page load
-        const node = document.getElementById('staff-username');
-
         // NOTE: Displaying a list of workstations will not work for users
         // of Hatch until the extension is updated to support /eg2/*/scko
         this.store.getWorkstations()
@@ -57,6 +54,23 @@ export class SckoBannerComponent implements OnInit {
         });
     }
 
+    ngAfterViewInit() {
+        if (this.auth.token()) {
+            this.focusNode('patron-username');
+        } else {
+            this.focusNode('staff-username');
+        }
+
+        this.scko.focusBarcode.subscribe(_ => this.focusNode('item-barcode'));
+    }
+
+    focusNode(id: string) {
+        setTimeout(() => {
+            const node = document.getElementById(id);
+            if (node) { (node as HTMLInputElement).select(); }
+        });
+    }
+
     applyWorkstation() {
         const wanted = this.route.snapshot.queryParamMap.get('workstation');
         if (!wanted) { return; } // use the default
@@ -112,7 +126,11 @@ export class SckoBannerComponent implements OnInit {
     submitPatronLogin() {
         this.patronLoginFailed = false;
         this.scko.loadPatron(this.patronUsername, this.patronPassword).finally(() => {
-            this.patronLoginFailed = this.scko.patronSummary === null;
+            if (this.scko.patronSummary === null) {
+                this.patronLoginFailed = true;
+            } else {
+                this.focusNode('item-barcode');
+            }
         });
     }
 
index c034896..0150ee3 100644 (file)
@@ -121,4 +121,6 @@ A {
     padding: 3px;
 }
 
-
+.scko-status-row {
+    font-size: 20px;
+}
index 3702f1d..20da9a7 100644 (file)
@@ -1,9 +1,11 @@
 
 <eg-scko-banner></eg-scko-banner>
 
-<div class="d-flex">
+<div class="d-flex scko-status-row mt-2">
   <div class="flex-1"></div>
-  <div class="text-primary">{{scko.statusDisplayText}}</div>
+  <div [ngClass]="{
+    'text-success': scko.statusDisplaySuccess, 
+    'text-danger': !scko.statusDisplaySuccess}">{{scko.statusDisplayText}}</div>
   <div class="flex-1"></div>
 </div>
 
   text="Item is checked out to another patron"></eg-string>
 <eg-string i18n-text key="scko.error.max_renewals"
   text="No more renewals allowed for item {{barcode}}"></eg-string>
+
+<eg-string i18n-text key="scko.error.patron_fines"
+  text="This account has too many fines to checkout."></eg-string>
+
 <eg-string i18n-text key="scko.error.item_not_cataloged"
   text="Item {{barcode}} was not found in the system.  Try re-scanning the item."></eg-string>
+
+<eg-string i18n-text key="scko.error.copy_circ_not_allowed"
+  text="Item {{barcode}} is not allowed to circulate"></eg-string>
 <eg-string i18n-text key="scko.error.actor_usr_barred" 
   text="The patron is barred"></eg-string>
 <eg-string i18n-text key="scko.error.asset_copy_circulate" 
index ccd4cd3..0cae316 100644 (file)
@@ -40,6 +40,7 @@ export class SckoService {
     logoutWarningTimeout = 20;
     logoutWarningTimerId: number;
     statusDisplayText = '';
+    statusDisplaySuccess: boolean;
 
     alertAudio = false;
     alertPopup = false;
@@ -52,6 +53,7 @@ export class SckoService {
     // We get this from the main scko component.
     logoutDialog: ConfirmDialogComponent;
     alertDialog: AlertDialogComponent;
+    focusBarcode: EventEmitter<void> = new EventEmitter<void>();
 
     constructor(
         private router: Router,
@@ -271,6 +273,8 @@ export class SckoService {
                 return this.renew(barcode);
             }
 
+            return ctx;
+
         // Checkout actions always takes us back to the main page
         // so we can see our items out in progress.
         })
@@ -304,8 +308,14 @@ export class SckoService {
     }
 
     notifyPatron(ctx: CheckoutContext) {
+        console.debug("notifyPatron()", ctx);
+
         this.statusDisplayText = '';
 
+        this.statusDisplaySuccess = !ctx.shouldPopup;
+
+        this.focusBarcode.emit();
+
         if (this.alertAudio && ctx.alertSound) {
             this.audio.play(ctx.alertSound);
         }
@@ -361,13 +371,7 @@ export class SckoService {
             return this.handleOpenCirc(ctx);
         }
 
-        if (this.overrideCheckoutEvents.length > 0) {
-            return this.handleOverride(ctx);
-        }
-
-        ctx.alertSound = 'error.scko.checkout';
-        ctx.shouldPopup = true;
-        return Promise.resolve(ctx);
+        return this.handleEvents(ctx);
     }
 
     handleOpenCirc(ctx: CheckoutContext): Promise<any> {
@@ -408,14 +412,11 @@ export class SckoService {
         return Promise.resolve(ctx);
     }
 
-    handleOverride(ctx: CheckoutContext): Promise<CheckoutContext> {
-        if (this.overrideCheckoutEvents.length === 0) {
-            return Promise.resolve(ctx);
-        }
-
+    handleEvents(ctx: CheckoutContext): Promise<CheckoutContext> {
         let override = true;
         let abortTransit = false;
         let lastErrorText = '';
+
         [].concat(ctx.result).some(res => {
 
             if (!this.overrideCheckoutEvents.includes(res.textcode)) {
@@ -471,8 +472,12 @@ export class SckoService {
                 return 'scko.error.max_renewals';
             case 'ITEM_NOT_CATALOGED':
                 return 'scko.error.item_not_cataloged';
+            case 'COPY_CIRC_NOT_ALLOWED':
+                return 'scko.error.copy_circ_not_allowed';
             case 'OPEN_CIRCULATION_EXISTS':
                 return 'scko.error.already_out';
+            case 'PATRON_EXCEEDS_FINES':
+                return 'scko.error.patron_fines';
             default:
                 if (evt.payload && evt.payload.fail_part) {
                     return 'scko.error.' +