LPXXX Angular patron search component
authorBill Erickson <berickxx@gmail.com>
Thu, 9 Jan 2020 22:20:42 +0000 (17:20 -0500)
committerBill Erickson <berickxx@gmail.com>
Thu, 9 Jan 2020 22:20:42 +0000 (17:20 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/booking/booking.module.ts
Open-ILS/src/eg2/src/app/staff/booking/pickup.component.ts
Open-ILS/src/eg2/src/app/staff/booking/return.component.ts
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.module.ts
Open-ILS/src/eg2/src/app/staff/share/patron.service.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/share/patron/patron.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/patron/search.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/patron/search.component.ts [new file with mode: 0644]

index 9b14137..dbcfb03 100644 (file)
@@ -11,7 +11,7 @@ import {PickupComponent} from './pickup.component';
 import {PullListComponent} from './pull-list.component';
 import {ReturnComponent} from './return.component';
 import {NoTimezoneSetComponent} from './no-timezone-set.component';
-import {PatronService} from '@eg/staff/share/patron.service';
+import {PatronModule} from '@eg/staff/share/patron/patron.module';
 import {BookingResourceBarcodeValidatorDirective} from './booking_resource_validator.directive';
 import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
 import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-select.module';
@@ -23,9 +23,9 @@ import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-sele
         BookingRoutingModule,
         ReactiveFormsModule,
         FmRecordEditorModule,
-        OrgFamilySelectModule
+        OrgFamilySelectModule,
+        PatronModule
     ],
-    providers: [PatronService],
     declarations: [
         CancelReservationDialogComponent,
         CreateReservationComponent,
index 028f7cf..076c413 100644 (file)
@@ -2,7 +2,7 @@ import {Component, OnInit, ViewChild, OnDestroy} from '@angular/core';
 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
 import {Subscription, of} from 'rxjs';
 import {single, filter, switchMap, debounceTime, tap} from 'rxjs/operators';
-import {PatronService} from '@eg/staff/share/patron.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {IdlObject} from '@eg/core/idl.service';
 import {ReservationsGridComponent} from './reservations-grid.component';
index d7a42f4..f37e10e 100644 (file)
@@ -4,7 +4,7 @@ import {FormGroup, FormControl, Validators} from '@angular/forms';
 import {NgbTabChangeEvent, NgbTabset} from '@ng-bootstrap/ng-bootstrap';
 import {Observable, from, of, Subscription} from 'rxjs';
 import { single, switchMap, tap, debounceTime } from 'rxjs/operators';
-import {PatronService} from '@eg/staff/share/patron.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {IdlObject} from '@eg/core/idl.service';
 import {ReservationsGridComponent} from './reservations-grid.component';
index d456ae0..7a991a2 100644 (file)
@@ -2,6 +2,12 @@
 <eg-staff-banner bannerText="Sandbox" i18n-bannerText>
 </eg-staff-banner>
 
+<div class="mt-4 mb-4">
+  <h4>Inline Patron Search Form</h4>
+  <eg-patron-search (patronsSelected)="patronSearchSelected($event)">
+  </eg-patron-search>
+</div>
+
 <eg-title 
   i18n-prefix i18n-suffix
   prefix=":) {{dynamicTitleText}}"
     <eg-grid-column name="hook"></eg-grid-column>
   </eg-grid>
 </div>
+
+
index 13cd009..c74773e 100644 (file)
@@ -463,5 +463,9 @@ export class SandboxComponent implements OnInit {
             printContext: 'default'
         });
     }
+
+    patronSearchSelected(selection: any) {
+        console.log('patron search selection', selection);
+    }
 }
 
index a33deb8..ba2d89e 100644 (file)
@@ -2,6 +2,7 @@ import {NgModule} from '@angular/core';
 import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
 import {StaffCommonModule} from '@eg/staff/common.module';
 import {TranslateModule} from '@eg/share/translate/translate.module';
