LPXXX Angular patron search component user/berick/lpxxx-ang-cat-hold-patron-search
authorBill Erickson <berickxx@gmail.com>
Fri, 10 Jan 2020 20:48:51 +0000 (15:48 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 10 Jan 2020 20:48:51 +0000 (15:48 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
12 files changed:
Open-ILS/src/eg2/src/app/share/combobox/combobox.component.ts
Open-ILS/src/eg2/src/app/share/grid/grid.ts
Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.html
Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts
Open-ILS/src/eg2/src/app/staff/share/patron/patron.module.ts
Open-ILS/src/eg2/src/app/staff/share/patron/profile-select.component.ts
Open-ILS/src/eg2/src/app/staff/share/patron/search-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/patron/search-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/patron/search.component.html
Open-ILS/src/eg2/src/app/staff/share/patron/search.component.ts

index 3d98604..316cd9a 100644 (file)
@@ -73,16 +73,19 @@ export class ComboboxComponent implements ControlValueAccessor, OnInit {
     // Allow the selected entry ID to be passed via the template
     // This does NOT not emit onChange events.
     @Input() set selectedId(id: any) {
-        if (id) {
-            if (this.entrylist.length) {
-                this.selected = this.entrylist.filter(e => e.id === id)[0];
-            }
+        if (id === undefined) { return; }
 
-            if (!this.selected) {
-                // It's possible the selected ID lives in a set of entries
-                // that are yet to be provided.
-                this.startId = id;
-            }
+        // clear on explicit null
+        if (id === null) { this.selected = null; }
+
+        if (this.entrylist.length) {
+            this.selected = this.entrylist.filter(e => e.id === id)[0];
+        }
+
+        if (!this.selected) {
+            // It's possible the selected ID lives in a set of entries
+            // that are yet to be provided.
+            this.startId = id;
         }
     }
 
index 87dfc2b..e662fd9 100644 (file)
@@ -229,9 +229,11 @@ export class GridColumnSet {
             if (idlInfo) {
                 col.idlFieldDef = idlInfo.idlField;
                 col.idlClass = idlInfo.idlClass.name;
+                if (!col.datatype) {
+                    col.datatype = col.idlFieldDef.datatype;
+                }
                 if (!col.label) {
                     col.label = col.idlFieldDef.label || col.idlFieldDef.name;
-                    col.datatype = col.idlFieldDef.datatype;
                 }
             }
         }
index 810b950..fe82873 100644 (file)
@@ -6,6 +6,7 @@ import {CatalogRoutingModule} from './routing.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 {CatalogComponent} from './catalog.component';
 import {SearchFormComponent} from './search-form.component';
 import {ResultsComponent} from './result/results.component';
@@ -64,6 +65,7 @@ import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module';
     HoldsModule,
     HoldingsModule,
     BookingModule,
+    PatronModule,
     MarcEditModule
   ],
   providers: [
index fa04d86..dca200d 100644 (file)
@@ -1,3 +1,7 @@
+
+<eg-patron-search-dialog #patronSearch>
+</eg-patron-search-dialog>
+
 <div class="row">
   <div class="col-lg-4">
     <h3 i18n>Place Hold 
@@ -7,7 +11,7 @@
     </h3>
   </div>
   <div class="col-lg-2 text-right">
-    <button class="btn btn-outline-dark btn-sm" [disabled]="true">
+    <button class="btn btn-outline-dark btn-sm" (click)="searchPatrons()">
       <span class="material-icons mat-icon-in-button align-middle" 
         i18n-title title="Search for Patron">search</span>
       <span class="align-middle" i18n>Search for Patron</span>
index 539c434..c1640b0 100644 (file)
@@ -13,6 +13,8 @@ import {StaffCatalogService} from '../catalog.service';
 import {HoldsService, HoldRequest,
     HoldRequestTarget} from '@eg/staff/share/holds/holds.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {PatronSearchDialogComponent
+  } from '@eg/staff/share/patron/search-dialog.component';
 
 class HoldContext {
     holdMeta: HoldRequestTarget;
@@ -63,6 +65,9 @@ export class HoldComponent implements OnInit {
     smsEnabled: boolean;
     placeHoldsClicked: boolean;
 
+    @ViewChild('patronSearch', {static: false})
+      patronSearch: PatronSearchDialogComponent;
+
     constructor(
         private router: Router,
         private route: ActivatedRoute,
@@ -398,6 +403,22 @@ export class HoldComponent implements OnInit {
             )
         );
     }
+
+    searchPatrons() {
+        this.patronSearch.open({size: 'xl'}).toPromise().then(
+            patrons => {
+                if (!patrons || patrons.length === 0) { return; }
+
+                const user = patrons[0];
+
+                this.user = user;
+                this.userBarcode =
+                    this.currentUserBarcode = user.card().barcode();
+                user.home_ou(this.org.get(user.home_ou()).id()); // de-flesh
+                this.applyUserSettings();
+            }
+        );
+    }
 }
 
 
index 869eff2..50ed791 100644 (file)
@@ -71,9 +71,11 @@ export class ResultsComponent implements OnInit, OnDestroy {
     }
 
     ngOnDestroy() {
-        this.routeSub.unsubscribe();
-        this.searchSub.unsubscribe();
-        this.basketSub.unsubscribe();
+        if (this.routeSub) {
+            this.routeSub.unsubscribe();
+            this.searchSub.unsubscribe();
+            this.basketSub.unsubscribe();
+        }
     }
 
     // Apply the select-all checkbox when all visible records
index 5406b12..ac6e9b3 100644 (file)
@@ -3,11 +3,13 @@ import {StaffCommonModule} from '@eg/staff/common.module';
 import {GridModule} from '@eg/share/grid/grid.module';
 import {PatronService} from './patron.service';
 import {PatronSearchComponent} from './search.component';
+import {PatronSearchDialogComponent} from './search-dialog.component';
 import {ProfileSelectComponent} from './profile-select.component';
 
 @NgModule({
     declarations: [
         PatronSearchComponent,
+        PatronSearchDialogComponent,
         ProfileSelectComponent
     ],
     imports: [
@@ -16,6 +18,7 @@ import {ProfileSelectComponent} from './profile-select.component';
     ],
     exports: [
         PatronSearchComponent,
+        PatronSearchDialogComponent,
         ProfileSelectComponent
     ],
     providers: [
index 9e81026..56ff560 100644 (file)
@@ -134,7 +134,15 @@ export class ProfileSelectComponent implements ControlValueAccessor, OnInit {
         this.cboxEntries.push(
             {id: grp.id(), label: this.grpLabel(groups, grp)});
 
-        groups.filter(g => g.parent() === grp.id())
+        groups
+            .filter(g => g.parent() === grp.id())
+            .sort((a, b) => {
+                if (a._display) {
+                    return a._display.position() < b._display.position() ? -1 : 1;
+                } else {
+                    return a.name() < b.name() ? -1 : 1;
+                }
+            })
             .forEach(child => this.sortGroups(groups, child));
     }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/search-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/patron/search-dialog.component.html
new file mode 100644 (file)
index 0000000..1006a63
--- /dev/null
@@ -0,0 +1,23 @@
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 class="modal-title"><span i18n>Patron Search</span></h4>
+    <button type="button" class="close" 
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <eg-patron-search #searchForm (patronsSelected)="patronsSelected($event)">
+    </eg-patron-search>
+  </div>
+  <div class="modal-footer">
+    <ng-container>
+      <button type="button" class="btn btn-warning" 
+        (click)="close()" i18n>Cancel</button>
+      <button type="button" class="btn btn-success" 
+        [disabled]="searchForm ? searchForm.getSelected().length === 0 : true"
+        (click)="close(searchForm.getSelected())" i18n>Select</button>
+    </ng-container>
+  </div>
+</ng-template>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/search-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/patron/search-dialog.component.ts
new file mode 100644 (file)
index 0000000..98e1c22
--- /dev/null
@@ -0,0 +1,36 @@
+import {Component, OnInit, Input, Output, ViewChild} from '@angular/core';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {PatronSearchComponent} from './search.component';
+
+/**
+ * Dialog container for patron search component
+ *
+ * <eg-patron-search-dialog (patronsSelected)="process($event)">
+ * </eg-patron-search-dialog>
+ */
+
+@Component({
+  selector: 'eg-patron-search-dialog',
+  templateUrl: 'search-dialog.component.html'
+})
+
+export class PatronSearchDialogComponent
+    extends DialogComponent implements OnInit {
+
+    @ViewChild('searchForm', {static: false})
+        searchForm: PatronSearchComponent;
+
+    constructor(private modal: NgbModal) { super(modal); }
+
+    ngOnInit() {}
+
+    // Fired when a row in the search grid is dbl-clicked / activated
+    patronsSelected(patrons: IdlObject[]) {
+        this.close(patrons);
+    }
+}
+
+
+
index 040c420..f2e3632 100644 (file)
         <eg-profile-select [useDisplayEntries]="true" 
           [(ngModel)]="search.profile">
         </eg-profile-select>
-        <!-- profile -->
       </div>
       <div class="col-lg-2">
+        <eg-org-select (onChange)="searchOrg = $event"
+          [applyOrgId]="searchOrg ? searchOrg.id() : null"
+          i18n-placeholder placeholder="Home Library">
+        </eg-org-select>
         <!-- home org -->
       </div>
       <div class="col-lg-2">
index 481d8b3..43f4fe1 100644 (file)
@@ -40,10 +40,12 @@ const INCLUDE_INACTIVE = 'eg.circ.patron.search.include_inactive';
 export class PatronSearchComponent implements OnInit, AfterViewInit {
 
     @ViewChild('searchGrid', {static: false}) searchGrid: GridComponent;
+
+    // Fired on dbl-click of a search result row.
     @Output() patronsSelected: EventEmitter<any>;
 
     search: any = {};
-    searchOrg: number;
+    searchOrg: IdlObject;
     expandForm: boolean;
     dataSource: GridDataSource;
     profileGroups: IdlObject[] = [];
@@ -63,7 +65,7 @@ export class PatronSearchComponent implements OnInit, AfterViewInit {
     }
 
     ngOnInit() {
-        this.searchOrg = this.org.root().id();
+        this.searchOrg = this.org.root();
         this.store.getItemBatch([EXPAND_FORM, INCLUDE_INACTIVE])
             .then(settings => {
                 this.expandForm = settings[EXPAND_FORM];
@@ -96,17 +98,18 @@ export class PatronSearchComponent implements OnInit, AfterViewInit {
         this.patronsSelected.emit([].concat(rows));
     }
 
+    getSelected(): IdlObject[] {
+        return this.searchGrid ?
+            this.searchGrid.context.getSelectedRows() : [];
+    }
+
     go() {
         this.searchGrid.reload();
     }
 
     clear() {
-        this.search = {};
-        this.searchOrg = this.org.root().id();
-    }
-
-    homeOrgChange(orgId: number) {
-        this.searchOrg = orgId;
+        this.search = {profile: null};
+        this.searchOrg = this.org.root();
     }
 
     getRows(pager: Pager, sort: any[]): Observable<any> {
@@ -142,7 +145,7 @@ export class PatronSearchComponent implements OnInit, AfterViewInit {
             pager.limit,
             sorter,
             null, // ?
-            this.searchOrg,
+            this.searchOrg.id(),
             DEFAULT_FLESH,
             pager.offset
         );