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';
14 selector: 'eg-grid-manage-filters-dialog',
15 templateUrl: './grid-manage-filters-dialog.component.html'
18 export class GridManageFiltersDialogComponent extends DialogComponent implements OnInit, OnDestroy {
20 @Input() gridContext: GridContext;
22 subscriptions: Subscription[] = [];
25 saveFilterNameModelChanged: Subject<string> = new Subject<string>();
26 nameCollision = false;
28 filterSetEntries: ComboboxEntry[] = [];
30 @ViewChild('manageFiltersForm', { static: false}) manageFiltersForm: NgForm;
31 @ViewChild('namedFilterSetSelector', { static: true}) namedFilterSetSelector: ComboboxComponent;
34 private modal: NgbModal,
35 private pcrud: PcrudService,
36 private renderer: Renderer2,
37 private store: ServerStoreService,
40 if (this.modal) {} // noop for delinting
45 this.subscriptions.push( this.onOpen$.subscribe(
47 const el = this.renderer.selectRootElement('#session_name');
48 if (el) { el.focus(); el.select(); }
52 this.subscriptions.push(
53 this.saveFilterNameModelChanged
55 // eslint-disable-next-line no-magic-numbers
57 distinctUntilChanged()
59 .subscribe( newText => {
60 this.saveFilterName = newText;
61 this.nameCollision = false;
63 this.store.getItem('eg.grid.filters.' + this.gridContext.persistKey).then( setting => {
65 if (setting[newText]) {
66 this.nameCollision = true;
74 this.refreshEntries();
78 this.subscriptions.forEach((subscription) => {
79 subscription.unsubscribe();
84 this.gridContext.saveFilters(this.saveFilterName);
85 this.refreshEntries();
86 this.nameCollision = true;
90 disableSaveNameTest(): boolean {
91 const isEmpty = (obj: any): boolean => {
92 return obj && Object.keys(obj).length === 0;
95 return isEmpty(this.gridContext?.dataSource?.filters);
98 disableSaveButtonTest(): boolean {
99 const isEmpty = (obj: any): boolean => {
100 return obj && Object.keys(obj).length === 0;
103 return this.nameCollision || this.saveFilterName === '' || isEmpty(this.gridContext?.dataSource?.filters);
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 });
114 if (this.gridContext.migrateLegacyFilterSets) {
115 this.attemptLegacyFilterSetMigration();
118 if (this.namedFilterSetSelector && this.filterSetEntries.length > 0) {
119 this.namedFilterSetSelector.selected = this.filterSetEntries[0];
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;
134 if (legacy_field === 'url_id') { return 'id'; }
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 '<=':
154 case 'in': case 'not in':
155 case 'between': case 'not between':
156 /* not supported, warn user */
157 operator = undefined;
159 filterOperator = '=';
160 filterValue = undefined;
166 filterOperator = '=';
172 filterOperator = '!=';
175 case 'like': case 'not like':
176 value = '%' + filterValue + '%';
177 /* not like needs special handling further below */
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;
192 filter[mappedFieldName][operator] = value;
197 filterValue: filterValue,
198 filterOperator: filterOperator,
199 filterInputDisabled: filterInputDisabled,
200 filterIncludeOrgAncestors: filterIncludeOrgAncestors,
201 filterIncludeOrgDescendants: filterIncludeOrgDescendants
204 return [ filter, control ];
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(
215 legacySets.forEach( (s:any) => {
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 );
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];
229 console.log('with legacy set ' + s.name()
230 + ', column not found for ' + f.field + ' (' + this.legacyFieldMap( f.field) + ')');
233 if (Object.keys(obj.filters).length > 0) {
234 this.store.getItem('eg.grid.filters.' + this.gridContext.persistKey).then( 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);