+import {PatronModule} from '@eg/staff/share/patron/patron.module';
 import {SandboxRoutingModule} from './routing.module';
 import {SandboxComponent} from './sandbox.component';
 import {ReactiveFormsModule} from '@angular/forms';
@@ -14,6 +15,7 @@ import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-sele
   ],
   imports: [
     StaffCommonModule,
+    PatronModule,
     TranslateModule,
     FmRecordEditorModule,
     OrgFamilySelectModule,
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron.service.ts b/Open-ILS/src/eg2/src/app/staff/share/patron.service.ts
deleted file mode 100644 (file)
index 12fd1e1..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-import {Injectable} from '@angular/core';
-import {NetService} from '@eg/core/net.service';
-import {AuthService} from '@eg/core/auth.service';
-import {Observable} from 'rxjs';
-
-
-@Injectable()
-export class PatronService {
-    constructor(
-        private net: NetService,
-        private auth: AuthService
-    ) {}
-
-    bcSearch(barcode: string): Observable<any> {
-        return this.net.request(
-            'open-ils.actor',
-            'open-ils.actor.get_barcodes',
-            this.auth.token(), this.auth.user().ws_ou(),
-           'actor', barcode.trim());
-    }
-
-}
-
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/patron.module.ts b/Open-ILS/src/eg2/src/app/staff/share/patron/patron.module.ts
new file mode 100644 (file)
index 0000000..55aa368
--- /dev/null
@@ -0,0 +1,24 @@
+import {NgModule} from '@angular/core';
+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';
+
+@NgModule({
+    declarations: [
+        PatronSearchComponent
+    ],
+    imports: [
+        StaffCommonModule,
+        GridModule
+    ],
+    exports: [
+        PatronSearchComponent
+    ],
+    providers: [
+        PatronService
+    ]
+})
+
+export class PatronModule {}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts b/Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts
new file mode 100644 (file)
index 0000000..12fd1e1
--- /dev/null
@@ -0,0 +1,23 @@
+import {Injectable} from '@angular/core';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {Observable} from 'rxjs';
+
+
+@Injectable()
+export class PatronService {
+    constructor(
+        private net: NetService,
+        private auth: AuthService
+    ) {}
+
+    bcSearch(barcode: string): Observable<any> {
+        return this.net.request(
+            'open-ils.actor',
+            'open-ils.actor.get_barcodes',
+            this.auth.token(), this.auth.user().ws_ou(),
+           'actor', barcode.trim());
+    }
+
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/search.component.html b/Open-ILS/src/eg2/src/app/staff/share/patron/search.component.html
new file mode 100644 (file)
index 0000000..919ecb0
--- /dev/null
@@ -0,0 +1,77 @@
+
+
+<div class="patron-search-form">
+  <div class="row">
+
+    <div class="col-lg-2">
+      <input class="form-control" type="text" id='focus-this-input'
+        i18n-aria-label aria-label="Last Name"
+        i18n-placeholder placeholder="Last Name"
+        [(ngModel)]="search.au.family_name"/>
+    </div>
+
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2">
+      <button class="btn btn-success" (click)="performSearch()" i18n>Search</button>
+    </div>
+    <div class="col-lg-2"></div>
+  </div>
+  <div class="row mt-2">
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+  </div>
+  <div class="row mt-2">
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+  </div>
+  <div class="row mt-2">
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+  </div>
+  <div class="row mt-2">
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+    <div class="col-lg-2"></div>
+  </div>
+</div>
+
+<div class="patron-search-grid">
+  <eg-grid #searchGrid idlClass="au" [dataSource]="dataSource"
+    [showDeclaredFieldsOnly]="true" persistKey="circ.patron.search">
+
+    <eg-grid-column path='id'></eg-grid-column>      
+    <eg-grid-column path='card.barcode'></eg-grid-column>
+    <eg-grid-column path='profile.name'></eg-grid-column>
+    <eg-grid-column path='family_name' [sortable]="true" [multiSortable]="true">
+    </eg-grid-column>
+    <eg-grid-column path='first_given_name' [sortable]="true" [multiSortable]="true">
+    </eg-grid-column>
+    <eg-grid-column path='second_given_name' [sortable]="true" [multiSortable]="true">
+    </eg-grid-column>
+    <eg-grid-column path='dob' [sortable]="true" [multiSortable]="true">
+    </eg-grid-column>
+    <eg-grid-column path='home_ou.shortname' i18n-label label="Home Library">
+    </eg-grid-column>
+    <eg-grid-column path='create_date' i18n-label label="Created On"
+      [sortable]="true" [multiSortable]="true">
+    </eg-grid-column>
+
+  </eg-grid>
+</div>
diff --git a/Open-ILS/src/eg2/src/app/staff/share/patron/search.component.ts b/Open-ILS/src/eg2/src/app/staff/share/patron/search.component.ts
new file mode 100644 (file)
index 0000000..b884dc2
--- /dev/null
@@ -0,0 +1,115 @@
+import {Component, Input, Output, OnInit, AfterViewInit,
+    EventEmitter, ViewChild, Renderer2} from '@angular/core';
+import {Observable, of} from 'rxjs';
+import {IdlService} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {OrgService} from '@eg/core/org.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ComboboxEntry, ComboboxComponent} from '@eg/share/combobox/combobox.component';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {Pager} from '@eg/share/util/pager';
+
+@Component({
+  selector: 'eg-patron-search',
+  templateUrl: './search.component.html'
+})
+
+export class PatronSearchComponent implements OnInit, AfterViewInit {
+
+    @ViewChild('searchGrid', {static: false}) searchGrid: GridComponent;
+    @Output() patronsSelected: EventEmitter<any>;
+
+    search = {
+        au: {},
+        aua: {},
+        include_inactive: false
+    };
+
+    searchSort = [
+        "family_name ASC",
+        "first_given_name ASC",
+        "second_given_name ASC",
+        "dob DESC"
+    ];
+
+    searchFlesh = [
+        "card", "settings", "standing_penalties", "addresses",
+        "billing_address", "mailing_address", "stat_cat_entries",
+        "waiver_entries", "usr_activity", "notes", "profile"
+    ];
+
+    dataSource: GridDataSource;
+
+    constructor(
+        private renderer: Renderer2,
+        private net: NetService,
+        private org: OrgService,
+        private auth: AuthService
+    ) {
+        this.patronsSelected = new EventEmitter<any>();
+        this.dataSource = new GridDataSource();
+        this.dataSource.getRows = (pager: Pager, sort: any[]) => {
+            return this.getRows(pager, sort);
+        };
+    }
+
+    ngOnInit() {
+    }
+
+    ngAfterViewInit() {
+        this.renderer.selectRootElement('#focus-this-input').focus();
+    }
+
+    rowsSelected(rows: any) {
+        this.patronsSelected.emit(rows);
+    }
+
+    getRows(pager: Pager, sort: any[]): Observable<any> {
+
+        const search = this.compileSearch();
+        if (!search) { return of(); }
+
+        return this.net.request(
+            'open-ils.actor',
+            'open-ils.actor.patron.search.advanced.fleshed',
+            this.auth.token(),
+            this.compileSearch(),
+            pager.limit,
+            this.searchSort,
+            null, // todo?
+            this.auth.user().ws_ou(),
+            this.searchFlesh,
+            pager.offset
+        );
+    }
+
+    isValue(val: any): boolean {
+        return (val !== null && val !== undefined && val !== '');
+    }
+
+    compileSearch(): any {
+
+        let hasSearch = false;
+        const search: Object = {};
+
+        Object.keys(this.search.au).forEach(field => {
+            const val = this.search.au[field];
+            if (this.isValue(val)) {
+                hasSearch = true;
+                search[field] = {value: val, group: 0}
+            }
+        });
+
+        return hasSearch ? search : null;
+    }
+
+    performSearch() {
+        this.searchGrid.reload();
+    }
+}
+