da23710484d4cb50944042a0436a67b7bab0cec4
[Evergreen.git] / Open-ILS / src / eg2 / src / app / share / grid / grid-filter-control.component.ts
1 import {Component, Input, OnInit, QueryList, 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
10 @Component({
11   selector: 'eg-grid-filter-control',
12   templateUrl: './grid-filter-control.component.html'
13 })
14
15 export class GridFilterControlComponent implements OnInit {
16
17     @Input() context: GridContext;
18     @Input() col:     GridColumn;
19
20
21     @ViewChildren(ComboboxComponent)   filterComboboxes: QueryList<ComboboxComponent>;
22     @ViewChildren(DateSelectComponent) dateSelects: QueryList<DateSelectComponent>;
23     @ViewChildren(OrgSelectComponent)  orgSelects: QueryList<OrgSelectComponent>;
24     @ViewChildren(NgbDropdown)         dropdowns: QueryList<NgbDropdown>;
25
26     constructor(
27         private org: OrgService
28     ) {}
29
30     ngOnInit() { }
31
32     operatorChanged(col: GridColumn) {
33         if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
34             col.filterInputDisabled = true;
35             col.filterValue = undefined;
36         } else {
37             col.filterInputDisabled = false;
38         }
39     }
40
41     applyOrgFilter(org: IdlObject, col: GridColumn) {
42         if (org == null) {
43             this.clearFilter(col);
44             return;
45         }
46         const ous: any[] = new Array();
47         if (col.filterIncludeOrgDescendants || col.filterIncludeOrgAncestors) {
48             if (col.filterIncludeOrgAncestors) {
49                 ous.push(...this.org.ancestors(org, true));
50             }
51             if (col.filterIncludeOrgDescendants) {
52                 ous.push(...this.org.descendants(org, true));
53             }
54         } else {
55             ous.push(org.id());
56         }
57         const filt: any = {};
58         filt[col.name] = {};
59         const op: string = (col.filterOperator === '=' ? 'in' : 'not in');
60         filt[col.name][op] = ous;
61         this.context.dataSource.filters[col.name] = [ filt ];
62         col.isFiltered = true;
63         this.context.reload();
64     }
65
66     applyLinkFilter($event, col: GridColumn) {
67         if ($event) {
68             col.filterValue = $event.id;
69             this.applyFilter(col);
70
71         } else {
72             // Value was cleared from the combobox
73             this.clearFilter(col);
74         }
75     }
76
77     // TODO: this was copied from date-select and
78     // really belongs in a date service
79     localDateFromYmd(ymd: string): Date {
80         const parts = ymd.split('-');
81         return new Date(
82             Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
83     }
84     applyDateFilter(dateStr: string, col: GridColumn, endDateStr: string) {
85         if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
86             this.applyFilter(col);
87         } else {
88             if (dateStr == null) {
89                 this.clearFilter(col);
90                 return;
91             }
92             const date: Date = this.localDateFromYmd(dateStr);
93             let date1 = new Date();
94             let date2 = new Date();
95             const op: string = col.filterOperator;
96             const filt: Object = {};
97             const filt2: Object = {};
98             const filters = new Array();
99             if (col.filterOperator === '>') {
100                 date1 = date;
101                 date1.setHours(23);
102                 date1.setMinutes(59);
103                 date1.setSeconds(59);
104                 filt[op] = date1.toISOString();
105                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
106                 filt2[col.name] = filt;
107                 filters.push(filt2);
108             } else if (col.filterOperator === '>=') {
109                 date1 = date;
110                 filt[op] = date1.toISOString();
111                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
112                 filt2[col.name] = filt;
113                 filters.push(filt2);
114             } else if (col.filterOperator === '<') {
115                 date1 = date;
116                 filt[op] = date1.toISOString();
117                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
118                 filt2[col.name] = filt;
119                 filters.push(filt2);
120             } else if (col.filterOperator === '<=') {
121                 date1 = date;
122                 date1.setHours(23);
123                 date1.setMinutes(59);
124                 date1.setSeconds(59);
125                 filt[op] = date1.toISOString();
126                 if (col.name === 'dob') { filt[op] = dateStr; } // special case
127                 filt2[col.name] = filt;
128                 filters.push(filt2);
129             } else if (col.filterOperator === '=') {
130                 date1 = new Date(date.valueOf());
131                 filt['>='] = date1.toISOString();
132                 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
133                 filt2[col.name] = filt;
134                 filters.push(filt2);
135
136                 date2 = new Date(date.valueOf());
137                 date2.setHours(23);
138                 date2.setMinutes(59);
139                 date2.setSeconds(59);
140                 const filt_a: Object = {};
141                 const filt2_a: Object = {};
142                 filt_a['<='] = date2.toISOString();
143                 if (col.name === 'dob') { filt_a['<='] = dateStr; } // special case
144                 filt2_a[col.name] = filt_a;
145                 filters.push(filt2_a);
146             } else if (col.filterOperator === '!=') {
147                 date1 = new Date(date.valueOf());
148                 filt['<'] = date1.toISOString();
149                 if (col.name === 'dob') { filt['<'] = dateStr; } // special case
150                 filt2[col.name] = filt;
151
152                 date2 = new Date(date.valueOf());
153                 date2.setHours(23);
154                 date2.setMinutes(59);
155                 date2.setSeconds(59);
156                 const filt_a: Object = {};
157                 const filt2_a: Object = {};
158                 filt_a['>'] = date2.toISOString();
159                 if (col.name === 'dob') { filt_a['>'] = dateStr; } // special case
160                 filt2_a[col.name] = filt_a;
161
162                 const date_filt: any = { '-or': [] };
163                 date_filt['-or'].push(filt2);
164                 date_filt['-or'].push(filt2_a);
165                 filters.push(date_filt);
166             } else if (col.filterOperator === 'between') {
167
168                 if (!endDateStr) {
169                     // User has not applied the second date yet.
170                     return;
171                 }
172
173                 date1 = date;
174                 date2 = this.localDateFromYmd(endDateStr);
175
176                 let date1op = '>=';
177                 let date2op = '<=';
178                 if (date1 > date2) {
179                     // don't make user care about the order
180                     // they enter the dates in
181                     date1op = '<=';
182                     date2op = '>=';
183                 }
184                 filt[date1op] = date1.toISOString();
185                 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
186                 filt2[col.name] = filt;
187                 filters.push(filt2);
188
189                 date2.setHours(23);
190                 date2.setMinutes(59);
191                 date2.setSeconds(59);
192                 const filt_a: Object = {};
193                 const filt2_a: Object = {};
194                 filt_a[date2op] = date2.toISOString();
195                 if (col.name === 'dob') { filt_a['<='] = endDateStr; } // special case
196                 filt2_a[col.name] = filt_a;
197                 filters.push(filt2_a);
198             }
199             this.context.dataSource.filters[col.name] = filters;
200             col.isFiltered = true;
201             this.context.reload();
202         }
203
204         // The date filter has autoClose=false so that interacting with
205         // date selectors won't result in closing the dropdown.  Once
206         // we've successfully applied a filter, force it closed.
207         this.closeDropdown();
208     }
209     clearDateFilter(col: GridColumn) {
210         delete this.context.dataSource.filters[col.name];
211         col.isFiltered = false;
212         this.context.reload();
213     }
214     applyBooleanFilter(col: GridColumn) {
215         if (!col.filterValue || col.filterValue === '') {
216             delete this.context.dataSource.filters[col.name];
217             col.isFiltered = false;
218             this.context.reload();
219         } else {
220             const val: string = col.filterValue;
221             const op = '=';
222             const filt: Object = {};
223             filt[op] = val;
224             const filt2: Object = {};
225             filt2[col.name] = filt;
226             this.context.dataSource.filters[col.name] = [ filt2 ];
227             col.isFiltered = true;
228             this.context.reload();
229         }
230     }
231     applyFilter(col: GridColumn) {
232         // fallback if the operator somehow was not set yet
233         if (col.filterOperator === undefined) { col.filterOperator = '='; }
234
235         if ( (col.filterOperator !== 'null') && (col.filterOperator !== 'not null') &&
236              (!col.filterValue || col.filterValue === '') &&
237              (col.filterValue !== '0') ) {
238             // if value is empty and we're _not_ checking for null/not null, clear
239             // the filter
240             delete this.context.dataSource.filters[col.name];
241             col.isFiltered = false;
242         } else {
243             let op: string = col.filterOperator;
244             let val: string = col.filterValue;
245             if (col.filterOperator === 'null') {
246                 op  = '=';
247                 val = null;
248             } else if (col.filterOperator === 'not null') {
249                 op  = '!=';
250                 val = null;
251             } else if (col.filterOperator === 'like' || col.filterOperator === 'not like') {
252                 val = '%' + val + '%';
253             } else if (col.filterOperator === 'startswith') {
254                 op = 'like';
255                 val = val + '%';
256             } else if (col.filterOperator === 'endswith') {
257                 op = 'like';
258                 val = '%' + val;
259             }
260             const filt: any = {};
261             if (col.filterOperator === 'not like') {
262                 filt['-not'] = {};
263                 filt['-not'][col.name] = {};
264                 filt['-not'][col.name]['like'] = val;
265                 this.context.dataSource.filters[col.name] = [ filt ];
266                 col.isFiltered = true;
267             } else {
268                 filt[col.name] = {};
269                 filt[col.name][op] = val;
270                 this.context.dataSource.filters[col.name] = [ filt ];
271                 col.isFiltered = true;
272             }
273         }
274         this.context.reload();
275     }
276     clearFilter(col: GridColumn) {
277         // clear filter values...
278         col.removeFilter();
279         // ... and inform the data source
280         delete this.context.dataSource.filters[col.name];
281         col.isFiltered = false;
282         this.reset();
283         this.context.reload();
284     }
285
286     closeDropdown() {
287         this.dropdowns.forEach(drp => { drp.close(); });
288     }
289
290     reset() {
291         this.filterComboboxes.forEach(ctl => { ctl.applyEntryId(null); });
292         this.dateSelects.forEach(ctl => { ctl.reset(); });
293         this.orgSelects.forEach(ctl => { ctl.reset(); });
294     }
295 }
296