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.
declare var js2JSON: (jsThing:any) => string;
declare var OpenSRF: any; // creating sessions
-export interface EgPcrudReqOps {
+interface EgPcrudReqOps {
authoritative?: boolean;
anonymous?: boolean;
idlist?: boolean;
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;
CookieModule.forRoot(),
EgCommonModule.forRoot()
],
- declarations: [EgHelloWorldComponent],
+ declarations: [
+ EgHelloWorldComponent,
+ //EgDialogComponent,
+ //EgConfirmDialogComponent
+ ],
entryComponents: [
EgHelloWorldComponent,
- //EgConfirmDialogComponent // declared in EgCommonModule
+ //EgDialogComponent,
+ //EgConfirmDialogComponent
]
})
.factory('ng2Title', downgradeInjectable(Title))
.directive('eg2HelloWorld',
downgradeComponent({component: EgHelloWorldComponent}))
+ /*
+ .directive('eg2ConfirmDialog',
+ downgradeComponent({component: EgConfirmDialogComponent}))
+ */
;
}, {
path: 'staff',
resolve : {startup : EgBaseResolver},
- loadChildren: './staff/app.module#EgStaffModule'
+ loadChildren: './staff/staff.module#EgStaffModule'
}
];
*/
@Component({
- selector: 'eg-component',
+ selector: 'eg-dialog',
template: '<ng-template></ng-template>'
})
export class EgDialogComponent {
})
/**
- * Promptation dialog that asks a yes/no question.
+ * Promptation dialog that requests user input.
*/
export class EgPromptDialogComponent extends EgDialogComponent {
// What question are we asking?
--- /dev/null
+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() {}
+}
+
+
const routes: Routes = [{
path: 'workstations',
- loadChildren: '@eg/staff/admin/workstation/workstations/app.module#ManageWorkstationsModule'
+ loadChildren: '@eg/staff/admin/workstation/workstations/workstations.module#ManageWorkstationsModule'
}];
@NgModule({
+++ /dev/null
-<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>
-
+++ /dev/null
-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 = '');
- }
-}
-
-
+++ /dev/null
-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 {}
-
-
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
--- /dev/null
+<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>
+
--- /dev/null
+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 = '');
+ }
+}
+
+
--- /dev/null
+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 {}
+
+
+++ /dev/null
-#staff-content-container {
- width: 95%;
- margin-top:56px;
- padding-right: 10px;
- padding-left: 10px;
- margin-right: auto;
- margin-left: auto;
-}
+++ /dev/null
-<!-- top navigation bar -->
-<eg-staff-nav-bar></eg-staff-nav-bar>
-
-<div id='staff-content-container'>
- <!-- page content -->
- <router-outlet></router-outlet>
-</div>
-
+++ /dev/null
-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]);
- }
-}
-
-
+++ /dev/null
-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 {}
-
+++ /dev/null
-<!-- search form sits atop every catalog page -->
-<eg-catalog-search-form></eg-catalog-search-form>
-
-<!-- search results, record details, etc. -->
-<router-outlet></router-outlet>
-
+++ /dev/null
-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();
- }
-}
-
+++ /dev/null
-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 {
-
-}
+++ /dev/null
-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});
- }
-
-}
-
-
--- /dev/null
+<!-- search form sits atop every catalog page -->
+<eg-catalog-search-form></eg-catalog-search-form>
+
+<!-- search results, record details, etc. -->
+<router-outlet></router-outlet>
+
--- /dev/null
+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();
+ }
+}
+
--- /dev/null
+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 {
+
+}
--- /dev/null
+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});
+ }
+
+}
+
+
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';
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';
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({
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[]>> {
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: [
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',
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',
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({
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';
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',
+++ /dev/null
-
-<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>
-
-
+++ /dev/null
-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);
- }
-}
-
-
+++ /dev/null
-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 {}
-
--- /dev/null
+
+<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>
+
+
--- /dev/null
+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);
+ }
+}
+
+
--- /dev/null
+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 {}
+
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: '',
{ path: 'patron',
children: [{
path: 'bcsearch',
- loadChildren: '@eg/staff/circ/patron/bcsearch/app.module#EgBcSearchModule'
+ loadChildren: '@eg/staff/circ/patron/bcsearch/bcsearch.module#EgBcSearchModule'
}]
}
];
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';
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'
--- /dev/null
+#staff-content-container {
+ width: 95%;
+ margin-top:56px;
+ padding-right: 10px;
+ padding-left: 10px;
+ margin-right: auto;
+ margin-left: auto;
+}
--- /dev/null
+<!-- top navigation bar -->
+<eg-staff-nav-bar></eg-staff-nav-bar>
+
+<div id='staff-content-container'>
+ <!-- page content -->
+ <router-outlet></router-outlet>
+</div>
+
--- /dev/null
+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]);
+ }
+}
+
+
--- /dev/null
+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 {}
+
</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>