import React from "react"; import { IResourceComponentsProps, useInvalidate, useTranslate } from "@refinedev/core"; import { useTable, List, EditButton, ShowButton, CloneButton } from "@refinedev/antd"; import { Table, Space, Button, Dropdown, Modal } from "antd"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import { ISpool } from "./model"; import { TableState, useInitialTableState, useSavedState, useStoreInitialState } from "../../utils/saveload"; import { EditOutlined, FilterOutlined, InboxOutlined, ToTopOutlined } from "@ant-design/icons"; import { DateColumn, FilteredQueryColumn, NumberColumn, RichColumn, SortedColumn, SpoolIconColumn, } from "../../components/column"; import { setSpoolArchived } from "./functions"; import SelectAndPrint from "../../components/selectAndPrintDialog"; import { useSpoolmanFilamentFilter, useSpoolmanLocations, useSpoolmanLotNumbers, useSpoolmanMaterials, } from "../../components/otherModels"; dayjs.extend(utc); const { confirm } = Modal; interface ISpoolCollapsed extends ISpool { combined_name: string; // Eg. "Prusa - PLA Red" "filament.id": number; "filament.material"?: string; } const namespace = "spoolList-v2"; export const SpoolList: React.FC = () => { const t = useTranslate(); const invalidate = useInvalidate(); // Load initial state const initialState = useInitialTableState(namespace); // State for the switch to show archived spools const [showArchived, setShowArchived] = useSavedState("spoolList-showArchived", false); // Fetch data from the API const { tableProps, sorters, setSorters, filters, setFilters, current, pageSize, setCurrent } = useTable({ meta: { queryParams: { ["allow_archived"]: showArchived, }, }, syncWithLocation: false, pagination: { mode: "server", current: initialState.pagination.current, pageSize: initialState.pagination.pageSize, }, sorters: { mode: "server", initial: initialState.sorters, }, filters: { mode: "server", initial: initialState.filters, }, }); // Create state for the columns to show const allColumns: (keyof ISpoolCollapsed & string)[] = [ "id", "combined_name", "filament.material", "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(initialState.showColumns ?? defaultColumns); // Store state in local storage const tableState: TableState = { sorters, filters, pagination: { current, pageSize }, showColumns, }; useStoreInitialState(namespace, 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, combined_name: filament_name, "filament.id": element.filament.id, "filament.material": element.filament.material, }; }), [tableProps.dataSource] ); // Function for opening an ant design modal that asks for confirmation for archiving a spool const archiveSpool = async (spool: ISpoolCollapsed, archive: boolean) => { await setSpoolArchived(spool, archive); invalidate({ resource: "spool", id: spool.id, invalidates: ["list", "detail"], }); }; const archiveSpoolPopup = async (spool: ISpoolCollapsed) => { // If the spool has no remaining weight, archive it immediately since it's likely not a mistake if (spool.remaining_weight && spool.remaining_weight == 0) { await archiveSpool(spool, true); } else { confirm({ title: t("spool.titles.archive"), content: t("spool.messages.archive"), okText: t("buttons.archive"), okType: "primary", cancelText: t("buttons.cancel"), onOk() { return archiveSpool(spool, true); }, }); } }; if (tableProps.pagination) { tableProps.pagination.showSizeChanger = true; } return ( ( <> ({ key: column, label: t(`spool.fields.${column.replace(".", "_")}`), })), selectedKeys: showColumns, selectable: true, multiple: true, onDeselect: (keys) => { setShowColumns(keys.selectedKeys); }, onSelect: (keys) => { setShowColumns(keys.selectedKeys); }, }} > {defaultButtons} )} > { if (record.archived) { return { style: { fontStyle: "italic", color: "#999", }, }; } else { return {}; } }} > {SortedColumn({ id: "id", i18ncat: "spool", dataSource, tableState, })} {SpoolIconColumn({ id: "combined_name", i18nkey: "spool.fields.filament_name", color: (record: ISpoolCollapsed) => record.filament.color_hex, dataSource, tableState, dataId: "filament.id", filterValueQuery: useSpoolmanFilamentFilter(), })} {FilteredQueryColumn({ id: "filament.material", i18nkey: "spool.fields.material", dataSource, tableState, filterValueQuery: useSpoolmanMaterials(), })} {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: 1, dataSource, tableState, })} {NumberColumn({ id: "remaining_length", i18ncat: "spool", unit: "mm", decimals: 1, defaultText: t("unknown"), dataSource, tableState, })} {FilteredQueryColumn({ id: "location", i18ncat: "spool", dataSource, tableState, filterValueQuery: useSpoolmanLocations(), })} {FilteredQueryColumn({ id: "lot_nr", i18ncat: "spool", dataSource, tableState, filterValueQuery: useSpoolmanLotNumbers(), })} {DateColumn({ id: "first_used", i18ncat: "spool", dataSource, tableState, })} {DateColumn({ id: "last_used", i18ncat: "spool", dataSource, tableState, })} {DateColumn({ id: "registered", i18ncat: "spool", dataSource, tableState, })} {RichColumn({ id: "comment", i18ncat: "spool", dataSource, tableState, })} ( {record.archived ? (
); }; export default SpoolList;