Client: Save/load state cleanup, sync with rest

This commit is contained in:
Donkie
2023-07-06 19:49:04 +02:00
parent f36eb86fc7
commit 30934842ff
4 changed files with 155 additions and 115 deletions

View File

@@ -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<IResourceComponentsProps> = () => {
// Load sorter state from local storage
const [sorters_initial] = React.useState<CrudSort[]>(() => {
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<CrudFilter[]>(() => {
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<IResourceComponentsProps> = () => {
},
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 || [])];

View File

@@ -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<IResourceComponentsProps> = () => {
// Load sorter state from local storage
const [sorters_initial] = React.useState<CrudSort[]>(() => {
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<ISpool>({
const { tableProps, sorters, filters, setSorters, setFilters } =
useTable<ISpool>({
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,
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<IResourceComponentsProps> = () => {
}
});
// Filter dataSource by the filters
const filteredDataSource = dataSource.filter(genericFilterer(filters));
// Sort dataSource by the sorters
filteredDataSource.sort(genericSorter(sorters));
return (
<List>
<List
headerButtons={({ defaultButtons }) => (
<>
<Button
type="primary"
icon={<FilterOutlined />}
onClick={() => {
setFilters([], "replace");
setSorters([{ field: "id", order: "asc" }]);
}}
>
Clear Filters
</Button>
{defaultButtons}
</>
)}
>
<Table
{...tableProps}
dataSource={dataSource}
dataSource={filteredDataSource}
pagination={{ showSizeChanger: true, pageSize: 20 }}
rowKey="id"
>
@@ -88,6 +106,8 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
title="Filament"
sorter={true}
sortOrder={getSortOrderForField(sorters, "filament_name")}
filters={filterPopulator(dataSource, "filament_name")}
filteredValue={getFiltersForField(filters, "filament_name")}
/>
<Table.Column
dataIndex="used_weight"
@@ -131,12 +151,16 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
title="Location"
sorter={true}
sortOrder={getSortOrderForField(sorters, "location")}
filters={filterPopulator(dataSource, "location")}
filteredValue={getFiltersForField(filters, "location")}
/>
<Table.Column
dataIndex="lot_nr"
title="Lot Nr"
sorter={true}
sortOrder={getSortOrderForField(sorters, "lot_nr")}
filters={filterPopulator(dataSource, "lot_nr")}
filteredValue={getFiltersForField(filters, "lot_nr")}
/>
<Table.Column
dataIndex={["first_used"]}

View File

@@ -1,9 +1,5 @@
import React from "react";
import {
IResourceComponentsProps,
BaseRecord,
CrudSort,
} from "@refinedev/core";
import { IResourceComponentsProps, BaseRecord } from "@refinedev/core";
import {
useTable,
List,
@@ -12,57 +8,77 @@ import {
DateField,
CloneButton,
} from "@refinedev/antd";
import { Table, Space } from "antd";
import { Table, Space, Button } from "antd";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import { genericSorter, getSortOrderForField } from "../../utils/sorting";
import {
genericFilterer,
genericSorter,
getSortOrderForField,
} from "../../utils/sorting";
import { IVendor } from "./model";
import {
useInitialTableState,
useStoreInitialState,
} from "../../utils/saveload";
import { FilterOutlined } from "@ant-design/icons";
dayjs.extend(utc);
export const VendorList: React.FC<IResourceComponentsProps> = () => {
// Load sorter state from local storage
const [sorters_initial] = React.useState<CrudSort[]>(() => {
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<IVendor>({
const { tableProps, sorters, filters, setSorters, setFilters } =
useTable<IVendor>({
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,
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 (
<List>
<List
headerButtons={({ defaultButtons }) => (
<>
<Button
type="primary"
icon={<FilterOutlined />}
onClick={() => {
setFilters([], "replace");
setSorters([{ field: "id", order: "asc" }]);
}}
>
Clear Filters
</Button>
{defaultButtons}
</>
)}
>
<Table
{...tableProps}
dataSource={dataSource}
dataSource={filteredDataSource}
pagination={{ showSizeChanger: true, pageSize: 20 }}
rowKey="id"
>

View File

@@ -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]);
}