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