From 151c3c7f4f07ef3b65659c075db90cebc08c6ac4 Mon Sep 17 00:00:00 2001 From: Donkie Date: Sun, 29 Oct 2023 20:37:58 +0100 Subject: [PATCH] Client Table update * Columns are now specified using the ant design columns property instead of being Table.Column components. This should make this WoW a bit more sense, we're not pretending to be components anymore. column.tsx now only returns objects, not nodes. * Added filament/spool collapsing as a useTable queryOptions so that the output object is directly the one we want, which will make the types play better together. * Added a more unified record action system. This enables us to add actions to column rightclick/long press, which is better UX for mobile devices. * Actions column is now hidden on smaller screens thanks to the above bullet. --- client/src/components/column.tsx | 136 ++++++++-- client/src/pages/filaments/list.tsx | 382 ++++++++++++++------------ client/src/pages/spools/list.tsx | 399 +++++++++++++++------------- client/src/pages/vendors/list.tsx | 97 ++++--- client/src/utils/filtering.ts | 7 + 5 files changed, 589 insertions(+), 432 deletions(-) diff --git a/client/src/components/column.tsx b/client/src/components/column.tsx index d1ece4d..8f9569c 100644 --- a/client/src/components/column.tsx +++ b/client/src/components/column.tsx @@ -1,6 +1,5 @@ -import { Col, Row, Spin, Table } from "antd"; -import { ColumnProps as AntdColumnProps } from "antd/es/table"; -import { ColumnFilterItem } from "antd/es/table/interface"; +import { Button, Col, Dropdown, Row, Space, Spin } from "antd"; +import { ColumnFilterItem, ColumnType } from "antd/es/table/interface"; import { getFiltersForField, typeFilters } from "../utils/filtering"; import { TableState } from "../utils/saveload"; import { getSortOrderForField, typeSorters } from "../utils/sorting"; @@ -13,6 +12,7 @@ import SpoolIcon from "../icon_spool.svg?react"; import { useTranslate } from "@refinedev/core"; import { enrichText } from "../utils/parsing"; import { UseQueryResult } from "@tanstack/react-query"; +import { Link, useNavigate } from "react-router-dom"; dayjs.extend(utc); @@ -27,7 +27,18 @@ const FilterDropdownLoading = () => { ); }; -interface BaseColumnProps { +interface Entity { + id: number; +} + +export interface Action { + name: string; + icon: React.ReactNode; + link?: string; + onClick?: () => void; +} + +interface BaseColumnProps { id: keyof Obj & string; dataId?: keyof Obj & string; i18ncat?: string; @@ -35,6 +46,7 @@ interface BaseColumnProps { dataSource: Obj[]; tableState: TableState; width?: number; + actions?: (record: Obj) => Action[]; } interface FilteredColumnProps { @@ -55,22 +67,28 @@ interface CustomColumnProps { ) => React.HTMLAttributes | React.TdHTMLAttributes; } -function Column(props: BaseColumnProps & FilteredColumnProps & CustomColumnProps) { +function Column( + props: BaseColumnProps & FilteredColumnProps & CustomColumnProps +): ColumnType | undefined { const t = useTranslate(); + const navigate = useNavigate(); + + // Hide if not in showColumns if (props.tableState.showColumns && !props.tableState.showColumns.includes(props.id)) { - return <>; + return undefined; } - const typedSorters = typeSorters(props.tableState.sorters); - const columnProps: AntdColumnProps = { + + const columnProps: ColumnType = { dataIndex: props.id, title: t(props.i18nkey ?? `${props.i18ncat}.fields.${props.id}`), sorter: true, - sortOrder: getSortOrderForField(typedSorters, props.dataId ?? props.id), + sortOrder: getSortOrderForField(typeSorters(props.tableState.sorters), props.dataId ?? props.id), filterMultiple: props.allowMultipleFilters ?? true, + width: props.width ?? undefined, + onCell: props.onCell ?? undefined, }; - if (props.width) { - columnProps.width = props.width; - } + + // Filter if (props.filters && props.filteredValue) { columnProps.filters = props.filters; columnProps.filteredValue = props.filteredValue; @@ -86,20 +104,50 @@ function Column(props: BaseColumnProps & FilteredColumnProps & CustomC columnProps.key = props.dataId; } } - if (props.render) { - columnProps.render = props.render; - } - if (props.onCell) { - columnProps.onCell = props.onCell; - } - return {...columnProps} />; + + // Render + const render = props.render ?? ((value) => <>{value}); + columnProps.render = (value, record, index) => { + if (!props.actions) { + return render(value, record, index); + } + + const actions = props.actions(record); + + return ( + ({ + key: action.name, + label: action.name, + icon: action.icon, + })), + onClick: (item) => { + const action = actions.find((action) => action.name === item.key); + if (action) { + if (action.link) { + navigate(action.link); + } else if (action.onClick) { + action.onClick(); + } + } + }, + }} + trigger={["contextMenu"]} + > +
{render(value, record, index)}
+
+ ); + }; + + return columnProps; } -export function SortedColumn(props: BaseColumnProps) { +export function SortedColumn(props: BaseColumnProps) { return Column(props); } -export function RichColumn(props: BaseColumnProps) { +export function RichColumn(props: BaseColumnProps) { return Column({ ...props, render: (value: string | undefined) => { @@ -108,12 +156,12 @@ export function RichColumn(props: BaseColumnProps) { }); } -interface FilteredQueryColumnProps extends BaseColumnProps { +interface FilteredQueryColumnProps extends BaseColumnProps { filterValueQuery: UseQueryResult; allowMultipleFilters?: boolean; } -export function FilteredQueryColumn(props: FilteredQueryColumnProps) { +export function FilteredQueryColumn(props: FilteredQueryColumnProps) { const query = props.filterValueQuery; let filters: ColumnFilterItem[] = []; @@ -143,13 +191,13 @@ export function FilteredQueryColumn(props: FilteredQueryColumnProps) { return Column({ ...props, filters, filteredValue, onFilterDropdownOpen, loadingFilters: query.isLoading }); } -interface NumberColumnProps extends BaseColumnProps { +interface NumberColumnProps extends BaseColumnProps { unit: string; decimals?: number; defaultText?: string; } -export function NumberColumn(props: NumberColumnProps) { +export function NumberColumn(props: NumberColumnProps) { return Column({ ...props, render: (value) => { @@ -170,7 +218,7 @@ export function NumberColumn(props: NumberColumnProps) { }); } -export function DateColumn(props: BaseColumnProps) { +export function DateColumn(props: BaseColumnProps) { return Column({ ...props, render: (value) => { @@ -186,11 +234,43 @@ export function DateColumn(props: BaseColumnProps) { }); } -interface SpoolIconColumnProps extends FilteredQueryColumnProps { +export function ActionsColumn(actionsFn: (record: Obj) => Action[]): ColumnType | undefined { + const t = useTranslate(); + + return { + title: t("table.actions"), + responsive: ["lg"], + render: (_, record) => { + const buttons = actionsFn(record).map((action) => { + if (action.link) { + return ( + +