212 lines
5.6 KiB
TypeScript
212 lines
5.6 KiB
TypeScript
import { Col, Row, Table } from "antd";
|
|
import { ColumnProps as AntdColumnProps } from "antd/es/table";
|
|
import { ColumnFilterItem } from "antd/es/table/interface";
|
|
import { getFiltersForField, typeFilters } from "../utils/filtering";
|
|
import { TableState } from "../utils/saveload";
|
|
import { getSortOrderForField, typeSorters } from "../utils/sorting";
|
|
import { NumberFieldUnit } from "./numberField";
|
|
import dayjs from "dayjs";
|
|
import utc from "dayjs/plugin/utc";
|
|
import { DateField, TextField } from "@refinedev/antd";
|
|
import Icon from "@ant-design/icons";
|
|
import { ReactComponent as SpoolIcon } from "../icon_spool.svg";
|
|
import { useTranslate } from "@refinedev/core";
|
|
import { enrichText } from "../utils/parsing";
|
|
import { UseQueryResult } from "@tanstack/react-query";
|
|
|
|
dayjs.extend(utc);
|
|
|
|
interface BaseColumnProps<Obj> {
|
|
id: keyof Obj & string;
|
|
i18ncat?: string;
|
|
i18nkey?: string;
|
|
dataSource: Obj[];
|
|
tableState: TableState;
|
|
}
|
|
|
|
interface FilteredColumnProps {
|
|
filters?: ColumnFilterItem[];
|
|
filteredValue?: string[];
|
|
allowMultipleFilters?: boolean;
|
|
}
|
|
|
|
interface CustomColumnProps<Obj> {
|
|
// 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<any> | React.TdHTMLAttributes<any>;
|
|
}
|
|
|
|
function Column<Obj>(props: BaseColumnProps<Obj> & FilteredColumnProps & CustomColumnProps<Obj>) {
|
|
const t = useTranslate();
|
|
if (props.tableState.showColumns && !props.tableState.showColumns.includes(props.id)) {
|
|
return <></>;
|
|
}
|
|
const typedSorters = typeSorters<Obj>(props.tableState.sorters);
|
|
const columnProps: AntdColumnProps<Obj> = {
|
|
dataIndex: props.id,
|
|
title: t(props.i18nkey ?? `${props.i18ncat}.fields.${props.id}`),
|
|
sorter: true,
|
|
sortOrder: getSortOrderForField(typedSorters, props.id),
|
|
filterMultiple: props.allowMultipleFilters ?? true,
|
|
};
|
|
if (props.filters && props.filteredValue) {
|
|
columnProps.filters = props.filters;
|
|
columnProps.filteredValue = props.filteredValue;
|
|
}
|
|
if (props.render) {
|
|
columnProps.render = props.render;
|
|
}
|
|
if (props.onCell) {
|
|
columnProps.onCell = props.onCell;
|
|
}
|
|
return <Table.Column<Obj> {...columnProps} />;
|
|
}
|
|
|
|
export function SortedColumn<Obj>(props: BaseColumnProps<Obj>) {
|
|
return Column(props);
|
|
}
|
|
|
|
export function RichColumn<Obj>(props: BaseColumnProps<Obj>) {
|
|
return Column({
|
|
...props,
|
|
render: (value: string | undefined) => {
|
|
return enrichText(value);
|
|
},
|
|
});
|
|
}
|
|
|
|
interface FilteredQueryColumnProps<Obj> extends BaseColumnProps<Obj> {
|
|
filterValueQuery: UseQueryResult<string[]>;
|
|
allowMultipleFilters?: boolean;
|
|
}
|
|
|
|
export function FilteredQueryColumn<Obj>(props: FilteredQueryColumnProps<Obj>) {
|
|
const query = props.filterValueQuery;
|
|
|
|
let filters: ColumnFilterItem[] = [];
|
|
if (query.data) {
|
|
filters = query.data.map((item) => {
|
|
return {
|
|
text: item,
|
|
value: item,
|
|
};
|
|
});
|
|
}
|
|
filters.push({
|
|
text: "<empty>",
|
|
value: "",
|
|
});
|
|
|
|
const typedFilters = typeFilters<Obj>(props.tableState.filters);
|
|
const filteredValue = getFiltersForField(typedFilters, props.id);
|
|
|
|
return Column({ ...props, filters, filteredValue });
|
|
}
|
|
|
|
interface NumberColumnProps<Obj> extends BaseColumnProps<Obj> {
|
|
unit: string;
|
|
decimals?: number;
|
|
defaultText?: string;
|
|
}
|
|
|
|
export function NumberColumn<Obj>(props: NumberColumnProps<Obj>) {
|
|
return Column({
|
|
...props,
|
|
render: (value) => {
|
|
if (value === null || value === undefined) {
|
|
return <TextField value={props.defaultText ?? ""} />;
|
|
}
|
|
return (
|
|
<NumberFieldUnit
|
|
value={value}
|
|
unit={props.unit}
|
|
options={{
|
|
maximumFractionDigits: props.decimals ?? 0,
|
|
minimumFractionDigits: props.decimals ?? 0,
|
|
}}
|
|
/>
|
|
);
|
|
},
|
|
});
|
|
}
|
|
|
|
export function DateColumn<Obj>(props: BaseColumnProps<Obj>) {
|
|
return Column({
|
|
...props,
|
|
render: (value) => {
|
|
return (
|
|
<DateField
|
|
hidden={!value}
|
|
value={dayjs.utc(value).local()}
|
|
title={dayjs.utc(value).local().format()}
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
/>
|
|
);
|
|
},
|
|
});
|
|
}
|
|
|
|
interface SpoolIconColumnProps<Obj> extends FilteredQueryColumnProps<Obj> {
|
|
color: (record: Obj) => string | undefined;
|
|
}
|
|
|
|
export function SpoolIconColumn<Obj>(props: SpoolIconColumnProps<Obj>) {
|
|
const query = props.filterValueQuery;
|
|
|
|
let filters: ColumnFilterItem[] = [];
|
|
if (query.data) {
|
|
filters = query.data.map((item) => {
|
|
return {
|
|
text: item,
|
|
value: item,
|
|
};
|
|
});
|
|
}
|
|
filters.push({
|
|
text: "<empty>",
|
|
value: "",
|
|
});
|
|
|
|
const typedFilters = typeFilters<Obj>(props.tableState.filters);
|
|
const filteredValue = getFiltersForField(typedFilters, props.id);
|
|
|
|
return Column({
|
|
...props,
|
|
filters,
|
|
filteredValue,
|
|
onCell: () => {
|
|
return {
|
|
style: {
|
|
paddingLeft: 0,
|
|
paddingTop: 0,
|
|
paddingBottom: 0,
|
|
},
|
|
};
|
|
},
|
|
render: (value, record: Obj) => {
|
|
const colorStr = props.color(record);
|
|
return (
|
|
<Row wrap={false} justify="space-around" align="middle">
|
|
{colorStr && (
|
|
<Col flex="none">
|
|
<Icon
|
|
component={SpoolIcon}
|
|
style={{
|
|
color: "#" + colorStr,
|
|
fontSize: 42,
|
|
marginRight: 0,
|
|
}}
|
|
/>
|
|
</Col>
|
|
)}
|
|
<Col flex="auto">{value}</Col>
|
|
</Row>
|
|
);
|
|
},
|
|
});
|
|
}
|