]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/share/patron/search.component.ts
LP 2061136 follow-up: ng lint --fix
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / patron / search.component.ts
1 import {Component, Input, Output, OnInit, AfterViewInit,
2     EventEmitter, ViewChild, Renderer2} from '@angular/core';
3 import {Observable, of} from 'rxjs';
4 import {map} from 'rxjs/operators';
5 import {IdlObject} from '@eg/core/idl.service';
6 import {NetService} from '@eg/core/net.service';
7 import {AuthService} from '@eg/core/auth.service';
8 import {OrgService} from '@eg/core/org.service';
9 import {ServerStoreService} from '@eg/core/server-store.service';
10 import {GridComponent} from '@eg/share/grid/grid.component';
11 import {GridDataSource} from '@eg/share/grid/grid';
12 import {Pager} from '@eg/share/util/pager';
13
14 const DEFAULT_SORT = [
15    'family_name ASC',
16     'first_given_name ASC',
17     'second_given_name ASC',
18     'dob DESC'
19 ];
20
21 const DEFAULT_FLESH = [
22     'card', 'settings', 'standing_penalties', 'addresses', 'billing_address',
23     'mailing_address', 'stat_cat_entries', 'waiver_entries', 'usr_activity',
24     'notes', 'profile'
25 ];
26
27 const EXPAND_FORM = 'eg.circ.patron.search.show_extras';
28 const INCLUDE_INACTIVE = 'eg.circ.patron.search.include_inactive';
29
30 @Component({
31   selector: 'eg-patron-search',
32   templateUrl: './search.component.html'
33 })
34
35 export class PatronSearchComponent implements OnInit, AfterViewInit {
36
37     @ViewChild('searchGrid', {static: false}) searchGrid: GridComponent;
38
39     // Fired on dbl-click of a search result row.
40     @Output() patronsSelected: EventEmitter<any>;
41
42     search: any = {};
43     searchOrg: IdlObject;
44     expandForm: boolean;
45     dataSource: GridDataSource;
46     profileGroups: IdlObject[] = [];
47
48     constructor(
49         private renderer: Renderer2,
50         private net: NetService,
51         public org: OrgService,
52         private auth: AuthService,
53         private store: ServerStoreService
54     ) {
55         this.patronsSelected = new EventEmitter<any>();
56         this.dataSource = new GridDataSource();
57         this.dataSource.getRows = (pager: Pager, sort: any[]) => {
58             return this.getRows(pager, sort);
59         };
60     }
61
62     ngOnInit() {
63         this.searchOrg = this.org.root();
64         this.store.getItemBatch([EXPAND_FORM, INCLUDE_INACTIVE])
65             .then(settings => {
66                 this.expandForm = settings[EXPAND_FORM];
67                 this.search.inactive = settings[INCLUDE_INACTIVE];
68             });
69     }
70
71     ngAfterViewInit() {
72         this.renderer.selectRootElement('#focus-this-input').focus();
73     }
74
75     toggleExpandForm() {
76         this.expandForm = !this.expandForm;
77         if (this.expandForm) {
78             this.store.setItem(EXPAND_FORM, true);
79         } else {
80             this.store.removeItem(EXPAND_FORM);
81         }
82     }
83
84     toggleIncludeInactive() {
85         if (this.search.inactive) { // value set by ngModel
86             this.store.setItem(INCLUDE_INACTIVE, true);
87         } else {
88             this.store.removeItem(INCLUDE_INACTIVE);
89         }
90     }
91
92     rowsSelected(rows: IdlObject | IdlObject[]) {
93         this.patronsSelected.emit([].concat(rows));
94     }
95
96     getSelected(): IdlObject[] {
97         return this.searchGrid ?
98             this.searchGrid.context.getSelectedRows() : [];
99     }
100
101     go() {
102         this.searchGrid.reload();
103     }
104
105     clear() {
106         this.search = {profile: null};
107     }
108
109     getRows(pager: Pager, sort: any[]): Observable<any> {
110
111         let observable: Observable<IdlObject>;
112
113         if (this.search.id) {
114             observable = this.searchById();
115         } else {
116             observable = this.searchByForm(pager, sort);
117         }
118
119         return observable.pipe(map(user => this.localFleshUser(user)));
120     }
121
122     localFleshUser(user: IdlObject): IdlObject {
123         user.home_ou(this.org.get(user.home_ou()));
124         return user;
125     }
126
127     searchByForm(pager: Pager, sort: any[]): Observable<IdlObject> {
128
129         const search = this.compileSearch();
130         if (!search) { return of(); }
131
132         const sorter = this.compileSort(sort);
133
134         return this.net.request(
135             'open-ils.actor',
136             'open-ils.actor.patron.search.advanced.fleshed',
137             this.auth.token(),
138             this.compileSearch(),
139             pager.limit,
140             sorter,
141             null, // ?
142             this.searchOrg.id(),
143             DEFAULT_FLESH,
144             pager.offset
145         );
146     }
147
148     searchById(): Observable<IdlObject> {
149         return this.net.request(
150             'open-ils.actor',
151             'open-ils.actor.user.fleshed.retrieve',
152             this.auth.token(), this.search.id, DEFAULT_FLESH
153         );
154     }
155
156     compileSort(sort: any[]): string[] {
157         if (!sort || sort.length === 0) { return DEFAULT_SORT; }
158         return sort.map(def => `${def.name} ${def.dir}`);
159     }
160
161     compileSearch(): any {
162
163         let hasSearch = false;
164         const search: Object = {};
165
166         Object.keys(this.search).forEach(field => {
167             search[field] = this.mapSearchField(field);
168             if (search[field]) { hasSearch = true; }
169         });
170
171         return hasSearch ? search : null;
172     }
173
174     isValue(val: any): boolean {
175         return (val !== null && val !== undefined && val !== '');
176     }
177
178     mapSearchField(field: string): any {
179
180         const value = this.search[field];
181         if (!this.isValue(value)) { return null; }
182
183         const chunk = {value: value, group: 0};
184
185         switch (field) {
186
187             case 'name': // name keywords
188             case 'inactive':
189                 delete chunk.group;
190                 break;
191
192             case 'street1':
193             case 'street2':
194             case 'city':
195             case 'state':
196             case 'post_code':
197                 chunk.group = 1;
198                 break;
199
200             case 'phone':
201             case 'ident':
202                 chunk.group = 2;
203                 break;
204
205             case 'card':
206                 chunk.group = 3;
207                 break;
208
209             case 'profile':
210                 chunk.group = 5;
211                 chunk.value = chunk.value.id(); // pgt object
212                 break;
213
214             case 'dob_day':
215             case 'dob_month':
216             case 'dob_year':
217                 chunk.group = 4;
218                 chunk.value = chunk.value.replace(/\D/g, '');
219
220                 if (!field.match(/year/)) {
221                     // force day/month to be 2 digits
222                     chunk[field].value = ('0' + value).slice(-2);
223                 }
224                 break;
225         }
226
227         // Confirm the value wasn't scrubbed away above
228         if (!this.isValue(chunk.value)) { return null; }
229
230         return chunk;
231     }
232 }
233