LPXXX Patron UI WIP
authorBill Erickson <berickxx@gmail.com>
Wed, 12 Aug 2020 16:50:59 +0000 (12:50 -0400)
committerBill Erickson <berickxx@gmail.com>
Fri, 21 Aug 2020 20:41:27 +0000 (16:41 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
16 files changed:
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.html [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/bcsearch.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/routing.module.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/patron-context.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.css [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/routing.module.ts
Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts

diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch.component.html
new file mode 100644 (file)
index 0000000..db013a6
--- /dev/null
@@ -0,0 +1,16 @@
+
+<div class="col-lg-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.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch.component.ts
new file mode 100644 (file)
index 0000000..ddd0433
--- /dev/null
@@ -0,0 +1,37 @@
+import {Component, OnInit, AfterViewInit} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+
+@Component({
+  templateUrl: 'bcsearch.component.html',
+  selector: 'eg-patron-barcode-search'
+})
+
+export class BcSearchComponent implements OnInit, AfterViewInit {
+
+    barcode = '';
+
+    constructor(
+        private route: ActivatedRoute,
+        private net: NetService,
+        private auth: AuthService
+    ) {}
+
+    ngOnInit() {
+        this.barcode = this.route.snapshot.paramMap.get('barcode');
+        if (this.barcode) {
+            this.findUser();
+        }
+    }
+
+    ngAfterViewInit() {
+        document.getElementById('barcode-search-input').focus();
+    }
+
+    findUser(): void {
+        alert('Searching for user ' + this.barcode);
+    }
+}
+
+
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
deleted file mode 100644 (file)
index e83cf9e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-
-<eg-staff-banner bannerText="Search for Patron by Barcode" i18n-bannerText>
-</eg-staff-banner>
-
-<div class="col-lg-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
deleted file mode 100644 (file)
index dac5048..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-import {Component, OnInit, Renderer2} from '@angular/core';
-import {ActivatedRoute} from '@angular/router';
-import {NetService} from '@eg/core/net.service';
-import {AuthService} from '@eg/core/auth.service';
-
-@Component({
-  templateUrl: 'bcsearch.component.html'
-})
-
-export class BcSearchComponent implements OnInit {
-
-    barcode = '';
-
-    constructor(
-        private route: ActivatedRoute,
-        private renderer: Renderer2,
-        private net: NetService,
-        private auth: AuthService
-    ) {}
-
-    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
deleted file mode 100644 (file)
index d1b16df..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-import {NgModule} from '@angular/core';
-import {StaffCommonModule} from '@eg/staff/common.module';
-import {BcSearchRoutingModule} from './routing.module';
-import {BcSearchComponent} from './bcsearch.component';
-
-@NgModule({
-  declarations: [
-    BcSearchComponent
-  ],
-  imports: [
-    StaffCommonModule,
-    BcSearchRoutingModule,
-  ],
-})
-
-export class BcSearchModule {}
-
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/bcsearch/routing.module.ts
deleted file mode 100644 (file)
index ce6783d..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-import {NgModule} from '@angular/core';
-import {RouterModule, Routes} from '@angular/router';
-import {BcSearchComponent} from './bcsearch.component';
-
-const routes: Routes = [
-  { path: '',
-    component: BcSearchComponent
-  },
-  { path: ':barcode',
-    component: BcSearchComponent
-  },
-];
-
-@NgModule({
-  imports: [RouterModule.forChild(routes)],
-  exports: [RouterModule]
-})
-
-export class BcSearchRoutingModule {}
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron-context.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron-context.ts
new file mode 100644 (file)
index 0000000..d88e421
--- /dev/null
@@ -0,0 +1,12 @@
+import {IdlObject} from '@eg/core/idl.service';
+
+export class PatronContext {
+
+    patron: IdlObject;
+
+    setPatron(patron: IdlObject) {
+        this.patron = patron;
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.css b/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.css
new file mode 100644 (file)
index 0000000..1c10d8a
--- /dev/null
@@ -0,0 +1,9 @@
+
+.patron-content-pane {
+  margin-top: 10px;
+  position: fixed;
+  height: 100%;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.html
new file mode 100644 (file)
index 0000000..f3f3866
--- /dev/null
@@ -0,0 +1,44 @@
+<eg-staff-banner bannerText="Manage Patrons" i18n-bannerText></eg-staff-banner>
+
+<div class="row">
+  <div class="col-lg-3">
+    <span *ngIf="context.patron" class="font-weight-bold" i18n>
+      {{patronService.namePart(context.patron, 'family_name')}}, 
+      {{patronService.namePart(context.patron, 'first_given_name')}} 
+      {{patronService.namePart(context.patron, 'second_given_name')}}
+    </span>
+  </div>
+  <div class="col-lg-9">
+    <ul ngbNav #patronNav="ngbNav" class="nav-tabs"
+      [activeId]="patronTab" (navChange)="beforeTabChange($event)">
+      <li ngbNavItem="search">
+        <a ngbNavLink i18n>Patron Search</a>
+        <ng-template ngbNavContent>
+          <div class="patron-content-pane">
+            <eg-patron-search (patronsClicked)="patronsSelected($event)">
+            </eg-patron-search> 
+          </div>
+        </ng-template>
+      </li>
+    </ul>
+  </div>
+</div>
+
+<div class="row">
+  <ng-container *ngIf="showSummary">
+    <div class="col-lg-3">
+      <ng-container *ngIf="context.patron">
+        <eg-patron-summary [context]="context"></eg-patron-summary>
+      </ng-container>
+    </div>
+    <div class="col-lg-9">
+      <div [ngbNavOutlet]="patronNav"></div>
+    </div>
+  </ng-container>
+  <ng-container *ngIf="!showSummary">
+    <div class="col-lg-12">
+      <div [ngbNavOutlet]="patronNav"></div>
+    </div>
+  </ng-container>
+</div>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.component.ts
new file mode 100644 (file)
index 0000000..d74310c
--- /dev/null
@@ -0,0 +1,93 @@
+import {Component, OnInit} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
+import {PatronContext} from './patron-context';
+import {PatronSearchComponent} from '@eg/staff/share/patron/search.component';
+
+const PATRON_FLESH_FIELDS = [
+       'card',
+       'cards',
+       'settings',
+       'standing_penalties',
+       'addresses',
+       'billing_address',
+       'mailing_address',
+       'stat_cat_entries',
+       'waiver_entries',
+       'usr_activity',
+       'notes',
+       'profile',
+       'net_access_level',
+       'ident_type',
+       'ident_type2',
+       'groups'
+];
+
+@Component({
+  templateUrl: 'patrons.component.html',
+  styleUrls: ['patrons.component.css']
+})
+export class PatronsComponent implements OnInit {
+
+    context: PatronContext;
+    patronTab = 'search';
+    patronId: number;
+
+    showSummary = true;
+
+    constructor(
+        private router: Router,
+        private route: ActivatedRoute,
+        private net: NetService,
+        private auth: AuthService,
+        public patronService: PatronService
+    ) {}
+
+    ngOnInit() {
+        this.route.paramMap.subscribe((params: ParamMap) => {
+            this.patronTab = params.get('tab') || 'search';
+            this.patronId = +params.get('id');
+            this.context = new PatronContext();
+
+            if (this.patronId) { this.loadPatron(); }
+        });
+    }
+
+    beforeTabChange(evt: NgbNavChangeEvent) {
+        // tab will change with route navigation.
+        evt.preventDefault();
+
+        this.patronTab = evt.nextId;
+        this.routeToTab();
+    }
+
+    routeToTab() {
+        const url =
+            `/staff/circ/patron/${this.patronId}/${this.patronTab}`;
+
+        this.router.navigate([url]);
+    }
+
+    patronsSelected(rows: any[]) {
+        if (rows.length === 1) {
+            const id = rows[0].id();
+            if (id !== this.patronId) {
+                this.patronId = id;
+                this.loadPatron();
+                return;
+            }
+        }
+    }
+
+    loadPatron() {
+               this.net.request(
+            'open-ils.actor',
+            'open-ils.actor.user.fleshed.retrieve',
+            this.auth.token(), this.patronId, PATRON_FLESH_FIELDS
+        ).subscribe(patron => this.context.setPatron(patron));
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patrons.module.ts
new file mode 100644 (file)
index 0000000..8b4857a
--- /dev/null
@@ -0,0 +1,34 @@
+import {NgModule} from '@angular/core';
+import {PatronRoutingModule} from './routing.module';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {HoldsModule} from '@eg/staff/share/holds/holds.module';
+import {HoldingsModule} from '@eg/staff/share/holdings/holdings.module';
+import {BookingModule} from '@eg/staff/share/booking/booking.module';
+import {PatronModule} from '@eg/staff/share/patron/patron.module';
+import {PatronsComponent} from './patrons.component';
+import {SummaryComponent} from './summary.component';
+import {BcSearchComponent} from './bcsearch.component';
+
+@NgModule({
+  declarations: [
+    PatronsComponent,
+    SummaryComponent,
+    BcSearchComponent
+  ],
+  imports: [
+    StaffCommonModule,
+    FmRecordEditorModule,
+    HoldsModule,
+    HoldingsModule,
+    BookingModule,
+    PatronModule,
+    PatronRoutingModule
+  ],
+  providers: [
+    // PatronService
+  ]
+})
+
+export class PatronsModule {}
+
index c2b7432..3b93404 100644 (file)
@@ -1,16 +1,25 @@
 import {NgModule} from '@angular/core';
 import {RouterModule, Routes} from '@angular/router';
+import {PatronsComponent} from './patrons.component';
 
-const routes: Routes = [
-  { path: 'bcsearch',
-    loadChildren: () =>
-      import('./bcsearch/bcsearch.module').then(m => m.BcSearchModule)
-  }
-];
+const routes: Routes = [{
+    path: '',
+    pathMatch: 'full',
+    redirectTo: 'search'
+  }, {
+    path: 'search',
+    component: PatronsComponent
+  }, {
+    path: 'bcsearch',
+    component: PatronsComponent
+  }, {
+    path: ':id/:tab',
+    component: PatronsComponent,
+}];
 
 @NgModule({
   imports: [RouterModule.forChild(routes)],
   exports: [RouterModule]
 })
 
-export class CircPatronRoutingModule {}
+export class PatronRoutingModule {}
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html
new file mode 100644 (file)
index 0000000..1dc27ce
--- /dev/null
@@ -0,0 +1,2 @@
+
+summary
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts
new file mode 100644 (file)
index 0000000..e7de281
--- /dev/null
@@ -0,0 +1,24 @@
+import {Component, OnInit, Input} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {NetService} from '@eg/core/net.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
+import {PatronContext} from './patron-context';
+
+@Component({
+  templateUrl: 'summary.component.html',
+  selector: 'eg-patron-summary'
+})
+export class SummaryComponent implements OnInit {
+
+    @Input() context: PatronContext;
+
+    constructor(
+        private net: NetService,
+        public patronService: PatronService
+    ) {}
+
+    ngOnInit() {
+    }
+}
+
index fae4330..af4bb91 100644 (file)
@@ -4,7 +4,7 @@ import {RouterModule, Routes} from '@angular/router';
 const routes: Routes = [
   { path: 'patron',
     loadChildren: () =>
-      import('./patron/routing.module').then(m => m.CircPatronRoutingModule)
+      import('./patron/patrons.module').then(m => m.PatronsModule)
   }
 ];
 
index e50fbb2..99fe61c 100644 (file)
@@ -47,5 +47,11 @@ export class PatronService {
         return this.pcrud.retrieve('au', id, pcrudOps).toPromise();
     }
 
+    // Returns a name part (e.g. family_name) with preference for
+    // preferred name value where available.
+    namePart(patron: IdlObject, part: string): string {
+        if (!patron) { return ''; }
+        return patron['pref_' + part]() || patron[part]();
+    }
 }