import { DateField, TextField } from "@refinedev/antd"; import { UseQueryResult } from "@tanstack/react-query"; import { Button, Col, Dropdown, Row, Space, Spin } from "antd"; import { ColumnFilterItem, ColumnType } from "antd/es/table/interface"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import { AlignType } from "rc-table/lib/interface"; import { Link } from "react-router-dom"; import { getFiltersForField, typeFilters } from "../utils/filtering"; import { enrichText } from "../utils/parsing"; import { Field, FieldType } from "../utils/queryFields"; import { TableState } from "../utils/saveload"; import { getSortOrderForField, typeSorters } from "../utils/sorting"; import { NumberFieldUnit, NumberFieldUnitRange } from "./numberField"; import SpoolIcon from "./spoolIcon"; dayjs.extend(utc); const FilterDropdownLoading = () => { return ( Loading... ); }; interface Entity { id: number; } export interface Action { name: string; icon: React.ReactNode; link?: string; onClick?: () => void; } interface BaseColumnProps { id: string | string[]; dataId?: keyof Obj & string; i18ncat?: string; i18nkey?: string; title?: string; align?: AlignType; sorter?: boolean; t: (key: string) => string; navigate: (link: string) => void; dataSource: Obj[]; tableState: TableState; width?: number; actions?: (record: Obj) => Action[]; transform?: (value: unknown) => unknown; render?: (rawValue: string | undefined, record: Obj) => React.ReactNode; } interface FilteredColumnProps { filters?: ColumnFilterItem[]; filteredValue?: string[]; allowMultipleFilters?: boolean; onFilterDropdownOpen?: () => void; loadingFilters?: boolean; } interface CustomColumnProps { // eslint-disable-next-line @typescript-eslint/no-explicit-any render?: (value: any, record: Obj, index: number) => React.ReactNode; onCell?: ( data: Obj, index?: number // eslint-disable-next-line @typescript-eslint/no-explicit-any ) => React.HTMLAttributes | React.TdHTMLAttributes; } function Column( props: BaseColumnProps & FilteredColumnProps & CustomColumnProps ): ColumnType | undefined { const t = props.t; const navigate = props.navigate; // Hide if not in showColumns const id = Array.isArray(props.id) ? props.id.join(".") : props.id; if (props.tableState.showColumns && !props.tableState.showColumns.includes(id)) { return undefined; } const columnProps: ColumnType = { dataIndex: props.id, align: props.align, title: props.title ?? t(props.i18nkey ?? `${props.i18ncat}.fields.${props.id}`), filterMultiple: props.allowMultipleFilters ?? true, width: props.width ?? undefined, onCell: props.onCell ?? undefined, }; // Sorting if (props.sorter) { columnProps.sorter = true; columnProps.sortOrder = getSortOrderForField( typeSorters(props.tableState.sorters), props.dataId ?? (props.id as keyof Obj) ); } // Filter if (props.filters && props.filteredValue) { columnProps.filters = props.filters; columnProps.filteredValue = props.filteredValue; if (props.loadingFilters) { columnProps.filterDropdown = ; } columnProps.onFilterDropdownOpenChange = (open) => { if (open && props.onFilterDropdownOpen) { props.onFilterDropdownOpen(); } }; if (props.dataId) { columnProps.key = props.dataId; } } // Render const render = props.render ?? ((rawValue) => { const value = props.transform ? props.transform(rawValue) : rawValue; return <>{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={["click"]} >
{render(value, record, index)}
); }; return columnProps; } export function SortedColumn(props: BaseColumnProps) { return Column({ ...props, sorter: true, }); } export function RichColumn( props: Omit, "transform"> & { transform?: (value: unknown) => string } ) { return Column({ ...props, render: (rawValue: string | undefined) => { const value = props.transform ? props.transform(rawValue) : rawValue; return enrichText(value); }, }); } interface FilteredQueryColumnProps extends BaseColumnProps { filterValueQuery: UseQueryResult; allowMultipleFilters?: boolean; } export function FilteredQueryColumn(props: FilteredQueryColumnProps) { const query = props.filterValueQuery; let filters: ColumnFilterItem[] = []; if (query.data) { filters = query.data.map((item) => { if (typeof item === "string") { return { text: item, value: '"' + item + '"', }; } return item; }); } filters.push({ text: "", value: "", }); const typedFilters = typeFilters(props.tableState.filters); const filteredValue = getFiltersForField(typedFilters, props.dataId ?? (props.id as keyof Obj)); const onFilterDropdownOpen = () => { query.refetch(); }; return Column({ ...props, filters, filteredValue, onFilterDropdownOpen, loadingFilters: query.isLoading }); } interface NumberColumnProps extends BaseColumnProps { unit: string; maxDecimals?: number; minDecimals?: number; defaultText?: string; } export function NumberColumn(props: NumberColumnProps) { return Column({ ...props, align: "right", render: (rawValue) => { const value = props.transform ? props.transform(rawValue) : rawValue; if (value === null || value === undefined) { return ; } return ( ); }, }); } export function DateColumn(props: BaseColumnProps) { return Column({ ...props, render: (rawValue) => { const value = props.transform ? props.transform(rawValue) : rawValue; return (