diff --git a/src/app/applications/services/applications-filters.service.ts b/src/app/applications/services/applications-filters.service.ts index f134590b9..6187d20a5 100644 --- a/src/app/applications/services/applications-filters.service.ts +++ b/src/app/applications/services/applications-filters.service.ts @@ -73,4 +73,9 @@ export class ApplicationsFiltersService { throw new Error(`Unknown filter type: ${filterFor} ${filterField}}`); } } + + retrieveField(filterField: string): ApplicationFilterField { + const values = Object.values(this.#rootField); + return values.findIndex(value => value.toLowerCase() === filterField.toLowerCase()); + } } diff --git a/src/app/components/filters/filters-dialog-and.component.spec.ts b/src/app/components/filters/filters-dialog-and.component.spec.ts index 290e51406..6d6755657 100644 --- a/src/app/components/filters/filters-dialog-and.component.spec.ts +++ b/src/app/components/filters/filters-dialog-and.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { DATA_FILTERS_SERVICE } from '@app/tokens/filters.token'; import { IconsService } from '@services/icons.service'; @@ -6,11 +6,10 @@ import { FiltersDialogAndComponent } from './filters-dialog-and.component'; describe('FiltersDialogAndComponent', () => { let component: FiltersDialogAndComponent; - let fixture: ComponentFixture>; let removeChangeSpy: jest.SpyInstance; beforeEach(async () => { - await TestBed.configureTestingModule({ + component = TestBed.configureTestingModule({ imports: [BrowserAnimationsModule], providers: [ FiltersDialogAndComponent, @@ -18,22 +17,17 @@ describe('FiltersDialogAndComponent', () => { { provide: DATA_FILTERS_SERVICE, useValue: { retrieveFiltersDefinitions: jest.fn(() => { return []; - }) + }), + retrieveLabel: jest.fn(), }} ] - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(FiltersDialogAndComponent); - component = fixture.componentInstance; + }).inject(FiltersDialogAndComponent); component.filter = { field: 1, for: 'root', operator: 0, value: 1 }; - fixture.detectChanges(); removeChangeSpy = jest.spyOn(component.removeChange, 'emit'); }); diff --git a/src/app/components/filters/filters-dialog-filter-field.component.spec.ts b/src/app/components/filters/filters-dialog-filter-field.component.spec.ts index 1ba94f2c6..318778180 100644 --- a/src/app/components/filters/filters-dialog-filter-field.component.spec.ts +++ b/src/app/components/filters/filters-dialog-filter-field.component.spec.ts @@ -1,21 +1,44 @@ -import { KeyValue } from '@angular/common'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { DATA_FILTERS_SERVICE } from '@app/tokens/filters.token'; import { FilterDefinition } from '@app/types/filter-definition'; -import { Filter, FilterInputOutput } from '@app/types/filters'; +import { Filter, FilterInputOutput, FilterValueOptions } from '@app/types/filters'; import { FiltersService } from '@services/filters.service'; import { FiltersDialogFilterFieldComponent } from './filters-dialog-filter-field.component'; describe('FiltersDialogFilterFieldComponent', () => { let component: FiltersDialogFilterFieldComponent; - let fixture: ComponentFixture>; + const mockDataFiltersService = { retrieveFiltersDefinitions: jest.fn(() => { return filterDefinitions; }), - retrieveLabel: jest.fn() + retrieveLabel: jest.fn((value: string) => { + const labels = Object.values(propertiesLabel); + return labels.map(label => label.toLowerCase() === value.toLowerCase()); + }), + retrieveField: jest.fn((value: string) => { + const values = Object.values(propertiesLabel); + return values.findIndex(label => label.toLowerCase() === value.toLowerCase()); + }) }; + const allStatuses = [ + {key: 0, value: 'Creation'}, + {key: 1, value: 'Submitted'}, + {key: 2, value: 'Ended'} + ]; + + const propertiesLabel: {[key: number]: string} = { + 0: 'undefined', + 1:'status', + 2:'task id', + 3:'children', + 4:'size', + 5:'-', + 6:'Start to End duration', + 7:'Created at', + }; + const filterDefinitions: FilterDefinition[] = [ { field: 4, @@ -26,10 +49,7 @@ describe('FiltersDialogFilterFieldComponent', () => { field: 1, type: 'status', for: 'root', - statuses: [ - {key: 'status1', value: '2'}, - {key: 'status2', value: '2'} - ] + statuses: allStatuses }, { field: 2, @@ -59,20 +79,15 @@ describe('FiltersDialogFilterFieldComponent', () => { ]; beforeEach(async () => { - await TestBed.configureTestingModule({ + component = TestBed.configureTestingModule({ imports: [ BrowserAnimationsModule ], providers: [ FiltersDialogFilterFieldComponent, FiltersService, { provide: DATA_FILTERS_SERVICE, useValue: mockDataFiltersService } ] - }).compileComponents(); - }); + }).inject(FiltersDialogFilterFieldComponent); - beforeEach(() => { - fixture = TestBed.createComponent(FiltersDialogFilterFieldComponent); - component = fixture.componentInstance; - component.filter = { field: 1, for: 'root', @@ -80,14 +95,120 @@ describe('FiltersDialogFilterFieldComponent', () => { value: 'someValue' }; component.first = true; - - fixture.detectChanges(); + component.ngOnInit(); }); it('should run', () => { expect(component).toBeTruthy(); }); + describe('ngOnInit', () => { + describe('filteredProperties', () => { + it('should filter properly', () => { + component.filteredProperties.subscribe(value => { + expect(value).toEqual(['status', 'Start to End duration', 'Created at']); + }); + component.propertyFormControl.setValue('at'); + component.propertyFormControl.updateValueAndValidity({emitEvent: true}); + }); + + it('should return all the list in case of null value', () => { + component.filteredProperties.subscribe(value => { + expect(value).toEqual(Object.values(propertiesLabel)); + }); + component.propertyFormControl.setValue(null); + component.propertyFormControl.updateValueAndValidity({emitEvent: true}); + }); + }); + + describe('filteredOperators', () => { + it('should filter properly', () => { + component.filteredOperators.subscribe(value => { + expect(value).toEqual(['equal', 'not equal']); + }); + component.propertyFormControl.setValue('equal'); + component.propertyFormControl.updateValueAndValidity({emitEvent: true}); + }); + + it('should return all the list in case of null value', () => { + component.filteredOperators.subscribe(value => { + expect(value).toEqual(['equal', 'not equal']); + }); + component.propertyFormControl.setValue(null); + component.propertyFormControl.updateValueAndValidity({emitEvent: true}); + }); + }); + + describe('filteredStatuses', () => { + it('should filter properly', () => { + component.filteredStatuses.subscribe(value => { + expect(value).toEqual(['sumbitted', 'Ended']); + }); + component.propertyFormControl.setValue('ed'); + component.propertyFormControl.updateValueAndValidity({emitEvent: true}); + }); + + it('should return all the list in case of null value', () => { + component.filteredStatuses.subscribe(value => { + expect(value).toEqual(Object.values(allStatuses)); + }); + component.propertyFormControl.setValue(null); + component.propertyFormControl.updateValueAndValidity({emitEvent: true}); + }); + }); + }); + + describe('retrieveStatusKey', () => { + it('should retrieve the key of the status', () => { + expect(component.retrieveStatusKey(allStatuses[0].value)).toEqual(0); + }); + + it('should return null if no label is provided', () => { + expect(component.retrieveStatusKey(null)).toEqual(null); + }); + + it('should return null if the label does not exists', () => { + expect(component.retrieveStatusKey('Unexisting')).toEqual(null); + }); + }); + + describe('retrieveStatusLabel', () => { + it('should retrieve status label', () => { + expect(component.retrieveStatusLabel(1)).toEqual('Submitted'); + }); + it('should retrieve empty status label when no status is found', () => { + expect(component.retrieveStatusLabel(null)).toEqual(''); + }); + it('should return empty string if allStatus is undefined', () => { + component.allStatuses = undefined as unknown as FilterValueOptions; + expect(component.retrieveStatusLabel(1)).toEqual(''); + }); + }); + + describe('onPropertyChange', () => { + it('should update filter', () => { + component.propertyFormControl.setValue('Created at'); + component.onPropertyChange(); + expect(component.filter).toEqual({ + for: 'root', + field: 7, + operator: null, + value: null + }); + }); + + it('should not update anything if the field does not exists', () => { + component.propertyFormControl.setValue('Unexisting'); + component.onPropertyChange(); + expect(component.filter).toEqual({ + field: 1, + for: 'root', + operator: 0, + value: 'someValue' + }); + }); + }); + //TODO: security type check it('should retrieve the label', () => { component.retrieveLabel(filterDefinitions[0]); @@ -97,21 +218,11 @@ describe('FiltersDialogFilterFieldComponent', () => { ); }); - //TODO: security type check - it('should change the component filter on field change', () => { - component.onFieldChange('options-2'); - expect(component.filter).toEqual({ - field: 2, - for: 'options', - operator: 0, - value: 'someValue' - }); - }); - //TODO: security type check it('should change the operator of the filter', () => { - component.onOperatorChange('3'); - expect(component.filter.operator).toEqual(3); + component.operatorFormControl.setValue('Equal'); + component.onOperatorChange(); + expect(component.filter.operator).toEqual(0); }); //TODO: security type check @@ -160,6 +271,15 @@ describe('FiltersDialogFilterFieldComponent', () => { component.onInputChange(inputEvent); expect(component.filter.value).toEqual(94350); }); + + it('should change the filter value to status if one status is passed', () => { + const inputEvent: FilterInputOutput = { + type: 'status', + value: 'Submitted' + }; + component.onInputChange(inputEvent); + expect(component.filter.value).toEqual(1); + }); }); describe('findInput', () => { @@ -265,10 +385,7 @@ describe('FiltersDialogFilterFieldComponent', () => { expect(component.findInput(statusFilter)).toEqual({ type: 'status', value: 'myStatus', - statuses: [ - {key: 'status1', value: '2'}, - {key: 'status2', value: '2'} - ] + statuses: allStatuses }); }); @@ -283,10 +400,7 @@ describe('FiltersDialogFilterFieldComponent', () => { expect(component.findInput(statusFilter)).toEqual({ type: 'status', value: null, - statuses: [ - {key: 'status1', value: '2'}, - {key: 'status2', value: '2'} - ] + statuses: allStatuses }); }); }); @@ -400,10 +514,7 @@ describe('FiltersDialogFilterFieldComponent', () => { operator: 1, value: 'myStatus' }; - expect(component.findStatuses(statusFilter)).toEqual([ - {key: 'status1', value: '2'}, - {key: 'status2', value: '2'} - ]); + expect(component.findStatuses(statusFilter)).toEqual(allStatuses); }); it('should return an empty status list if the filter has no field', () => { @@ -446,17 +557,4 @@ describe('FiltersDialogFilterFieldComponent', () => { }; expect(component.findOperator(filter)).toEqual(new FiltersService()['filterNumberOperators']); }); - - it('should track by field', () => { - expect(component.trackByField(0, filterDefinitions[0])).toEqual('root4'); - }); - - it('should track by operator', () => { - const operator: KeyValue = { - key: 'greater than', - value: '0' - }; - - expect(component.trackByOperator(0, operator)).toBe(operator.key); - }); }); \ No newline at end of file diff --git a/src/app/components/filters/filters-dialog-filter-field.component.ts b/src/app/components/filters/filters-dialog-filter-field.component.ts index 856950141..7cdeab5fb 100644 --- a/src/app/components/filters/filters-dialog-filter-field.component.ts +++ b/src/app/components/filters/filters-dialog-filter-field.component.ts @@ -1,11 +1,15 @@ import { FilterNumberOperator } from '@aneoconsultingfr/armonik.api.angular'; -import { KeyValue, KeyValuePipe, NgFor, NgIf } from '@angular/common'; -import { Component, Input, inject } from '@angular/core'; +import { AsyncPipe, KeyValuePipe, NgFor, NgIf } from '@angular/common'; +import { Component, Input, OnInit, inject } from '@angular/core'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; +import { Observable, map, startWith } from 'rxjs'; import { DATA_FILTERS_SERVICE } from '@app/tokens/filters.token'; -import { FilterDefinition, FilterFor } from '@app/types/filter-definition'; -import { Filter, FilterInput, FilterInputOutput, FilterInputType, FilterInputValueDuration, FilterInputValueString, FilterValueOptions } from '@app/types/filters'; +import { FilterDefinition } from '@app/types/filter-definition'; +import { Filter, FilterInput, FilterInputOutput, FilterInputType, FilterInputValueDuration, FilterInputValueString, FilterValueOptions, MaybeNull } from '@app/types/filters'; import { FiltersService } from '@services/filters.service'; import { FiltersDialogInputComponent } from './filters-dialog-input.component'; @@ -16,23 +20,27 @@ import { FiltersDialogInputComponent } from './filters-dialog-input.component'; And Property - - - {{ retrieveLabel(definition) }} + + + + {{ property }} - + - + Operator - - - {{ operator.value }} + + + + {{ operator }} - + - + `, styles: [` :host { @@ -56,34 +64,162 @@ span { MatFormFieldModule, MatSelectModule, FiltersDialogInputComponent, + MatAutocompleteModule, + MatInputModule, + AsyncPipe, + FormsModule, + ReactiveFormsModule ], providers: [ FiltersService, ], }) -export class FiltersDialogFilterFieldComponent { +export class FiltersDialogFilterFieldComponent implements OnInit { @Input({ required: true }) first: boolean; @Input({ required: true }) filter: Filter; + allProperties: FilterDefinition[]; + propertyFormControl: FormControl; + filteredProperties: Observable; + + allOperators: Record; + operatorFormControl: FormControl; + filteredOperators: Observable; + + allStatuses: FilterValueOptions; + statusFormControl: FormControl; + filteredStatuses: Observable; + #filtersService = inject(FiltersService); #dataFiltersService = inject(DATA_FILTERS_SERVICE); - get filtersDefinitions() { - return this.#dataFiltersService.retrieveFiltersDefinitions(); + ngOnInit(): void { + // Property form handling + this.propertyFormControl = new FormControl(this.columnValue); + this.allProperties = this.#dataFiltersService.retrieveFiltersDefinitions(); + this.filteredProperties = this.propertyFormControl.valueChanges.pipe( + startWith(''), + map(value => this._filterProperties(value)) + ); + + // Operator form handling + this.allOperators = this.findOperator(this.filter); + this.operatorFormControl = new FormControl(this.retrieveOperatorLabel(this.filter.operator)); + this.filteredOperators = this.operatorFormControl.valueChanges.pipe( + startWith(''), + map(value => this._filterOperators(value)) + ); + + // Statuses form handling + this.allStatuses = this.findStatuses(this.filter); + this.statusFormControl = new FormControl(this.retrieveStatusLabel(this.filter.value as MaybeNull)); + this.filteredStatuses = this.statusFormControl.valueChanges.pipe( + startWith(''), + map(value => this._filterStatuses(value)) + ); + } + + private _filterProperties(value: MaybeNull): string[] { + const labelledProperties = this.allProperties.map(property => this.retrieveLabel(property)); + if (value === null) { + return labelledProperties; + } else { + const filterValue = value.toLowerCase(); + return labelledProperties.filter(label => label.toLowerCase().includes(filterValue)); + } + } + + private _filterOperators(value: MaybeNull): string[] { + const labelledOperators = Object.values(this.allOperators); + if (value === null) { + return labelledOperators; + } else { + const filterValue = value.toLowerCase(); + return labelledOperators.filter(operator => operator.toLowerCase().includes(filterValue)); + } + } + + private _filterStatuses(value: MaybeNull): string[] { + const labelledStatuses = Object.values(this.allStatuses).map(status => status.value); + if (value === null) { + return labelledStatuses; + } else { + const filterValue = value.toLowerCase(); + return labelledStatuses.filter(status => status.toLowerCase().includes(filterValue)); + } + } + + get columnValue() { + return this.filter.field && this.filter.for ? this.#dataFiltersService.retrieveLabel(this.filter.for, this.filter.field) : ''; } retrieveLabel(filterDefinition: FilterDefinition) { return this.#dataFiltersService.retrieveLabel(filterDefinition.for, filterDefinition.field); } - onFieldChange(event: string) { - const [for_, key] = event.split('-'); - this.filter.for = for_ as FilterFor; - this.filter.field = Number(key) as T | U; + retrieveOperatorLabel(operator: MaybeNull): string { + return operator !== null ? this.allOperators[operator] : ''; } - onOperatorChange(event: string) { - this.filter.operator = Number(event); + retrieveStatusLabel(status: MaybeNull): string { + if (this.allStatuses !== undefined) { + const foundStatus = status ? this.allStatuses[status as number] : undefined; + if (foundStatus !== undefined) { + return foundStatus.value; + } else { + return ''; + } + } + return ''; + } + + retrieveOperatorKey(operator: string) { + const labelledOperators = Object.values(this.allOperators); + const value = labelledOperators.find(label => label.toLowerCase() === operator.toLowerCase()); + return Object.keys(this.allOperators).filter(key => this.allOperators[Number(key)] === value); + } + + retrieveStatusKey(status: MaybeNull) { + if (!status) { + return null; + } + const key = this.allStatuses.find(label => label.value.toLowerCase() === status.toLowerCase())?.key; + return key !== undefined ? key : null; + } + + onPropertyChange() { + const formValue = this.propertyFormControl.value; + if (formValue) { + const field = this.#dataFiltersService.retrieveField(formValue); + + if (field === -1) { + return; + } + + const for_ = this.allProperties.find(value => value.field === field)?.for; + if (!for_) { + return; + } + + this.filter.for = for_; + this.filter.field = field as T | U; + + this.allOperators = this.findOperator(this.filter); + this.operatorFormControl.setValue(''); + this.filter.operator = null; + + this.allStatuses = this.findStatuses(this.filter); + this.statusFormControl.setValue(''); + this.filter.value = null; + } + } + + onOperatorChange() { + const formValue = this.operatorFormControl.value; + if (formValue) { + const key = this.retrieveOperatorKey(formValue); + this.filter.operator = key !== undefined ? Number(key) : null; + } } onInputChange(event: FilterInputOutput) { @@ -97,8 +233,12 @@ export class FiltersDialogFilterFieldComponent) { - return definition.for + definition.field; - } - - trackByOperator(_: number, operator: KeyValue) { - return operator.key; - } - #findFilterMetadata(filter: Filter): FilterDefinition | null { return this.#dataFiltersService.retrieveFiltersDefinitions().find(f => f.for === filter.for && f.field === filter.field) ?? null; } diff --git a/src/app/components/filters/filters-dialog-input.component.spec.ts b/src/app/components/filters/filters-dialog-input.component.spec.ts index a3acd90f3..cac246dbd 100644 --- a/src/app/components/filters/filters-dialog-input.component.spec.ts +++ b/src/app/components/filters/filters-dialog-input.component.spec.ts @@ -1,4 +1,6 @@ // eslint-disable-next-line import/no-unresolved +import { FormControl } from '@angular/forms'; +// eslint-disable-next-line import/no-unresolved import { NgxMatDatepickerInputEvent } from '@angular-material-components/datetime-picker/lib/datepicker-input-base'; import { FiltersDialogInputComponent } from './filters-dialog-input.component'; @@ -8,6 +10,7 @@ describe('FiltersDialogInputComponent', () => { type: 'string', value: 'someValue' }; + component.statusFormControl = new FormControl(); const valueChangeSpy = jest.spyOn(component.valueChange, 'emit'); it('should run', () => { @@ -67,10 +70,10 @@ describe('FiltersDialogInputComponent', () => { it('should emit on status change', () => { const event = 'newStatus'; - - component.onStatusChange(event); + component.statusFormControl.setValue(event); + component.onStatusChange(); expect(valueChangeSpy).toHaveBeenCalledWith({ - type: 'string', + type: 'status', value: event }); }); diff --git a/src/app/components/filters/filters-dialog-input.component.ts b/src/app/components/filters/filters-dialog-input.component.ts index 131f2ccac..edaa04525 100644 --- a/src/app/components/filters/filters-dialog-input.component.ts +++ b/src/app/components/filters/filters-dialog-input.component.ts @@ -1,11 +1,13 @@ -import { NgFor, NgIf } from '@angular/common'; +import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { MatSelectModule } from '@angular/material/select'; import { NgxMatDatetimePickerModule, NgxMatNativeDateModule, NgxMatTimepickerModule } from '@angular-material-components/datetime-picker'; // eslint-disable-next-line import/no-unresolved import { NgxMatDatepickerInputEvent } from '@angular-material-components/datetime-picker/lib/datepicker-input-base'; +import { Observable } from 'rxjs'; import { FilterInput, FilterInputOutput, FilterInputType } from '@app/types/filters'; @@ -38,9 +40,10 @@ import { FilterInput, FilterInputOutput, FilterInputType } from '@app/types/filt Value - - {{ option.value }} - + + + {{ option }} + @@ -60,14 +63,18 @@ mat-form-field { NgFor, MatFormFieldModule, MatInputModule, - MatSelectModule, NgxMatTimepickerModule, NgxMatDatetimePickerModule, NgxMatNativeDateModule, + AsyncPipe, + MatAutocompleteModule, + ReactiveFormsModule ], }) export class FiltersDialogInputComponent { @Input({ required: true }) input: FilterInput; + @Input({ required: true }) statusFormControl: FormControl; + @Input({ required: true }) filteredStatuses: Observable; // Maybe we will need to emit the type of value in order to be able to correctly handle the value. // Créer des types en fonction du type de champ @@ -98,10 +105,12 @@ export class FiltersDialogInputComponent { }); } - onStatusChange(event: string): void { + onStatusChange(): void { + const formValue = this.statusFormControl.value; + this.valueChange.emit({ - type: 'string', - value: event, + type: 'status', + value: formValue ? formValue : null, }); } diff --git a/src/app/partitions/services/partitions-filters.service.ts b/src/app/partitions/services/partitions-filters.service.ts index da949f60b..ae1b5bb3f 100644 --- a/src/app/partitions/services/partitions-filters.service.ts +++ b/src/app/partitions/services/partitions-filters.service.ts @@ -86,4 +86,9 @@ export class PartitionsFiltersService { throw new Error(`Unknown filter type: ${filterFor} ${filterField}}`); } } + + retrieveField(filterField: string): PartitionFilterField { + const values = Object.values(this.#rootField); + return values.findIndex(value => value.toLowerCase() === filterField.toLowerCase()); + } } diff --git a/src/app/results/services/results-filters.service.ts b/src/app/results/services/results-filters.service.ts index 36efcdda7..081409a87 100644 --- a/src/app/results/services/results-filters.service.ts +++ b/src/app/results/services/results-filters.service.ts @@ -100,4 +100,9 @@ export class ResultsFiltersService { throw new Error(`Unknown filter type: ${filterFor} ${filterField}}`); } } + + retrieveField(filterField: string): ResultFilterField { + const values = Object.values(this.#rootField); + return values.findIndex(value => value.toLowerCase() === filterField.toLowerCase()); + } } diff --git a/src/app/sessions/services/sessions-filters.service.ts b/src/app/sessions/services/sessions-filters.service.ts index 8f03c6d9d..7b723ddd6 100644 --- a/src/app/sessions/services/sessions-filters.service.ts +++ b/src/app/sessions/services/sessions-filters.service.ts @@ -145,4 +145,9 @@ export class SessionsFiltersService { throw new Error(`Unknown filter type: ${filterFor} ${filterField}}`); } } + + retrieveField(filterField: string): SessionFilterField { + const values = Object.values(this.#rootField); + return values.findIndex(value => value.toLowerCase() === filterField.toLowerCase()); + } } diff --git a/src/app/tasks/services/tasks-filters.service.ts b/src/app/tasks/services/tasks-filters.service.ts index 38f7a74d1..02f7ba54c 100644 --- a/src/app/tasks/services/tasks-filters.service.ts +++ b/src/app/tasks/services/tasks-filters.service.ts @@ -192,4 +192,9 @@ export class TasksFiltersService { throw new Error(`Unknown filter type: ${filterFor} ${filterField}`); } } + + retrieveField(filterField: string): TaskFilterField { + const values = Object.values(this.#rootField); + return values.findIndex(value => value.toLowerCase() === filterField.toLowerCase()); + } } diff --git a/src/app/types/filter-definition.ts b/src/app/types/filter-definition.ts index 2cdd56d9f..cdb48d412 100644 --- a/src/app/types/filter-definition.ts +++ b/src/app/types/filter-definition.ts @@ -96,4 +96,5 @@ export abstract class DataFilterService { abstract saveFilters(filters: FiltersAnd[]): void; abstract restoreFilters(): FiltersAnd[]; abstract resetFilters(): FiltersAnd[]; + abstract retrieveField(filterField: string): U; } \ No newline at end of file diff --git a/src/app/types/filters.ts b/src/app/types/filters.ts index 32652daee..fdff5eabc 100644 --- a/src/app/types/filters.ts +++ b/src/app/types/filters.ts @@ -105,9 +105,13 @@ export interface FilterInputOutputDate { value: MaybeNull; } +export interface FilterInputOutputStatus { + type: 'status'; + value: MaybeNull; +} export interface FilterInputOutputDuration { type: 'duration'; value: MaybeNull } -export type FilterInputOutput = FilterInputOutputString | FilterInputOutputNumber | FilterInputOutputDate | FilterInputOutputDuration; +export type FilterInputOutput = FilterInputOutputString | FilterInputOutputNumber | FilterInputOutputDate | FilterInputOutputDuration | FilterInputOutputStatus;