]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/grid/grid-manage-filters-dialog.component.ts
LP 2061136 follow-up: ng lint --fix
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / share / grid / grid-manage-filters-dialog.component.ts
1 /* eslint-disable no-empty */
2 import {ComboboxEntry, ComboboxComponent} from '@eg/share/combobox/combobox.component';
3 import {Component, Input, OnInit, OnDestroy, ViewChild, Renderer2} from '@angular/core';
4 import {GridContext} from '@eg/share/grid/grid';
5 import {DialogComponent} from '@eg/share/dialog/dialog.component';
6 import {NgForm} from '@angular/forms';
7 import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
8 import {PcrudService} from '@eg/core/pcrud.service';
9 import {ServerStoreService} from '@eg/core/server-store.service';
10 import {Subject, Subscription} from 'rxjs';
11 import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
12
13 @Component({
14     selector: 'eg-grid-manage-filters-dialog',
15     templateUrl: './grid-manage-filters-dialog.component.html'
16 })
17
18 export class GridManageFiltersDialogComponent extends DialogComponent implements OnInit, OnDestroy {
19
20     @Input() gridContext: GridContext;
21
22     subscriptions: Subscription[] = [];
23
24     saveFilterName = '';
25     saveFilterNameModelChanged: Subject<string> = new Subject<string>();
26     nameCollision = false;
27
28     filterSetEntries: ComboboxEntry[] = [];
29
30     @ViewChild('manageFiltersForm', { static: false}) manageFiltersForm: NgForm;
31     @ViewChild('namedFilterSetSelector', { static: true}) namedFilterSetSelector: ComboboxComponent;
32
33     constructor(
34         private modal: NgbModal,
35         private pcrud: PcrudService,
36         private renderer: Renderer2,
37         private store: ServerStoreService,
38     ) {
39         super(modal);
40         if (this.modal) {} // noop for delinting
41     }
42
43     ngOnInit() {
44
45         this.subscriptions.push( this.onOpen$.subscribe(
46             _ => {
47                 const el = this.renderer.selectRootElement('#session_name');
48                 if (el) { el.focus(); el.select(); }
49             }
50         ));
51
52         this.subscriptions.push(
53             this.saveFilterNameModelChanged
54                 .pipe(
55                     // eslint-disable-next-line no-magic-numbers
56                     debounceTime(300),
57                     distinctUntilChanged()
58                 )
59                 .subscribe( newText => {
60                     this.saveFilterName = newText;
61                     this.nameCollision = false;
62                     if (newText !== '') {
63                         this.store.getItem('eg.grid.filters.' + this.gridContext.persistKey).then( setting => {
64                             if (setting) {
65                                 if (setting[newText]) {
66                                     this.nameCollision = true;
67                                 }
68                             }
69                         });
70                     }
71                 })
72         );
73
74         this.refreshEntries();
75     }
76
77     ngOnDestroy() {
78         this.subscriptions.forEach((subscription) => {
79             subscription.unsubscribe();
80         });
81     }
82
83     saveFilters() {
84         this.gridContext.saveFilters(this.saveFilterName);
85         this.refreshEntries();
86         this.nameCollision = true;
87         this.close();
88     }
89
90     disableSaveNameTest(): boolean {
91         const isEmpty = (obj: any): boolean => {
92             return obj && Object.keys(obj).length === 0;
93         };
94
95         return isEmpty(this.gridContext?.dataSource?.filters);
96     }
97
98     disableSaveButtonTest(): boolean {
99         const isEmpty = (obj: any): boolean => {
100             return obj && Object.keys(obj).length === 0;
101         };
102
103         return this.nameCollision || this.saveFilterName === '' || isEmpty(this.gridContext?.dataSource?.filters);
104     }
105
106     refreshEntries() {
107         this.filterSetEntries = [];
108         this.store.getItem('eg.grid.filters.' + this.gridContext.persistKey).then( setting => {
109             if (setting /* for testing only: && Object.keys( setting ).length > 0 */) {
110                 Object.keys(setting).forEach( key => {
111                     this.filterSetEntries.push({ id: key, label: key });
112                 });
113             } else {
114                 if (this.gridContext.migrateLegacyFilterSets) {
115                     this.attemptLegacyFilterSetMigration();
116                 }
117             }
118             if (this.namedFilterSetSelector && this.filterSetEntries.length > 0) {
119                 this.namedFilterSetSelector.selected = this.filterSetEntries[0];
120             }
121         });
122     }
123
124     legacyFieldMap(legacy_field: string): string {
125         if (this.gridContext.idlClass === 'uvuv') {
126             if (legacy_field === 'url_id') { return 'url'; }
127             if (legacy_field === 'attempt_id') { return 'id'; }
128             if (legacy_field === 'res_time') { return 'res_time'; }
129             if (legacy_field === 'res_code') { return 'res_code'; }
130             if (legacy_field === 'res_text') { return 'res_text'; }
131             if (legacy_field === 'req_time') { return 'req_time'; }
132             return 'url.' + legacy_field;
133         } else {
134             if (legacy_field === 'url_id') { return 'id'; }
135         }
136
137         return legacy_field;
138     }
139
140     legacyOperatorValueMap(field_name: string, field_datatype: string, legacy_operator: string, legacy_value: any): any {
141         let operator = legacy_operator;
142         let value = legacy_value;
143         let filterOperator = legacy_operator;
144         let filterValue = legacy_value;
145         const filterInputDisabled = false;
146         const filterIncludeOrgAncestors = false;
147         const filterIncludeOrgDescendants = false;
148         let notSupported = false;
149         if (field_datatype) {} // delint TODO: remove this?
150         switch(legacy_operator) {
151             case '=': case '!=': case '>': case '<': case '>=': case '<=':
152                 /* same */
153                 break;
154             case 'in': case 'not in':
155             case 'between': case 'not between':
156                 /* not supported, warn user */
157                 operator = undefined;
158                 value = undefined;
159                 filterOperator = '=';
160                 filterValue = undefined;
161                 notSupported = true;
162                 break;
163             case 'null':
164                 operator = '=';
165                 value = undefined;
166                 filterOperator = '=';
167                 filterValue = null;
168                 break;
169             case 'not null':
170                 operator = '!=';
171                 value = undefined;
172                 filterOperator = '!=';
173                 filterValue = null;
174                 break;
175             case 'like': case 'not like':
176                 value = '%' + filterValue + '%';
177                 /* not like needs special handling further below */
178                 break;
179         }
180         if (notSupported) {
181             return undefined;
182         }
183
184         const filter = {};
185         const mappedFieldName = this.legacyFieldMap(field_name);
186         filter[mappedFieldName] = {};
187         if (operator === 'not like') {
188             filter[mappedFieldName]['-not'] = {};
189             filter[mappedFieldName]['-not'][mappedFieldName] = {};
190             filter[mappedFieldName]['-not'][mappedFieldName]['like'] = value;
191         } else {
192             filter[mappedFieldName][operator] = value;
193         }
194
195         const control = {
196             isFiltered: true,
197             filterValue: filterValue,
198             filterOperator: filterOperator,
199             filterInputDisabled: filterInputDisabled,
200             filterIncludeOrgAncestors: filterIncludeOrgAncestors,
201             filterIncludeOrgDescendants: filterIncludeOrgDescendants
202         };
203
204         return [ filter, control ];
205     }
206
207     attemptLegacyFilterSetMigration() {
208     // The legacy interface allows you to define multiple filters for the same column, which our current filters
209     // do not support (well, the dataSource.filters part can, but not the grid.context.filterControls).  The legacy
210     // filters also have an unintuitive additive behavior if you do that.  We should take the last filter and warn
211     // the user if this happens.  None of the filters for date columns is working correctly in the legacy UI, so no
212     // need to map those.  We also not able to support between, not between, in, and not in.
213         this.pcrud.search('cfdfs', {'interface':this.gridContext.migrateLegacyFilterSets},{},{'atomic':true}).subscribe(
214             (legacySets) => {
215                 legacySets.forEach( (s:any) => {
216                     const obj = {
217                         'filters' : {},
218                         'controls' : {}
219                     };
220                     console.log('migrating legacy set ' + s.name(), s );
221                     JSON.parse( s.filters() ).forEach( (f:any) => {
222                         const mappedFieldName = this.legacyFieldMap(f.field);
223                         const c = this.gridContext.columnSet.getColByName( mappedFieldName );
224                         if (c) {
225                             const r = this.legacyOperatorValueMap(f.field, c.datatype, f.operator, f.value || f.values);
226                             obj['filters'][mappedFieldName] = [ r[0] ];
227                             obj['controls'][mappedFieldName] = r[1];
228                         } else {
229                             console.log('with legacy set ' + s.name()
230                                 + ', column not found for ' + f.field + ' (' + this.legacyFieldMap( f.field) + ')');
231                         }
232                     });
233                     if (Object.keys(obj.filters).length > 0) {
234                         this.store.getItem('eg.grid.filters.' + this.gridContext.persistKey).then( setting => {
235                             setting ||= {};
236                             setting[s.name()] = obj;
237                             this.store.setItem('eg.grid.filters.' + this.gridContext.persistKey, setting).then( res => {
238                                 this.refreshEntries();
239                                 console.log('save toast here',res);
240                             });
241                         });
242                     }
243                 });
244             }
245         );
246     }
247 }