From fb23024ed0e60df9600734b1e2027c6317c38958 Mon Sep 17 00:00:00 2001 From: Donkie Date: Tue, 23 Jan 2024 18:58:04 +0100 Subject: [PATCH] Extra fields now finished for filaments --- client/public/locales/en/common.json | 2 +- client/src/components/column.tsx | 193 ++++++++++++++-- client/src/components/dateTimePicker.tsx | 33 +++ client/src/components/extraFields.tsx | 211 ++++++++++++++++++ client/src/components/inputNumberRange.tsx | 57 +++++ client/src/components/numberField.tsx | 33 +++ client/src/pages/filaments/create.tsx | 45 +++- client/src/pages/filaments/edit.tsx | 39 +++- client/src/pages/filaments/list.tsx | 110 ++++----- client/src/pages/filaments/model.tsx | 1 + client/src/pages/filaments/show.tsx | 8 +- .../pages/settings/ExtraFieldsSettings.tsx | 68 ++---- client/src/pages/settings/GeneralSettings.tsx | 2 +- client/src/pages/spools/list.tsx | 22 +- client/src/pages/vendors/list.tsx | 2 + .../{pages/settings => utils}/queryFields.ts | 2 +- .../settings => utils}/querySettings.ts | 0 spoolman/extra_fields.py | 10 +- 18 files changed, 699 insertions(+), 139 deletions(-) create mode 100644 client/src/components/dateTimePicker.tsx create mode 100644 client/src/components/extraFields.tsx create mode 100644 client/src/components/inputNumberRange.tsx rename client/src/{pages/settings => utils}/queryFields.ts (98%) rename client/src/{pages/settings => utils}/querySettings.ts (100%) diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index 1386cb4..21c1326 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -264,7 +264,7 @@ }, "extra_fields": { "tab": "Extra Fields", - "description": "

Here you can add extra custom fields to your entities.

Once a field is added, you can not change its key or type, and for choice type fields you can not remove choices or change the multi choice state. If you remove a field, the associated data for all entities will be deleted.

Default value is only applied to new items.

Extra fields can not be sorted or filtered in the table views.

", + "description": "

Here you can add extra custom fields to your entities.

Once a field is added, you can not change its key or type, and for choice type fields you can not remove choices or change the multi choice state. If you remove a field, the associated data for all entities will be deleted.

The key is what other programs read/write the data as, so if your custom field is supposed to integrate with a third-party program, make sure to set it correctly. Default value is only applied to new items.

Extra fields can not be sorted or filtered in the table views.

", "params": { "key": "Key", "name": "Name", diff --git a/client/src/components/column.tsx b/client/src/components/column.tsx index 53f89a7..a773e3d 100644 --- a/client/src/components/column.tsx +++ b/client/src/components/column.tsx @@ -3,7 +3,7 @@ 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"; -import { NumberFieldUnit } from "./numberField"; +import { NumberFieldUnit, NumberFieldUnitRange } from "./numberField"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import { DateField, TextField } from "@refinedev/antd"; @@ -12,7 +12,8 @@ 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"; +import { Link } from "react-router-dom"; +import { Field, FieldType } from "../utils/queryFields"; dayjs.extend(utc); @@ -39,14 +40,19 @@ export interface Action { } interface BaseColumnProps { - id: keyof Obj & string; + id: string | string[]; dataId?: keyof Obj & string; i18ncat?: string; i18nkey?: string; + title?: string; + sorter?: boolean; + t: (key: string) => string; + navigate: (link: string) => void; dataSource: Obj[]; tableState: TableState; width?: number; actions?: (record: Obj) => Action[]; + transform?: (value: unknown) => unknown; } interface FilteredColumnProps { @@ -70,24 +76,32 @@ interface CustomColumnProps { function Column( props: BaseColumnProps & FilteredColumnProps & CustomColumnProps ): ColumnType | undefined { - const t = useTranslate(); - const navigate = useNavigate(); + const t = props.t; + const navigate = props.navigate; // Hide if not in showColumns - if (props.tableState.showColumns && !props.tableState.showColumns.includes(props.id)) { + 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, - title: t(props.i18nkey ?? `${props.i18ncat}.fields.${props.id}`), - sorter: true, - sortOrder: getSortOrderForField(typeSorters(props.tableState.sorters), props.dataId ?? props.id), + 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; @@ -106,7 +120,12 @@ function Column( } // Render - const render = props.render ?? ((value) => <>{value}); + 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); @@ -144,13 +163,19 @@ function Column( } export function SortedColumn(props: BaseColumnProps) { - return Column(props); -} - -export function RichColumn(props: BaseColumnProps) { return Column({ ...props, - render: (value: string | undefined) => { + 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); }, }); @@ -182,7 +207,7 @@ export function FilteredQueryColumn(props: FilteredQueryColu }); const typedFilters = typeFilters(props.tableState.filters); - const filteredValue = getFiltersForField(typedFilters, props.dataId ?? props.id); + const filteredValue = getFiltersForField(typedFilters, props.dataId ?? (props.id as keyof Obj)); const onFilterDropdownOpen = () => { query.refetch(); @@ -193,14 +218,16 @@ export function FilteredQueryColumn(props: FilteredQueryColu interface NumberColumnProps extends BaseColumnProps { unit: string; - decimals?: number; + maxDecimals?: number; + minDecimals?: number; defaultText?: string; } export function NumberColumn(props: NumberColumnProps) { return Column({ ...props, - render: (value) => { + render: (rawValue) => { + const value = props.transform ? props.transform(rawValue) : rawValue; if (value === null || value === undefined) { return ; } @@ -209,8 +236,8 @@ export function NumberColumn(props: NumberColumnProps) value={value} unit={props.unit} options={{ - maximumFractionDigits: props.decimals ?? 0, - minimumFractionDigits: props.decimals ?? 0, + maximumFractionDigits: props.maxDecimals ?? 0, + minimumFractionDigits: props.minDecimals ?? props.maxDecimals ?? 0, }} /> ); @@ -221,7 +248,8 @@ export function NumberColumn(props: NumberColumnProps) export function DateColumn(props: BaseColumnProps) { return Column({ ...props, - render: (value) => { + render: (rawValue) => { + const value = props.transform ? props.transform(rawValue) : rawValue; return (