LPXXX Patron UI WIP
authorBill Erickson <berickxx@gmail.com>
Thu, 13 Aug 2020 20:56:33 +0000 (16:56 -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>
14 files changed:
Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.css
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/summary-component.css [deleted file]
Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.css [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts
Open-ILS/src/eg2/src/app/staff/share/patron/search.component.ts
Open-ILS/src/eg2/src/styles.css

diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.html
new file mode 100644 (file)
index 0000000..6a1647b
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="row pb-2 pt-2">
+  <div class="ml-auto">
+    <button class="btn btn-outline-dark" i18n>Print</button>
+    <button class="btn btn-outline-dark ml-3" i18n>Save</button>
+    <button class="btn btn-outline-dark ml-3" i18n>Save &amp; Clone</button>
+  </div>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts
new file mode 100644 (file)
index 0000000..c538ab7
--- /dev/null
@@ -0,0 +1,25 @@
+import {Component, OnInit, Input} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {OrgService} from '@eg/core/org.service';
+import {NetService} from '@eg/core/net.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
+import {PatronManagerService} from './patron.service';
+
+@Component({
+  templateUrl: 'edit-toolbar.component.html',
+  selector: 'eg-patron-edit-toolbar'
+})
+export class EditToolbarComponent implements OnInit {
+
+    constructor(
+        private org: OrgService,
+        private net: NetService,
+        public patronService: PatronService,
+        public context: PatronManagerService
+    ) {}
+
+    ngOnInit() {
+    }
+}
+
index 383f2fc..035f79c 100644 (file)
@@ -1,11 +1,3 @@
-<div class="row d-flex">
-  <div class="flex-1"> </div>
-  <button class="btn btn-outline-dark">BUTTON 1</button>
-  <button class="btn btn-outline-dark ml-2">BUTTON 2</button>
-  <button class="btn btn-outline-dark ml-2">BUTTON 3</button>
-  <button class="btn btn-outline-dark ml-2">BUTTON 4</button>
-</div>
-
 <div class="{{contentPaneClass}}">
   <div class="row mb-5">
     <div class="col-lg-12">
index 061ae91..6101229 100644 (file)
@@ -12,9 +12,6 @@ import {PatronManagerService} from './patron.service';
 })
 export class EditComponent implements OnInit {
 
-    // CSS class for the main edit pane
-    @Input() contentPaneClass = '';
-
     constructor(
         private org: OrgService,
         private net: NetService,
index dbe3658..d4abd5f 100644 (file)
@@ -1,11 +1,5 @@
 
-::ng-deep .patron-content-pane {
-  margin-top: 10px;
-  padding-top: 10px;
-  margin-right: 5px;
-  padding-right: 5px;
-  position: fixed;
-  height: 100%;
-  overflow-y: auto;
+::ng-deep legend {
+  font-size: 1rem; /* defaults to 1.5 */
 }
 
index cd8ec32..5994268 100644 (file)
-<eg-staff-banner bannerText="Manage Patrons" i18n-bannerText></eg-staff-banner>
 
-<div class="row">
-  <div class="col-lg-3">
-    <h3 *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')}}
-    </h3>
-  </div>
-  <div class="col-lg-9">
-    <ul ngbNav #patronNav="ngbNav" class="nav-tabs"
-      [activeId]="patronTab" (navChange)="beforeTabChange($event)">
-
-      <li ngbNavItem="checkout">
-        <a ngbNavLink i18n>Checkout</a>
-        <ng-template ngbNavContent>
-          <div class="patron-content-pane">
-            <eg-patron-checkout></eg-patron-checkout> 
-          </div>
-        </ng-template>
-      </li>
-
-      <li ngbNavItem="edit">
-        <a ngbNavLink i18n>Edit</a>
-        <ng-template ngbNavContent>
-          <eg-patron-edit contentPaneClass="patron-content-pane">
-          </eg-patron-edit> 
-        </ng-template>
-      </li>
-
-      <li ngbNavItem="search">
-        <a ngbNavLink i18n>Patron Search</a>
-        <ng-template ngbNavContent>
-          <div class="patron-content-pane">
-            <eg-patron-search 
-              (selectionChange)="patronSelectionChange($event)"
-              (patronsSelected)="patronsSelected($event)">
-            </eg-patron-search> 
-          </div>
-        </ng-template>
-      </li>
+<ng-container *ngIf="!context.patron">
+  <eg-staff-banner bannerText="Manage Patrons" i18n-bannerText>
+  </eg-staff-banner>
+</ng-container>
 
-    </ul>
-  </div>
-</div>
+<ng-container *ngIf="context.patron">
+  <eg-staff-banner i18n-bannerText bannerText="
+    {{patronService.namePart(context.patron, 'family_name')}}, 
+    {{patronService.namePart(context.patron, 'first_given_name')}} 
+    {{patronService.namePart(context.patron, 'second_given_name')}}">
+  </eg-staff-banner>
+</ng-container>
+
+<div class="row">
 
-<div class="row" *ngIf="viewInitDone">
-  <ng-container *ngIf="showSummary">
+  <ng-container *ngIf="showSummaryPane()">
     <div class="col-lg-3">
-      <ng-container *ngIf="context.patron">
-        <eg-patron-summary></eg-patron-summary>
-      </ng-container>
-    </div>
-    <div class="col-lg-9">
-      <div [ngbNavOutlet]="patronNav"></div>
+      <div class="sticky-top-with-nav bg-white">
+        <ng-container *ngIf="context.patron">
+          <eg-patron-summary></eg-patron-summary>
+        </ng-container>
+      </div>
     </div>
   </ng-container>
-  <ng-container *ngIf="!showSummary">
-    <div class="col-lg-12">
+
+  <div [ngClass]="{'col-lg-9': showSummaryPane(), 'col-lg-12': !showSummaryPane()}">
+
+    <div class="sticky-top-with-nav bg-white">
+      <ul ngbNav #patronNav="ngbNav" class="nav-tabs"
+        [activeId]="patronTab" (navChange)="beforeTabChange($event)">
+
+        <li ngbNavItem="checkout">
+          <a ngbNavLink i18n>Checkout</a>
+          <ng-template ngbNavContent>
+            <div class="">
+              <eg-patron-checkout></eg-patron-checkout> 
+            </div>
+          </ng-template>
+        </li>
+
+        <li ngbNavItem="items_out">
+          <a ngbNavLink i18n>Items Out</a>
+          <ng-template ngbNavContent>
+            <div class="">
+            </div>
+          </ng-template>
+        </li>
+
+        <li ngbNavItem="holds">
+          <a ngbNavLink i18n>Holds</a>
+          <ng-template ngbNavContent>
+            <div class="">
+            </div>
+          </ng-template>
+        </li>
+
+        <li ngbNavItem="bills">
+          <a ngbNavLink i18n>Bills</a>
+          <ng-template ngbNavContent>
+            <div class="">
+            </div>
+          </ng-template>
+        </li>
+
+        <li ngbNavItem="messages">
+          <a ngbNavLink i18n>Messages</a>
+          <ng-template ngbNavContent>
+            <div class="">
+            </div>
+          </ng-template>
+        </li>
+
+        <li ngbNavItem="edit">
+          <a ngbNavLink i18n>Edit</a>
+          <ng-template ngbNavContent>
+            <eg-patron-edit 
+              contentPaneClass="patron-content-pane div-scroll-vert">
+            </eg-patron-edit> 
+          </ng-template>
+        </li>
+
+        <li ngbDropdown ngbNavItem="other">
+          <a href (click)="false" class="nav-link" ngbDropdownToggle>Other</a>
+          <div ngbDropdownMenu>
+            <a routerLink="/staff/circ/patron/{{patronId}}/alerts" 
+              ngbDropdownItem i18n>Alerts</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/notes" 
+              ngbDropdownItem i18n>Notes</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/triggered_events" 
+              ngbDropdownItem i18n>Triggered Events / Notifications</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/message_center" 
+              ngbDropdownItem i18n>Message Center</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/stat_cats"
+              ngbDropdownItem i18n>Statistical Categories</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/surveys"
+              ngbDropdownItem i18n>Surveys</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/group"
+              ngbDropdownItem i18n>Group Member Details</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/edit_perms"
+              ngbDropdownItem i18n>User Permission Editor</a>
+            <a routerLink="/staff/circ/patron/{{patronId}}/credentials"
+              ngbDropdownItem i18n>Test Password</a>
+            <a href="/eg/staff/acq/requests/user/{{patronId}}" 
+              target="_top"
+              ngbDropdownItem i18n>Acquisition Patron Requests</a>
+            <a routerLink="/staff/booking/manage_reservations/by_patron/{{patronId}}"
+              target="_top"
+              ngbDropdownItem i18n>Booking: Manage Reservations</a>
+            <a routerLink="/staff/booking/create_reservation/for_patron/{{patronId}}"
+              target="_top"
+              ngbDropdownItem i18n>Booking: Create Reservation</a>
+            <a routerLink="/staff/booking/pickup/by_patron/{{patronId}}"
+              target="_top"
+              ngbDropdownItem i18n>Booking: Pick Up Reservations</a>
+            <a routerLink="/staff/booking/return/by_patron/{{patronId}}"
+              target="_top"
+              ngbDropdownItem i18n></a>
+            <a href='javascript:;' (click)="purgeAccount()"
+              [disabled]="disablePurge()"
+              ngbDropdownItem i18n>Completely Purge Account</a>
+          </div>
+          <ng-template ngbNavContent>
+            <!-- display selected altTab component -->
+            OTHER STUFF: {{altTab}}
+          </ng-template>
+        </li>
+
+        <li ngbNavItem="search" class="ml-auto">
+          <a ngbNavLink i18n>Patron Search</a>
+          <ng-template ngbNavContent>
+            <div class="">
+              <eg-patron-search
+                (selectionChange)="patronSelectionChange($event)"
+                (patronsActivated)="patronsActivated($event)">
+              </eg-patron-search> 
+            </div>
+          </ng-template>
+        </li>
+      </ul>
+
+      <ng-container *ngIf="patronTab === 'edit'">
+        <!-- put the editor toolbar up here in the sticky section -->
+        <eg-patron-edit-toolbar></eg-patron-edit-toolbar>
+      </ng-container>
+
+    </div><!-- end of sticky top -->
+
+    <div class="pt-3">
       <div [ngbNavOutlet]="patronNav"></div>
     </div>
-  </ng-container>
+  </div>
 </div>
 
index fa5774b..11cee90 100644 (file)
@@ -7,16 +7,19 @@ import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronManagerService} from './patron.service';
 import {PatronSearchComponent} from '@eg/staff/share/patron/search.component';
 
+const MAIN_TABS =
+    ['checkout', 'items_out', 'holds', 'bills', 'messages', 'edit', 'search'];
+
 @Component({
   templateUrl: 'patron.component.html',
   styleUrls: ['patron.component.css']
 })
 export class PatronComponent implements OnInit, AfterViewInit {
 
-    patronTab = 'search';
     patronId: number;
+    patronTab = 'search';
+    altTab: string;
     showSummary = true;
-    viewInitDone = false;
 
     constructor(
         private router: Router,
@@ -36,6 +39,13 @@ export class PatronComponent implements OnInit, AfterViewInit {
             this.patronTab = params.get('tab') || 'search';
             this.patronId = +params.get('id');
 
+            if (MAIN_TABS.includes(this.patronTab)) {
+                this.altTab = null;
+            } else {
+                this.altTab = this.patronTab;
+                this.patronTab = 'other';
+            }
+
             const prevId =
                 this.context.patron ? this.context.patron.id() : null;
 
@@ -50,11 +60,7 @@ export class PatronComponent implements OnInit, AfterViewInit {
         });
     }
 
-    // Trick that allows us to set a value for ngbNavOutlet, whose
-    // component is not avilable until after view init, without
-    // firing "expression changed after check" errors.
     ngAfterViewInit() {
-        setTimeout(() => this.viewInitDone = true);
     }
 
     beforeTabChange(evt: NgbNavChangeEvent) {
@@ -73,6 +79,9 @@ export class PatronComponent implements OnInit, AfterViewInit {
             case 'bcsearch':
                 url += this.patronTab;
                 break;
+            case 'other':
+                url += `${this.patronId}/${this.altTab}`;
+                break;
             default:
                 url += `${this.patronId}/${this.patronTab}`;
         }
@@ -80,6 +89,10 @@ export class PatronComponent implements OnInit, AfterViewInit {
         this.router.navigate([url]);
     }
 
+    showSummaryPane(): boolean {
+        return this.showSummary || this.patronTab === 'search';
+    }
+
     // Patron row single-clicked in the grid.  Load the patron without
     // leaving the search tab.
     patronSelectionChange(ids: number[]) {
@@ -94,7 +107,7 @@ export class PatronComponent implements OnInit, AfterViewInit {
     }
 
     // Route to checkout tab for selected patron.
-    patronsSelected(rows: any[]) {
+    patronsActivated(rows: any[]) {
         if (rows.length !== 1) { return; }
 
         const id = rows[0].id();
@@ -103,5 +116,14 @@ export class PatronComponent implements OnInit, AfterViewInit {
         this.routeToTab();
     }
 
+    disablePurge(): boolean {
+        return
+            this.context.patron.super_user() === 't' ||
+            this.patronId === this.auth.user().id();
+    }
+
+    purgeAccount() {
+        // show scary warning, etc.
+    }
 }
 
index d16f6f1..76cd7dc 100644 (file)
@@ -11,6 +11,7 @@ import {PatronComponent} from './patron.component';
 import {SummaryComponent} from './summary.component';
 import {CheckoutComponent} from './checkout.component';
 import {EditComponent} from './edit.component';
+import {EditToolbarComponent} from './edit-toolbar.component';
 import {BcSearchComponent} from './bcsearch.component';
 
 @NgModule({
@@ -19,6 +20,7 @@ import {BcSearchComponent} from './bcsearch.component';
     SummaryComponent,
     CheckoutComponent,
     EditComponent,
+    EditToolbarComponent,
     BcSearchComponent
   ],
   imports: [
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary-component.css b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary-component.css
deleted file mode 100644 (file)
index 71ba8a4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-
-.patron-summary-container .row:nth-child(odd) {
-  background-color: rgb(248, 248, 248);
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.css b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.css
new file mode 100644 (file)
index 0000000..71ba8a4
--- /dev/null
@@ -0,0 +1,6 @@
+
+.patron-summary-container .row:nth-child(odd) {
+  background-color: rgb(248, 248, 248);
+}
+
+
index 57bd105..ecba5a6 100644 (file)
@@ -1,5 +1,12 @@
 
 <div class="patron-summary-container">
+
+  <h3 *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')}}
+  </h3>
+
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Profile</div>
     <div class="col-lg-7">{{context.patron.profile().name()}}</div>
     <div class="col-lg-5" i18n>Email</div>
     <div class="col-lg-7">XXXXXXXXXX</div>
   </div>
-  
+
+  <hr class="m-1"/>
+
+  <div class="row mb-1" *ngFor="let addr of context.patron.addresses()">
+    <div class="col-lg-12">
+      <fieldset>
+        <legend i18n>{{addr.address_type()}}</legend><!-- todo -->
+        <div i18n>{{addr.street1()}} {{addr.street2()}}</div>
+        <div i18n>{{addr.city()}}, {{addr.state()}} {{addr.post_code()}}</div>
+      </fieldset>
+      <!-- todo textarea for copy -->
+    </div>
+  </div>
 </div>
index 4381075..90da1f9 100644 (file)
@@ -8,7 +8,7 @@ import {PatronManagerService} from './patron.service';
 
 @Component({
   templateUrl: 'summary.component.html',
-  styleUrls: ['summary-component.css'],
+  styleUrls: ['summary.component.css'],
   selector: 'eg-patron-summary'
 })
 export class SummaryComponent implements OnInit {
index d09daf2..3fd88ef 100644 (file)
@@ -43,7 +43,7 @@ export class PatronSearchComponent implements OnInit, AfterViewInit {
 
     // Fires on dbl-click or Enter while one or more search result
     // rows are selected.
-    @Output() patronsSelected: EventEmitter<any>;
+    @Output() patronsActivated: EventEmitter<any>;
 
     // Fires when the selection of search result rows changes.
     // Emits an array of patron IDs
@@ -62,7 +62,7 @@ export class PatronSearchComponent implements OnInit, AfterViewInit {
         private auth: AuthService,
         private store: ServerStoreService
     ) {
-        this.patronsSelected = new EventEmitter<any>();
+        this.patronsActivated = new EventEmitter<any>();
         this.selectionChange = new EventEmitter<number[]>();
         this.dataSource = new GridDataSource();
         this.dataSource.getRows = (pager: Pager, sort: any[]) => {
@@ -105,7 +105,7 @@ export class PatronSearchComponent implements OnInit, AfterViewInit {
     }
 
     rowsActivated(rows: IdlObject | IdlObject[]) {
-        this.patronsSelected.emit([].concat(rows));
+        this.patronsActivated.emit([].concat(rows));
     }
 
     getSelected(): IdlObject[] {
index d1144fd..4459b94 100644 (file)
@@ -141,6 +141,14 @@ h5 {font-size: .95rem}
   font-size: 99%;
 }
 
+/* Items stick to the top of the page once scrolled past,
+ * leaving room above for the nav bar */
+.sticky-top-with-nav {
+  top: 48px;
+  position: sticky;
+  z-index: 1;
+}
+
 /* --------------------------------------------------------------------------
 /* Form Validation CSS - https://angular.io/guide/form-validation
  * TODO: these colors don't fit the EG color scheme