304 lines
7.7 KiB
TypeScript
304 lines
7.7 KiB
TypeScript
import React from "react";
|
|
import {
|
|
IResourceComponentsProps,
|
|
BaseRecord,
|
|
useTranslate,
|
|
} from "@refinedev/core";
|
|
import {
|
|
useTable,
|
|
List,
|
|
EditButton,
|
|
ShowButton,
|
|
CloneButton,
|
|
} from "@refinedev/antd";
|
|
import { Table, Space, Button, Dropdown } from "antd";
|
|
import dayjs from "dayjs";
|
|
import utc from "dayjs/plugin/utc";
|
|
import { genericSorter, typeSorters } from "../../utils/sorting";
|
|
import { genericFilterer, typeFilters } from "../../utils/filtering";
|
|
import { ISpool } from "./model";
|
|
import {
|
|
TableState,
|
|
useInitialTableState,
|
|
useStoreInitialState,
|
|
} from "../../utils/saveload";
|
|
import { EditOutlined, FilterOutlined } from "@ant-design/icons";
|
|
import {
|
|
DateColumn,
|
|
FilteredColumn,
|
|
NumberColumn,
|
|
SortedColumn,
|
|
SpoolIconColumn,
|
|
} from "../../components/column";
|
|
|
|
dayjs.extend(utc);
|
|
|
|
interface ISpoolCollapsed extends ISpool {
|
|
filament_name: string;
|
|
}
|
|
|
|
export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
|
const t = useTranslate();
|
|
|
|
// Load initial state
|
|
const initialState = useInitialTableState("spoolList");
|
|
|
|
// Fetch data from the API
|
|
const {
|
|
tableProps,
|
|
sorters,
|
|
setSorters,
|
|
filters,
|
|
setFilters,
|
|
current,
|
|
pageSize,
|
|
setCurrent,
|
|
setPageSize,
|
|
} = useTable<ISpool>({
|
|
syncWithLocation: false,
|
|
pagination: {
|
|
mode: "off", // Perform pagination in antd's Table instead. Otherwise client-side sorting/filtering doesn't work.
|
|
current: initialState.pagination.current,
|
|
pageSize: initialState.pagination.pageSize,
|
|
},
|
|
sorters: {
|
|
mode: "off", // Disable server-side sorting
|
|
initial: initialState.sorters,
|
|
},
|
|
filters: {
|
|
mode: "off", // Disable server-side filtering
|
|
initial: initialState.filters,
|
|
},
|
|
});
|
|
|
|
// Create state for the columns to show
|
|
const allColumns: (keyof ISpoolCollapsed & string)[] = [
|
|
"id",
|
|
"filament_name",
|
|
"used_weight",
|
|
"remaining_weight",
|
|
"used_length",
|
|
"remaining_length",
|
|
"location",
|
|
"lot_nr",
|
|
"first_used",
|
|
"last_used",
|
|
"registered",
|
|
"comment",
|
|
];
|
|
const defaultColumns = allColumns.filter(
|
|
(column_id) =>
|
|
["registered", "used_length", "remaining_length", "lot_nr"].indexOf(
|
|
column_id
|
|
) === -1
|
|
);
|
|
const [showColumns, setShowColumns] = React.useState<string[]>(
|
|
initialState.showColumns ?? defaultColumns
|
|
);
|
|
|
|
// Type the sorters and filters
|
|
const typedSorters = typeSorters<ISpoolCollapsed>(sorters);
|
|
const typedFilters = typeFilters<ISpoolCollapsed>(filters);
|
|
|
|
// Store state in local storage
|
|
const tableState: TableState = {
|
|
sorters,
|
|
filters,
|
|
pagination: { current, pageSize },
|
|
showColumns,
|
|
};
|
|
useStoreInitialState("spoolList", tableState);
|
|
|
|
// Collapse the dataSource to a mutable list and add a filament_name field
|
|
const dataSource: ISpoolCollapsed[] = React.useMemo(
|
|
() =>
|
|
(tableProps.dataSource ?? []).map((element) => {
|
|
let filament_name: string;
|
|
if (element.filament.vendor && "name" in element.filament.vendor) {
|
|
filament_name = `${element.filament.vendor.name} - ${element.filament.name}`;
|
|
} else {
|
|
filament_name =
|
|
element.filament.name ?? element.filament.id.toString();
|
|
}
|
|
return { ...element, filament_name };
|
|
}),
|
|
[tableProps.dataSource]
|
|
);
|
|
|
|
// Filter and sort the dataSource
|
|
const filteredDataSource = React.useMemo(() => {
|
|
const filtered = dataSource.filter(genericFilterer(typedFilters));
|
|
filtered.sort(genericSorter(typedSorters));
|
|
return filtered;
|
|
}, [dataSource, typedFilters, typedSorters]);
|
|
|
|
return (
|
|
<List
|
|
headerButtons={({ defaultButtons }) => (
|
|
<>
|
|
<Button
|
|
type="primary"
|
|
icon={<FilterOutlined />}
|
|
onClick={() => {
|
|
setFilters([], "replace");
|
|
setSorters([{ field: "id", order: "asc" }]);
|
|
setCurrent(1);
|
|
}}
|
|
>
|
|
{t("buttons.clearFilters")}
|
|
</Button>
|
|
<Dropdown
|
|
trigger={["click"]}
|
|
menu={{
|
|
items: allColumns.map((column) => ({
|
|
key: column,
|
|
label: t(`spool.fields.${column}`),
|
|
})),
|
|
selectedKeys: showColumns,
|
|
selectable: true,
|
|
multiple: true,
|
|
onDeselect: (keys) => {
|
|
setShowColumns(keys.selectedKeys);
|
|
},
|
|
onSelect: (keys) => {
|
|
setShowColumns(keys.selectedKeys);
|
|
},
|
|
}}
|
|
>
|
|
<Button type="primary" icon={<EditOutlined />}>
|
|
{t("buttons.hideColumns")}
|
|
</Button>
|
|
</Dropdown>
|
|
{defaultButtons}
|
|
</>
|
|
)}
|
|
>
|
|
<Table
|
|
{...tableProps}
|
|
dataSource={filteredDataSource}
|
|
pagination={{
|
|
showSizeChanger: true,
|
|
current: current,
|
|
pageSize: pageSize,
|
|
onChange: (page, pageSize) => {
|
|
setCurrent(page);
|
|
setPageSize(pageSize);
|
|
},
|
|
}}
|
|
rowKey="id"
|
|
>
|
|
{SortedColumn({
|
|
id: "id",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{SpoolIconColumn({
|
|
id: "filament_name",
|
|
i18ncat: "spool",
|
|
color: (record: ISpoolCollapsed) => record.filament.color_hex,
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{NumberColumn({
|
|
id: "used_weight",
|
|
i18ncat: "spool",
|
|
unit: "g",
|
|
decimals: 1,
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{NumberColumn({
|
|
id: "remaining_weight",
|
|
i18ncat: "spool",
|
|
unit: "g",
|
|
decimals: 1,
|
|
defaultText: t("unknown"),
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{NumberColumn({
|
|
id: "used_length",
|
|
i18ncat: "spool",
|
|
unit: "mm",
|
|
decimals: 0,
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{NumberColumn({
|
|
id: "remaining_length",
|
|
i18ncat: "spool",
|
|
unit: "mm",
|
|
decimals: 0,
|
|
defaultText: t("unknown"),
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{FilteredColumn({
|
|
id: "location",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{FilteredColumn({
|
|
id: "lot_nr",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{DateColumn({
|
|
id: "first_used",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{DateColumn({
|
|
id: "last_used",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{DateColumn({
|
|
id: "registered",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
{SortedColumn({
|
|
id: "comment",
|
|
i18ncat: "spool",
|
|
dataSource,
|
|
tableState,
|
|
})}
|
|
<Table.Column
|
|
title={t("table.actions")}
|
|
render={(_, record: BaseRecord) => (
|
|
<Space>
|
|
<EditButton
|
|
hideText
|
|
title={t("buttons.edit")}
|
|
size="small"
|
|
recordItemId={record.id}
|
|
/>
|
|
<ShowButton
|
|
hideText
|
|
title={t("buttons.show")}
|
|
size="small"
|
|
recordItemId={record.id}
|
|
/>
|
|
<CloneButton
|
|
hideText
|
|
title={t("buttons.clone")}
|
|
size="small"
|
|
recordItemId={record.id}
|
|
/>
|
|
</Space>
|
|
)}
|
|
/>
|
|
</Table>
|
|
</List>
|
|
);
|
|
};
|
|
|
|
export default SpoolList;
|