LP#1626157 Ang5 file names best practices; more dialog stuff
authorBill Erickson <berickxx@gmail.com>
Thu, 12 Apr 2018 21:26:18 +0000 (21:26 +0000)
committerBill Erickson <berickxx@gmail.com>
Thu, 12 Apr 2018 21:26:18 +0000 (21:26 +0000)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
51 files changed:
Open-ILS/src/eg2/src/app/core/README
Open-ILS/src/eg2/src/app/core/pcrud.ts
Open-ILS/src/eg2/src/app/migration.module.ts
Open-ILS/src/eg2/src/app/routing.module.ts
Open-ILS/src/eg2/src/app/share/dialog/dialog.component.ts
Open-ILS/src/eg2/src/app/share/dialog/prompt.component.ts
Open-ILS/src/eg2/src/app/share/hello-world.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/workstation/routing.module.ts
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/routing.module.ts
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/app.component.css [deleted file]
Open-ILS/src/eg2/src/app/staff/app.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/app.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/app.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/catalog/app.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/catalog/app.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/catalog/app.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/catalog/app.service.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/catalog/record/copies.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/record/pagination.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/resolver.service.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/facets.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/pagination.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/routing.module.ts
Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/routing.module.ts
Open-ILS/src/eg2/src/app/staff/circ/routing.module.ts
Open-ILS/src/eg2/src/app/staff/routing.module.ts
Open-ILS/src/eg2/src/app/staff/staff.component.css [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/staff.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/staff.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/staff.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/welcome.component.html

index 58828be..3cf0ec4 100644 (file)
@@ -1,8 +1,9 @@
 Core Angular services and assocated types/classes.
 
-Core services are imported and exported by the base module, which means
-they are automatically added as dependencies to ALL applications.
+Core services are imported and exported by the base module and 
+automatically added as dependencies to ALL applications.
 
-1. Only add services here that are universally required!
-2. Avoid path navigation in the core services as paths will vary by application.
+1. Only add services here that are universally required.
+2. Avoid URL path navigation in the core services as paths will vary 
+   by application.
 
index e8a3ec5..6a8dc7d 100644 (file)
@@ -8,7 +8,7 @@ import {EgAuthService} from './auth';
 declare var js2JSON: (jsThing:any) => string;
 declare var OpenSRF: any; // creating sessions
 
-export interface EgPcrudReqOps {
+interface EgPcrudReqOps {
     authoritative?: boolean;
     anonymous?: boolean;
     idlist?: boolean;
index bf3bd69..92cec10 100644 (file)
@@ -31,7 +31,8 @@ import {EgPcrudService} from '@eg/core/pcrud';
 import {EgOrgService} from '@eg/core/org';
 
 // Downgraded components
-//import {EgConfirmDialogComponent} from '@eg/share/confirm-dialog.component';
+//import {EgDialogComponent} from '@eg/share/dialog/dialog.component';
+//import {EgConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
 import {EgHelloWorldComponent} from '@eg/share/hello-world.component';
 
 declare var angular: any;
@@ -44,10 +45,15 @@ declare var angular: any;
     CookieModule.forRoot(),
     EgCommonModule.forRoot()
   ],
-  declarations: [EgHelloWorldComponent],
+  declarations: [
+    EgHelloWorldComponent,
+    //EgDialogComponent,
+    //EgConfirmDialogComponent
+  ],
   entryComponents: [
     EgHelloWorldComponent,
-    //EgConfirmDialogComponent // declared in EgCommonModule
+    //EgDialogComponent,
+    //EgConfirmDialogComponent
   ]
 })
 
@@ -77,6 +83,10 @@ export class EgMigrationModule {
             .factory('ng2Title', downgradeInjectable(Title))
             .directive('eg2HelloWorld',
                 downgradeComponent({component: EgHelloWorldComponent}))
+            /*
+            .directive('eg2ConfirmDialog',
+                downgradeComponent({component: EgConfirmDialogComponent}))
+                */
 
         ;
 
index a1547f8..5945213 100644 (file)
@@ -16,7 +16,7 @@ const routes: Routes = [
   }, {
     path: 'staff', 
     resolve : {startup : EgBaseResolver},
-    loadChildren: './staff/app.module#EgStaffModule'
+    loadChildren: './staff/staff.module#EgStaffModule'
   }
 ];
 
