import { memo, useCallback, useState, useEffect, useMemo } from 'react' import { Form, Button, Popconfirm } from 'antd' import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons' import { invokeWebApiWrapperAsync } from '@components/factory' import { hasPermission } from '@utils' import { Table } from '.' import { EditableCell } from './EditableCell' const newRowKeyValue = 'newRow' const actions = { insert: (data, idWell) => [idWell, data], insertRange: (data, idWell) => [idWell, [data].flat(1)], update: (data, idWell, idRecord) => [idWell, idRecord && data.id, data], delete: (data, idWell) => [idWell, data.id], } export const makeTableAction = ({ service, permission, action, actionName, recordParser, idWell, idRecord = false, setLoader, errorMsg = 'Не удалось выполнить операцию', onComplete, }) => hasPermission(permission) && service && action && ( (record) => invokeWebApiWrapperAsync( async () => { const data = recordParser?.(record) ?? record const params = actions[action]?.(data, idWell, idRecord).filter(Boolean) if (params?.length > 0) await service[action](...params) await onComplete?.() }, setLoader, errorMsg, actionName ) ) export const tryAddKeys = (items) => { if (!items?.length || !items[0]) return [] if (items[0].key) return items return items.map((item, index) => ({...item, key: item.key ?? item.id ?? index })) } const EditableTableComponents = { body: { cell: EditableCell }} /** * @param onChange - Метод вызывается со всем dataSource с измененными элементами после любого действия * @param onRowAdd - Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается * @param onRowEdit - Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается * @param onRowDelete - Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается */ export const EditableTable = memo(({ columns, dataSource, onChange, onRowAdd, onRowEdit, onRowDelete, additionalButtons, buttonsWidth, ...otherTableProps }) => { const [form] = Form.useForm() const [data, setData] = useState(tryAddKeys(dataSource)) const [editingKey, setEditingKey] = useState('') const onAdd = useMemo(() => typeof onRowAdd === 'function' ? onRowAdd : makeTableAction(onRowAdd), [onRowAdd]) const onEdit = useMemo(() => typeof onRowEdit === 'function' ? onRowEdit : makeTableAction(onRowEdit), [onRowEdit]) const onDelete = useMemo(() => typeof onRowDelete === 'function' ? onRowDelete : makeTableAction(onRowDelete), [onRowDelete]) const isEditing = useCallback((record) => record?.key === editingKey, [editingKey]) const edit = useCallback((record) => { form.setFieldsValue({...record}) setEditingKey(record.key) }, [form]) const cancel = useCallback(() => { if (editingKey === newRowKeyValue) { const newData = [...data] const index = newData.findIndex((item) => newRowKeyValue === item.key) newData.splice(index, 1) setData(newData) } setEditingKey('') }, [data, editingKey]) const addNewRow = useCallback(async () => { let newRow = { ...form.initialValues, key:newRowKeyValue } const newData = [newRow, ...data] setData(newData) edit(newRow) }, [data, edit, form.initialValues]) const save = useCallback(async (record) => { try { const row = await form.validateFields() const newData = [...data] const index = newData.findIndex((item) => record.key === item.key) const item = newData[index] const newItem = { ...item, ...row } newData.splice(index, 1, newItem) if (item.key === newRowKeyValue) item.key = newRowKeyValue + newData.length const isAdding = editingKey === newRowKeyValue setEditingKey('') setData(newData) if (isAdding) try { onAdd(newItem) } catch (err) { console.log('callback onRowAdd fault:', err) } else try { onEdit(newItem) } catch (err) { console.log('callback onRowEdit fault:', err) } try { onChange?.(newData) } catch (err) { console.log('callback onChange fault:', err) } } catch (errInfo) { console.log('Validate Failed:', errInfo) } }, [data, editingKey, form, onChange, onAdd, onEdit]) const deleteRow = useCallback((record) => { const newData = [...data] const index = newData.findIndex((item) => record.key === item.key) newData.splice(index, 1) setData(newData) onDelete(record) onChange?.(newData) }, [data, onChange, onDelete]) const handleColumn = useCallback((col) => { if (col.children) col.children = col.children.map(handleColumn) if (!col.editable) return col return { ...col, onCell: (record) => ({ ...col.onCell?.(record), editing: isEditing(record), record, dataIndex: col.dataIndex ?? col.key, key: col.key ?? col.dataIndex, input: col.input, isRequired: col.isRequired, title: col.title, datatype: col.datatype, formItemClass: col.formItemClass, formItemRules: col.formItemRules, initialValue: col.initialValue, }), } }, [isEditing]) const operationColumn = useMemo(() => ({ width: buttonsWidth ?? 82, title: !!onAdd && (