Client: Save/load state cleanup, sync with rest
This commit is contained in:
@@ -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 || [])];
|
||||
|
||||
@@ -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>({
|
||||
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<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: 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"]}
|
||||
|
||||
90
client/src/pages/vendors/list.tsx
vendored
90
client/src/pages/vendors/list.tsx
vendored
@@ -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>({
|
||||
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<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: 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"
|
||||
>
|
||||
|
||||
28
client/src/utils/saveload.ts
Normal file
28
client/src/utils/saveload.ts
Normal 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]);
|
||||
}
|
||||
Reference in New Issue
Block a user