index 2782805..397a6db 100644 (file)
@@ -8,7 +8,7 @@ import {NgbModal, NgbModalRef} from '@ng-bootstrap/ng-bootstrap';
  */ 
 
 @Component({
-    selector: 'eg-component',
+    selector: 'eg-dialog',
     template: '<ng-template></ng-template>'
 })
 export class EgDialogComponent {
index abf96ec..179efeb 100644 (file)
@@ -7,7 +7,7 @@ import {EgDialogComponent} from '@eg/share/dialog/dialog.component';
 })
 
 /**
- * Promptation dialog that asks a yes/no question.
+ * Promptation dialog that requests user input.
  */
 export class EgPromptDialogComponent extends EgDialogComponent {
     // What question are we asking?
diff --git a/Open-ILS/src/eg2/src/app/share/hello-world.component.ts b/Open-ILS/src/eg2/src/app/share/hello-world.component.ts
new file mode 100644 (file)
index 0000000..92daf1c
--- /dev/null
@@ -0,0 +1,14 @@
+import {Component, Input} from '@angular/core';
+
+@Component({
+  selector: 'eg-hello-world',
+  template: `
+    <div>Hello, World {{message}}!</div>
+    `
+})
+export class EgHelloWorldComponent {
+    @Input() public message: string;
+    constructor() {}
+}
+
+
index 114c312..bd300d7 100644 (file)
@@ -3,7 +3,7 @@ import {RouterModule, Routes} from '@angular/router';
 
 const routes: Routes = [{
     path: 'workstations',
-    loadChildren: '@eg/staff/admin/workstation/workstations/app.module#ManageWorkstationsModule'
+    loadChildren: '@eg/staff/admin/workstation/workstations/workstations.module#ManageWorkstationsModule'
 }];
 
 @NgModule({
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.component.html b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.component.html
deleted file mode 100644 (file)
index 490f9a6..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<eg-staff-banner bannerText="Workstation Administration" i18n-bannerText>
-</eg-staff-banner>
-
-<!-- this will remain hidden until opened -->
-<eg-confirm-dialog 
-  #workstationExistsDialog 
-  i18n-dialogTitle i18n-dialogBody
-  dialogTitle="Workstation Exists"
-  dialogBody='Workstation "{{newName}}" already exists.  Use it anyway?'>
-</eg-confirm-dialog>
-
-<div class="row">
-  <div class="col-8 offset-1 mt-3">
-    <div class="alert alert-warning" *ngIf="removeWorkstation" i18n>
-      Workstation {{removeWorkstation}} is no longer valid.  Removing registration.
-    </div>
-    <div class="alert alert-danger" *ngIf="workstations.length == 0">
-      <span i18n>Please register a workstation.</span>
-    </div>
-
-    <div class="row">
-      <div class="col" i18n>Register a New Workstation For This Browser</div>
-    </div>
-    <div class="row mt-2">
-      <div class="col-2">
-        <eg-org-select 
-          (onChange)="orgOnChange($event)"
-          [hideOrgs]="hideOrgs"
-          [disableOrgs]="disableOrgs"
-          [initialOrg]="initialOrg"
-          [placeholder]="'Owner'" >
-        </eg-org-select>
-      </div>
-      <div class="col-6">
-        <div class="input-group">
-          <input type='text'
-            class='form-control'
-            i18n-title
-            title="Workstation Name"
-            i18n-placeholder
-            placeholder="Workstation Name"
-            [(ngModel)]='newName'/>
-          <div class="input-group-btn">
-            <button class="btn btn-outline-dark" 
-              [disabled]="!newName || !newOwner"
-              (click)="registerWorkstation()">
-              <span i18n>Register</span>
-            </button>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="row mt-3 pt-3 border border-left-0 border-right-0 border-bottom-0 border-light">
-      <div class="col">
-        <span i18n>Workstations Registered With This Browser</span>
-      </div>
-    </div>
-    <div class="row">
-      <div class="col-8">
-        <select class="form-control" [(ngModel)]="selectedName">
-          <option *ngFor="let ws of workstations" value="{{ws.name}}">
-            <span *ngIf="ws.name == defaultName" i18n>
-              {{ws.name}} (Default)
-            </span>
-            <span *ngIf="ws.name != defaultName">
-              {{ws.name}}
-            </span>
-          </option>
-        </select>
-      </div>
-    </div>
-    <div class="row mt-2">
-      <div class="col-md-6">
-        <button i18n class="btn btn-success" 
-          (click)="useNow()" [disabled]="!selected">
-          Use Now
-        </button>
-        <button i18n class="btn btn-outline-dark" 
-          (click)="setDefault()" [disabled]="!selected">
-          Mark As Default
-        </button>
-        <button i18n class="btn btn-danger"
-          (click)="removeSelected()"
-          [disabled]="!selected || isRemoving || !canDeleteSelected()">
-          Remove
-        </button>
-      </div>
-    </div>
-  </div>
-</div>
-
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.component.ts
deleted file mode 100644 (file)
index 2e04c35..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-import {Component, OnInit, ViewChild} from '@angular/core';
-import {Router, ActivatedRoute} from '@angular/router';
-import {EgStoreService} from '@eg/core/store';
-import {EgIdlObject} from '@eg/core/idl';
-import {EgNetService} from '@eg/core/net';
-import {EgPermService} from '@eg/core/perm';
-import {EgAuthService} from '@eg/core/auth';
-import {EgOrgService} from '@eg/core/org';
-import {EgEventService} from '@eg/core/event';
-import {EgConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
-
-// Slim version of the WS that's stored in the cache.
-interface Workstation {
-    id: number;
-    name: string;
-    owning_lib: number;
-}
-
-@Component({
-  templateUrl: 'app.component.html'
-})
-export class WorkstationsComponent implements OnInit {
-
-    selectedName: string;
-    workstations: Workstation[] = [];
-    removeWorkstation: string;
-    newOwner: EgIdlObject;
-    newName: string;
-    defaultName: string;
-
-    @ViewChild('workstationExistsDialog')
-    private wsExistsDialog: EgConfirmDialogComponent;
-
-    // Org selector options.
-    hideOrgs: number[];
-    disableOrgs: number[];
-    orgOnChange = (org: EgIdlObject): void => {
-        this.newOwner = org;
-    }
-
-    constructor(
-        private router: Router,
-        private route: ActivatedRoute,
-        private evt: EgEventService,
-        private net: EgNetService,
-        private store: EgStoreService,
-        private auth: EgAuthService,
-        private org: EgOrgService,
-        private perm: EgPermService
-    ) {}
-
-    ngOnInit() {
-        this.store.getItem('eg.workstation.all')
-        .then(list => this.workstations = list || [])
-        .then(noop => this.store.getItem('eg.workstation.default'))
-        .then(defWs => {
-            this.defaultName = defWs;
-            this.selectedName = this.auth.workstation() || defWs 
-        })
-        .then(noop => {
-            let rm = this.route.snapshot.paramMap.get('remove');
-            if (rm) this.removeSelected(this.removeWorkstation = rm)
-        })
-
-        this.perm.hasWorkPermAt(['REGISTER_WORKSTATION'], true)
-        .then(perms => {
-            // Disable org units that cannot have users and any
-            // that this user does not have work perms for.
-            this.disableOrgs = 
-                this.org.filterList({canHaveUsers : false}, true)
-                .concat(this.org.filterList(
-                    {notInList : perms.REGISTER_WORKSTATION}, true));
-        });
-    }
-
-    selected(): Workstation {
-        return this.workstations.filter(
-          ws => {return ws.name == this.selectedName})[0];
-    }
-
-    useNow(): void {
-        if (!this.selected()) return;
-        this.router.navigate(['/staff/login'], 
-          {queryParams: {workstation: this.selected().name}});
-    }
-
-    setDefault(): void {
-      if (!this.selected()) return;
-      this.defaultName = this.selected().name;
-      this.store.setItem('eg.workstation.default', this.defaultName);
-    }
-
-    removeSelected(name?: string): void {
-        if (!name) name = this.selected().name;
-
-        this.workstations = this.workstations.filter(w => w.name != name);
-        this.store.setItem('eg.workstation.all', this.workstations);
-
-        if (this.defaultName == name) {
-            this.defaultName = null;
-            this.store.removeItem('eg.workstation.default');
-        }
-    }
-    
-    canDeleteSelected(): boolean {
-        return true;
-    }
-
-    registerWorkstation(): void {
-        console.log(`Registering new workstation ` +
-            `"${this.newName}" at ${this.newOwner.shortname()}`);
-
-        this.newName = this.newOwner.shortname() + '-' + this.newName;
-
-        this.registerWorkstationApi().then(
-            wsId => this.registerWorkstationLocal(wsId));
-
-    }
-
-    private handleCollision(): Promise<number> {
-        return new Promise((resolve, reject) => {
-            this.wsExistsDialog.open()
-            .then(
-                confirmed => {
-                    this.registerWorkstationApi(true).then(
-                        wsId => resolve(wsId),
-                        notOk => reject(notOk)
-                    )
-                },
-                dismissed => reject(dismissed)
-            )
-        });
-    }
-
-
-    private registerWorkstationApi(override?: boolean): Promise<number> {
-        let method = 'open-ils.actor.workstation.register';
-        if (override) method += '.override';
-
-        return new Promise((resolve, reject) => {
-            this.net.request(
-                'open-ils.actor', method,
-                this.auth.token(), this.newName, this.newOwner.id()
-            ).subscribe(wsId => {
-                let evt = this.evt.parse(wsId);  
-                if (evt) {
-                    if (evt.textcode == 'WORKSTATION_NAME_EXISTS') {
-                        this.handleCollision().then(
-                            id => resolve(id),
-                            notOk => reject(notOk)
-                        )
-                    } else {
-                        console.error(`Registration failed ${evt}`);
-                        reject();
-                    }
-                } else {
-                   resolve(wsId); 
-                }
-            });
-        });
-    }
-
-    private registerWorkstationLocal(wsId: number) {
-        let ws: Workstation = {
-            id: wsId,
-            name: this.newName,
-            owning_lib: this.newOwner.id()
-        };
-
-        this.workstations.push(ws);
-        this.store.setItem('eg.workstation.all', this.workstations)
-        .then(ok => this.newName = '');
-    }
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/app.module.ts
deleted file mode 100644 (file)
index 00d9593..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-import {NgModule} from '@angular/core';
-import {EgStaffCommonModule} from '@eg/staff/common.module';
-import {WorkstationsRoutingModule} from './routing.module';
-import {WorkstationsComponent} from './app.component';
-
-@NgModule({
-  declarations: [
-    WorkstationsComponent,
-  ],
-  imports: [
-    EgStaffCommonModule,
-    WorkstationsRoutingModule
-  ]
-})
-
-export class ManageWorkstationsModule {}
-
-
index f1ac37e..a29f19b 100644 (file)
@@ -1,6 +1,6 @@
 import {NgModule}             from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
-import {WorkstationsComponent} from './app.component';
+import {WorkstationsComponent} from './workstations.component';
 
 // Note that we need a path value (e.g. 'manage') because without it
 // there is nothing for the router to match, unless we rely on the parent
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.html
new file mode 100644 (file)
index 0000000..490f9a6
--- /dev/null
@@ -0,0 +1,91 @@
+<eg-staff-banner bannerText="Workstation Administration" i18n-bannerText>
+</eg-staff-banner>
+
+<!-- this will remain hidden until opened -->
+<eg-confirm-dialog 
+  #workstationExistsDialog 
+  i18n-dialogTitle i18n-dialogBody
+  dialogTitle="Workstation Exists"
+  dialogBody='Workstation "{{newName}}" already exists.  Use it anyway?'>
+</eg-confirm-dialog>
+
+<div class="row">
+  <div class="col-8 offset-1 mt-3">
+    <div class="alert alert-warning" *ngIf="removeWorkstation" i18n>
+      Workstation {{removeWorkstation}} is no longer valid.  Removing registration.
+    </div>
+    <div class="alert alert-danger" *ngIf="workstations.length == 0">
+      <span i18n>Please register a workstation.</span>
+    </div>
+
+    <div class="row">
+      <div class="col" i18n>Register a New Workstation For This Browser</div>
+    </div>
+    <div class="row mt-2">
+      <div class="col-2">
+        <eg-org-select 
+          (onChange)="orgOnChange($event)"
+          [hideOrgs]="hideOrgs"
+          [disableOrgs]="disableOrgs"
+          [initialOrg]="initialOrg"
+          [placeholder]="'Owner'" >
+        </eg-org-select>
+      </div>
+      <div class="col-6">
+        <div class="input-group">
+          <input type='text'
+            class='form-control'
+            i18n-title
+            title="Workstation Name"
+            i18n-placeholder
+            placeholder="Workstation Name"
+            [(ngModel)]='newName'/>
+          <div class="input-group-btn">
+            <button class="btn btn-outline-dark" 
+              [disabled]="!newName || !newOwner"
+              (click)="registerWorkstation()">
+              <span i18n>Register</span>
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="row mt-3 pt-3 border border-left-0 border-right-0 border-bottom-0 border-light">
+      <div class="col">
+        <span i18n>Workstations Registered With This Browser</span>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-8">
+        <select class="form-control" [(ngModel)]="selectedName">
+          <option *ngFor="let ws of workstations" value="{{ws.name}}">
+            <span *ngIf="ws.name == defaultName" i18n>
+              {{ws.name}} (Default)
+            </span>
+            <span *ngIf="ws.name != defaultName">
+              {{ws.name}}
+            </span>
+          </option>
+        </select>
+      </div>
+    </div>
+    <div class="row mt-2">
+      <div class="col-md-6">
+        <button i18n class="btn btn-success" 
+          (click)="useNow()" [disabled]="!selected">
+          Use Now
+        </button>
+        <button i18n class="btn btn-outline-dark" 
+          (click)="setDefault()" [disabled]="!selected">
+          Mark As Default
+        </button>
+        <button i18n class="btn btn-danger"
+          (click)="removeSelected()"
+          [disabled]="!selected || isRemoving || !canDeleteSelected()">
+          Remove
+        </button>
+      </div>
+    </div>
+  </div>
+</div>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts
new file mode 100644 (file)
index 0000000..690a485
--- /dev/null
@@ -0,0 +1,176 @@
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {Router, ActivatedRoute} from '@angular/router';
+import {EgStoreService} from '@eg/core/store';
+import {EgIdlObject} from '@eg/core/idl';
+import {EgNetService} from '@eg/core/net';
+import {EgPermService} from '@eg/core/perm';
+import {EgAuthService} from '@eg/core/auth';
+import {EgOrgService} from '@eg/core/org';
+import {EgEventService} from '@eg/core/event';
+import {EgConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+
+// Slim version of the WS that's stored in the cache.
+interface Workstation {
+    id: number;
+    name: string;
+    owning_lib: number;
+}
+
+@Component({
+  templateUrl: 'workstations.component.html'
+})
+export class WorkstationsComponent implements OnInit {
+
+    selectedName: string;
+    workstations: Workstation[] = [];
+    removeWorkstation: string;
+    newOwner: EgIdlObject;
+    newName: string;
+    defaultName: string;
+
+    @ViewChild('workstationExistsDialog')
+    private wsExistsDialog: EgConfirmDialogComponent;
+
+    // Org selector options.
+    hideOrgs: number[];
+    disableOrgs: number[];
+    orgOnChange = (org: EgIdlObject): void => {
+        this.newOwner = org;
+    }
+
+    constructor(
+        private router: Router,
+        private route: ActivatedRoute,
+        private evt: EgEventService,
+        private net: EgNetService,
+        private store: EgStoreService,
+        private auth: EgAuthService,
+        private org: EgOrgService,
+        private perm: EgPermService
+    ) {}
+
+    ngOnInit() {
+        this.store.getItem('eg.workstation.all')
+        .then(list => this.workstations = list || [])
+        .then(noop => this.store.getItem('eg.workstation.default'))
+        .then(defWs => {
+            this.defaultName = defWs;
+            this.selectedName = this.auth.workstation() || defWs 
+        })
+        .then(noop => {
+            let rm = this.route.snapshot.paramMap.get('remove');
+            if (rm) this.removeSelected(this.removeWorkstation = rm)
+        })
+
+        this.perm.hasWorkPermAt(['REGISTER_WORKSTATION'], true)
+        .then(perms => {
+            // Disable org units that cannot have users and any
+            // that this user does not have work perms for.
+            this.disableOrgs = 
+                this.org.filterList({canHaveUsers : false}, true)
+                .concat(this.org.filterList(
+                    {notInList : perms.REGISTER_WORKSTATION}, true));
+        });
+    }
+
+    selected(): Workstation {
+        return this.workstations.filter(
+          ws => {return ws.name == this.selectedName})[0];
+    }
+
+    useNow(): void {
+        if (!this.selected()) return;
+        this.router.navigate(['/staff/login'], 
+          {queryParams: {workstation: this.selected().name}});
+    }
+
+    setDefault(): void {
+      if (!this.selected()) return;
+      this.defaultName = this.selected().name;
+      this.store.setItem('eg.workstation.default', this.defaultName);
+    }
+
+    removeSelected(name?: string): void {
+        if (!name) name = this.selected().name;
+
+        this.workstations = this.workstations.filter(w => w.name != name);
+        this.store.setItem('eg.workstation.all', this.workstations);
+
+        if (this.defaultName == name) {
+            this.defaultName = null;
+            this.store.removeItem('eg.workstation.default');
+        }
+    }
+    
+    canDeleteSelected(): boolean {
+        return true;
+    }
+
+    registerWorkstation(): void {
+        console.log(`Registering new workstation ` +
+            `"${this.newName}" at ${this.newOwner.shortname()}`);
+
+        this.newName = this.newOwner.shortname() + '-' + this.newName;
+
+        this.registerWorkstationApi().then(
+            wsId => this.registerWorkstationLocal(wsId));
+
+    }
+
+    private handleCollision(): Promise<number> {
+        return new Promise((resolve, reject) => {
+            this.wsExistsDialog.open()
+            .then(
+                confirmed => {
+                    this.registerWorkstationApi(true).then(
+                        wsId => resolve(wsId),
+                        notOk => reject(notOk)
+                    )
+                },
+                dismissed => reject(dismissed)
+            )
+        });
+    }
+
+
+    private registerWorkstationApi(override?: boolean): Promise<number> {
+        let method = 'open-ils.actor.workstation.register';
+        if (override) method += '.override';
+
+        return new Promise((resolve, reject) => {
+            this.net.request(
+                'open-ils.actor', method,
+                this.auth.token(), this.newName, this.newOwner.id()
+            ).subscribe(wsId => {
+                let evt = this.evt.parse(wsId);  
+                if (evt) {
+                    if (evt.textcode == 'WORKSTATION_NAME_EXISTS') {
+                        this.handleCollision().then(
+                            id => resolve(id),
+                            notOk => reject(notOk)
+                        )
+                    } else {
+                        console.error(`Registration failed ${evt}`);
+                        reject();
+                    }
+                } else {
+                   resolve(wsId); 
+                }
+            });
+        });
+    }
+
+    private registerWorkstationLocal(wsId: number) {
+        let ws: Workstation = {
+            id: wsId,
+            name: this.newName,
+            owning_lib: this.newOwner.id()
+        };
+
+        this.workstations.push(ws);
+        this.store.setItem('eg.workstation.all', this.workstations)
+        .then(ok => this.newName = '');
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.module.ts
new file mode 100644 (file)
index 0000000..064b24d
--- /dev/null
@@ -0,0 +1,18 @@
+import {NgModule} from '@angular/core';
+import {EgStaffCommonModule} from '@eg/staff/common.module';
+import {WorkstationsRoutingModule} from './routing.module';
+import {WorkstationsComponent} from './workstations.component';
+
+@NgModule({
+  declarations: [
+    WorkstationsComponent,
+  ],
+  imports: [
+    EgStaffCommonModule,
+    WorkstationsRoutingModule
+  ]
+})
+
+export class ManageWorkstationsModule {}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/app.component.css b/Open-ILS/src/eg2/src/app/staff/app.component.css
deleted file mode 100644 (file)
index 508d879..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#staff-content-container {
-  width: 95%;
-  margin-top:56px;
-  padding-right: 10px;
-  padding-left: 10px;
-  margin-right: auto;
-  margin-left: auto;
-}
diff --git a/Open-ILS/src/eg2/src/app/staff/app.component.html b/Open-ILS/src/eg2/src/app/staff/app.component.html
deleted file mode 100644 (file)
index 7bd463a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- top navigation bar -->
-<eg-staff-nav-bar></eg-staff-nav-bar>
-
-<div id='staff-content-container'>
-  <!-- page content -->
-  <router-outlet></router-outlet>
-</div>
-
diff --git a/Open-ILS/src/eg2/src/app/staff/app.component.ts b/Open-ILS/src/eg2/src/app/staff/app.component.ts
deleted file mode 100644 (file)
index f4bf36f..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-import {Component, OnInit, NgZone} from '@angular/core';
-import {Router, ActivatedRoute, NavigationEnd} from '@angular/router';
-import {EgAuthService, EgAuthWsState} from '@eg/core/auth';
-import {EgNetService} from '@eg/core/net';
-
-@Component({
-  templateUrl: 'app.component.html',
-  styleUrls: ['app.component.css']
-})
-
-export class EgStaffComponent implements OnInit {
-
-    readonly loginPath = '/staff/login';
-    readonly wsAdminBasePath = '/staff/admin/workstation/workstations/';
-
-    constructor(
-        private router: Router,
-        private route: ActivatedRoute,
-        private zone: NgZone,
-        private net: EgNetService,
-        private auth: EgAuthService
-    ) {}
-
-    ngOnInit() {
-
-        console.debug('EgStaffComponent:ngOnInit()');
-
-        // Fires on all in-app router navigation, but not initial page load.
-        this.router.events.subscribe(routeEvent => {
-            if (routeEvent instanceof NavigationEnd) {
-                //console.debug(`EgStaffComponent routing to ${routeEvent.url}`);
-                this.basicAuthChecks(routeEvent.url);
-            }
-        });
-
-        // Redirect to the login page on any auth timeout events.
-        this.net.authExpired$.subscribe(expireEvent => {
-
-            // If the expired authtoken was identified locally (i.e. 
-            // in this browser tab) notify all tabs of imminent logout.
-            if (!expireEvent.viaExternal) this.auth.broadcastLogout();
-
-            console.debug('Auth session has expired. Redirecting to login');
-            this.auth.redirectUrl = this.router.url;
-
-            // https://github.com/angular/angular/issues/18254
-            // When a tab redirects to a login page as a result of
-            // another tab broadcasting a logout, ngOnInit() fails to
-            // fire in the login component, until the user interacts
-            // with the page.  Fix it by wrapping it in zone.run().
-            // This is the only navigate() where I have seen this happen.
-            this.zone.run(() => {
-                this.router.navigate([this.loginPath]);
-            });
-        });
-
-        this.route.data.subscribe((data: {staffResolver : any}) => {
-            // Data fetched via EgStaffResolver is available here.
-        });
-    }
-
-    /**
-     * Verifying the auth token on every route is overkill, since
-     * an expired token will make itself known with the first API
-     * call or when the auth session poll discovers it, but we do
-     * want to prevent navigation from the login or workstation admin
-     * page, since these can be accessed without a valid authtoken or
-     * workstation, respectively, once the initial route resolvers have
-     * done their jobs.
-     */
-    basicAuthChecks(url: string): void {
-
-        // Access to login page is always granted
-        if (url == this.loginPath) return;
-
-        if (!this.auth.token()) 
-            this.router.navigate([this.loginPath]);
-
-        // Access to workstation admin page is granted regardless
-        // of workstation validity.
-        if (url.indexOf(this.wsAdminBasePath) >= 0) return;
-
-        if (this.auth.workstationState != EgAuthWsState.VALID)
-            this.router.navigate([this.wsAdminBasePath]);
-    }
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/app.module.ts b/Open-ILS/src/eg2/src/app/staff/app.module.ts
deleted file mode 100644 (file)
index dfdd41b..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-import {NgModule} from '@angular/core';
-import {EgStaffCommonModule} from '@eg/staff/common.module';
-
-import {EgStaffComponent} from './app.component';
-import {EgStaffRoutingModule} from './routing.module';
-import {EgStaffNavComponent} from './nav.component';
-import {EgStaffLoginComponent} from './login.component';
-import {EgStaffSplashComponent} from './splash.component';
-
-@NgModule({
-  declarations: [
-    EgStaffComponent,
-    EgStaffNavComponent,
-    EgStaffSplashComponent,
-    EgStaffLoginComponent
-  ],
-  imports: [
-    EgStaffCommonModule.forRoot(),
-    EgStaffRoutingModule
-  ]
-})
-
-export class EgStaffModule {}
-
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/app.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/app.component.html
deleted file mode 100644 (file)
index 1596454..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<!-- search form sits atop every catalog page -->
-<eg-catalog-search-form></eg-catalog-search-form>
-
-<!-- search results, record details, etc. -->
-<router-outlet></router-outlet>
-
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/app.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/app.component.ts
deleted file mode 100644 (file)
index 8fffa13..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-import {Component, OnInit} from '@angular/core';
-import {StaffCatalogService} from './app.service';
-
-@Component({
-  templateUrl: 'app.component.html'
-})
-export class EgCatalogComponent implements OnInit {
-
-    constructor(private staffCat: StaffCatalogService) {}
-
-    ngOnInit() {
-        // Create the search context that will be used by all of my
-        // child components.  After initial creation, the context is
-        // reset and updated as needed to apply new search parameters.
-        this.staffCat.createContext();
-    }
-}
-
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/app.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/app.module.ts
deleted file mode 100644 (file)
index fbcf894..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-import {NgModule} from '@angular/core';
-import {EgStaffCommonModule} from '@eg/staff/common.module';
-import {EgUnapiService} from '@eg/share/unapi';
-import {EgCatalogRoutingModule} from './routing.module';
-import {EgCatalogService} from '@eg/share/catalog/catalog.service';
-import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
-import {EgCatalogComponent} from './app.component';
-import {SearchFormComponent} from './search-form.component';
-import {ResultsComponent} from './result/results.component';
-import {RecordComponent} from './record/record.component';
-import {CopiesComponent} from './record/copies.component';
-import {EgBibSummaryComponent} from '../share/bib-summary.component';
-import {ResultPaginationComponent} from './result/pagination.component';
-import {ResultFacetsComponent} from './result/facets.component';
-import {ResultRecordComponent} from './result/record.component';
-import {StaffCatalogService} from './app.service';
-import {RecordPaginationComponent} from './record/pagination.component';
-
-@NgModule({
-  declarations: [
-    EgCatalogComponent,
-    ResultsComponent,
-    RecordComponent,
-    CopiesComponent,
-    EgBibSummaryComponent,
-    SearchFormComponent,
-    ResultRecordComponent,
-    ResultFacetsComponent,
-    ResultPaginationComponent,
-    RecordPaginationComponent
-  ],
-  imports: [
-    EgStaffCommonModule,
-    EgCatalogRoutingModule
-  ],
-  providers: [
-    EgUnapiService,
-    EgCatalogService,
-    EgCatalogUrlService,
-    StaffCatalogService
-  ]
-})
-
-export class EgCatalogModule { 
-
-}
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/app.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/app.service.ts
deleted file mode 100644 (file)
index 0cf04ca..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-import {Injectable} from '@angular/core';
-import {Router, ActivatedRoute} from '@angular/router';
-import {EgIdlObject} from '@eg/core/idl';
-import {EgOrgService} from '@eg/core/org';
-import {EgCatalogService} from '@eg/share/catalog/catalog.service';
-import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
-import {CatalogSearchContext} from '@eg/share/catalog/search-context';
-
-/**
- * Shared bits needed by the staff version of the catalog.
- */
-
-@Injectable()
-export class StaffCatalogService {
-
-    searchContext: CatalogSearchContext;
-    routeIndex: number = 0;
-    defaultSearchOrg: EgIdlObject;
-    defaultSearchLimit: number;
-
-    // TODO: does unapi support pref-lib for result-page copy counts?
-    prefOrg: EgIdlObject; 
-
-    constructor(
-        private router: Router,
-        private route: ActivatedRoute,
-        private org: EgOrgService,
-        private cat: EgCatalogService,
-        private catUrl: EgCatalogUrlService
-    ) { }
-
-    createContext(): void {
-        // Initialize the search context from the load-time URL params.
-        // Do this here so the search form and other context data are
-        // applied on every page, not just the search results page.  The
-        // search results pages will handle running the actual search.
-        this.searchContext = 
-            this.catUrl.fromUrlParams(this.route.snapshot.queryParamMap);
-
-        this.searchContext.org = this.org; // service, not searchOrg
-        this.searchContext.isStaff = true;
-        this.applySearchDefaults();
-    }
-
-    applySearchDefaults(): void {
-        if (!this.searchContext.searchOrg) {
-            this.searchContext.searchOrg = 
-                this.defaultSearchOrg || this.org.root();
-        }
-
-        if (!this.searchContext.pager.limit) {
-            this.searchContext.pager.limit = this.defaultSearchLimit || 20;
-        }
-    }
-
-    /**
-     * Redirect to the search results page while propagating the current
-     * search paramters into the URL.  Let the search results component
-     * execute the actual search.
-     */
-    search(): void {
-        if (!this.searchContext.isSearchable()) return;
-
-        let params = this.catUrl.toUrlParams(this.searchContext);
-        
-        // Force a new search every time this method is called, even if
-        // it's the same as the active search.  Since router navigation
-        // exits early when the route + params is identical, add a
-        // random token to the route params to force a full navigation.
-        // This also resolves a problem where only removing secondary+
-        // versions of a query param fail to cause a route navigation.
-        // (E.g. going from two query= params to one).  Investigation
-        // pending.
-        params.ridx=''+this.routeIndex++;
-
-        this.router.navigate(
-          ['/staff/catalog/search'], {queryParams: params});
-    }
-
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.html
new file mode 100644 (file)
index 0000000..1596454
--- /dev/null
@@ -0,0 +1,6 @@
+<!-- search form sits atop every catalog page -->
+<eg-catalog-search-form></eg-catalog-search-form>
+
+<!-- search results, record details, etc. -->
+<router-outlet></router-outlet>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts
new file mode 100644 (file)
index 0000000..0324ed4
--- /dev/null
@@ -0,0 +1,18 @@
+import {Component, OnInit} from '@angular/core';
+import {StaffCatalogService} from './catalog.service';
+
+@Component({
+  templateUrl: 'catalog.component.html'
+})
+export class EgCatalogComponent implements OnInit {
+
+    constructor(private staffCat: StaffCatalogService) {}
+
+    ngOnInit() {
+        // Create the search context that will be used by all of my
+        // child components.  After initial creation, the context is
+        // reset and updated as needed to apply new search parameters.
+        this.staffCat.createContext();
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
new file mode 100644 (file)
index 0000000..80a1499
--- /dev/null
@@ -0,0 +1,46 @@
+import {NgModule} from '@angular/core';
+import {EgStaffCommonModule} from '@eg/staff/common.module';
+import {EgUnapiService} from '@eg/share/unapi';
+import {EgCatalogRoutingModule} from './routing.module';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
+import {EgCatalogComponent} from './catalog.component';
+import {SearchFormComponent} from './search-form.component';
+import {ResultsComponent} from './result/results.component';
+import {RecordComponent} from './record/record.component';
+import {CopiesComponent} from './record/copies.component';
+import {EgBibSummaryComponent} from '../share/bib-summary.component';
+import {ResultPaginationComponent} from './result/pagination.component';
+import {ResultFacetsComponent} from './result/facets.component';
+import {ResultRecordComponent} from './result/record.component';
+import {StaffCatalogService} from './catalog.service';
+import {RecordPaginationComponent} from './record/pagination.component';
+
+@NgModule({
+  declarations: [
+    EgCatalogComponent,
+    ResultsComponent,
+    RecordComponent,
+    CopiesComponent,
+    EgBibSummaryComponent,
+    SearchFormComponent,
+    ResultRecordComponent,
+    ResultFacetsComponent,
+    ResultPaginationComponent,
+    RecordPaginationComponent
+  ],
+  imports: [
+    EgStaffCommonModule,
+    EgCatalogRoutingModule
+  ],
+  providers: [
+    EgUnapiService,
+    EgCatalogService,
+    EgCatalogUrlService,
+    StaffCatalogService
+  ]
+})
+
+export class EgCatalogModule { 
+
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
new file mode 100644 (file)
index 0000000..0cf04ca
--- /dev/null
@@ -0,0 +1,82 @@
+import {Injectable} from '@angular/core';
+import {Router, ActivatedRoute} from '@angular/router';
+import {EgIdlObject} from '@eg/core/idl';
+import {EgOrgService} from '@eg/core/org';
+import {EgCatalogService} from '@eg/share/catalog/catalog.service';
+import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
+import {CatalogSearchContext} from '@eg/share/catalog/search-context';
+
+/**
+ * Shared bits needed by the staff version of the catalog.
+ */
+
+@Injectable()
+export class StaffCatalogService {
+
+    searchContext: CatalogSearchContext;
+    routeIndex: number = 0;
+    defaultSearchOrg: EgIdlObject;
+    defaultSearchLimit: number;
+
+    // TODO: does unapi support pref-lib for result-page copy counts?
+    prefOrg: EgIdlObject; 
+
+    constructor(
+        private router: Router,
+        private route: ActivatedRoute,
+        private org: EgOrgService,
+        private cat: EgCatalogService,
+        private catUrl: EgCatalogUrlService
+    ) { }
+
+    createContext(): void {
+        // Initialize the search context from the load-time URL params.
+        // Do this here so the search form and other context data are
+        // applied on every page, not just the search results page.  The
+        // search results pages will handle running the actual search.
+        this.searchContext = 
+            this.catUrl.fromUrlParams(this.route.snapshot.queryParamMap);
+
+        this.searchContext.org = this.org; // service, not searchOrg
+        this.searchContext.isStaff = true;
+        this.applySearchDefaults();
+    }
+
+    applySearchDefaults(): void {
+        if (!this.searchContext.searchOrg) {
+            this.searchContext.searchOrg = 
+                this.defaultSearchOrg || this.org.root();
+        }
+
+        if (!this.searchContext.pager.limit) {
+            this.searchContext.pager.limit = this.defaultSearchLimit || 20;
+        }
+    }
+
+    /**
+     * Redirect to the search results page while propagating the current
+     * search paramters into the URL.  Let the search results component
+     * execute the actual search.
+     */
+    search(): void {
+        if (!this.searchContext.isSearchable()) return;
+
+        let params = this.catUrl.toUrlParams(this.searchContext);
+        
+        // Force a new search every time this method is called, even if
+        // it's the same as the active search.  Since router navigation
+        // exits early when the route + params is identical, add a
+        // random token to the route params to force a full navigation.
+        // This also resolves a problem where only removing secondary+
+        // versions of a query param fail to cause a route navigation.
+        // (E.g. going from two query= params to one).  Investigation
+        // pending.
+        params.ridx=''+this.routeIndex++;
+
+        this.router.navigate(
+          ['/staff/catalog/search'], {queryParams: params});
+    }
+
+}
+
+
index b8e05f0..cae7920 100644 (file)
@@ -1,6 +1,6 @@
 import {Component, OnInit, Input} from '@angular/core';
 import {EgNetService} from '@eg/core/net';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 import {Pager} from '@eg/share/util/pager';
 import {EgOrgService} from '@eg/core/org';
 
index a7535f6..31fee2c 100644 (file)
@@ -3,7 +3,7 @@ import {Router} from '@angular/router';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
 import {CatalogSearchContext} from '@eg/share/catalog/search-context';
 import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 import {Pager} from '@eg/share/util/pager';
 
 
index 78552eb..61e5759 100644 (file)
@@ -5,7 +5,7 @@ import {EgIdlObject} from '@eg/core/idl';
 import {CatalogSearchContext, CatalogSearchState} 
   from '@eg/share/catalog/search-context';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 import {EgBibSummaryComponent} from '../../share/bib-summary.component';
 
 @Component({
index 45fc8c5..8452323 100644 (file)
@@ -8,7 +8,7 @@ import {EgOrgService} from '@eg/core/org';
 import {EgAuthService} from '@eg/core/auth';
 import {EgPcrudService} from '@eg/core/pcrud';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
-import {StaffCatalogService} from './app.service';
+import {StaffCatalogService} from './catalog.service';
 
 @Injectable()
 export class EgCatalogResolver implements Resolve<Promise<any[]>> {
index 8101ced..be68968 100644 (file)
@@ -1,7 +1,7 @@
 import {Component, OnInit, Input} from '@angular/core';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
 import {CatalogSearchContext, FacetFilter} from '@eg/share/catalog/search-context';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 
 export const FACET_CONFIG = {
     display: [
index 8dbb4d8..189fbce 100644 (file)
@@ -1,7 +1,7 @@
 import {Component, OnInit, Input} from '@angular/core';
 import {EgCatalogService} from '@eg/share/catalog/catalog.service';
 import {CatalogSearchContext} from '@eg/share/catalog/search-context';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 
 @Component({
   selector: 'eg-catalog-result-pagination',
index beee4cf..cf903f5 100644 (file)
@@ -5,7 +5,7 @@ import {EgCatalogService} from '@eg/share/catalog/catalog.service';
 import {CatalogSearchContext} from '@eg/share/catalog/search-context';
 import {EgNetService} from '@eg/core/net';
 import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 
 @Component({
   selector: 'eg-catalog-result-record',
index ff2d36c..ff9c259 100644 (file)
@@ -7,7 +7,7 @@ import {EgCatalogUrlService} from '@eg/share/catalog/catalog-url.service';
 import {CatalogSearchContext, CatalogSearchState}
   from '@eg/share/catalog/search-context';
 import {EgPcrudService} from '@eg/core/pcrud';
-import {StaffCatalogService} from '../app.service';
+import {StaffCatalogService} from '../catalog.service';
 import {EgIdlObject} from '@eg/core/idl';
 
 @Component({
index 9125c9d..2376f80 100644 (file)
@@ -1,6 +1,6 @@
 import {NgModule} from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
-import {EgCatalogComponent} from './app.component';
+import {EgCatalogComponent} from './catalog.component';
 import {ResultsComponent} from './result/results.component';
 import {RecordComponent} from './record/record.component';
 import {EgCatalogResolver} from './resolver.service';
index 9e5b807..615e608 100644 (file)
@@ -4,7 +4,7 @@ import {EgOrgService} from '@eg/core/org';
 import {EgCatalogService,} from '@eg/share/catalog/catalog.service';
 import {CatalogSearchContext, CatalogSearchState} 
   from '@eg/share/catalog/search-context';
-import {StaffCatalogService} from './app.service';
+import {StaffCatalogService} from './catalog.service';
 
 @Component({
   selector: 'eg-catalog-search-form',
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.component.html
deleted file mode 100644 (file)
index 58d5c4c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-
-<eg-staff-banner bannerText="Search for Patron by Barcode" i18n-bannerText>
-</eg-staff-banner>
-
-<div class="col-4">
-  <div class="input-group">
-    <div class="input-group-prepend">
-      <span class="input-group-text" i18n>Barcode:</span>
-    </div>
-    <input type='text' id='barcode-search-input' class="form-control" 
-      placeholder="Barcode" i18n-placeholder [ngModel]='barcode'/>
-    <div class="input-group-append">
-      <button class="btn btn-outline-secondary" 
-        (click)="findUser()" i18n>Submit</button>
-    </div>
-  </div>
-</div>
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.component.ts
deleted file mode 100644 (file)
index 294a88e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-import {Component, OnInit, Renderer} from '@angular/core';
-import {ActivatedRoute} from '@angular/router';
-import {EgNetService} from '@eg/core/net';
-import {EgAuthService} from '@eg/core/auth';
-
-@Component({
-  templateUrl: 'app.component.html'
-})
-
-export class EgBcSearchComponent implements OnInit {
-
-    barcode: string = '';
-
-    constructor(
-        private route: ActivatedRoute,
-        private renderer: Renderer,
-        private net: EgNetService,
-        private auth: EgAuthService
-    ) {}
-
-    ngOnInit() {
-
-        this.renderer.selectRootElement('#barcode-search-input').focus();
-        this.barcode = this.route.snapshot.paramMap.get('barcode');
-
-        if (this.barcode) {
-            this.findUser();
-        }
-    }
-
-    findUser(): void {
-        alert('Searching for user ' + this.barcode);
-    }
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/app.module.ts
deleted file mode 100644 (file)
index bb5f688..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-import {NgModule}                from '@angular/core';
-import {EgStaffCommonModule}     from '@eg/staff/common.module';
-import {EgBcSearchRoutingModule} from './routing.module';
-import {EgBcSearchComponent}     from './app.component';
-
-@NgModule({
-  declarations: [
-    EgBcSearchComponent
-  ],
-  imports: [
-    EgStaffCommonModule,
-    EgBcSearchRoutingModule,
-  ],
-})
-
-export class EgBcSearchModule {}
-
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.html
new file mode 100644 (file)
index 0000000..58d5c4c
--- /dev/null
@@ -0,0 +1,19 @@
+
+<eg-staff-banner bannerText="Search for Patron by Barcode" i18n-bannerText>
+</eg-staff-banner>
+
+<div class="col-4">
+  <div class="input-group">
+    <div class="input-group-prepend">
+      <span class="input-group-text" i18n>Barcode:</span>
+    </div>
+    <input type='text' id='barcode-search-input' class="form-control" 
+      placeholder="Barcode" i18n-placeholder [ngModel]='barcode'/>
+    <div class="input-group-append">
+      <button class="btn btn-outline-secondary" 
+        (click)="findUser()" i18n>Submit</button>
+    </div>
+  </div>
+</div>
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts
new file mode 100644 (file)
index 0000000..35e9e85
--- /dev/null
@@ -0,0 +1,36 @@
+import {Component, OnInit, Renderer} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {EgNetService} from '@eg/core/net';
+import {EgAuthService} from '@eg/core/auth';
+
+@Component({
+  templateUrl: 'bcsearch.component.html'
+})
+
+export class EgBcSearchComponent implements OnInit {
+
+    barcode: string = '';
+
+    constructor(
+        private route: ActivatedRoute,
+        private renderer: Renderer,
+        private net: EgNetService,
+        private auth: EgAuthService
+    ) {}
+
+    ngOnInit() {
+
+        this.renderer.selectRootElement('#barcode-search-input').focus();
+        this.barcode = this.route.snapshot.paramMap.get('barcode');
+
+        if (this.barcode) {
+            this.findUser();
+        }
+    }
+
+    findUser(): void {
+        alert('Searching for user ' + this.barcode);
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.module.ts
new file mode 100644 (file)
index 0000000..cbb97b5
--- /dev/null
@@ -0,0 +1,17 @@
+import {NgModule}                from '@angular/core';
+import {EgStaffCommonModule}     from '@eg/staff/common.module';
+import {EgBcSearchRoutingModule} from './routing.module';
+import {EgBcSearchComponent}     from './bcsearch.component';
+
+@NgModule({
+  declarations: [
+    EgBcSearchComponent
+  ],
+  imports: [
+    EgStaffCommonModule,
+    EgBcSearchRoutingModule,
+  ],
+})
+
+export class EgBcSearchModule {}
+
index a8c5a04..82f8a8b 100644 (file)
@@ -1,6 +1,6 @@
 import {NgModule}             from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
-import {EgBcSearchComponent}  from './app.component';
+import {EgBcSearchComponent}  from './bcsearch.component';
 
 const routes: Routes = [
   { path: '',
index a61417a..969decd 100644 (file)
@@ -5,7 +5,7 @@ const routes: Routes = [
   { path: 'patron',
     children: [{
       path: 'bcsearch',
-      loadChildren: '@eg/staff/circ/patron/bcsearch/app.module#EgBcSearchModule'
+      loadChildren: '@eg/staff/circ/patron/bcsearch/bcsearch.module#EgBcSearchModule'
     }]
   }
 ];
index a25baf9..49a04a3 100644 (file)
@@ -1,7 +1,7 @@
 import {NgModule} from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
 import {EgStaffResolver} from './resolver.service';
-import {EgStaffComponent} from './app.component';
+import {EgStaffComponent} from './staff.component';
 import {EgStaffLoginComponent} from './login.component';
 import {EgStaffSplashComponent} from './splash.component';
 
@@ -28,7 +28,7 @@ const routes: Routes = [{
     loadChildren : '@eg/staff/circ/routing.module#EgCircRoutingModule'
   }, {
     path: 'catalog',
-    loadChildren : '@eg/staff/catalog/app.module#EgCatalogModule'
+    loadChildren : '@eg/staff/catalog/catalog.module#EgCatalogModule'
   }, {
     path: 'admin',
     loadChildren : '@eg/staff/admin/routing.module#EgAdminRoutingModule'
diff --git a/Open-ILS/src/eg2/src/app/staff/staff.component.css b/Open-ILS/src/eg2/src/app/staff/staff.component.css
new file mode 100644 (file)
index 0000000..508d879
--- /dev/null
@@ -0,0 +1,8 @@
+#staff-content-container {
+  width: 95%;
+  margin-top:56px;
+  padding-right: 10px;
+  padding-left: 10px;
+  margin-right: auto;
+  margin-left: auto;
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/staff.component.html b/Open-ILS/src/eg2/src/app/staff/staff.component.html
new file mode 100644 (file)
index 0000000..7bd463a
--- /dev/null
@@ -0,0 +1,8 @@
+<!-- top navigation bar -->
+<eg-staff-nav-bar></eg-staff-nav-bar>
+
+<div id='staff-content-container'>
+  <!-- page content -->
+  <router-outlet></router-outlet>
+</div>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/staff.component.ts b/Open-ILS/src/eg2/src/app/staff/staff.component.ts
new file mode 100644 (file)
index 0000000..864beab
--- /dev/null
@@ -0,0 +1,88 @@
+import {Component, OnInit, NgZone} from '@angular/core';
+import {Router, ActivatedRoute, NavigationEnd} from '@angular/router';
+import {EgAuthService, EgAuthWsState} from '@eg/core/auth';
+import {EgNetService} from '@eg/core/net';
+
+@Component({
+  templateUrl: 'staff.component.html',
+  styleUrls: ['staff.component.css']
+})
+
+export class EgStaffComponent implements OnInit {
+
+    readonly loginPath = '/staff/login';
+    readonly wsAdminBasePath = '/staff/admin/workstation/workstations/';
+
+    constructor(
+        private router: Router,
+        private route: ActivatedRoute,
+        private zone: NgZone,
+        private net: EgNetService,
+        private auth: EgAuthService
+    ) {}
+
+    ngOnInit() {
+
+        console.debug('EgStaffComponent:ngOnInit()');
+
+        // Fires on all in-app router navigation, but not initial page load.
+        this.router.events.subscribe(routeEvent => {
+            if (routeEvent instanceof NavigationEnd) {
+                //console.debug(`EgStaffComponent routing to ${routeEvent.url}`);
+                this.basicAuthChecks(routeEvent.url);
+            }
+        });
+
+        // Redirect to the login page on any auth timeout events.
+        this.net.authExpired$.subscribe(expireEvent => {
+
+            // If the expired authtoken was identified locally (i.e. 
+            // in this browser tab) notify all tabs of imminent logout.
+            if (!expireEvent.viaExternal) this.auth.broadcastLogout();
+
+            console.debug('Auth session has expired. Redirecting to login');
+            this.auth.redirectUrl = this.router.url;
+
+            // https://github.com/angular/angular/issues/18254
+            // When a tab redirects to a login page as a result of
+            // another tab broadcasting a logout, ngOnInit() fails to
+            // fire in the login component, until the user interacts
+            // with the page.  Fix it by wrapping it in zone.run().
+            // This is the only navigate() where I have seen this happen.
+            this.zone.run(() => {
+                this.router.navigate([this.loginPath]);
+            });
+        });
+
+        this.route.data.subscribe((data: {staffResolver : any}) => {
+            // Data fetched via EgStaffResolver is available here.
+        });
+    }
+
+    /**
+     * Verifying the auth token on every route is overkill, since
+     * an expired token will make itself known with the first API
+     * call or when the auth session poll discovers it, but we do
+     * want to prevent navigation from the login or workstation admin
+     * page, since these can be accessed without a valid authtoken or
+     * workstation, respectively, once the initial route resolvers have
+     * done their jobs.
+     */
+    basicAuthChecks(url: string): void {
+
+        // Access to login page is always granted
+        if (url == this.loginPath) return;
+
+        if (!this.auth.token()) 
+            this.router.navigate([this.loginPath]);
+
+        // Access to workstation admin page is granted regardless
+        // of workstation validity.
+        if (url.indexOf(this.wsAdminBasePath) >= 0) return;
+
+        if (this.auth.workstationState != EgAuthWsState.VALID)
+            this.router.navigate([this.wsAdminBasePath]);
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/staff.module.ts b/Open-ILS/src/eg2/src/app/staff/staff.module.ts
new file mode 100644 (file)
index 0000000..52f6abd
--- /dev/null
@@ -0,0 +1,24 @@
+import {NgModule} from '@angular/core';
+import {EgStaffCommonModule} from '@eg/staff/common.module';
+
+import {EgStaffComponent} from './staff.component';
+import {EgStaffRoutingModule} from './routing.module';
+import {EgStaffNavComponent} from './nav.component';
+import {EgStaffLoginComponent} from './login.component';
+import {EgStaffSplashComponent} from './splash.component';
+
+@NgModule({
+  declarations: [
+    EgStaffComponent,
+    EgStaffNavComponent,
+    EgStaffSplashComponent,
+    EgStaffLoginComponent
+  ],
+  imports: [
+    EgStaffCommonModule.forRoot(),
+    EgStaffRoutingModule
+  ]
+})
+
+export class EgStaffModule {}
+
index 3ce97cc..eaa1c71 100644 (file)
@@ -5,7 +5,7 @@
   </p>
   <hr class="my-4"/>
   <p i18n>
-    But maybe you meant to go to the <a routerLink="/staff/splash">staff page</a>
-    or <a routerLink="/catalog">the catalog.</a>
+    But maybe you meant to go to the 
+    <a routerLink="/staff/splash">staff page</a>
   </p>
 </div>