LP1840773 SCKO Angular
authorBill Erickson <berickxx@gmail.com>
Mon, 13 Jun 2022 19:36:38 +0000 (15:36 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 14 Jun 2022 19:37:22 +0000 (15:37 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/core/auth.service.ts
Open-ILS/src/eg2/src/app/scko/banner.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/scko/banner.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/scko/routing.module.ts
Open-ILS/src/eg2/src/app/scko/scko.component.html
Open-ILS/src/eg2/src/app/scko/scko.component.ts
Open-ILS/src/eg2/src/app/scko/scko.module.ts
Open-ILS/src/eg2/src/app/scko/scko.service.ts [new file with mode: 0644]

index 7a04f58..682b730 100644 (file)
@@ -40,6 +40,9 @@ export enum AuthWsState {
 @Injectable({providedIn: 'root'})
 export class AuthService {
 
+    // Override this to store authtokens, etc. in a different location
+    storePrefix = 'eg.auth';
+
     private authChannel: any;
 
     private activeUser: AuthUser = null;
@@ -62,7 +65,7 @@ export class AuthService {
 
     // Returns true if we are currently in op-change mode.
     opChangeIsActive(): boolean {
-        return Boolean(this.store.getLoginSessionItem('eg.auth.time.oc'));
+        return Boolean(this.store.getLoginSessionItem(`${this.storePrefix}.time.oc`));
     }
 
     // - Accessor functions always refer to the active user.
@@ -92,8 +95,8 @@ export class AuthService {
             // Only necessary on new page loads.  During op-change,
             // for example, we already have an activeUser.
             this.activeUser = new AuthUser(
-                this.store.getLoginSessionItem('eg.auth.token'),
-                this.store.getLoginSessionItem('eg.auth.time')
+                this.store.getLoginSessionItem(`${this.storePrefix}.token`),
+                this.store.getLoginSessionItem(`${this.storePrefix}.time`)
             );
         }
 
@@ -175,8 +178,8 @@ export class AuthService {
     handleLoginOk(args: AuthLoginArgs, evt: EgEvent, isOpChange: boolean): Promise<void> {
 
         if (isOpChange) {
-            this.store.setLoginSessionItem('eg.auth.token.oc', this.token());
-            this.store.setLoginSessionItem('eg.auth.time.oc', this.authtime());
+            this.store.setLoginSessionItem(`${this.storePrefix}.token.oc`, this.token());
+            this.store.setLoginSessionItem(`${this.storePrefix}.time.oc`, this.authtime());
         }
 
         this.activeUser = new AuthUser(
@@ -185,8 +188,8 @@ export class AuthService {
             args.workstation
         );
 
-        this.store.setLoginSessionItem('eg.auth.token', this.token());
-        this.store.setLoginSessionItem('eg.auth.time', this.authtime());
+        this.store.setLoginSessionItem(`${this.storePrefix}.token`, this.token());
+        this.store.setLoginSessionItem(`${this.storePrefix}.time`, this.authtime());
 
         return Promise.resolve();
     }
@@ -195,14 +198,14 @@ export class AuthService {
         if (this.opChangeIsActive()) {
             this.deleteSession();
             this.activeUser = new AuthUser(
-                this.store.getLoginSessionItem('eg.auth.token.oc'),
-                this.store.getLoginSessionItem('eg.auth.time.oc'),
+                this.store.getLoginSessionItem(`${this.storePrefix}.token.oc`),
+                this.store.getLoginSessionItem(`${this.storePrefix}.time.oc`),
                 this.activeUser.workstation
             );
-            this.store.removeLoginSessionItem('eg.auth.token.oc');
-            this.store.removeLoginSessionItem('eg.auth.time.oc');
-            this.store.setLoginSessionItem('eg.auth.token', this.token());
-            this.store.setLoginSessionItem('eg.auth.time', this.authtime());
+            this.store.removeLoginSessionItem(`${this.storePrefix}.token.oc`);
+            this.store.removeLoginSessionItem(`${this.storePrefix}.time.oc`);
+            this.store.setLoginSessionItem(`${this.storePrefix}.token`, this.token());
+            this.store.setLoginSessionItem(`${this.storePrefix}.time`, this.authtime());
         }
         // Re-fetch the user.
         return this.testAuthToken();
diff --git a/Open-ILS/src/eg2/src/app/scko/banner.component.html b/Open-ILS/src/eg2/src/app/scko/banner.component.html
new file mode 100644 (file)
index 0000000..61e6aa9
--- /dev/null
@@ -0,0 +1,98 @@
+
+<div id="scko-banner" class="pb-2">
+       <div id="scko-logo-div">
+               <img src="/images/self_eg_logo.png"/>
+       </div>
+  <div class="scko-scan-container mt-3">
+    <ng-container *ngIf="scko.auth.user() && !scko.patron">
+      <div id="scko-scan-input-text" i18n>
+        Please log in with your username or library barcode.
+      </div>
+      <div class="d-flex mt-3 mb-3">
+        <div class="flex-1"></div>
+        <div>
+          <form (ngSubmit)="submitPatronLogin()" #patronLoginForm="ngForm"
+            autocomplete="off" class="form-validated form-inline">
+
+            <input type="text" class="form-control border border-dark shadow-rounded" 
+              autocomplete="off" id="patron-username" required 
+              [(ngModel)]="patronUsername" placeholder="Username or Barcode" i18n-placeholder>
+
+            <label class="sr-only" for="patron-password" i18n>Password</label>
+
+            <input type="password" class="form-control shadow border border-dark rounded ml-2" 
+              autocomplete="off" id="patron-password" required
+              [(ngModel)]="patronUsername" placeholder="Password" i18n-placeholder>
+
+            <button type="submit" class="btn btn-dark ml-2">Submit</button>
+          </form>
+        </div>
+        <div class="flex-1"></div>
+      </div>
+    </ng-container>
+
+  </div>
+</div>
+
+<div *ngIf="!scko.auth.user()" class="container mt-3">
+
+  <div class="col-lg-6 offset-lg-3">
+    <fieldset>
+      <legend class="mb-0" i18n><h1>Staff Account Login</h1></legend>
+      <hr class="mt-1"/>
+      <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>
+          <input 
+            type="text" 
+            class="form-control col-lg-8"
+            id="staff-username" 
+            name="staff-username"
+            required
+            autocomplete="username"
+            i18n-placeholder
+            placeholder="Staff Username" 
+            [(ngModel)]="staffUsername"/>
+        </div>
+
+        <div class="form-group row">
+          <label class="col-lg-4 text-right font-weight-bold" for="password" i18n>Password</label>
+          <input 
+            type="password" 
+            class="form-control col-lg-8"
+            id="staff-password" 
+            name="staff-password"
+            required
+            autocomplete="current-password"
+            i18n-placeholder
+            placeholder="Staff Password" 
+            [(ngModel)]="staffPassword"/>
+        </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>
+          <select 
+            class="form-control col-lg-8" 
+            id="workstation" 
+            name="workstation"
+            required
+            [(ngModel)]="args.workstation">
+            <option *ngFor="let ws of workstations" [value]="ws.name">
+              {{ws.name}}
+            </option>
+          </select>
+        </div>
+
+        <div class="row">
+          <div class="col-lg-2 offset-lg-4 pl-0">
+            <button type="submit" class="btn btn-outline-dark" i18n>Sign In</button>
+          </div>
+          <div class="col-lg-4" *ngIf="loginFailed">
+            <div class="badge badge-warning p-2" i18n>Login Failed</div>
+          </div>
+        </div>
+      </form>
+    </fieldset>
+  </div>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/scko/banner.component.ts b/Open-ILS/src/eg2/src/app/scko/banner.component.ts
new file mode 100644 (file)
index 0000000..8bd9a56
--- /dev/null
@@ -0,0 +1,113 @@
+import {Component, OnInit, 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';
+import {StoreService} from '@eg/core/store.service';
+import {SckoService} from './scko.service';
+import {OrgService} from '@eg/core/org.service';
+
+@Component({
+  selector: 'eg-scko-banner',
+  templateUrl: 'banner.component.html'
+})
+
+export class SckoBannerComponent implements OnInit {
+
+    workstations: any[];
+    loginFailed = false;
+    workstationNotFound = false;
+
+    patronUsername: string;
+    patronPassword: string;
+
+    staffUsername: string;
+    staffPassword: string;
+    staffWorkstation: string;
+
+    constructor(
+        private route: ActivatedRoute,
+        private store: StoreService,
+        private auth: AuthService,
+        private ngLocation: Location,
+        private org: OrgService,
+        public scko: SckoService
+    ) {}
+
+    ngOnInit() {
+
+        const node = document.getElementById('staff-username');
+
+        console.log('PFX', this.scko.auth.storePrefix);
+        console.log(this.scko.auth.token());
+        console.log(this.scko.auth.user());
+
+        // 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()
+        .then(wsList => {
+            this.workstations = wsList;
+            return this.store.getDefaultWorkstation();
+        }).then(def => {
+            this.staffWorkstation = def;
+            this.applyWorkstation();
+        });
+    }
+
+    applyWorkstation() {
+        const wanted = this.route.snapshot.queryParamMap.get('workstation');
+        if (!wanted) { return; } // use the default
+
+        const exists = this.workstations.filter(w => w.name === wanted)[0];
+        if (exists) {
+            this.staffWorkstation = wanted;
+        } else {
+            console.error(`Unknown workstation requested: ${wanted}`);
+        }
+    }
+
+    submitStaffLogin() {
+
+        this.loginFailed = false;
+
+        const args = {
+            type: 'persistent',
+            username: this.staffUsername,
+            password: this.staffPassword,
+            workstation: this.staffWorkstation
+        };
+
+        this.loginFailed = false;
+        this.workstationNotFound = false;
+
+        this.auth.login(args).then(
+            ok => {
+
+                if (this.auth.workstationState === AuthWsState.NOT_FOUND_SERVER) {
+                    this.loginFailed = true;
+                    this.workstationNotFound = true;
+
+                } else {
+
+                    // Initial login clears cached org unit setting values
+                    // and user/workstation setting values
+                    this.org.clearCachedSettings().then(_ => {
+
+                        // Force reload of the app after a successful login.
+                        // This allows the route resolver to re-run with a
+                        // valid auth token and workstation.
+                        window.location.href =
+                            this.ngLocation.prepareExternalUrl('/scko');
+
+                    });
+                }
+            },
+            notOk => {
+                this.loginFailed = true;
+            }
+        );
+    }
+
+    submitPatronLogin() {
+    }
+}
+
index be056ad..6d52229 100644 (file)
@@ -4,7 +4,12 @@ import {SckoComponent} from './scko.component';
 
 const routes: Routes = [{
   path: '',
-  component: SckoComponent
+  component: SckoComponent,
+  /*
+  children: [{
+
+  }]
+  */
 }];
 
 @NgModule({
index 33a7795..24bf192 100644 (file)
@@ -1,34 +1,7 @@
 
-<div id="scko-banner" class="pb-2">
-       <div id="scko-logo-div">
-               <img src="/images/self_eg_logo.png"/>
-       </div>
-  <div class="scko-scan-container mt-3">
-    <ng-container *ngIf="!patron">
-      <div id="scko-scan-input-text" i18n>
-        Please log in with your username or library barcode.
-      </div>
-      <div class="row form-validated form-inline d-flex mt-3 mb-3">
-        <div class="flex-1"></div>
-        <div>
-          <label class="sr-only" for="patron-username" i18n>Username or Barcode</label>
-          <input type="text" class="form-control border border-dark shadow-rounded" 
-            id="patron-username" placeholder="Username or Barcode" i18n-placeholder>
-          <label class="sr-only" for="patron-password" i18n>Password</label>
-          <input type="text" class="form-control shadow border border-dark rounded ml-2" 
-            id="patron-password" placeholder="Password" i18n-placeholder>
-            <button type="submit" class="btn btn-dark ml-2">Submit</button>
-          </div>
-        <div class="flex-1"></div>
-      </div>
-    </ng-container>
-  </div>
-</div>
+<eg-scko-banner></eg-scko-banner>
 
-<div id='staff-content-container'>
-  <!-- page content -->
-  <router-outlet></router-outlet>
-</div>
+<router-outlet></router-outlet>
 
 <!-- global toast alerts -->
 <eg-toast></eg-toast>
index 2ca8289..e3f90a5 100644 (file)
@@ -1,24 +1,20 @@
-import {Component, OnInit, NgZone, HostListener} from '@angular/core';
+import {Component, OnInit, ViewEncapsulation} from '@angular/core';
 import {Router, ActivatedRoute, NavigationEnd} from '@angular/router';
-import {AuthService, AuthWsState} from '@eg/core/auth.service';
-import {NetService} from '@eg/core/net.service';
-import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {AuthService} from '@eg/core/auth.service';
+import {SckoService} from './scko.service';
 
 @Component({
   templateUrl: 'scko.component.html',
-  styleUrls: ['scko.component.css']
+  styleUrls: ['scko.component.css'],
+  encapsulation: ViewEncapsulation.None
 })
 
 export class SckoComponent implements OnInit {
 
-    // Currently active patron account object.
-    patron: IdlObject;
-
     constructor(
         private router: Router,
         private route: ActivatedRoute,
-        private net: NetService,
-        private auth: AuthService,
+        public  scko: SckoService
     ) {}
 
     ngOnInit() {
index f386f6b..31f202d 100644 (file)
@@ -6,15 +6,21 @@ import {TitleComponent} from '@eg/share/title/title.component';
 
 import {SckoComponent} from './scko.component';
 import {SckoRoutingModule} from './routing.module';
+import {SckoService} from './scko.service';
+import {SckoBannerComponent} from './banner.component';
 
 @NgModule({
   declarations: [
     SckoComponent,
+    SckoBannerComponent,
   ],
   imports: [
     EgCommonModule,
     CommonWidgetsModule,
     SckoRoutingModule
+  ],
+  providers: [
+    SckoService
   ]
 })
 
diff --git a/Open-ILS/src/eg2/src/app/scko/scko.service.ts b/Open-ILS/src/eg2/src/app/scko/scko.service.ts
new file mode 100644 (file)
index 0000000..cdfc91d
--- /dev/null
@@ -0,0 +1,24 @@
+import {Injectable, EventEmitter} from '@angular/core';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {EventService, EgEvent} from '@eg/core/event.service';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+
+@Injectable({providedIn: 'root'})
+export class SckoService {
+
+    // Currently active patron account object.
+    patron: IdlObject;
+
+    constructor(
+        private net: NetService,
+        public auth: AuthService
+    ) {
+        this.auth.storePrefix = 'eg.scko';
+    }
+}
+
+
+