]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/grid/grid-filter-control.component.ts
LP2061136 - Stamping 1405 DB upgrade script
[Evergreen.git] / Open-ILS / src / eg2 / src / app / share / grid / grid-filter-control.component.ts
1 /* eslint-disable eqeqeq */
2 import {Component, Input, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core';
3 import {GridContext, GridColumn} from './grid';
4 import {IdlObject} from '@eg/core/idl.service';
5 import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component';
6 import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
7 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
8 import {OrgService} from '@eg/core/org.service';
9 import {NgbDropdown} from '@ng-bootstrap/ng-bootstrap';
10
11 @Component({
12     selector: 'eg-grid-filter-control',
13     templateUrl: './grid-filter-control.component.html'
14 })
15
16 export class GridFilterControlComponent implements OnInit {
17
18     @Input() context: GridContext;
19     @Input() col:     GridColumn;
20
21
22     @ViewChildren(ComboboxComponent)   filterComboboxes: QueryList<ComboboxComponent>;
23     @ViewChildren(OrgSelectComponent)  orgSelects: QueryList<OrgSelectComponent>;
24     @ViewChildren(NgbDropdown)         dropdowns: QueryList<NgbDropdown>;
25
26     @ViewChild('dateSelectOne') dateSelectOne: DateSelectComponent;
27     @ViewChild('dateSelectTwo') dateSelectTwo: DateSelectComponent;
28
29     // So we can use (ngModelChange) on the link combobox
30     linkFilterEntry: ComboboxEntry = null;
31
32     constructor(
33         private org: OrgService
34     ) {}
35
36     ngOnInit() {
37         if (this.col.filterValue !== undefined) {
38             this.applyFilter(this.col);
39         }
40     }
41
42     operatorChanged(col: GridColumn) {
43         if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
44             col.filterInputDisabled = true;
45             col.filterValue = undefined;
46         } else {
47             col.filterInputDisabled = false;
48         }
49     }
50
51     applyOrgFilter(col: GridColumn) {
52         let org: IdlObject = (col.filterValue as unknown) as IdlObject;
53
54         if (org == null) {
55             this.clearFilter(col);
56             return;
57         }
58         org = this.org.get(org); // if coming from a Named Filter Set, filterValue would be an an org id
59         const ous: any[] = new Array();
60         if (col.filterIncludeOrgDescendants || col.filterIncludeOrgAncestors) {
61             if (col.filterIncludeOrgAncestors) {
62                 ous.push(...this.org.ancestors(org, true));
63             }
64             if (col.filterIncludeOrgDescendants) {
65                 ous.push(...this.org.descendants(org, true));
66             }
67         } else {
68             ous.push(org.id());
69         }
70         const filt: any = {};
71         filt[col.name] = {};
72         const op: string = (col.filterOperator === '=' ? 'in' : 'not in');
73         filt[col.name][op] = ous;
74         this.context.dataSource.filters[col.name] = [ filt ];
75         col.isFiltered = true;
76         this.context.reload();
77
78         this.closeDropdown();
79     }
80
81     applyLinkFilter(col: GridColumn) {
82         if (col.filterValue) {
83             this.applyFilter(col);
84
85         } else {
86             // Value was cleared from the combobox
87             this.clearFilter(col);
88         }
89     }
90
91     // TODO: this was copied from date-select and
92     // really belongs in a date service
93     localDateFromYmd(ymd: string): Date {
94         const parts = ymd.split('-');
95         return new Date(
96             Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
97     }
98
99     /* eslint-disable no-magic-numbers */
100     applyDateFilter(col: GridColumn) {
101         const dateStr = this.dateSelectOne.currentAsYmd();
102         const endDateStr =
103             this.dateSelectTwo ? this.dateSelectTwo.currentAsYmd() : null;
104
105         if (endDateStr && !dateStr) {
106             // User has applied a second date (e.g. between) but cleared
107             // the first date.  Avoid applying the filter until
108             // dateStr gets a value or endDateStr is cleared or the
109             // operator changes.
110             return;
111         }
112
113         if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
114             this.applyFilter(col);
115         } else {
116             if (dateStr == null) {
117                 this.clearFilter(col);
118                 return;
119             }
120             const date: Date = this.localDateFromYmd(dateStr);
121             let date1 = new Date();
122             let date2 = new Date();
123             const op: string = col.filterOperator;
124             const filt: Object = {};
125             const filt2: Object = {};
126             const filters = new Array();
127             if (col.filterOperator === '>') {
128                 date1 = date;
129                 date1.setHours(23);
130                 date1.setMinutes(59);
131                 date1.setSeconds(59);
132                 filt[op] = date1.toISOString();
133                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
134                 filt2[col.name] = filt;
135                 filters.push(filt2);
136             } else if (col.filterOperator === '>=') {
137                 date1 = date;
138                 filt[op] = date1.toISOString();
139                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
140                 filt2[col.name] = filt;
141                 filters.push(filt2);
142             } else if (col.filterOperator === '<') {
143                 date1 = date;
144                 filt[op] = date1.toISOString();
145                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
146                 filt2[col.name] = filt;
147                 filters.push(filt2);
148             } else if (col.filterOperator === '<=') {
149                 date1 = date;
150                 date1.setHours(23);
151                 date1.setMinutes(59);
152                 date1.setSeconds(59);
153                 filt[op] = date1.toISOString();
154                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
155                 filt2[col.name] = filt;
156                 filters.push(filt2);
157             } else if (col.filterOperator === '=') {
158                 date1 = new Date(date.valueOf());
159                 filt['>='] = date1.toISOString();
160                 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
161                 filt2[col.name] = filt;
162                 filters.push(filt2);
163
164                 date2 = new Date(date.valueOf());
165                 date2.setHours(23);
166                 date2.setMinutes(59);
167                 date2.setSeconds(59);
168                 const filt_a: Object = {};
169                 const filt2_a: Object = {};
170                 filt_a['<='] = date2.toISOString();
171                 if (col.name === 'dob') { filt_a['<='] = dateStr; } // special case
172                 filt2_a[col.name] = filt_a;
173                 filters.push(filt2_a);
174             } else if (col.filterOperator === '!=') {
175                 date1 = new Date(date.valueOf());
176                 filt['<'] = date1.toISOString();
177                 if (col.name === 'dob') { filt['<'] = dateStr; } // special case
178                 filt2[col.name] = filt;
179
180                 date2 = new Date(date.valueOf());
181                 date2.setHours(23);
182                 date2.setMinutes(59);
183                 date2.setSeconds(59);
184                 const filt_a: Object = {};
185                 const filt2_a: Object = {};
186                 filt_a['>'] = date2.toISOString();
187                 if (col.name === 'dob') { filt_a['>'] = dateStr; } // special case
188                 filt2_a[col.name] = filt_a;
189
190                 const date_filt: any = { '-or': [] };
191                 date_filt['-or'].push(filt2);
192                 date_filt['-or'].push(filt2_a);
193                 filters.push(date_filt);
194             } else if (col.filterOperator === 'between') {
195
196                 if (!endDateStr) {
197                     // User has not applied the second date yet.
198                     return;
199                 }
200
201                 date1 = date;
202                 date2 = this.localDateFromYmd(endDateStr);
203
204                 let date1op = '>=';
205                 let date2op = '<=';
206                 if (date1 > date2) {
207                     // don't make user care about the order
208                     // they enter the dates in
209                     date1op = '<=';
210                     date2op = '>=';
211                 }
212                 filt[date1op] = date1.toISOString();
213                 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
214                 filt2[col.name] = filt;
215                 filters.push(filt2);
216
217                 date2.setHours(23);
218                 date2.setMinutes(59);
219                 date2.setSeconds(59);
220                 const filt_a: Object = {};
221                 const filt2_a: Object = {};
222                 filt_a[date2op] = date2.toISOString();
223                 if (col.name === 'dob') { filt_a['<='] = endDateStr; } // special case
224                 filt2_a[col.name] = filt_a;
225                 filters.push(filt2_a);
226             }
227             this.context.dataSource.filters[col.name] = filters;
228             col.isFiltered = true;
229             this.context.reload();
230             this.closeDropdown();
231         }
232     }
233     /* eslint-enable no-magic-numbers */
234
235     clearDateFilter(col: GridColumn) {
236         delete this.context.dataSource.filters[col.name];
237         col.isFiltered = false;
238         this.context.reload();
239         this.closeDropdown();
240     }
241     applyBooleanFilter(col: GridColumn) {
242         if (!col.filterValue || col.filterValue === '') {
243             delete this.context.dataSource.filters[col.name];
244             col.isFiltered = false;
245             this.context.reload();
246         } else {
247             const val: string = col.filterValue;
248             const op = '=';
249             const filt: Object = {};
250             filt[op] = val;
251             const filt2: Object = {};
252             filt2[col.name] = filt;
253             this.context.dataSource.filters[col.name] = [ filt2 ];
254             col.isFiltered = true;
255             this.context.reload();
256         }
257
258         this.closeDropdown();
259     }
260
261     applyFilterCommon(col: GridColumn) {
262
263         switch (col.datatype) {
264             case 'link':
265                 col.filterValue =
266                     this.linkFilterEntry ? this.linkFilterEntry.id : null;
267                 return this.applyLinkFilter(col);
268             case 'bool':
269                 return this.applyBooleanFilter(col);
270             case 'timestamp':
271                 return this.applyDateFilter(col);
272             case 'org_unit':
273                 return this.applyOrgFilter(col);
274             default:
275                 return this.applyFilter(col);
276         }
277     }
278
279     applyFilter(col: GridColumn) {
280         // fallback if the operator somehow was not set yet
281         if (col.filterOperator === undefined) { col.filterOperator = '='; }
282
283         if ( (col.filterOperator !== 'null') && (col.filterOperator !== 'not null') &&
284              (!col.filterValue || col.filterValue === '') &&
285              (col.filterValue !== '0') ) {
286             // if value is empty and we're _not_ checking for null/not null, clear
287             // the filter
288             delete this.context.dataSource.filters[col.name];
289             col.isFiltered = false;
290         } else {
291             let op: string = col.filterOperator;
292             let val: string = col.filterValue;
293             if (col.filterOperator === 'null') {
294                 op  = '=';
295                 val = null;
296             } else if (col.filterOperator === 'not null') {
297                 op  = '!=';
298                 val = null;
299             } else if (col.filterOperator === 'like' || col.filterOperator === 'not like') {
300                 val = '%' + val + '%';
301             } else if (col.filterOperator === 'startswith') {
302                 op = 'like';
303                 val = val + '%';
304             } else if (col.filterOperator === 'endswith') {
305                 op = 'like';
306                 val = '%' + val;
307             }
308             const filt: any = {};
309             if (col.filterOperator === 'not like') {
310                 filt['-not'] = {};
311                 filt['-not'][col.name] = {};
312                 filt['-not'][col.name]['like'] = val;
313                 this.context.dataSource.filters[col.name] = [ filt ];
314                 col.isFiltered = true;
315             } else {
316                 filt[col.name] = {};
317                 filt[col.name][op] = val;
318                 this.context.dataSource.filters[col.name] = [ filt ];
319                 col.isFiltered = true;
320             }
321         }
322         this.context.reload();
323         this.closeDropdown();
324     }
325     clearFilter(col: GridColumn) {
326         // clear filter values...
327         col.removeFilter();
328         // ... and inform the data source
329         delete this.context.dataSource.filters[col.name];
330         col.isFiltered = false;
331         this.reset();
332         this.context.reload();
333         this.closeDropdown();
334     }
335
336     closeDropdown() {
337         // Timeout allows actions to occur before closing (some) dropdows
338         // clears the values (e.g. link selector)
339         setTimeout(() => this.dropdowns.forEach(drp => { drp.close(); }));
340     }
341
342     reset() {
343         if (this.filterComboboxes) {
344             this.filterComboboxes.forEach(ctl => { ctl.applyEntryId(null); });
345         }
346         if (this.orgSelects) {
347             this.orgSelects.forEach(ctl => { ctl.reset(); });
348         }
349         if (this.dateSelectOne) { this.dateSelectOne.reset(); }
350         if (this.dateSelectTwo) { this.dateSelectTwo.reset(); }
351     }
352 }
353