import { memo, useEffect, useState, ReactNode } from 'react' import { InputNumber, Select, Tag, SelectProps } from 'antd' import { ColumnProps } from 'antd/lib/table' import { DefaultOptionType, SelectValue } from 'antd/lib/select' import { Rule } from 'rc-field-form/lib/interface' import { SimpleTimezoneDto } from '@api' import { makeNumericSorter, makeStringSorter } from './sorters' export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters' export { EditableTable, makeActionHandler } from './EditableTable' export { DatePickerWrapper } from './DatePickerWrapper' export { Table } from './Table' export type { BaseTableColumn, TableColumns, TableContainer } from './Table' export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/ export const defaultPagination = { defaultPageSize: 14, showSizeChanger: true, } export const makeNumericRender = (fixed?: number) => (value: any, _: object): ReactNode => { let val = '-' if ((value ?? null) !== null && Number.isFinite(+value)) { val = (fixed ?? null) !== null ? (+value).toFixed(fixed) : (+value).toPrecision(5) } return (
{val}
) } export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string): columnPropsOther => ({ editable: true, initialValue: 0, width: 100, sorter: sorterKey ? makeNumericSorter(sorterKey) : undefined, formItemRules: [ { required: true, message: 'Введите число', pattern: RegExpIsFloat, }, ], render: makeNumericRender(fixed), }) /* other - объект с дополнительными свойствами колонки поддерживаются все базовые свойства из описания https://ant.design/components/table/#Column плю дополнительные для колонок EditableTable: */ type columnPropsOther = ColumnProps & { // редактируемая колонка editable?: boolean // react компонента редактора input?: ReactNode // значение может быть пустым isRequired?: boolean // css класс для , если требуется formItemClass?: string // массив правил валидации значений https://ant.design/components/form/#Rule formItemRules?: Rule[] // дефолтное значение при добавлении новой строки initialValue?: string | number render?: (...attributes: any) => any } export const makeColumn = (title: string | ReactNode, key: string, other?: columnPropsOther) => ({ title: title, key: key, dataIndex: key, ...other, }) export const makeColumnsPlanFact = ( title: string | ReactNode, key: string | string[], columsOther?: any | any[], gruopOther?: any ) => { let keyPlanLocal: string let keyFactLocal: string if (key instanceof Array) { keyPlanLocal = key[0] keyFactLocal = key[1] } else { keyPlanLocal = key + 'Plan' keyFactLocal = key + 'Fact' } let columsOtherLocal : any[2] if (columsOther instanceof Array) columsOtherLocal = [columsOther[0], columsOther[1]] else columsOtherLocal = [columsOther, columsOther] return { title: title, ...gruopOther, children: [ makeColumn('план', keyPlanLocal, columsOtherLocal[0]), makeColumn('факт', keyFactLocal, columsOtherLocal[1]), ] } } export const makeFilterTextMatch = (key: string | number) => ( (filterValue: string | number, dataItem: any) => dataItem[key] === filterValue ) export const makeGroupColumn = (title: string, children: object[]) => ({ title, children }) export const makeTextColumn = ( title: string, dataIndex: string, filters: object[], sorter?: (key: string) => any, render?: any, other?: any ) => ({ title: title, dataIndex: dataIndex, key: dataIndex, filters: filters, onFilter: filters ? makeFilterTextMatch(dataIndex) : null, sorter: sorter ?? makeStringSorter(dataIndex), render: render, ...other }) export const makeNumericColumn = ( title: string, dataIndex: string, filters: object[], filterDelegate: (key: string | number) => any, renderDelegate: (_: any, row: object) => any, width: string, other?: columnPropsOther ) => ({ title: title, dataIndex: dataIndex, key: dataIndex, filters: filters, onFilter: filterDelegate ? filterDelegate(dataIndex) : null, sorter: makeNumericSorter(dataIndex), width: width, input: , render: renderDelegate ?? makeNumericRender(), align: 'right', ...other }) export const makeNumericColumnPlanFact = ( title: string, dataIndex: string, filters: object[], filterDelegate: (key: string | number) => any, renderDelegate: (_: any, row: object) => any, width: string ) => makeGroupColumn(title, [ makeNumericColumn('п', dataIndex + 'Plan', filters, filterDelegate, renderDelegate, width), makeNumericColumn('ф', dataIndex + 'Fact', filters, filterDelegate, renderDelegate, width), ]) export const makeNumericStartEnd = ( title: string, dataIndex: string, fixed: number, filters: object[], filterDelegate: (key: string | number) => any, renderDelegate: (_: any, row: object) => any, width: string, ) => makeGroupColumn(title, [ makeNumericColumn('старт', dataIndex + 'Start', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Start')), makeNumericColumn('конец', dataIndex + 'End', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'End')) ]) export const makeNumericMinMax = ( title: string, dataIndex: string, fixed: number, filters: object[], filterDelegate: (key: string | number) => any, renderDelegate: (_: any, row: object) => any, width: string, ) => makeGroupColumn(title, [ makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')), makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max')), ]) export const makeNumericAvgRange = ( title: string, dataIndex: string, fixed: number, filters: object[], filterDelegate: (key: string | number) => any, renderDelegate: (_: any, row: object) => any, width: string ) => makeGroupColumn(title, [ makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')), makeNumericColumn('сред', dataIndex + 'Avg', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Avg')), makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max')) ]) export const makeSelectColumn = ( title: string, dataIndex: string, options: DefaultOptionType[], defaultValue?: T, other?: columnPropsOther, selectOther?: SelectProps ) => makeColumn(title, dataIndex, { ...other, input: ) }) export const makeTagColumn = >( title: string, dataIndex: string, options: T[], value_key: string, label_key: string, other?: columnPropsOther, tagOther?: SelectProps ) => { const InputComponent = makeTagInput(value_key, label_key) return makeColumn(title, dataIndex, { ...other, render: (item?: T[]) => item?.map((elm: T) => {other?.render?.(elm) ?? elm[label_key]}) ?? '-', input: , }) } type PaginationContainer = { skip?: number take?: number count?: number items?: any[] | null } export const makePaginationObject = (сontainer: PaginationContainer, ...other: any) => ({ ...other, pageSize: сontainer.take, total: сontainer.count ?? сontainer.items?.length ?? 0, current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1)) }) const rawTimezones = { 'Калининград': 2, 'Москва': 3, 'Самара': 4, 'Екатеринбург': 5, 'Омск': 6, 'Красноярск': 7, 'Новосибирск': 7, 'Иркутск': 8, 'Чита': 9, 'Владивосток': 10, 'Магадан': 11, 'Южно-Сахалинск': 11, 'Среднеколымск': 11, 'Анадырь': 12, 'Петропавловск-Камчатский': 12, } const timezoneOptions = Object .entries(rawTimezones) .sort((a, b) => a[1] - b[1]) .map(([id, hours]) => ({ label: `UTC${hours > 0 ? '+':''}${('0' + hours).slice(-2)} :: ${id}`, value: id, })) export const TimezoneSelect = memo(({ onChange, ...other }) => { const [id, setId] = useState(null) useEffect(() => onChange?.({ timezoneId: id, hours: id ? rawTimezones[id] : 0, isOverride: false, }, []), [id, onChange]) return (