From bc713018d183ea2ac9f319761132b3656aec69e7 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Wed, 10 Jan 2024 18:49:28 +0100 Subject: [PATCH 01/11] feat(filter): enable multi-selection filter --- .../notes-manager/notes-manager.component.ts | 12 +- .../configurable-enum.service.ts | 5 +- .../enum-dropdown/enum-dropdown.component.ts | 2 +- .../date-range-filter-panel.component.html | 2 +- .../date-range-filter-panel.component.spec.ts | 11 +- .../date-range-filter-panel.component.ts | 16 +- .../date-range-filter.component.spec.ts | 27 +-- .../date-range-filter.component.ts | 17 +- .../default-interaction-types.ts | 4 - src/app/core/entity-list/EntityListConfig.ts | 4 +- src/app/core/entity/model/entity.ts | 2 +- .../filter-generator.service.spec.ts | 48 ++--- .../filter-generator.service.ts | 15 +- src/app/core/filter/filter.service.ts | 20 +- .../core/filter/filter/filter.component.html | 2 +- .../filter/filter/filter.component.spec.ts | 18 +- .../core/filter/filter/filter.component.ts | 28 ++- src/app/core/filter/filters/booleanFilter.ts | 29 +++ .../filter/filters/configurableEnumFilter.ts | 25 +++ src/app/core/filter/filters/dateFilter.ts | 72 +++++++ src/app/core/filter/filters/entityFilter.ts | 22 ++ src/app/core/filter/filters/filters.spec.ts | 52 ++--- src/app/core/filter/filters/filters.ts | 188 ++---------------- .../list-filter/list-filter.component.html | 13 +- .../list-filter/list-filter.component.spec.ts | 2 +- .../list-filter/list-filter.component.ts | 23 +-- .../todos/todo-list/todo-list.component.ts | 3 +- 27 files changed, 340 insertions(+), 322 deletions(-) create mode 100644 src/app/core/filter/filters/booleanFilter.ts create mode 100644 src/app/core/filter/filters/configurableEnumFilter.ts create mode 100644 src/app/core/filter/filters/dateFilter.ts create mode 100644 src/app/core/filter/filters/entityFilter.ts diff --git a/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts b/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts index 972167ac4f..cfd4932806 100644 --- a/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts +++ b/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts @@ -22,6 +22,7 @@ import { Angulartics2Module } from "angulartics2"; import { MatMenuModule } from "@angular/material/menu"; import { FaDynamicIconComponent } from "../../../core/common-components/fa-dynamic-icon/fa-dynamic-icon.component"; import { RouteTarget } from "../../../route-target"; +import { DataFilter } from "../../../core/common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; /** * additional config specifically for NotesManagerComponent @@ -63,28 +64,27 @@ export class NotesManagerComponent implements OnInit { { key: "urgent", label: $localize`:Filter-option for notes:Urgent`, - filter: { "warningLevel.id": WarningLevel.URGENT }, + filter: { "warningLevel.id": WarningLevel.URGENT } as DataFilter, }, { key: "follow-up", label: $localize`:Filter-option for notes:Needs Follow-Up`, filter: { - "warningLevel.id": { $in: [WarningLevel.URGENT, WarningLevel.WARNING] }, - }, + "warningLevel.id": { $in: [WarningLevel.WARNING] }, + } as DataFilter, }, - { key: "", label: $localize`All`, filter: {} }, ]; private dateFS: FilterSelectionOption[] = [ { key: "current-week", label: $localize`:Filter-option for notes:This Week`, - filter: { date: this.getWeeksFilter(0) }, + filter: { date: this.getWeeksFilter(0) } as DataFilter, }, { key: "last-week", label: $localize`:Filter-option for notes:Since Last Week`, - filter: { date: this.getWeeksFilter(1) }, + filter: { date: this.getWeeksFilter(1) } as DataFilter, }, { key: "", label: $localize`All`, filter: {} }, ]; diff --git a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts index 169ff72cbb..5a945d42d3 100644 --- a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts +++ b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts @@ -30,10 +30,11 @@ export class ConfigurableEnumService { getEnumValues( id: string, ): T[] { - return this.getEnum(id).values as T[]; + let configurableEnum = this.getEnum(id); + return configurableEnum == null ? [] : (configurableEnum.values as T[]); } - getEnum(id: string): ConfigurableEnum { + getEnum(id: string): ConfigurableEnum | null { if (!this.enums) { return; } diff --git a/src/app/core/basic-datatypes/configurable-enum/enum-dropdown/enum-dropdown.component.ts b/src/app/core/basic-datatypes/configurable-enum/enum-dropdown/enum-dropdown.component.ts index 0a71e6821d..cdf6b647d9 100644 --- a/src/app/core/basic-datatypes/configurable-enum/enum-dropdown/enum-dropdown.component.ts +++ b/src/app/core/basic-datatypes/configurable-enum/enum-dropdown/enum-dropdown.component.ts @@ -63,7 +63,7 @@ export class EnumDropdownComponent implements OnChanges { if (changes.hasOwnProperty("enumId") || changes.hasOwnProperty("form")) { this.invalidOptions = this.prepareInvalidOptions(); } - this.options = [...this.enumEntity.values, ...this.invalidOptions]; + this.options = [...this.enumEntity?.values, ...this.invalidOptions]; } private prepareInvalidOptions(): ConfigurableEnumValue[] { diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html index b0bf659070..14cbed0aba 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html @@ -18,7 +18,7 @@ (mouseenter)="preselectAllRange()" (mouseleave)="unselectRange()" (click)="selectRangeAndClose('all')" - [class.selected-option]="filter.selectedOption === '_'" + [class.selected-option]="filter.selectedOptionsKeys.length === 0" > All diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts index b63dc2ae5f..414a10069a 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts @@ -12,7 +12,8 @@ import { HarnessLoader } from "@angular/cdk/testing"; import { DateRange } from "@angular/material/datepicker"; import { MatCalendarHarness } from "@angular/material/datepicker/testing"; import moment from "moment"; -import { DateFilter } from "../../../../filter/filters/filters"; + +import { DateFilter } from "../../../../filter/filters/dateFilter"; describe("DateRangeFilterPanelComponent", () => { let component: DateRangeFilterPanelComponent; @@ -22,7 +23,7 @@ describe("DateRangeFilterPanelComponent", () => { beforeEach(async () => { dateFilter = new DateFilter("test", "Test", defaultDateFilters); - dateFilter.selectedOption = "1"; + dateFilter.selectedOptionsKeys = ["1"]; jasmine.clock().mockDate(moment("2023-04-08").toDate()); await TestBed.configureTestingModule({ imports: [MatNativeDateModule], @@ -85,7 +86,7 @@ describe("DateRangeFilterPanelComponent", () => { moment("2023-04-08").startOf("day").toDate(), ); expect(filterRange.end).toEqual(moment("2023-04-08").endOf("day").toDate()); - expect(dateFilter.selectedOption).toBe("0"); + expect(dateFilter.selectedOptionsKeys).toEqual(["0"]); }); it("should highlight the date range when hovering over a option", async () => { @@ -114,9 +115,9 @@ describe("DateRangeFilterPanelComponent", () => { } }); - it("should return '_' as filter.selectedOption when 'all' option has been chosen", async () => { + it("should return empty array as filter.selectedOption when 'all' option has been chosen", async () => { component.selectRangeAndClose("all"); - expect(dateFilter.selectedOption).toEqual("_"); + expect(dateFilter.selectedOptionsKeys).toEqual([]); }); it("should correctly calculate date ranges based on the config", () => { diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts index 1764e848b8..10cb51c6b4 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts @@ -1,10 +1,10 @@ import { Component, Inject } from "@angular/core"; import { DateRange, + MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER, MatDatepickerModule, MatDateSelectionModel, MatRangeDateSelectionModel, - MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER, } from "@angular/material/datepicker"; import { MAT_DIALOG_DATA, @@ -16,8 +16,8 @@ import { NgForOf } from "@angular/common"; import { DateRangeFilterConfigOption } from "../../../../entity-list/EntityListConfig"; import moment from "moment"; import { FormsModule } from "@angular/forms"; -import { DateFilter } from "../../../../filter/filters/filters"; import { dateToString } from "../../../../../utils/utils"; +import { DateFilter } from "../../../../filter/filters/dateFilter"; export const defaultDateFilters: DateRangeFilterConfigOption[] = [ { @@ -91,9 +91,9 @@ export class DateRangeFilterPanelComponent { selectRangeAndClose(index: number | "all"): void { if (typeof index === "number") { - this.filter.selectedOption = index.toString(); + this.filter.selectedOptionsKeys = [index.toString()]; } else { - this.filter.selectedOption = "_"; + this.filter.selectedOptionsKeys = []; } this.dialogRef.close(); } @@ -102,11 +102,11 @@ export class DateRangeFilterPanelComponent { if (!this.selectedRangeValue?.start || this.selectedRangeValue?.end) { this.selectedRangeValue = new DateRange(selectedDate, null); } else { - const start = this.selectedRangeValue.start; - this.filter.selectedOption = + const start: Date = this.selectedRangeValue.start; + this.filter.selectedOptionsKeys = start < selectedDate - ? dateToString(start) + "_" + dateToString(selectedDate) - : dateToString(selectedDate) + "_" + dateToString(start); + ? [dateToString(start), dateToString(selectedDate)] + : [dateToString(selectedDate), dateToString(start)]; this.dialogRef.close(); } } diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts index 69b266e69c..25b1e9716e 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts @@ -4,9 +4,9 @@ import { DateRangeFilterComponent } from "./date-range-filter.component"; import { MatDialog } from "@angular/material/dialog"; import { MatNativeDateModule } from "@angular/material/core"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; -import { DateFilter } from "../../../filter/filters/filters"; import { defaultDateFilters } from "./date-range-filter-panel/date-range-filter-panel.component"; import moment from "moment"; +import { DateFilter } from "../../../filter/filters/dateFilter"; describe("DateRangeFilterComponent", () => { let component: DateRangeFilterComponent; @@ -30,12 +30,12 @@ describe("DateRangeFilterComponent", () => { it("should set the correct date filter when a new option is selected", () => { const dateFilter = new DateFilter("test", "Test", defaultDateFilters); - dateFilter.selectedOption = "9"; + dateFilter.selectedOptionsKeys = ["9"]; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); jasmine.clock().mockDate(moment("2023-05-18").toDate()); - dateFilter.selectedOption = "0"; + dateFilter.selectedOptionsKeys = ["0"]; component.filterConfig = dateFilter; let expectedDataFilter = { test: { @@ -45,7 +45,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDataFilter); - dateFilter.selectedOption = "1"; + dateFilter.selectedOptionsKeys = ["1"]; component.filterConfig = dateFilter; expectedDataFilter = { test: { @@ -55,7 +55,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDataFilter); - dateFilter.selectedOption = "_"; + dateFilter.selectedOptionsKeys = []; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); jasmine.clock().uninstall(); @@ -64,15 +64,15 @@ describe("DateRangeFilterComponent", () => { it("should set the correct date filter when inputting a specific date range via the URL", () => { let dateFilter = new DateFilter("test", "test", []); - dateFilter.selectedOption = "1_2_3"; + dateFilter.selectedOptionsKeys = ["1_2_3"]; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); - dateFilter.selectedOption = "_"; + dateFilter.selectedOptionsKeys = []; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); - dateFilter.selectedOption = "2022-9-18_"; + dateFilter.selectedOptionsKeys = ["2022-9-18", ""]; component.filterConfig = dateFilter; let testFilter: { $gte?: string; $lte?: string } = { $gte: "2022-09-18" }; let expectedDateFilter = { @@ -80,7 +80,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDateFilter); - dateFilter.selectedOption = "_2023-01-3"; + dateFilter.selectedOptionsKeys = ["", "2023-01-3"]; component.filterConfig = dateFilter; testFilter = { $lte: "2023-01-03" }; expectedDateFilter = { @@ -88,7 +88,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDateFilter); - dateFilter.selectedOption = "2022-9-18_2023-01-3"; + dateFilter.selectedOptionsKeys = ["2022-9-18", "2023-01-3"]; component.filterConfig = dateFilter; testFilter = { $gte: "2022-09-18", @@ -107,9 +107,10 @@ describe("DateRangeFilterComponent", () => { component.dateChangedManually(); - expect(component.dateFilter.selectedOption).toEqual( - "2021-10-28_2024-02-12", - ); + expect(component.dateFilter.selectedOptionsKeys).toEqual([ + "2021-10-28", + "2024-02-12", + ]); let expectedDataFilter = { test: { $gte: "2021-10-28", diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts index be2a4a83ef..874fc63b8d 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts @@ -1,12 +1,13 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; import { MatDialog } from "@angular/material/dialog"; import { Entity } from "../../../entity/model/entity"; -import { DateFilter, Filter } from "../../../filter/filters/filters"; +import { Filter } from "../../../filter/filters/filters"; import { DateRangeFilterPanelComponent } from "./date-range-filter-panel/date-range-filter-panel.component"; import { MatFormFieldModule } from "@angular/material/form-field"; import { MatDatepickerModule } from "@angular/material/datepicker"; import { FormsModule } from "@angular/forms"; import { dateToString, isValidDate } from "../../../../utils/utils"; +import { DateFilter } from "../../../filter/filters/dateFilter"; @Component({ selector: "app-date-range-filter", @@ -20,7 +21,7 @@ export class DateRangeFilterComponent { toDate: Date; dateFilter: DateFilter; - @Output() selectedOptionChange = new EventEmitter(); + @Output() selectedOptionChange = new EventEmitter(); @Input() set filterConfig(value: Filter) { this.dateFilter = value as DateFilter; @@ -37,16 +38,16 @@ export class DateRangeFilterComponent { ) { this.fromDate = range.start; this.toDate = range.end; - this.selectedOptionChange.emit(this.dateFilter.selectedOption); + this.selectedOptionChange.emit(this.dateFilter.selectedOptionsKeys); } } dateChangedManually() { - this.dateFilter.selectedOption = - (isValidDate(this.fromDate) ? dateToString(this.fromDate) : "") + - "_" + - (isValidDate(this.toDate) ? dateToString(this.toDate) : ""); - this.selectedOptionChange.emit(this.dateFilter.selectedOption); + this.dateFilter.selectedOptionsKeys = [ + isValidDate(this.fromDate) ? dateToString(this.fromDate) : "", + isValidDate(this.toDate) ? dateToString(this.toDate) : "", + ]; + this.selectedOptionChange.emit(this.dateFilter.selectedOptionsKeys); } openDialog(e: Event) { diff --git a/src/app/core/config/default-config/default-interaction-types.ts b/src/app/core/config/default-config/default-interaction-types.ts index ad8ddfee3f..44bc8350d9 100644 --- a/src/app/core/config/default-config/default-interaction-types.ts +++ b/src/app/core/config/default-config/default-interaction-types.ts @@ -1,10 +1,6 @@ import { InteractionType } from "../../../child-dev-project/notes/model/interaction-type.interface"; export const defaultInteractionTypes: InteractionType[] = [ - { - id: "", - label: "", - }, { id: "VISIT", label: $localize`:Interaction type/Category of a Note:Home Visit`, diff --git a/src/app/core/entity-list/EntityListConfig.ts b/src/app/core/entity-list/EntityListConfig.ts index 0491f51340..8b6b6c65d4 100644 --- a/src/app/core/entity-list/EntityListConfig.ts +++ b/src/app/core/entity-list/EntityListConfig.ts @@ -23,7 +23,7 @@ export interface EntityListConfig { /** * Optional config for which columns are displayed. - * By default all columns are shown + * By default, all columns are shown */ columnGroups?: ColumnGroupsConfig; @@ -55,7 +55,7 @@ export interface ColumnGroupsConfig { default?: string; /** - * The name of the group group that should be selected by default on a mobile device. + * The name of the group that should be selected by default on a mobile device. * Default is the name of the first group. */ mobile?: string; diff --git a/src/app/core/entity/model/entity.ts b/src/app/core/entity/model/entity.ts index 1a05718d53..ae0ee720fd 100644 --- a/src/app/core/entity/model/entity.ts +++ b/src/app/core/entity/model/entity.ts @@ -66,7 +66,7 @@ export class Entity { /** * True if this type's schema has been customized dynamically from the config. */ - static _isCustomizedType?: boolean; + static _isCustomizedType?: boolean; // todo should be private or renamed to "isCustomizedType" /** * Defining which attribute values of an entity should be shown in the `.toString()` method. diff --git a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts index e39895f970..75d2af2491 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts @@ -14,15 +14,12 @@ import { Child } from "../../../child-dev-project/children/model/child"; import moment from "moment"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { FilterService } from "../filter.service"; -import { - BooleanFilter, - ConfigurableEnumFilter, - DateFilter, - EntityFilter, - FilterSelectionOption, - SelectableFilter, -} from "../filters/filters"; +import { FilterSelectionOption, SelectableFilter } from "../filters/filters"; import { Entity } from "../../entity/model/entity"; +import { DateFilter } from "../filters/dateFilter"; +import { BooleanFilter } from "../filters/booleanFilter"; +import { ConfigurableEnumFilter } from "../filters/configurableEnumFilter"; +import { EntityFilter } from "../filters/entityFilter"; describe("FilterGeneratorService", () => { let service: FilterGeneratorService; @@ -61,7 +58,6 @@ describe("FilterGeneratorService", () => { return { key: option.key, label: option.label }; }), ).toEqual([ - { key: "all", label: "All" }, { key: "true", label: "Private" }, { key: "false", label: "Government" }, ]); @@ -71,9 +67,6 @@ describe("FilterGeneratorService", () => { const interactionTypes = defaultInteractionTypes.map((it) => jasmine.objectContaining({ key: it.id, label: it.label }), ); - interactionTypes.push( - jasmine.objectContaining({ key: "all", label: "All" }), - ); const schema = Note.schema.get("category"); let filterOptions = ( @@ -133,10 +126,9 @@ describe("FilterGeneratorService", () => { defaultInteractionTypes[2], ]; - // indices are increased by one as first option is "all" + expect(filter([note], filterOptions.options[1])).toEqual([note]); expect(filter([note], filterOptions.options[2])).toEqual([note]); - expect(filter([note], filterOptions.options[3])).toEqual([note]); - expect(filter([note], filterOptions.options[4])).toEqual([]); + expect(filter([note], filterOptions.options[3])).toEqual([]); Note.schema.delete("otherEnum"); }); @@ -164,17 +156,17 @@ describe("FilterGeneratorService", () => { expect(filterOptions.label).toEqual(schema.label); expect(filterOptions.name).toEqual("schoolId"); const allRelations = [csr1, csr2, csr3, csr4]; - const allFilter = filterOptions.options.find((opt) => opt.key === "all"); - expect(allFilter.label).toEqual("All"); - expect(filter(allRelations, allFilter)).toEqual(allRelations); - const school1Filter = filterOptions.options.find( - (opt) => opt.key === school1.getId(), - ); + // const allFilter: FilterSelectionOption = filterOptions.options.find( + // (opt) => opt.key === "all", + // ); + // expect(allFilter.label).toEqual("All"); + // expect(filter(allRelations, allFilter)).toEqual(allRelations); + const school1Filter: FilterSelectionOption = + filterOptions.options.find((opt) => opt.key === school1.getId()); expect(school1Filter.label).toEqual(school1.name); expect(filter(allRelations, school1Filter)).toEqual([csr1, csr4]); - const school2Filter = filterOptions.options.find( - (opt) => opt.key === school2.getId(), - ); + const school2Filter: FilterSelectionOption = + filterOptions.options.find((opt) => opt.key === school2.getId()); expect(school2Filter.label).toEqual(school2.name); expect(filter(allRelations, school2Filter)).toEqual([csr2, csr3]); }); @@ -203,7 +195,6 @@ describe("FilterGeneratorService", () => { }); expect(comparableOptions).toEqual( jasmine.arrayWithExactContents([ - { key: "", label: "All" }, { key: "muslim", label: "muslim" }, { key: "christian", label: "christian" }, ]), @@ -218,7 +209,6 @@ describe("FilterGeneratorService", () => { label: "Date", default: "today", options: [ - { key: "", label: "All", filter: {} }, { key: "today", label: "Today", @@ -239,15 +229,15 @@ describe("FilterGeneratorService", () => { expect(filterOptions.label).toEqual(prebuiltFilter.label); expect(filterOptions.name).toEqual(prebuiltFilter.id); expect(filterOptions.options).toEqual(prebuiltFilter.options); - expect(filterOptions.selectedOption).toEqual(prebuiltFilter.default); + expect(filterOptions.selectedOptionsKeys).toEqual([prebuiltFilter.default]); const todayNote = new Note(); todayNote.date = new Date(); const yesterdayNote = new Note(); const notes = [todayNote, yesterdayNote]; yesterdayNote.date = moment().subtract(1, "day").toDate(); - const allFilter = filterOptions.options.find((f) => f.key === ""); - expect(filter(notes, allFilter)).toEqual(notes); + // const allFilter = filterOptions.options.find((f) => f.key === ""); + // expect(filter(notes, allFilter)).toEqual(notes); const todayFilter = filterOptions.options.find((f) => f.key === "today"); expect(filter(notes, todayFilter)).toEqual([todayNote]); const beforeFilter = filterOptions.options.find((f) => f.key === "before"); diff --git a/src/app/core/filter/filter-generator/filter-generator.service.ts b/src/app/core/filter/filter-generator/filter-generator.service.ts index a07e83e514..962bbc74a5 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.ts @@ -1,10 +1,7 @@ import { Injectable } from "@angular/core"; import { - BooleanFilter, - ConfigurableEnumFilter, - DateFilter, - EntityFilter, Filter, + FilterSelectionOption, SelectableFilter, } from "../filters/filters"; import { @@ -21,6 +18,10 @@ import { FilterService } from "../filter.service"; import { defaultDateFilters } from "../../basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component"; import { EntitySchemaService } from "../../entity/schema/entity-schema.service"; import { DateDatatype } from "../../basic-datatypes/date/date.datatype"; +import { DateFilter } from "../filters/dateFilter"; +import { BooleanFilter } from "../filters/booleanFilter"; +import { ConfigurableEnumFilter } from "../filters/configurableEnumFilter"; +import { EntityFilter } from "../filters/entityFilter"; @Injectable({ providedIn: "root", @@ -99,7 +100,9 @@ export class FilterGeneratorService { ); } else { const options = [...new Set(data.map((c) => c[filterConfig.id]))]; - const fSO = SelectableFilter.generateOptions(options, filterConfig.id); + const fSO: FilterSelectionOption[] = + SelectableFilter.generateOptions(options, filterConfig.id); + filter = new SelectableFilter( filterConfig.id, fSO, @@ -108,7 +111,7 @@ export class FilterGeneratorService { } if (filterConfig.hasOwnProperty("default")) { - filter.selectedOption = filterConfig.default; + filter.selectedOptionsKeys = [filterConfig.default]; } if (filter instanceof SelectableFilter) { diff --git a/src/app/core/filter/filter.service.ts b/src/app/core/filter/filter.service.ts index 178652f229..e9c3fa44d6 100644 --- a/src/app/core/filter/filter.service.ts +++ b/src/app/core/filter/filter.service.ts @@ -11,6 +11,7 @@ import { } from "@ucast/mongo2js"; import moment from "moment"; import { ConfigurableEnumService } from "../basic-datatypes/configurable-enum/configurable-enum.service"; +import { Filter as EntityFilter } from "./filters/filters"; /** * Utility service to help handling and aligning filters with entities. @@ -27,6 +28,22 @@ export class FilterService { constructor(private enumService: ConfigurableEnumService) {} + combineFilters( + entityFilters: EntityFilter[], + ): DataFilter { + if (entityFilters.length === 0) { + return {} as DataFilter; + } + + return { + $and: [ + ...entityFilters.map((value: EntityFilter): DataFilter => { + return value.getFilter(); + }), + ], + } as unknown as DataFilter; + } + /** * Builds a predicate for a given filter object. * This predicate can be used to filter arrays of objects. @@ -36,6 +53,7 @@ export class FilterService { * ``` * @param filter a valid filter object, e.g. as provided by the `FilterComponent` */ + // todo: check usage for array usage (typing not working) getFilterPredicate(filter: DataFilter) { return this.filterFactory(filter); } @@ -44,7 +62,7 @@ export class FilterService { * Patches an entity with values required to pass the filter query. * This patch happens in-place. * @param entity the entity to be patched - * @param filter the filter which the entity should pass afterwards + * @param filter the filter which the entity should pass afterward */ alignEntityWithFilter(entity: T, filter: DataFilter) { const schema = entity.getSchema(); diff --git a/src/app/core/filter/filter/filter.component.html b/src/app/core/filter/filter/filter.component.html index 50977eb8c8..4c63f19492 100644 --- a/src/app/core/filter/filter/filter.component.html +++ b/src/app/core/filter/filter/filter.component.html @@ -2,7 +2,7 @@ { it("should set up category filter from configurable enum", async () => { component.entityType = Note; - const t1 = defaultInteractionTypes[1]; + const t1 = defaultInteractionTypes[0]; const n1 = new Note(); n1.category = t1; const n2 = new Note(); - n2.category = defaultInteractionTypes[2]; + n2.category = defaultInteractionTypes[1]; component.entities = [n1, n2]; component.onlyShowRelevantFilterOptions = true; component.filterConfig = [{ id: "category" }]; @@ -44,14 +44,20 @@ describe("FilterComponent", () => { const selection = await loader.getHarness(MatSelectHarness); await selection.open(); const options = await selection.getOptions(); - expect(options).toHaveSize(3); + expect(options).toHaveSize(2); - const selectedOption = await options[1].getText(); + const selectedOption = await options[0].getText(); expect(selectedOption).toEqual(t1.label); - await options[1].click(); + await options[0].click(); const selected = await selection.getValueText(); expect(selected).toEqual(t1.label); - expect(component.filterObj).toEqual({ "category.id": t1.id } as any); + expect(component.filterObj).toEqual({ + $and: [ + { + $or: [{ "category.id": t1.id }], + }, + ], + } as any); }); }); diff --git a/src/app/core/filter/filter/filter.component.ts b/src/app/core/filter/filter/filter.component.ts index 710298369f..37554476e7 100644 --- a/src/app/core/filter/filter/filter.component.ts +++ b/src/app/core/filter/filter/filter.component.ts @@ -11,12 +11,13 @@ import { Entity, EntityConstructor } from "../../entity/model/entity"; import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; import { FilterGeneratorService } from "../filter-generator/filter-generator.service"; import { ActivatedRoute, Params, Router } from "@angular/router"; -import { getUrlWithoutParams } from "../../../utils/utils"; import { ListFilterComponent } from "../list-filter/list-filter.component"; -import { NgForOf, NgIf } from "@angular/common"; +import { AsyncPipe, JsonPipe, NgForOf, NgIf } from "@angular/common"; import { Angulartics2Module } from "angulartics2"; import { DateRangeFilterComponent } from "../../basic-datatypes/date/date-range-filter/date-range-filter.component"; import { Filter } from "../filters/filters"; +import { getUrlWithoutParams } from "../../../utils/utils"; +import { FilterService } from "../filter.service"; /** * This component can be used to display filters, for example above tables. @@ -30,6 +31,8 @@ import { Filter } from "../filters/filters"; Angulartics2Module, DateRangeFilterComponent, NgIf, + JsonPipe, + AsyncPipe, ], standalone: true, }) @@ -63,7 +66,7 @@ export class FilterComponent implements OnChanges { */ @Input() filterObj: DataFilter; /** - * A event emitter that notifies about updates of the filter. + * An event emitter that notifies about updates of the filter. */ @Output() filterObjChange = new EventEmitter>(); @@ -72,6 +75,7 @@ export class FilterComponent implements OnChanges { constructor( private filterGenerator: FilterGeneratorService, + private filterService: FilterService, private router: Router, private route: ActivatedRoute, ) {} @@ -89,19 +93,19 @@ export class FilterComponent implements OnChanges { } } - filterOptionSelected(filter: Filter, selectedOption: string) { - filter.selectedOption = selectedOption; + filterOptionSelected(filter: Filter, selectedOptions: string[]) { + filter.selectedOptionsKeys = selectedOptions; this.applyFilterSelections(); if (this.useUrlQueryParams) { - this.updateUrl(filter.name, selectedOption); + this.updateUrl(filter.name, selectedOptions.toString()); } } private applyFilterSelections() { - const previousFilter = JSON.stringify(this.filterObj); - const newFilter = this.filterSelections.reduce( - (obj, filter) => Object.assign(obj, filter.getFilter()), - {} as DataFilter, + const previousFilter: string = JSON.stringify(this.filterObj); + + const newFilter: DataFilter = this.filterService.combineFilters( + this.filterSelections, ); if (previousFilter === JSON.stringify(newFilter)) { @@ -129,7 +133,9 @@ export class FilterComponent implements OnChanges { const params = parameters || this.route.snapshot.queryParams; this.filterSelections.forEach((f) => { if (params.hasOwnProperty(f.name)) { - f.selectedOption = params[f.name]; + f.selectedOptionsKeys = params[f.name].split(","); + } else { + f.selectedOptionsKeys = []; } }); } diff --git a/src/app/core/filter/filters/booleanFilter.ts b/src/app/core/filter/filters/booleanFilter.ts new file mode 100644 index 0000000000..f060ed20da --- /dev/null +++ b/src/app/core/filter/filters/booleanFilter.ts @@ -0,0 +1,29 @@ +import { Entity } from "../../entity/model/entity"; +import { BooleanFilterConfig } from "../../entity-list/EntityListConfig"; +import { SelectableFilter } from "./filters"; +import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; + +export class BooleanFilter extends SelectableFilter { + constructor(name: string, label: string, config?: BooleanFilterConfig) { + super( + name, + [ + { + key: "true", + label: + config.true ?? $localize`:Filter label default boolean true:Yes`, + filter: { [config.id]: true } as DataFilter, + }, + { + key: "false", + label: + config.false ?? $localize`:Filter label default boolean true:No`, + filter: { + [config.id]: { $in: [false, undefined] }, + } as DataFilter, + }, + ], + label, + ); + } +} diff --git a/src/app/core/filter/filters/configurableEnumFilter.ts b/src/app/core/filter/filters/configurableEnumFilter.ts new file mode 100644 index 0000000000..c23b6f5fcb --- /dev/null +++ b/src/app/core/filter/filters/configurableEnumFilter.ts @@ -0,0 +1,25 @@ +import { Entity } from "../../entity/model/entity"; +import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/configurable-enum.interface"; +import { FilterSelectionOption, SelectableFilter } from "./filters"; +import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; + +export class ConfigurableEnumFilter< + T extends Entity, +> extends SelectableFilter { + constructor( + name: string, + label: string, + enumValues: ConfigurableEnumValue[], + ) { + let options: FilterSelectionOption[] = []; + options.push( + ...enumValues.map((enumValue: ConfigurableEnumValue) => ({ + key: enumValue.id, + label: enumValue.label, + color: enumValue.color, + filter: { [name + ".id"]: enumValue.id } as DataFilter, + })), + ); + super(name, options, label); + } +} diff --git a/src/app/core/filter/filters/dateFilter.ts b/src/app/core/filter/filters/dateFilter.ts new file mode 100644 index 0000000000..c16cd40b2f --- /dev/null +++ b/src/app/core/filter/filters/dateFilter.ts @@ -0,0 +1,72 @@ +import { Entity } from "../../entity/model/entity"; +import { DateRangeFilterConfigOption } from "../../entity-list/EntityListConfig"; +import { DateRange } from "@angular/material/datepicker"; +import { calculateDateRange } from "../../basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component"; +import moment from "moment"; +import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; +import { Filter } from "./filters"; +import { isValidDate } from "../../../utils/utils"; + +/** + * Represents a filter for date values. + * The filter can either be one of the predefined options or two manually entered dates. + */ +export class DateFilter extends Filter { + constructor( + public name: string, + public label: string = name, + public rangeOptions: DateRangeFilterConfigOption[], + ) { + super(name, label); + this.selectedOptionsKeys = []; + } + + /** + * Returns the date range according to the selected option or dates + */ + getDateRange(): DateRange { + let selectedOption = this.getSelectedOption(); + if (selectedOption) { + return calculateDateRange(selectedOption); + } + const dates = this.selectedOptionsKeys; + if (dates?.length == 2) { + return this.getDateRangeFromDateStrings(dates[0], dates[1]); + } + return new DateRange(undefined, undefined); + } + + getFilter(): DataFilter { + const range = this.getDateRange(); + const filterObject: { $gte?: string; $lte?: string } = {}; + if (range.start) { + filterObject.$gte = moment(range.start).format("YYYY-MM-DD"); + } + if (range.end) { + filterObject.$lte = moment(range.end).format("YYYY-MM-DD"); + } + if (filterObject.$gte || filterObject.$lte) { + return { + [this.name]: filterObject, + } as DataFilter; + } + return {} as DataFilter; + } + + getSelectedOption() { + return this.rangeOptions[this.selectedOptionsKeys as any]; + } + + private getDateRangeFromDateStrings( + dateStr1: string, + dateStr2: string, + ): DateRange { + let date1 = moment(dateStr1).toDate(); + let date2 = moment(dateStr2).toDate(); + + return new DateRange( + isValidDate(date1) ? date1 : undefined, + isValidDate(date2) ? date2 : undefined, + ); + } +} diff --git a/src/app/core/filter/filters/entityFilter.ts b/src/app/core/filter/filters/entityFilter.ts new file mode 100644 index 0000000000..d26af48246 --- /dev/null +++ b/src/app/core/filter/filters/entityFilter.ts @@ -0,0 +1,22 @@ +import { Entity } from "../../entity/model/entity"; +import { FilterSelectionOption, SelectableFilter } from "./filters"; + +export class EntityFilter extends SelectableFilter { + constructor(name: string, label: string, filterEntities) { + filterEntities.sort((a, b) => a.toString().localeCompare(b.toString())); + const options: FilterSelectionOption[] = []; + options.push( + ...filterEntities.map((filterEntity) => ({ + key: filterEntity.getId(), + label: filterEntity.toString(), + filter: { + $or: [ + { [name]: filterEntity.getId() }, + { [name]: { $elemMatch: { $eq: filterEntity.getId() } } }, + ], + }, + })), + ); + super(name, options, label); + } +} diff --git a/src/app/core/filter/filters/filters.spec.ts b/src/app/core/filter/filters/filters.spec.ts index 4c85f0243f..113cf06c46 100644 --- a/src/app/core/filter/filters/filters.spec.ts +++ b/src/app/core/filter/filters/filters.spec.ts @@ -1,11 +1,13 @@ -import { BooleanFilter, Filter, SelectableFilter } from "./filters"; +import { Filter, SelectableFilter } from "./filters"; import { FilterService } from "../filter.service"; +import { BooleanFilter } from "./booleanFilter"; +import { Entity } from "../../entity/model/entity"; describe("Filters", () => { const filterService = new FilterService(undefined); function testFilter( - filterObj: Filter, + filterObj: Filter, testData: any[], expectedFilteredResult: any[], ) { @@ -25,24 +27,24 @@ describe("Filters", () => { }); it("init new options", () => { - const fs = new SelectableFilter( - "", - [{ key: "", label: "", filter: "" }], - "", + const filter = new SelectableFilter( + "name", + [{ key: "option-1", label: "op", filter: {} }], + "name", ); - const keys = ["x", "y"]; - fs.options = SelectableFilter.generateOptions(keys, "category"); + const keys: string[] = ["x", "y"]; + filter.options = SelectableFilter.generateOptions(keys, "category"); - expect(fs.options).toHaveSize(keys.length + 1); + expect(filter.options).toHaveSize(keys.length); - fs.selectedOption = "x"; + filter.selectedOptionsKeys = ["x"]; const testData = [ { id: 1, category: "x" }, { id: 2, category: "y" }, ]; - const filteredData = testFilter(fs, testData, [testData[0]]); + const filteredData = testFilter(filter, testData, [testData[0]]); expect(filteredData[0].category).toBe("x"); }); @@ -58,27 +60,17 @@ describe("Filters", () => { const recordTrue = { value: true }; const recordFalse = { value: false }; - const recordUndefined = {}; - filter.selectedOption = "true"; - testFilter( - filter, - [recordFalse, recordTrue, recordUndefined], - [recordTrue], - ); + filter.selectedOptionsKeys = ["true"]; + testFilter(filter, [recordFalse, recordTrue], [recordTrue]); - filter.selectedOption = "false"; - testFilter( - filter, - [recordFalse, recordTrue, recordUndefined], - [recordFalse, recordUndefined], - ); + filter.selectedOptionsKeys = ["false"]; + testFilter(filter, [recordFalse, recordTrue], [recordFalse]); - filter.selectedOption = "all"; - testFilter( - filter, - [recordFalse, recordTrue, recordUndefined], - [recordFalse, recordTrue, recordUndefined], - ); + filter.selectedOptionsKeys = []; + testFilter(filter, [recordFalse, recordTrue], [recordFalse, recordTrue]); + + filter.selectedOptionsKeys = ["true", "false"]; + testFilter(filter, [recordFalse, recordTrue], [recordFalse, recordTrue]); }); }); diff --git a/src/app/core/filter/filters/filters.ts b/src/app/core/filter/filters/filters.ts index ab60a04953..4997b94721 100644 --- a/src/app/core/filter/filters/filters.ts +++ b/src/app/core/filter/filters/filters.ts @@ -15,22 +15,13 @@ * along with ndb-core. If not, see . */ -import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/configurable-enum.interface"; -import { - BooleanFilterConfig, - DateRangeFilterConfigOption, -} from "../../entity-list/EntityListConfig"; import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; import { Entity } from "../../entity/model/entity"; -import { DateRange } from "@angular/material/datepicker"; -import { isValidDate } from "../../../utils/utils"; -import { calculateDateRange } from "../../basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component"; -import moment from "moment/moment"; export abstract class Filter { - public selectedOption: string; + public selectedOptionsKeys: string[] = []; - constructor( + protected constructor( public name: string, public label: string = name, ) {} @@ -38,61 +29,6 @@ export abstract class Filter { abstract getFilter(): DataFilter; } -/** - * Represents a filter for date values. - * The filter can either be one of the predefined options or two manually entered dates. - */ -export class DateFilter extends Filter { - constructor( - public name: string, - public label: string = name, - public rangeOptions: DateRangeFilterConfigOption[], - ) { - super(name, label); - this.selectedOption = "1"; - } - - /** - * Returns the date range according to the selected option or dates - */ - getDateRange(): DateRange { - if (this.getSelectedOption()) { - return calculateDateRange(this.getSelectedOption()); - } - const dates = this.selectedOption?.split("_"); - if (dates?.length == 2) { - const firstDate = moment(dates[0]).toDate(); - const secondDate = moment(dates[1]).toDate(); - return new DateRange( - isValidDate(firstDate) ? firstDate : undefined, - isValidDate(secondDate) ? secondDate : undefined, - ); - } - return new DateRange(undefined, undefined); - } - - getFilter(): DataFilter { - const range = this.getDateRange(); - const filterObject: { $gte?: string; $lte?: string } = {}; - if (range.start) { - filterObject.$gte = moment(range.start).format("YYYY-MM-DD"); - } - if (range.end) { - filterObject.$lte = moment(range.end).format("YYYY-MM-DD"); - } - if (filterObject.$gte || filterObject.$lte) { - return { - [this.name]: filterObject, - } as DataFilter; - } - return {} as DataFilter; - } - - getSelectedOption() { - return this.rangeOptions[this.selectedOption as any]; - } -} - /** * Generic configuration for a filter with different selectable {@link FilterSelectionOption} options. * @@ -119,13 +55,7 @@ export class SelectableFilter extends Filter { valuesToMatchAsOptions: string[], attributeName: string, ): FilterSelectionOption[] { - const options = [ - { - key: "", - label: $localize`:generic filter option showing all entries:All`, - filter: {} as DataFilter, - }, - ]; + const options: FilterSelectionOption[] = []; options.push( ...valuesToMatchAsOptions @@ -153,18 +83,17 @@ export class SelectableFilter extends Filter { public label: string = name, ) { super(name, label); - this.selectedOption = this.options[0]?.key; + this.selectedOptionsKeys = []; } - /** default filter will keep all items in the result */ - defaultFilter = {}; - /** * Get the full filter option by its key. * @param key The identifier of the requested option */ - getOption(key: string): FilterSelectionOption { - return this.options.find((option) => option.key === key); + getOption(key: string): FilterSelectionOption | undefined { + return this.options.find((option: FilterSelectionOption): boolean => { + return option.key === key; + }); } /** @@ -172,94 +101,19 @@ export class SelectableFilter extends Filter { * If the given key is undefined or invalid, the returned filter matches any elements. */ public getFilter(): DataFilter { - const option = this.getOption(this.selectedOption); - - if (!option) { - return this.defaultFilter as DataFilter; - } else { - return option.filter; + let filters: DataFilter[] = this.selectedOptionsKeys + .map((value: string) => this.getOption(value)) + .filter((value: FilterSelectionOption) => value !== undefined) + .map((previousValue: FilterSelectionOption) => { + return previousValue.filter as DataFilter; + }); + + if (filters.length === 0) { + return {} as DataFilter; } - } -} - -export class BooleanFilter extends SelectableFilter { - constructor(name: string, label: string, config?: BooleanFilterConfig) { - super( - name, - [ - { - key: "all", - label: config.all ?? $localize`:Filter label:All`, - filter: {}, - }, - { - key: "true", - label: - config.true ?? $localize`:Filter label default boolean true:Yes`, - filter: { [config.id]: true }, - }, - { - key: "false", - label: - config.false ?? $localize`:Filter label default boolean true:No`, - filter: { $or: [{ [config.id]: false }, { [config.id]: undefined }] }, - }, - ], - label, - ); - } -} - -export class ConfigurableEnumFilter< - T extends Entity, -> extends SelectableFilter { - constructor( - name: string, - label: string, - enumValues: ConfigurableEnumValue[], - ) { - let options: FilterSelectionOption[] = [ - { - key: "all", - label: $localize`:Filter label:All`, - filter: {}, - }, - ]; - options.push( - ...enumValues.map((enumValue) => ({ - key: enumValue.id, - label: enumValue.label, - color: enumValue.color, - filter: { [name + ".id"]: enumValue.id }, - })), - ); - super(name, options, label); - } -} - -export class EntityFilter extends SelectableFilter { - constructor(name: string, label: string, filterEntities) { - filterEntities.sort((a, b) => a.toString().localeCompare(b.toString())); - const options: FilterSelectionOption[] = [ - { - key: "all", - label: $localize`:Filter label:All`, - filter: {}, - }, - ]; - options.push( - ...filterEntities.map((filterEntity) => ({ - key: filterEntity.getId(), - label: filterEntity.toString(), - filter: { - $or: [ - { [name]: filterEntity.getId() }, - { [name]: { $elemMatch: { $eq: filterEntity.getId() } } }, - ], - }, - })), - ); - super(name, options, label); + return { + $or: [...filters], + } as unknown as DataFilter; } } @@ -280,5 +134,5 @@ export interface FilterSelectionOption { /** * The filter query which should be used if this filter is selected */ - filter: DataFilter | any; + filter: DataFilter; } diff --git a/src/app/core/filter/list-filter/list-filter.component.html b/src/app/core/filter/list-filter/list-filter.component.html index 523a6f4081..b92ed24291 100644 --- a/src/app/core/filter/list-filter/list-filter.component.html +++ b/src/app/core/filter/list-filter/list-filter.component.html @@ -1,14 +1,19 @@ - {{ _filterConfig.label || _filterConfig.name }} - + {{ filterConfig.label || filterConfig.name }} + + @for (option of filterConfig.options; track option.key) { {{ option.label }} + } diff --git a/src/app/core/filter/list-filter/list-filter.component.spec.ts b/src/app/core/filter/list-filter/list-filter.component.spec.ts index 701cbe4f11..a37e4a5f3b 100644 --- a/src/app/core/filter/list-filter/list-filter.component.spec.ts +++ b/src/app/core/filter/list-filter/list-filter.component.spec.ts @@ -1,8 +1,8 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { ListFilterComponent } from "./list-filter.component"; -import { SelectableFilter } from "../filters/filters"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; +import { SelectableFilter } from "../filters/filters"; describe("ListFilterComponent", () => { let component: ListFilterComponent; diff --git a/src/app/core/filter/list-filter/list-filter.component.ts b/src/app/core/filter/list-filter/list-filter.component.ts index 1c4093041e..2e561dcb6e 100644 --- a/src/app/core/filter/list-filter/list-filter.component.ts +++ b/src/app/core/filter/list-filter/list-filter.component.ts @@ -1,10 +1,11 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { Filter, SelectableFilter } from "../filters/filters"; import { Entity } from "../../entity/model/entity"; import { MatFormFieldModule } from "@angular/material/form-field"; import { MatSelectModule } from "@angular/material/select"; import { BorderHighlightDirective } from "../../common-components/border-highlight/border-highlight.directive"; -import { NgForOf } from "@angular/common"; +import { JsonPipe, NgForOf } from "@angular/common"; +import { ReactiveFormsModule } from "@angular/forms"; +import { SelectableFilter } from "../filters/filters"; @Component({ selector: "app-list-filter", @@ -12,22 +13,16 @@ import { NgForOf } from "@angular/common"; imports: [ MatFormFieldModule, MatSelectModule, + ReactiveFormsModule, BorderHighlightDirective, NgForOf, + JsonPipe, ], standalone: true, }) export class ListFilterComponent { - @Input() - public set filterConfig(value: Filter) { - this._filterConfig = value as SelectableFilter; - } - _filterConfig: SelectableFilter; - @Input() selectedOption: string; - @Output() selectedOptionChange = new EventEmitter(); - - selectOption(selectedOptionKey: string) { - this.selectedOption = selectedOptionKey; - this.selectedOptionChange.emit(selectedOptionKey); - } + @Input({ transform: (value: any) => value as SelectableFilter }) + filterConfig: SelectableFilter; + @Input() selectedOptions: string[]; + @Output() selectedOptionChange: EventEmitter = new EventEmitter(); } diff --git a/src/app/features/todos/todo-list/todo-list.component.ts b/src/app/features/todos/todo-list/todo-list.component.ts index 09bcdaef42..8675c2f3ab 100644 --- a/src/app/features/todos/todo-list/todo-list.component.ts +++ b/src/app/features/todos/todo-list/todo-list.component.ts @@ -15,6 +15,7 @@ import { FilterSelectionOption } from "../../../core/filter/filters/filters"; import { RouteTarget } from "../../../route-target"; import { CurrentUserSubject } from "../../../core/session/current-user-subject"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; +import { DataFilter } from "../../../core/common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; @UntilDestroy() @RouteTarget("TodoList") @@ -160,5 +161,5 @@ const filterCurrentlyActive: FilterSelectionOption = { ], }, ], - }, + } as DataFilter, }; From 29887a95c61d5a98deb0009c4ac8b5e293ee09b8 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Fri, 12 Jan 2024 11:12:17 +0100 Subject: [PATCH 02/11] feat(filter): pr review --- .../configurable-enum.service.ts | 4 +- .../filter/filter/filter.component.spec.ts | 70 +++++++++++++++++++ .../core/filter/filter/filter.component.ts | 10 ++- src/app/core/filter/filters/filters.ts | 20 ++---- 4 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts index 5a945d42d3..d4d5e2c8c3 100644 --- a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts +++ b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts @@ -31,10 +31,10 @@ export class ConfigurableEnumService { id: string, ): T[] { let configurableEnum = this.getEnum(id); - return configurableEnum == null ? [] : (configurableEnum.values as T[]); + return configurableEnum ? (configurableEnum.values as T[]) : []; } - getEnum(id: string): ConfigurableEnum | null { + getEnum(id: string): ConfigurableEnum | undefined { if (!this.enums) { return; } diff --git a/src/app/core/filter/filter/filter.component.spec.ts b/src/app/core/filter/filter/filter.component.spec.ts index 15670835b7..9a9c1f63ac 100644 --- a/src/app/core/filter/filter/filter.component.spec.ts +++ b/src/app/core/filter/filter/filter.component.spec.ts @@ -7,15 +7,34 @@ import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed"; import { HarnessLoader } from "@angular/cdk/testing"; import { MatSelectHarness } from "@angular/material/select/testing"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; +import { ActivatedRoute } from "@angular/router"; + +export class ActivatedRouteMock { + public snapshot = { + queryParams: {}, + }; +} describe("FilterComponent", () => { let component: FilterComponent; let fixture: ComponentFixture; let loader: HarnessLoader; + let activatedRouteMock = new ActivatedRouteMock(); + beforeEach(async () => { + activatedRouteMock.snapshot = { + queryParams: {}, + }; + await TestBed.configureTestingModule({ imports: [FilterComponent, MockedTestingModule.withState()], + providers: [ + { + provide: ActivatedRoute, + useValue: activatedRouteMock, + }, + ], }).compileComponents(); fixture = TestBed.createComponent(FilterComponent); @@ -28,6 +47,57 @@ describe("FilterComponent", () => { expect(component).toBeTruthy(); }); + it("should have no filter selected when url params are empty", async () => { + component.entityType = Note; + component.useUrlQueryParams = true; + component.filterConfig = [{ id: "category" }]; + + await component.ngOnChanges({ filterConfig: true } as any); + + expect(component.filterSelections.length).toBe(1); + expect(component.filterSelections[0].name).toBe("category"); + expect(component.filterSelections[0].selectedOptionsKeys).toBeEmpty(); + }); + + it("should load url params and set single filter value", async () => { + component.entityType = Note; + component.useUrlQueryParams = true; + component.filterConfig = [{ id: "category" }]; + + activatedRouteMock.snapshot = { + queryParams: { + category: "foo", + }, + }; + + await component.ngOnChanges({ filterConfig: true } as any); + + expect(component.filterSelections.length).toBe(1); + expect(component.filterSelections[0].name).toBe("category"); + expect(component.filterSelections[0].selectedOptionsKeys.length).toBe(1); + expect(component.filterSelections[0].selectedOptionsKeys[0]).toBe("foo"); + }); + + it("should load url params and set multiple filter value", async () => { + component.entityType = Note; + component.useUrlQueryParams = true; + component.filterConfig = [{ id: "category" }]; + + activatedRouteMock.snapshot = { + queryParams: { + category: "foo,bar", + }, + }; + + await component.ngOnChanges({ filterConfig: true } as any); + + expect(component.filterSelections.length).toBe(1); + expect(component.filterSelections[0].name).toBe("category"); + expect(component.filterSelections[0].selectedOptionsKeys.length).toBe(2); + expect(component.filterSelections[0].selectedOptionsKeys[0]).toBe("foo"); + expect(component.filterSelections[0].selectedOptionsKeys[1]).toBe("bar"); + }); + it("should set up category filter from configurable enum", async () => { component.entityType = Note; const t1 = defaultInteractionTypes[0]; diff --git a/src/app/core/filter/filter/filter.component.ts b/src/app/core/filter/filter/filter.component.ts index 37554476e7..a89e5b3563 100644 --- a/src/app/core/filter/filter/filter.component.ts +++ b/src/app/core/filter/filter/filter.component.ts @@ -10,9 +10,9 @@ import { FilterConfig } from "../../entity-list/EntityListConfig"; import { Entity, EntityConstructor } from "../../entity/model/entity"; import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; import { FilterGeneratorService } from "../filter-generator/filter-generator.service"; -import { ActivatedRoute, Params, Router } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { ListFilterComponent } from "../list-filter/list-filter.component"; -import { AsyncPipe, JsonPipe, NgForOf, NgIf } from "@angular/common"; +import { NgForOf, NgIf } from "@angular/common"; import { Angulartics2Module } from "angulartics2"; import { DateRangeFilterComponent } from "../../basic-datatypes/date/date-range-filter/date-range-filter.component"; import { Filter } from "../filters/filters"; @@ -31,8 +31,6 @@ import { FilterService } from "../filter.service"; Angulartics2Module, DateRangeFilterComponent, NgIf, - JsonPipe, - AsyncPipe, ], standalone: true, }) @@ -126,11 +124,11 @@ export class FilterComponent implements OnChanges { }); } - private loadUrlParams(parameters?: Params) { + private loadUrlParams() { if (!this.useUrlQueryParams) { return; } - const params = parameters || this.route.snapshot.queryParams; + const params = this.route.snapshot.queryParams; this.filterSelections.forEach((f) => { if (params.hasOwnProperty(f.name)) { f.selectedOptionsKeys = params[f.name].split(","); diff --git a/src/app/core/filter/filters/filters.ts b/src/app/core/filter/filters/filters.ts index 4997b94721..1fbffd8117 100644 --- a/src/app/core/filter/filters/filters.ts +++ b/src/app/core/filter/filters/filters.ts @@ -55,19 +55,13 @@ export class SelectableFilter extends Filter { valuesToMatchAsOptions: string[], attributeName: string, ): FilterSelectionOption[] { - const options: FilterSelectionOption[] = []; - - options.push( - ...valuesToMatchAsOptions - .filter((k) => !!k) - .map((k) => ({ - key: k.toLowerCase(), - label: k.toString(), - filter: { [attributeName]: k } as DataFilter, - })), - ); - - return options; + return valuesToMatchAsOptions + .filter((k) => !!k) + .map((k) => ({ + key: k.toLowerCase(), + label: k.toString(), + filter: { [attributeName]: k } as DataFilter, + })); } /** From 0b91e90aef44682ce04292d0b06bd41af9458985 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Fri, 12 Jan 2024 11:44:15 +0100 Subject: [PATCH 03/11] feat(filter): pr review --- .../filter/filter/filter.component.spec.ts | 18 ++++++++++++++++++ src/app/core/filter/filter/filter.component.ts | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/app/core/filter/filter/filter.component.spec.ts b/src/app/core/filter/filter/filter.component.spec.ts index 9a9c1f63ac..cd80d431aa 100644 --- a/src/app/core/filter/filter/filter.component.spec.ts +++ b/src/app/core/filter/filter/filter.component.spec.ts @@ -98,6 +98,24 @@ describe("FilterComponent", () => { expect(component.filterSelections[0].selectedOptionsKeys[1]).toBe("bar"); }); + it("should load url params and set no filter value when empty", async () => { + component.entityType = Note; + component.useUrlQueryParams = true; + component.filterConfig = [{ id: "category" }]; + + activatedRouteMock.snapshot = { + queryParams: { + category: "", + }, + }; + + await component.ngOnChanges({ filterConfig: true } as any); + + expect(component.filterSelections.length).toBe(1); + expect(component.filterSelections[0].name).toBe("category"); + expect(component.filterSelections[0].selectedOptionsKeys).toBeEmpty(); + }); + it("should set up category filter from configurable enum", async () => { component.entityType = Note; const t1 = defaultInteractionTypes[0]; diff --git a/src/app/core/filter/filter/filter.component.ts b/src/app/core/filter/filter/filter.component.ts index a89e5b3563..a6a891512e 100644 --- a/src/app/core/filter/filter/filter.component.ts +++ b/src/app/core/filter/filter/filter.component.ts @@ -131,7 +131,8 @@ export class FilterComponent implements OnChanges { const params = this.route.snapshot.queryParams; this.filterSelections.forEach((f) => { if (params.hasOwnProperty(f.name)) { - f.selectedOptionsKeys = params[f.name].split(","); + let values: string[] = params[f.name].split(","); + f.selectedOptionsKeys = values.filter((value) => value !== ""); } else { f.selectedOptionsKeys = []; } From 99652b01854354cce458bf8ba56f62cff0b02050 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Fri, 12 Jan 2024 12:42:13 +0100 Subject: [PATCH 04/11] feat(filter): pr review --- .../date-range-filter-panel.component.html | 2 +- .../date-range-filter-panel.component.spec.ts | 6 +++--- .../date-range-filter-panel.component.ts | 6 +++--- .../date-range-filter.component.spec.ts | 20 +++++++++---------- .../date-range-filter.component.ts | 6 +++--- .../filter-generator.service.spec.ts | 4 +++- .../filter-generator.service.ts | 2 +- .../core/filter/filter/filter.component.html | 2 +- .../filter/filter/filter.component.spec.ts | 14 ++++++------- .../core/filter/filter/filter.component.ts | 6 ++---- src/app/core/filter/filters/dateFilter.ts | 6 +++--- src/app/core/filter/filters/filters.spec.ts | 10 +++++----- src/app/core/filter/filters/filters.ts | 6 +++--- 13 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html index 14cbed0aba..4e6944dbdb 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.html @@ -18,7 +18,7 @@ (mouseenter)="preselectAllRange()" (mouseleave)="unselectRange()" (click)="selectRangeAndClose('all')" - [class.selected-option]="filter.selectedOptionsKeys.length === 0" + [class.selected-option]="filter.selectedOptionValues.length === 0" > All diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts index 414a10069a..8acf5cb210 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.spec.ts @@ -23,7 +23,7 @@ describe("DateRangeFilterPanelComponent", () => { beforeEach(async () => { dateFilter = new DateFilter("test", "Test", defaultDateFilters); - dateFilter.selectedOptionsKeys = ["1"]; + dateFilter.selectedOptionValues = ["1"]; jasmine.clock().mockDate(moment("2023-04-08").toDate()); await TestBed.configureTestingModule({ imports: [MatNativeDateModule], @@ -86,7 +86,7 @@ describe("DateRangeFilterPanelComponent", () => { moment("2023-04-08").startOf("day").toDate(), ); expect(filterRange.end).toEqual(moment("2023-04-08").endOf("day").toDate()); - expect(dateFilter.selectedOptionsKeys).toEqual(["0"]); + expect(dateFilter.selectedOptionValues).toEqual(["0"]); }); it("should highlight the date range when hovering over a option", async () => { @@ -117,7 +117,7 @@ describe("DateRangeFilterPanelComponent", () => { it("should return empty array as filter.selectedOption when 'all' option has been chosen", async () => { component.selectRangeAndClose("all"); - expect(dateFilter.selectedOptionsKeys).toEqual([]); + expect(dateFilter.selectedOptionValues).toEqual([]); }); it("should correctly calculate date ranges based on the config", () => { diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts index 10cb51c6b4..d4871f900c 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component.ts @@ -91,9 +91,9 @@ export class DateRangeFilterPanelComponent { selectRangeAndClose(index: number | "all"): void { if (typeof index === "number") { - this.filter.selectedOptionsKeys = [index.toString()]; + this.filter.selectedOptionValues = [index.toString()]; } else { - this.filter.selectedOptionsKeys = []; + this.filter.selectedOptionValues = []; } this.dialogRef.close(); } @@ -103,7 +103,7 @@ export class DateRangeFilterPanelComponent { this.selectedRangeValue = new DateRange(selectedDate, null); } else { const start: Date = this.selectedRangeValue.start; - this.filter.selectedOptionsKeys = + this.filter.selectedOptionValues = start < selectedDate ? [dateToString(start), dateToString(selectedDate)] : [dateToString(selectedDate), dateToString(start)]; diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts index 25b1e9716e..df35a6a00e 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts @@ -30,12 +30,12 @@ describe("DateRangeFilterComponent", () => { it("should set the correct date filter when a new option is selected", () => { const dateFilter = new DateFilter("test", "Test", defaultDateFilters); - dateFilter.selectedOptionsKeys = ["9"]; + dateFilter.selectedOptionValues = ["9"]; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); jasmine.clock().mockDate(moment("2023-05-18").toDate()); - dateFilter.selectedOptionsKeys = ["0"]; + dateFilter.selectedOptionValues = ["0"]; component.filterConfig = dateFilter; let expectedDataFilter = { test: { @@ -45,7 +45,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDataFilter); - dateFilter.selectedOptionsKeys = ["1"]; + dateFilter.selectedOptionValues = ["1"]; component.filterConfig = dateFilter; expectedDataFilter = { test: { @@ -55,7 +55,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDataFilter); - dateFilter.selectedOptionsKeys = []; + dateFilter.selectedOptionValues = []; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); jasmine.clock().uninstall(); @@ -64,15 +64,15 @@ describe("DateRangeFilterComponent", () => { it("should set the correct date filter when inputting a specific date range via the URL", () => { let dateFilter = new DateFilter("test", "test", []); - dateFilter.selectedOptionsKeys = ["1_2_3"]; + dateFilter.selectedOptionValues = ["1_2_3"]; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); - dateFilter.selectedOptionsKeys = []; + dateFilter.selectedOptionValues = []; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); - dateFilter.selectedOptionsKeys = ["2022-9-18", ""]; + dateFilter.selectedOptionValues = ["2022-9-18", ""]; component.filterConfig = dateFilter; let testFilter: { $gte?: string; $lte?: string } = { $gte: "2022-09-18" }; let expectedDateFilter = { @@ -80,7 +80,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDateFilter); - dateFilter.selectedOptionsKeys = ["", "2023-01-3"]; + dateFilter.selectedOptionValues = ["", "2023-01-3"]; component.filterConfig = dateFilter; testFilter = { $lte: "2023-01-03" }; expectedDateFilter = { @@ -88,7 +88,7 @@ describe("DateRangeFilterComponent", () => { }; expect(component.dateFilter.getFilter()).toEqual(expectedDateFilter); - dateFilter.selectedOptionsKeys = ["2022-9-18", "2023-01-3"]; + dateFilter.selectedOptionValues = ["2022-9-18", "2023-01-3"]; component.filterConfig = dateFilter; testFilter = { $gte: "2022-09-18", @@ -107,7 +107,7 @@ describe("DateRangeFilterComponent", () => { component.dateChangedManually(); - expect(component.dateFilter.selectedOptionsKeys).toEqual([ + expect(component.dateFilter.selectedOptionValues).toEqual([ "2021-10-28", "2024-02-12", ]); diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts index 874fc63b8d..301a1832ff 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.ts @@ -38,16 +38,16 @@ export class DateRangeFilterComponent { ) { this.fromDate = range.start; this.toDate = range.end; - this.selectedOptionChange.emit(this.dateFilter.selectedOptionsKeys); + this.selectedOptionChange.emit(this.dateFilter.selectedOptionValues); } } dateChangedManually() { - this.dateFilter.selectedOptionsKeys = [ + this.dateFilter.selectedOptionValues = [ isValidDate(this.fromDate) ? dateToString(this.fromDate) : "", isValidDate(this.toDate) ? dateToString(this.toDate) : "", ]; - this.selectedOptionChange.emit(this.dateFilter.selectedOptionsKeys); + this.selectedOptionChange.emit(this.dateFilter.selectedOptionValues); } openDialog(e: Event) { diff --git a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts index 75d2af2491..9c40cbb5d5 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts @@ -229,7 +229,9 @@ describe("FilterGeneratorService", () => { expect(filterOptions.label).toEqual(prebuiltFilter.label); expect(filterOptions.name).toEqual(prebuiltFilter.id); expect(filterOptions.options).toEqual(prebuiltFilter.options); - expect(filterOptions.selectedOptionsKeys).toEqual([prebuiltFilter.default]); + expect(filterOptions.selectedOptionValues).toEqual([ + prebuiltFilter.default, + ]); const todayNote = new Note(); todayNote.date = new Date(); diff --git a/src/app/core/filter/filter-generator/filter-generator.service.ts b/src/app/core/filter/filter-generator/filter-generator.service.ts index 962bbc74a5..f1ae794197 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.ts @@ -111,7 +111,7 @@ export class FilterGeneratorService { } if (filterConfig.hasOwnProperty("default")) { - filter.selectedOptionsKeys = [filterConfig.default]; + filter.selectedOptionValues = [filterConfig.default]; } if (filter instanceof SelectableFilter) { diff --git a/src/app/core/filter/filter/filter.component.html b/src/app/core/filter/filter/filter.component.html index 4c63f19492..3f3395c447 100644 --- a/src/app/core/filter/filter/filter.component.html +++ b/src/app/core/filter/filter/filter.component.html @@ -2,7 +2,7 @@ { expect(component.filterSelections.length).toBe(1); expect(component.filterSelections[0].name).toBe("category"); - expect(component.filterSelections[0].selectedOptionsKeys).toBeEmpty(); + expect(component.filterSelections[0].selectedOptionValues).toBeEmpty(); }); it("should load url params and set single filter value", async () => { @@ -74,8 +74,8 @@ describe("FilterComponent", () => { expect(component.filterSelections.length).toBe(1); expect(component.filterSelections[0].name).toBe("category"); - expect(component.filterSelections[0].selectedOptionsKeys.length).toBe(1); - expect(component.filterSelections[0].selectedOptionsKeys[0]).toBe("foo"); + expect(component.filterSelections[0].selectedOptionValues.length).toBe(1); + expect(component.filterSelections[0].selectedOptionValues[0]).toBe("foo"); }); it("should load url params and set multiple filter value", async () => { @@ -93,9 +93,9 @@ describe("FilterComponent", () => { expect(component.filterSelections.length).toBe(1); expect(component.filterSelections[0].name).toBe("category"); - expect(component.filterSelections[0].selectedOptionsKeys.length).toBe(2); - expect(component.filterSelections[0].selectedOptionsKeys[0]).toBe("foo"); - expect(component.filterSelections[0].selectedOptionsKeys[1]).toBe("bar"); + expect(component.filterSelections[0].selectedOptionValues.length).toBe(2); + expect(component.filterSelections[0].selectedOptionValues[0]).toBe("foo"); + expect(component.filterSelections[0].selectedOptionValues[1]).toBe("bar"); }); it("should load url params and set no filter value when empty", async () => { @@ -113,7 +113,7 @@ describe("FilterComponent", () => { expect(component.filterSelections.length).toBe(1); expect(component.filterSelections[0].name).toBe("category"); - expect(component.filterSelections[0].selectedOptionsKeys).toBeEmpty(); + expect(component.filterSelections[0].selectedOptionValues).toBeEmpty(); }); it("should set up category filter from configurable enum", async () => { diff --git a/src/app/core/filter/filter/filter.component.ts b/src/app/core/filter/filter/filter.component.ts index a6a891512e..976982121f 100644 --- a/src/app/core/filter/filter/filter.component.ts +++ b/src/app/core/filter/filter/filter.component.ts @@ -92,7 +92,7 @@ export class FilterComponent implements OnChanges { } filterOptionSelected(filter: Filter, selectedOptions: string[]) { - filter.selectedOptionsKeys = selectedOptions; + filter.selectedOptionValues = selectedOptions; this.applyFilterSelections(); if (this.useUrlQueryParams) { this.updateUrl(filter.name, selectedOptions.toString()); @@ -132,9 +132,7 @@ export class FilterComponent implements OnChanges { this.filterSelections.forEach((f) => { if (params.hasOwnProperty(f.name)) { let values: string[] = params[f.name].split(","); - f.selectedOptionsKeys = values.filter((value) => value !== ""); - } else { - f.selectedOptionsKeys = []; + f.selectedOptionValues = values.filter((value) => value !== ""); } }); } diff --git a/src/app/core/filter/filters/dateFilter.ts b/src/app/core/filter/filters/dateFilter.ts index c16cd40b2f..155cf43918 100644 --- a/src/app/core/filter/filters/dateFilter.ts +++ b/src/app/core/filter/filters/dateFilter.ts @@ -18,7 +18,7 @@ export class DateFilter extends Filter { public rangeOptions: DateRangeFilterConfigOption[], ) { super(name, label); - this.selectedOptionsKeys = []; + this.selectedOptionValues = []; } /** @@ -29,7 +29,7 @@ export class DateFilter extends Filter { if (selectedOption) { return calculateDateRange(selectedOption); } - const dates = this.selectedOptionsKeys; + const dates = this.selectedOptionValues; if (dates?.length == 2) { return this.getDateRangeFromDateStrings(dates[0], dates[1]); } @@ -54,7 +54,7 @@ export class DateFilter extends Filter { } getSelectedOption() { - return this.rangeOptions[this.selectedOptionsKeys as any]; + return this.rangeOptions[this.selectedOptionValues as any]; } private getDateRangeFromDateStrings( diff --git a/src/app/core/filter/filters/filters.spec.ts b/src/app/core/filter/filters/filters.spec.ts index 113cf06c46..d7f10b0fc6 100644 --- a/src/app/core/filter/filters/filters.spec.ts +++ b/src/app/core/filter/filters/filters.spec.ts @@ -38,7 +38,7 @@ describe("Filters", () => { expect(filter.options).toHaveSize(keys.length); - filter.selectedOptionsKeys = ["x"]; + filter.selectedOptionValues = ["x"]; const testData = [ { id: 1, category: "x" }, @@ -61,16 +61,16 @@ describe("Filters", () => { const recordTrue = { value: true }; const recordFalse = { value: false }; - filter.selectedOptionsKeys = ["true"]; + filter.selectedOptionValues = ["true"]; testFilter(filter, [recordFalse, recordTrue], [recordTrue]); - filter.selectedOptionsKeys = ["false"]; + filter.selectedOptionValues = ["false"]; testFilter(filter, [recordFalse, recordTrue], [recordFalse]); - filter.selectedOptionsKeys = []; + filter.selectedOptionValues = []; testFilter(filter, [recordFalse, recordTrue], [recordFalse, recordTrue]); - filter.selectedOptionsKeys = ["true", "false"]; + filter.selectedOptionValues = ["true", "false"]; testFilter(filter, [recordFalse, recordTrue], [recordFalse, recordTrue]); }); }); diff --git a/src/app/core/filter/filters/filters.ts b/src/app/core/filter/filters/filters.ts index 1fbffd8117..2c77e1b253 100644 --- a/src/app/core/filter/filters/filters.ts +++ b/src/app/core/filter/filters/filters.ts @@ -19,7 +19,7 @@ import { DataFilter } from "../../common-components/entity-subrecord/entity-subr import { Entity } from "../../entity/model/entity"; export abstract class Filter { - public selectedOptionsKeys: string[] = []; + public selectedOptionValues: string[] = []; protected constructor( public name: string, @@ -77,7 +77,7 @@ export class SelectableFilter extends Filter { public label: string = name, ) { super(name, label); - this.selectedOptionsKeys = []; + this.selectedOptionValues = []; } /** @@ -95,7 +95,7 @@ export class SelectableFilter extends Filter { * If the given key is undefined or invalid, the returned filter matches any elements. */ public getFilter(): DataFilter { - let filters: DataFilter[] = this.selectedOptionsKeys + let filters: DataFilter[] = this.selectedOptionValues .map((value: string) => this.getOption(value)) .filter((value: FilterSelectionOption) => value !== undefined) .map((previousValue: FilterSelectionOption) => { From da4e7d9ad633c81a92f2a5b571f1e1de7903828e Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Fri, 12 Jan 2024 13:41:04 +0100 Subject: [PATCH 05/11] feat(filter): pr review --- .../children/children-list/children-list.component.spec.ts | 1 - src/app/core/entity-list/EntityListConfig.ts | 1 - .../core/entity-list/entity-list/entity-list.component.spec.ts | 1 - .../filter/filter-generator/filter-generator.service.spec.ts | 1 - src/app/core/filter/filters/filters.spec.ts | 1 - 5 files changed, 5 deletions(-) diff --git a/src/app/child-dev-project/children/children-list/children-list.component.spec.ts b/src/app/child-dev-project/children/children-list/children-list.component.spec.ts index e22dde4032..a7d75a2b03 100644 --- a/src/app/child-dev-project/children/children-list/children-list.component.spec.ts +++ b/src/app/child-dev-project/children/children-list/children-list.component.spec.ts @@ -51,7 +51,6 @@ describe("ChildrenListComponent", () => { default: "true", true: "Currently active children", false: "Currently inactive children", - all: "All children", } as BooleanFilterConfig, { id: "center", diff --git a/src/app/core/entity-list/EntityListConfig.ts b/src/app/core/entity-list/EntityListConfig.ts index 8b6b6c65d4..07feaaef03 100644 --- a/src/app/core/entity-list/EntityListConfig.ts +++ b/src/app/core/entity-list/EntityListConfig.ts @@ -82,7 +82,6 @@ export interface BasicFilterConfig { export interface BooleanFilterConfig extends BasicFilterConfig { true: string; false: string; - all: string; } export interface PrebuiltFilterConfig extends BasicFilterConfig { diff --git a/src/app/core/entity-list/entity-list/entity-list.component.spec.ts b/src/app/core/entity-list/entity-list/entity-list.component.spec.ts index f82b61e4d5..fd586b6689 100644 --- a/src/app/core/entity-list/entity-list/entity-list.component.spec.ts +++ b/src/app/core/entity-list/entity-list/entity-list.component.spec.ts @@ -60,7 +60,6 @@ describe("EntityListComponent", () => { default: "true", true: "Currently active children", false: "Currently inactive children", - all: "All children", } as BooleanFilterConfig, { id: "center", diff --git a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts index 9c40cbb5d5..821bb7228e 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts @@ -42,7 +42,6 @@ describe("FilterGeneratorService", () => { id: "privateSchool", true: "Private", false: "Government", - all: "All", type: "boolean", }; const schema = School.schema.get("privateSchool"); diff --git a/src/app/core/filter/filters/filters.spec.ts b/src/app/core/filter/filters/filters.spec.ts index d7f10b0fc6..d94d74333f 100644 --- a/src/app/core/filter/filters/filters.spec.ts +++ b/src/app/core/filter/filters/filters.spec.ts @@ -55,7 +55,6 @@ describe("Filters", () => { default: "true", true: "is true", false: "is not true", - all: "All", }); const recordTrue = { value: true }; From deb02629080f4c2aa0972072c1333de7b56a1956 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Mon, 15 Jan 2024 14:27:47 +0100 Subject: [PATCH 06/11] fix: use correct import paths --- .../notes/notes-manager/notes-manager.component.ts | 6 ++++-- src/app/core/filter/filters/booleanFilter.ts | 3 +-- src/app/core/filter/filters/configurableEnumFilter.ts | 3 +-- src/app/core/filter/filters/dateFilter.ts | 3 +-- src/app/core/filter/filters/filters.ts | 9 --------- src/app/features/todos/todo-list/todo-list.component.ts | 5 ++++- 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts b/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts index 4019726693..1f01fad22c 100644 --- a/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts +++ b/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts @@ -3,7 +3,10 @@ import { Note } from "../model/note"; import { NoteDetailsComponent } from "../note-details/note-details.component"; import { ActivatedRoute } from "@angular/router"; import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; -import { FilterSelectionOption } from "../../../core/filter/filters/filters"; +import { + DataFilter, + FilterSelectionOption, +} from "../../../core/filter/filters/filters"; import { FormDialogService } from "../../../core/form-dialog/form-dialog.service"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { LoggingService } from "../../../core/logging/logging.service"; @@ -22,7 +25,6 @@ import { Angulartics2Module } from "angulartics2"; import { MatMenuModule } from "@angular/material/menu"; import { FaDynamicIconComponent } from "../../../core/common-components/fa-dynamic-icon/fa-dynamic-icon.component"; import { RouteTarget } from "../../../route-target"; -import { DataFilter } from "../../../core/common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; /** * additional config specifically for NotesManagerComponent diff --git a/src/app/core/filter/filters/booleanFilter.ts b/src/app/core/filter/filters/booleanFilter.ts index f060ed20da..da9629ebfc 100644 --- a/src/app/core/filter/filters/booleanFilter.ts +++ b/src/app/core/filter/filters/booleanFilter.ts @@ -1,7 +1,6 @@ import { Entity } from "../../entity/model/entity"; import { BooleanFilterConfig } from "../../entity-list/EntityListConfig"; -import { SelectableFilter } from "./filters"; -import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; +import { DataFilter, SelectableFilter } from "./filters"; export class BooleanFilter extends SelectableFilter { constructor(name: string, label: string, config?: BooleanFilterConfig) { diff --git a/src/app/core/filter/filters/configurableEnumFilter.ts b/src/app/core/filter/filters/configurableEnumFilter.ts index c23b6f5fcb..21802e853f 100644 --- a/src/app/core/filter/filters/configurableEnumFilter.ts +++ b/src/app/core/filter/filters/configurableEnumFilter.ts @@ -1,7 +1,6 @@ import { Entity } from "../../entity/model/entity"; import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/configurable-enum.interface"; -import { FilterSelectionOption, SelectableFilter } from "./filters"; -import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; +import { DataFilter, FilterSelectionOption, SelectableFilter } from "./filters"; export class ConfigurableEnumFilter< T extends Entity, diff --git a/src/app/core/filter/filters/dateFilter.ts b/src/app/core/filter/filters/dateFilter.ts index 155cf43918..21b0cef8dd 100644 --- a/src/app/core/filter/filters/dateFilter.ts +++ b/src/app/core/filter/filters/dateFilter.ts @@ -3,8 +3,7 @@ import { DateRangeFilterConfigOption } from "../../entity-list/EntityListConfig" import { DateRange } from "@angular/material/datepicker"; import { calculateDateRange } from "../../basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component"; import moment from "moment"; -import { DataFilter } from "../../common-components/entity-subrecord/entity-subrecord/entity-subrecord-config"; -import { Filter } from "./filters"; +import { DataFilter, Filter } from "./filters"; import { isValidDate } from "../../../utils/utils"; /** diff --git a/src/app/core/filter/filters/filters.ts b/src/app/core/filter/filters/filters.ts index cca50198a3..78cd90664b 100644 --- a/src/app/core/filter/filters/filters.ts +++ b/src/app/core/filter/filters/filters.ts @@ -15,16 +15,7 @@ * along with ndb-core. If not, see . */ -import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/configurable-enum.interface"; -import { - BooleanFilterConfig, - DateRangeFilterConfigOption, -} from "../../entity-list/EntityListConfig"; import { Entity } from "../../entity/model/entity"; -import { DateRange } from "@angular/material/datepicker"; -import { isValidDate } from "../../../utils/utils"; -import { calculateDateRange } from "../../basic-datatypes/date/date-range-filter/date-range-filter-panel/date-range-filter-panel.component"; -import moment from "moment/moment"; import { MongoQuery } from "@casl/ability"; /** diff --git a/src/app/features/todos/todo-list/todo-list.component.ts b/src/app/features/todos/todo-list/todo-list.component.ts index f0be113766..81b7b2c3db 100644 --- a/src/app/features/todos/todo-list/todo-list.component.ts +++ b/src/app/features/todos/todo-list/todo-list.component.ts @@ -4,7 +4,10 @@ import { PrebuiltFilterConfig } from "../../../core/entity-list/EntityListConfig import { TodoDetailsComponent } from "../todo-details/todo-details.component"; import moment from "moment"; import { EntityListComponent } from "../../../core/entity-list/entity-list/entity-list.component"; -import { FilterSelectionOption } from "../../../core/filter/filters/filters"; +import { + DataFilter, + FilterSelectionOption, +} from "../../../core/filter/filters/filters"; import { RouteTarget } from "../../../route-target"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { Sort } from "@angular/material/sort"; From 6f7a9cb7ca92182a3c2bcecfa2ef908810fb1b6d Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 15 Jan 2024 15:05:03 +0100 Subject: [PATCH 07/11] small code cleanups --- src/app/core/filter/filter/filter.component.spec.ts | 2 +- src/app/core/filter/filter/filter.component.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/core/filter/filter/filter.component.spec.ts b/src/app/core/filter/filter/filter.component.spec.ts index 7ec5a1d0f8..42624cff98 100644 --- a/src/app/core/filter/filter/filter.component.spec.ts +++ b/src/app/core/filter/filter/filter.component.spec.ts @@ -9,7 +9,7 @@ import { MatSelectHarness } from "@angular/material/select/testing"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { ActivatedRoute } from "@angular/router"; -export class ActivatedRouteMock { +class ActivatedRouteMock { public snapshot = { queryParams: {}, }; diff --git a/src/app/core/filter/filter/filter.component.ts b/src/app/core/filter/filter/filter.component.ts index 213d355ad6..7c22b90d6c 100644 --- a/src/app/core/filter/filter/filter.component.ts +++ b/src/app/core/filter/filter/filter.component.ts @@ -130,8 +130,9 @@ export class FilterComponent implements OnChanges { const params = this.route.snapshot.queryParams; this.filterSelections.forEach((f) => { if (params.hasOwnProperty(f.name)) { - let values: string[] = params[f.name].split(","); - f.selectedOptionValues = values.filter((value) => value !== ""); + f.selectedOptionValues = params[f.name] + .split(",") + .filter((value) => value !== ""); } }); } From 1c38fd9bff4dba49e55313fb82371e61450bb351 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 15 Jan 2024 15:07:52 +0100 Subject: [PATCH 08/11] removed status prebuilt filter REQUIRES MIGRATION --- .../notes-manager/notes-manager.component.ts | 21 ------------------- src/app/core/config/config-fix.ts | 4 +--- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts b/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts index 1f01fad22c..aebb7b3ec0 100644 --- a/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts +++ b/src/app/child-dev-project/notes/notes-manager/notes-manager.component.ts @@ -14,7 +14,6 @@ import { EntityListComponent } from "../../../core/entity-list/entity-list/entit import { applyUpdate } from "../../../core/entity/model/entity-update"; import { EntityListConfig } from "../../../core/entity-list/EntityListConfig"; import { EventNote } from "../../attendance/model/event-note"; -import { WarningLevel } from "../../warning-level"; import { DynamicComponentConfig } from "../../../core/config/dynamic-components/dynamic-component-config.interface"; import { merge } from "rxjs"; import moment from "moment"; @@ -61,21 +60,6 @@ export class NotesManagerComponent implements OnInit { entityConstructor = Note; notes: Note[]; - private statusFS: FilterSelectionOption[] = [ - { - key: "urgent", - label: $localize`:Filter-option for notes:Urgent`, - filter: { "warningLevel.id": WarningLevel.URGENT } as DataFilter, - }, - { - key: "follow-up", - label: $localize`:Filter-option for notes:Needs Follow-Up`, - filter: { - "warningLevel.id": { $in: [WarningLevel.WARNING] }, - } as DataFilter, - }, - ]; - private dateFS: FilterSelectionOption[] = [ { key: "current-week", @@ -149,11 +133,6 @@ export class NotesManagerComponent implements OnInit { (filter) => filter.type === "prebuilt", )) { switch (prebuiltFilter.id) { - case "status": { - prebuiltFilter["options"] = this.statusFS; - prebuiltFilter["default"] = ""; - break; - } case "date": { prebuiltFilter["options"] = this.dateFS; prebuiltFilter["default"] = "current-week"; diff --git a/src/app/core/config/config-fix.ts b/src/app/core/config/config-fix.ts index 6f25242354..43439959a6 100644 --- a/src/app/core/config/config-fix.ts +++ b/src/app/core/config/config-fix.ts @@ -194,9 +194,7 @@ export const defaultJsonConfig = { }, "filters": [ { - "id": "status", - "label": $localize`:Filter label:Status`, - "type": "prebuilt" + "id": "warningLevel" }, { "id": "date", From f9d9ae2ec40237a6ec232b237e63f2de99a1a8ff Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 15 Jan 2024 15:18:32 +0100 Subject: [PATCH 09/11] more code cleanups --- .../configurable-enum/configurable-enum.service.ts | 2 +- .../date-range-filter/date-range-filter.component.spec.ts | 2 +- src/app/core/filter/filters/configurableEnumFilter.ts | 7 +++---- src/app/core/filter/filters/dateFilter.ts | 6 +++--- src/app/core/filter/filters/filters.ts | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts index d4d5e2c8c3..e78205f0f7 100644 --- a/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts +++ b/src/app/core/basic-datatypes/configurable-enum/configurable-enum.service.ts @@ -30,7 +30,7 @@ export class ConfigurableEnumService { getEnumValues( id: string, ): T[] { - let configurableEnum = this.getEnum(id); + const configurableEnum = this.getEnum(id); return configurableEnum ? (configurableEnum.values as T[]) : []; } diff --git a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts index df35a6a00e..52ffd25b2a 100644 --- a/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts +++ b/src/app/core/basic-datatypes/date/date-range-filter/date-range-filter.component.spec.ts @@ -64,7 +64,7 @@ describe("DateRangeFilterComponent", () => { it("should set the correct date filter when inputting a specific date range via the URL", () => { let dateFilter = new DateFilter("test", "test", []); - dateFilter.selectedOptionValues = ["1_2_3"]; + dateFilter.selectedOptionValues = ["1", "2", "3"]; component.filterConfig = dateFilter; expect(component.dateFilter.getFilter()).toEqual({}); diff --git a/src/app/core/filter/filters/configurableEnumFilter.ts b/src/app/core/filter/filters/configurableEnumFilter.ts index 21802e853f..9cf50edde4 100644 --- a/src/app/core/filter/filters/configurableEnumFilter.ts +++ b/src/app/core/filter/filters/configurableEnumFilter.ts @@ -10,14 +10,13 @@ export class ConfigurableEnumFilter< label: string, enumValues: ConfigurableEnumValue[], ) { - let options: FilterSelectionOption[] = []; - options.push( - ...enumValues.map((enumValue: ConfigurableEnumValue) => ({ + const options: FilterSelectionOption[] = enumValues.map( + (enumValue: ConfigurableEnumValue) => ({ key: enumValue.id, label: enumValue.label, color: enumValue.color, filter: { [name + ".id"]: enumValue.id } as DataFilter, - })), + }), ); super(name, options, label); } diff --git a/src/app/core/filter/filters/dateFilter.ts b/src/app/core/filter/filters/dateFilter.ts index 21b0cef8dd..a25ae6e19b 100644 --- a/src/app/core/filter/filters/dateFilter.ts +++ b/src/app/core/filter/filters/dateFilter.ts @@ -24,7 +24,7 @@ export class DateFilter extends Filter { * Returns the date range according to the selected option or dates */ getDateRange(): DateRange { - let selectedOption = this.getSelectedOption(); + const selectedOption = this.getSelectedOption(); if (selectedOption) { return calculateDateRange(selectedOption); } @@ -60,8 +60,8 @@ export class DateFilter extends Filter { dateStr1: string, dateStr2: string, ): DateRange { - let date1 = moment(dateStr1).toDate(); - let date2 = moment(dateStr2).toDate(); + const date1 = moment(dateStr1).toDate(); + const date2 = moment(dateStr2).toDate(); return new DateRange( isValidDate(date1) ? date1 : undefined, diff --git a/src/app/core/filter/filters/filters.ts b/src/app/core/filter/filters/filters.ts index 78cd90664b..9a4a6feab1 100644 --- a/src/app/core/filter/filters/filters.ts +++ b/src/app/core/filter/filters/filters.ts @@ -103,7 +103,7 @@ export class SelectableFilter extends Filter { * If the given key is undefined or invalid, the returned filter matches any elements. */ public getFilter(): DataFilter { - let filters: DataFilter[] = this.selectedOptionValues + const filters: DataFilter[] = this.selectedOptionValues .map((value: string) => this.getOption(value)) .filter((value: FilterSelectionOption) => value !== undefined) .map((previousValue: FilterSelectionOption) => { From 242baf1a1a161ea42963f74dbf3baf43606e25f5 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Mon, 15 Jan 2024 15:49:09 +0100 Subject: [PATCH 10/11] fix: pr feedback --- .../filter/filter-generator/filter-generator.service.spec.ts | 2 -- src/app/core/filter/filter.service.ts | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts index 821bb7228e..b85d56a2bc 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts @@ -237,8 +237,6 @@ describe("FilterGeneratorService", () => { const yesterdayNote = new Note(); const notes = [todayNote, yesterdayNote]; yesterdayNote.date = moment().subtract(1, "day").toDate(); - // const allFilter = filterOptions.options.find((f) => f.key === ""); - // expect(filter(notes, allFilter)).toEqual(notes); const todayFilter = filterOptions.options.find((f) => f.key === "today"); expect(filter(notes, todayFilter)).toEqual([todayNote]); const beforeFilter = filterOptions.options.find((f) => f.key === "before"); diff --git a/src/app/core/filter/filter.service.ts b/src/app/core/filter/filter.service.ts index 498f0e5261..c4b9d49cd0 100644 --- a/src/app/core/filter/filter.service.ts +++ b/src/app/core/filter/filter.service.ts @@ -10,9 +10,8 @@ import { } from "@ucast/mongo2js"; import moment from "moment"; import { ConfigurableEnumService } from "../basic-datatypes/configurable-enum/configurable-enum.service"; -import { Filter as EntityFilter } from "./filters/filters"; +import { DataFilter, Filter as EntityFilter } from "./filters/filters"; import { MongoQuery } from "@casl/ability"; -import { DataFilter } from "./filters/filters"; /** * Utility service to help handling and aligning filters with entities. @@ -54,7 +53,6 @@ export class FilterService { * ``` * @param filter a valid filter object, e.g. as provided by the `FilterComponent` */ - // todo: check usage for array usage (typing not working) getFilterPredicate(filter: DataFilter) { return this.filterFactory(filter as MongoQuery); } From 68459b95e000456ba76d7022cbee0818bbc0ae45 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Mon, 15 Jan 2024 15:49:33 +0100 Subject: [PATCH 11/11] fix: pr feedback --- .../filter/filter-generator/filter-generator.service.spec.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts index b85d56a2bc..1b33351185 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts @@ -155,11 +155,6 @@ describe("FilterGeneratorService", () => { expect(filterOptions.label).toEqual(schema.label); expect(filterOptions.name).toEqual("schoolId"); const allRelations = [csr1, csr2, csr3, csr4]; - // const allFilter: FilterSelectionOption = filterOptions.options.find( - // (opt) => opt.key === "all", - // ); - // expect(allFilter.label).toEqual("All"); - // expect(filter(allRelations, allFilter)).toEqual(allRelations); const school1Filter: FilterSelectionOption = filterOptions.options.find((opt) => opt.key === school1.getId()); expect(school1Filter.label).toEqual(school1.name);