From a954ef3df5a90a281c0bbd68ab8045b15344656e Mon Sep 17 00:00:00 2001 From: Donkie Date: Thu, 6 Jul 2023 19:00:32 +0200 Subject: [PATCH] Client: Added filtering capabilities to filaments --- client/src/pages/filaments/list.tsx | 19 +++++-- client/src/utils/sorting.ts | 78 ++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/client/src/pages/filaments/list.tsx b/client/src/pages/filaments/list.tsx index 928ed46..da59043 100644 --- a/client/src/pages/filaments/list.tsx +++ b/client/src/pages/filaments/list.tsx @@ -18,6 +18,8 @@ import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import { IFilament } from "./model"; import { + filterPopulator, + genericFilterer, genericSorter, getSortOrderForField, } from "../../utils/sorting"; @@ -40,7 +42,7 @@ export const FilamentList: React.FC = () => { }); // Fetch data from the API - const { tableProps, sorters } = useTable({ + const { tableProps, sorters, filters } = useTable({ syncWithLocation: false, pagination: { mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work. @@ -49,6 +51,9 @@ export const FilamentList: React.FC = () => { mode: "off", // Disable server-side sorting initial: sorters_initial, }, + filters: { + mode: "off", // Disable server-side filtering + }, }); // Store sorter state in local storage @@ -68,15 +73,17 @@ export const FilamentList: React.FC = () => { } }); - // Sort dataSource by the sorters - dataSource.sort(genericSorter(sorters)); + // Filter dataSource by the filters + const filteredDataSource = dataSource.filter(genericFilterer(filters)); + // Sort dataSource by the sorters + filteredDataSource.sort(genericSorter(sorters)); return ( @@ -94,18 +101,21 @@ export const FilamentList: React.FC = () => { sorters_initial, "vendor_name" )} + filters={filterPopulator(dataSource, "vendor_name")} /> = () => { sorters_initial, "article_number" )} + filters={filterPopulator(dataSource, "article_number")} /> { + for (const filter of filters) { + if (!("field" in filter)) { + console.error("Filter must be of type LogicalFilter"); + return false; + } + if (!Array.isArray(filter.value)) { + console.error("Filter value must be an array of strings."); + return false; + } + let value = record[filter.field]; + if (value === undefined || value === null) { + value = ""; + } + if (typeof value !== "string") { + console.error("Only string fields can be filtered, field is of type " + typeof value); + return false; + } + switch (filter.operator) { + case "in": + if (!filter.value.includes(value)) { + return false; + } + break; + default: + console.error(`Not implemented operator: ${filter.operator}`); + return false; + } + } + return true; + } +} + +/** + * Populates an array of `ColumnFilterItem` objects based on the unique values of a given field in an array of objects. + * @param dataSource An array of objects to filter. + * @param field The name of the field to filter on. + * @returns An array of `ColumnFilterItem` objects representing the unique values of the specified field in the input array. + */ +export function filterPopulator(dataSource: IObj[], field: string): ColumnFilterItem[] { + const filters: ColumnFilterItem[] = []; + dataSource.forEach((element) => { + const value = element[field]; + if (typeof value === "string" && value !== "") { + // Make sure it's not already in the filters + if (filters.find((f) => f.value === value)) { + return; + } + filters.push({ + text: value, + value: value, + }); + } + }); + // Sort the filters + filters.sort((a, b) => { + if (typeof a.text !== "string" || typeof b.text !== "string") { + return 0; + } + return a.text.localeCompare(b.text); + }); + filters.push({ + text: "", + value: "", + }); + return filters; +}