From 30934842ff39e476a5b9ecfa89c86d16d3ebf4c4 Mon Sep 17 00:00:00 2001 From: Donkie Date: Thu, 6 Jul 2023 19:49:04 +0200 Subject: [PATCH] Client: Save/load state cleanup, sync with rest --- client/src/pages/filaments/list.tsx | 50 +++----------- client/src/pages/spools/list.tsx | 102 +++++++++++++++++----------- client/src/pages/vendors/list.tsx | 90 ++++++++++++++---------- client/src/utils/saveload.ts | 28 ++++++++ 4 files changed, 155 insertions(+), 115 deletions(-) create mode 100644 client/src/utils/saveload.ts diff --git a/client/src/pages/filaments/list.tsx b/client/src/pages/filaments/list.tsx index 21d2ed5..fb28313 100644 --- a/client/src/pages/filaments/list.tsx +++ b/client/src/pages/filaments/list.tsx @@ -1,10 +1,5 @@ import React from "react"; -import { - IResourceComponentsProps, - BaseRecord, - CrudSort, - CrudFilter, -} from "@refinedev/core"; +import { IResourceComponentsProps, BaseRecord } from "@refinedev/core"; import { useTable, List, @@ -26,32 +21,16 @@ import { getSortOrderForField, } from "../../utils/sorting"; import { FilterOutlined } from "@ant-design/icons"; +import { + useInitialTableState, + useStoreInitialState, +} from "../../utils/saveload"; dayjs.extend(utc); export const FilamentList: React.FC = () => { - // Load sorter state from local storage - const [sorters_initial] = React.useState(() => { - const storedSorters = localStorage.getItem("filamentListSorters"); - if (storedSorters) { - return JSON.parse(storedSorters); - } - return [ - { - field: "id", - order: "asc", - }, - ]; - }); - - // Load filter state from local storage - const [filters_initial] = React.useState(() => { - const storedFilters = localStorage.getItem("filamentListFilters"); - if (storedFilters) { - return JSON.parse(storedFilters); - } - return []; - }); + // Load initial state + const initialState = useInitialTableState("filamentList"); // Fetch data from the API const { tableProps, sorters, filters, setSorters, setFilters } = @@ -62,23 +41,16 @@ export const FilamentList: React.FC = () => { }, sorters: { mode: "off", // Disable server-side sorting - initial: sorters_initial, + initial: initialState.sorters, }, filters: { mode: "off", // Disable server-side filtering - initial: filters_initial, + initial: initialState.filters, }, }); - // Store sorter state in local storage - React.useEffect(() => { - localStorage.setItem("filamentListSorters", JSON.stringify(sorters)); - }, [sorters]); - - // Store filter state in local storage - React.useEffect(() => { - localStorage.setItem("filamentListFilters", JSON.stringify(filters)); - }, [filters]); + // Store state in local storage + useStoreInitialState("filamentList", { sorters, filters }); // Copy dataSource to avoid mutating the original const dataSource = [...(tableProps.dataSource || [])]; diff --git a/client/src/pages/spools/list.tsx b/client/src/pages/spools/list.tsx index b610f9e..728946a 100644 --- a/client/src/pages/spools/list.tsx +++ b/client/src/pages/spools/list.tsx @@ -1,9 +1,5 @@ import React from "react"; -import { - IResourceComponentsProps, - BaseRecord, - CrudSort, -} from "@refinedev/core"; +import { IResourceComponentsProps, BaseRecord } from "@refinedev/core"; import { useTable, List, @@ -13,53 +9,53 @@ import { TextField, CloneButton, } from "@refinedev/antd"; -import { Table, Space } from "antd"; +import { Table, Space, Button } from "antd"; import { NumberFieldUnit } from "../../components/numberField"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; -import { genericSorter, getSortOrderForField } from "../../utils/sorting"; +import { + filterPopulator, + genericFilterer, + genericSorter, + getFiltersForField, + getSortOrderForField, +} from "../../utils/sorting"; import { ISpool } from "./model"; +import { + useInitialTableState, + useStoreInitialState, +} from "../../utils/saveload"; +import { FilterOutlined } from "@ant-design/icons"; dayjs.extend(utc); export const SpoolList: React.FC = () => { - // Load sorter state from local storage - const [sorters_initial] = React.useState(() => { - const storedSorters = localStorage.getItem("spoolListSorters"); - if (storedSorters) { - return JSON.parse(storedSorters); - } - return [ - { - field: "id", - order: "asc", - }, - ]; - }); + // Load initial state + const initialState = useInitialTableState("spoolList"); // Fetch data from the API - const { tableProps, sorters } = useTable({ - syncWithLocation: false, - pagination: { - mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work. - }, - sorters: { - mode: "off", // Disable server-side sorting - initial: sorters_initial, - }, - }); + const { tableProps, sorters, filters, setSorters, setFilters } = + useTable({ + syncWithLocation: false, + pagination: { + mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work. + }, + sorters: { + mode: "off", // Disable server-side sorting + initial: initialState.sorters, + }, + filters: { + mode: "off", // Disable server-side filtering + initial: initialState.filters, + }, + }); - // Store sorter state in local storage - React.useEffect(() => { - localStorage.setItem("spoolListSorters", JSON.stringify(sorters)); - }, [sorters]); + // Store state in local storage + useStoreInitialState("spoolList", { sorters, filters }); // Copy dataSource to avoid mutating the original const dataSource = [...(tableProps.dataSource || [])]; - // Sort dataSource by the sorters - dataSource.sort(genericSorter(sorters)); - // Add a filament_name field to the dataSource dataSource.forEach((element) => { if (element.filament.vendor && "name" in element.filament.vendor) { @@ -69,11 +65,33 @@ export const SpoolList: React.FC = () => { } }); + // Filter dataSource by the filters + const filteredDataSource = dataSource.filter(genericFilterer(filters)); + + // Sort dataSource by the sorters + filteredDataSource.sort(genericSorter(sorters)); + return ( - + ( + <> + + {defaultButtons} + + )} + > @@ -88,6 +106,8 @@ export const SpoolList: React.FC = () => { title="Filament" sorter={true} sortOrder={getSortOrderForField(sorters, "filament_name")} + filters={filterPopulator(dataSource, "filament_name")} + filteredValue={getFiltersForField(filters, "filament_name")} /> = () => { title="Location" sorter={true} sortOrder={getSortOrderForField(sorters, "location")} + filters={filterPopulator(dataSource, "location")} + filteredValue={getFiltersForField(filters, "location")} /> = () => { - // Load sorter state from local storage - const [sorters_initial] = React.useState(() => { - const storedSorters = localStorage.getItem("vendorListSorters"); - if (storedSorters) { - return JSON.parse(storedSorters); - } - return [ - { - field: "id", - order: "asc", - }, - ]; - }); + // Load initial state + const initialState = useInitialTableState("vendorList"); // Fetch data from the API - const { tableProps, sorters } = useTable({ - syncWithLocation: false, - pagination: { - mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work. - }, - sorters: { - mode: "off", // Disable server-side sorting - initial: sorters_initial, - }, - }); + const { tableProps, sorters, filters, setSorters, setFilters } = + useTable({ + syncWithLocation: false, + pagination: { + mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work. + }, + sorters: { + mode: "off", // Disable server-side sorting + initial: initialState.sorters, + }, + filters: { + mode: "off", // Disable server-side filtering + initial: initialState.filters, + }, + }); - // Store sorter state in local storage - React.useEffect(() => { - localStorage.setItem("vendorListSorters", JSON.stringify(sorters)); - }, [sorters]); + // Store state in local storage + useStoreInitialState("vendorList", { sorters, filters }); // Copy dataSource to avoid mutating the original const dataSource = [...(tableProps.dataSource || [])]; + // Filter dataSource by the filters + const filteredDataSource = dataSource.filter(genericFilterer(filters)); + // Sort dataSource by the sorters - dataSource.sort(genericSorter(sorters)); + filteredDataSource.sort(genericSorter(sorters)); return ( - + ( + <> + + {defaultButtons} + + )} + >
diff --git a/client/src/utils/saveload.ts b/client/src/utils/saveload.ts new file mode 100644 index 0000000..b07ca65 --- /dev/null +++ b/client/src/utils/saveload.ts @@ -0,0 +1,28 @@ +import React from "react"; +import { CrudFilter, CrudSort } from "@refinedev/core"; + +interface TableState { + sorters: CrudSort[]; + filters: CrudFilter[]; +} + +export function useInitialTableState(tableId: string): TableState { + const [initialState] = React.useState(() => { + const savedSorters = localStorage.getItem(`${tableId}-sorters`); + const savedFilters = localStorage.getItem(`${tableId}-filters`); + const sorters = savedSorters ? JSON.parse(savedSorters) : [{ field: "id", order: "asc" }]; + const filters = savedFilters ? JSON.parse(savedFilters) : []; + return { sorters, filters }; + }); + return initialState; +} + +export function useStoreInitialState(tableId: string, state: TableState) { + React.useEffect(() => { + localStorage.setItem(`${tableId}-sorters`, JSON.stringify(state.sorters)); + }, [tableId, state.sorters]); + + React.useEffect(() => { + localStorage.setItem(`${tableId}-filters`, JSON.stringify(state.filters)); + }, [tableId, state.filters]); +}