LP1904036 print/copy patron address; summary styling
authorBill Erickson <berickxx@gmail.com>
Tue, 2 Mar 2021 15:43:59 +0000 (10:43 -0500)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:25 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
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/circ/grid.component.html
Open-ILS/src/eg2/src/app/staff/share/circ/grid.component.ts
Open-ILS/src/sql/Pg/upgrade/XXXX.data.angular-patron.sql

index 4708178..32f773b 100644 (file)
@@ -1,13 +1,14 @@
 
 <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 *ngIf="patron()" class="font-weight-bold" i18n>
+    {{patronService.namePart(patron(), 'family_name')}}, 
+    {{patronService.namePart(patron(), 'first_given_name')}} 
+    {{patronService.namePart(patron(), 'second_given_name')}}
   </h3>
 
-  <div class="row mb-1 alert alert-danger p-0" *ngIf="context.alerts.accountExpiresSoon">
+  <div class="row mb-1 alert alert-danger p-0" 
+    *ngIf="context.alerts.accountExpiresSoon">
     <div class="col-lg-12" i18n>
       Patron account will expire soon.  Please renew.
     </div>
 
   <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-7">{{patron().profile().name()}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Home Library</div>
-    <div class="col-lg-7">{{context.orgSn(context.patron.home_ou())}}</div>
+    <div class="col-lg-7">{{context.orgSn(patron().home_ou())}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Net Access</div>
-    <div class="col-lg-7">{{context.patron.net_access_level().name()}}</div>
+    <div class="col-lg-7">{{patron().net_access_level().name()}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Date of Birth</div>
-    <div class="col-lg-7">{{context.patron.dob() | date:'shortDate'}}</div>
+    <div class="col-lg-7">{{patron().dob() | date:'shortDate'}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Parent/Guardian</div>
-    <div class="col-lg-7">{{context.patron.guardian()}}</div>
+    <div class="col-lg-7">{{patron().guardian()}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Last Activity</div>
     <div class="col-lg-7">
-      <ng-container *ngIf="context.patron.usr_activity()[0]">
-        {{context.patron.usr_activity()[0].event_time() | date:'shortDate'}}
+      <ng-container *ngIf="patron().usr_activity()[0]">
+        {{patron().usr_activity()[0].event_time() | date:'shortDate'}}
       </ng-container>
     </div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Last Updated</div>
-    <div class="col-lg-7">{{context.patron.last_update_time() | date:'shortDate'}}</div>
+    <div class="col-lg-7">{{patron().last_update_time() | date:'shortDate'}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Create Date</div>
-    <div class="col-lg-7">{{context.patron.create_date() | date:'shortDate'}}</div>
+    <div class="col-lg-7">{{patron().create_date() | date:'shortDate'}}</div>
   </div>
   <div class="row" [ngClass]="{'alert alert-danger p-0': context.alerts.accountExpired}">
     <div class="col-lg-5" i18n>Expire Date</div>
-    <div class="col-lg-7">{{context.patron.expire_date() | date:'shortDate'}}</div>
+    <div class="col-lg-7">{{patron().expire_date() | date:'shortDate'}}</div>
   </div>
 
   <hr class="m-1"/>
       <div class="col-lg-5" i18n>Items Out</div>
       <div class="col-lg-7">{{context.patronStats.checkouts.total_out}}</div>
     </div>
-    <div class="row mb-1">
+    <div class="row mb-1"
+      [ngClass]="{'alert alert-danger p-0': context.patronStats.checkouts.overdue > 0}">
       <div class="col-lg-5" i18n>Overdue</div>
       <div class="col-lg-7">{{context.patronStats.checkouts.overdue}}</div>
     </div>
-    <div class="row mb-1">
+    <div class="row mb-1"
+      [ngClass]="{'alert alert-danger p-0': context.patronStats.checkouts.long_overdue > 0}">
       <div class="col-lg-5" i18n>Long Overdue</div>
       <div class="col-lg-7">{{context.patronStats.checkouts.long_overdue}}</div>
     </div>
-    <div class="row mb-1">
+    <div class="row mb-1"
+      [ngClass]="{'alert alert-danger p-0': context.patronStats.checkouts.claims_returned > 0}">
       <div class="col-lg-5" i18n>Claimed Returned</div>
       <div class="col-lg-7">{{context.patronStats.checkouts.claims_returned}}</div>
     </div>
-    <div class="row mb-1">
+    <div class="row mb-1"
+      [ngClass]="{'alert alert-danger p-0': context.patronStats.checkouts.lost > 0}">
       <div class="col-lg-5" i18n>Lost</div>
       <div class="col-lg-7">{{context.patronStats.checkouts.lost}}</div>
     </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Card</div>
     <div class="col-lg-7">
-      {{context.patron.card() ? context.patron.card().barcode() : ''}}
+      {{patron().card() ? patron().card().barcode() : ''}}
     </div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Username</div>
-    <div class="col-lg-7">{{context.patron.usrname()}}</div>
+    <div class="col-lg-7">{{patron().usrname()}}</div>
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Day Phone</div>
-    <div class="col-lg-7">{{context.patron.day_phone()}}</div> 
+    <div class="col-lg-7">{{patron().day_phone()}}</div> 
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Evening Phone</div>
-    <div class="col-lg-7">{{context.patron.evening_phone()}}</div> 
+    <div class="col-lg-7">{{patron().evening_phone()}}</div> 
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Other Phone</div>
-    <div class="col-lg-7">{{context.patron.other_phone()}}</div> 
+    <div class="col-lg-7">{{patron().other_phone()}}</div> 
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>ID1 </div>
-    <div class="col-lg-7">{{context.patron.ident_value()}}</div> 
+    <div class="col-lg-7">{{patron().ident_value()}}</div> 
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>ID2</div>
-    <div class="col-lg-7">{{context.patron.ident_value2()}}</div> 
+    <div class="col-lg-7">{{patron().ident_value2()}}</div> 
   </div>
   <div class="row mb-1">
     <div class="col-lg-5" i18n>Email</div>
     <div class="col-lg-7">
       <!-- TODO: mailto link -->
-      {{context.patron.email()}}
+      {{patron().email()}}
     </div> 
   </div>
 
   <hr class="m-1"/>
 
-  <div class="row mb-1" *ngFor="let addr of context.patron.addresses()">
+  <div class="row mb-1" *ngFor="let addr of patron().addresses()">
     <div class="col-lg-12">
       <fieldset>
-        <legend i18n>{{addr.address_type()}}</legend><!-- todo -->
+        <legend class="d-flex" [ngClass]="{'alert alert-danger p-0': addr.valid() == 'f'}">
+          <div class="flex-1">{{addr.address_type()}}</div>
+          <div>
+            <a class="mr-2" href="javascript:;" 
+              (click)="copyAddress(addr)" i18n>copy</a>
+            <a class="mr-2" href="javascript:;" 
+              (click)="printAddress(addr)" i18n>print</a>
+          </div>
+        </legend>
         <div i18n>{{addr.street1()}} {{addr.street2()}}</div>
         <div i18n>{{addr.city()}}, {{addr.state()}} {{addr.post_code()}}</div>
       </fieldset>
-      <!-- todo textarea for copy -->
+
+      <!-- hidden textare used only for copying the text -->
+      <textarea id="patron-address-copy-{{addr.id()}}" rows="2"              
+        style="visibility:hidden">
+{{patron().first_given_name()}} {{patron().second_given_name()}} {{patron().family_name()}}
+{{addr.street1()}} {{addr.street2()}}
+{{addr.city()}}, {{addr.state()}} {{addr.post_code()}}</textarea>
     </div>
   </div>
 </div>
index aac3c49..dd1a830 100644 (file)
@@ -2,9 +2,11 @@ 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 {IdlObject} from '@eg/core/idl.service';
 import {NetService} from '@eg/core/net.service';
 import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronContextService} from './patron.service';
+import {PrintService} from '@eg/share/print/print.service';
 
 @Component({
   templateUrl: 'summary.component.html',
@@ -16,11 +18,49 @@ export class SummaryComponent implements OnInit {
     constructor(
         private org: OrgService,
         private net: NetService,
+        private printer: PrintService,
         public patronService: PatronService,
         public context: PatronContextService
     ) {}
 
     ngOnInit() {
     }
+
+    patron(): IdlObject {
+        return this.context.patron;
+    }
+
+    printAddress(addr: IdlObject) {
+        this.printer.print({
+            templateName: 'patron_address',
+            contextData: {
+                patron: this.context.patron,
+                address: addr
+            },
+            printContext: 'default'
+        });
+    }
+
+    copyAddress(addr: IdlObject) {
+        // Note navigator.clipboard requires special permissions.
+        // This is hinky, but gets the job done without the perms.
+
+        const node = document.getElementById(
+            `patron-address-copy-${addr.id()}`) as HTMLTextAreaElement;
+
+        // Un-hide the textarea just long enough to copy its data.
+        // Using node.style instead of *ngIf in hopes it
+        // will be quicker, so the user never sees the textarea.
+        node.style.visibility = 'visible';
+        node.focus();
+        node.select();
+
+        if (!document.execCommand('copy')) {
+            console.error('Copy command failed');
+        }
+
+        node.style.visibility = 'hidden';
+    }
+
 }
 
index 28353ae..5a79e40 100644 (file)
@@ -39,8 +39,7 @@
   [useLocalSort]="true" [cellTextGenerator]="cellTextGenerator">
 
   <eg-grid-toolbar-action
-    i18n-group group="View" i18n-label label="Print Item Receipt(s)"
-    (onClick)="printReceipts($event)">
+    i18n-label label="Print Item Receipt(s)" (onClick)="printReceipts($event)">
   </eg-grid-toolbar-action>
 
   <eg-grid-toolbar-action
     (onClick)="renewWithDate($event)">
   </eg-grid-toolbar-action>
 
+  <eg-grid-toolbar-action group="Show" i18n-group 
+    i18n-label label="Show Last Few Circulation" (onClick)="showRecentCircs($event)">
+  </eg-grid-toolbar-action>
+
+  <eg-grid-toolbar-action group="Show" i18n-group 
+    i18n-label label="Show Triggered Events" (onClick)="showTriggeredEvents($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- Columns =========================================== -->
+
   <eg-grid-column [index]="true" path="index" [hidden]="true"
     label="Row Index" i18n-label></eg-grid-column>
 
index 59b57d9..8b79cba 100644 (file)
@@ -604,5 +604,19 @@ export class CircGridComponent implements OnInit {
             }
         );
     }
+
+    showRecentCircs(rows: CircGridEntry[]) {
+        const copyId = this.getCopyIds(rows)[0];
+        if (copyId) {
+            window.open('/eg/staff/cat/item/' + copyId + '/circ_list');
+        }
+    }
+
+    showTriggeredEvents(rows: CircGridEntry[]) {
+        const copyId = this.getCopyIds(rows)[0];
+        if (copyId) {
+            window.open('/eg/staff/cat/item/' + copyId + '/triggered_events');
+        }
+    }
 }
 
index 0a7457f..4a713c3 100644 (file)
@@ -57,6 +57,8 @@ UPDATE config.print_template SET template = $TEMPLATE$
 </div>
 $TEMPLATE$ WHERE name = 'items_out';
 
+UPDATE config.print_template SET active = TRUE WHERE name = 'patron_address';
+
 COMMIT;