Merge pull request 'del-row' (#24) from del-row into test
Reviewed-on: #24
This commit is contained in:
commit
75f86e6ef7
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRealtime } from '../../contexts/RealtimeContext';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
|
||||
const ContainerForText = styled.span`
|
||||
@ -42,7 +43,7 @@ const highlightText = (text, searchQuery) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Cell = React.memo(({ cell, row, column, onClick, globalFilter, backgroundColor, color }) => {
|
||||
const Cell = React.memo(({ cell, row, column, onClick, globalFilter, backgroundColor, color, isEditable }) => {
|
||||
const cellValue = cell.getValue();
|
||||
const textValue = String(cellValue || '');
|
||||
|
||||
@ -51,14 +52,11 @@ const Cell = React.memo(({ cell, row, column, onClick, globalFilter, backgroundC
|
||||
return highlightText(textValue, globalFilter);
|
||||
}, [textValue, globalFilter]);
|
||||
|
||||
const {
|
||||
isCellLocked,
|
||||
getCellLockInfo,
|
||||
updateCell,
|
||||
subscribeToErrors,
|
||||
tryLockCell,
|
||||
unlockCell
|
||||
} = useRealtime();
|
||||
const lockedStyles = !isEditable ? {
|
||||
border: '2px solid #d3d3d3',
|
||||
color: '#525252',
|
||||
cursor: 'not-allowed',
|
||||
} : {};
|
||||
|
||||
|
||||
return (
|
||||
@ -80,6 +78,7 @@ const Cell = React.memo(({ cell, row, column, onClick, globalFilter, backgroundC
|
||||
padding: '8px',
|
||||
backgroundColor: backgroundColor,
|
||||
color: color,
|
||||
...lockedStyles
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
|
||||
@ -7,7 +7,7 @@ const EditCell = ({ refCell, onChange, disabled, value }) => {
|
||||
const left = tdFrag.offsetLeft;
|
||||
const width = refCell.current.offsetParent.offsetWidth;
|
||||
|
||||
const translateY = 'translateY(' + trFrag.offsetTop + 'px)';
|
||||
const translateY = trFrag.style.transform;
|
||||
|
||||
function useAutoResize(value) {
|
||||
const ref = useRef(null);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// components/RealtimeTable/index.js
|
||||
import React, { useState, useMemo, useRef, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useMemo, useRef, useEffect, useCallback, useTransition } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import {
|
||||
useMaterialReactTable,
|
||||
@ -21,15 +21,22 @@ import {
|
||||
getTablePaperStyles,
|
||||
} from './constants/tableConfig';
|
||||
import { useRealtime } from './contexts/RealtimeContext';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
const { data, setData } = useRealtimeData(formId, sheetName, direction);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
||||
const [selectedColumn, setSelectedColumn] = useState();
|
||||
const columnVirtualizerInstanceRef = useRef(null);
|
||||
const [rowSelection, setRowSelection] = useState({});
|
||||
const [isLoadingData, setIsLoadingData] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const handleGlobalFilterChange = (value) => {
|
||||
startTransition(() => {
|
||||
setGlobalFilter(value);
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
isConnected,
|
||||
@ -40,6 +47,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
error: wsError,
|
||||
updateCell: contextUpdateCell,
|
||||
addRow: contextAddRow,
|
||||
deleteRow: contextDeleteRow,
|
||||
} = useRealtime();
|
||||
|
||||
const {
|
||||
@ -83,9 +91,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
setRowSelection((prev) => ({
|
||||
[rowId]: !prev[rowId],
|
||||
}));
|
||||
}, [rowSelection])
|
||||
}, [])
|
||||
|
||||
|
||||
// Создание колонок с интеграцией WebSocket
|
||||
const columns = useMemo(() => {
|
||||
if (!columnsConfig || !columnsConfig.columns) return [];
|
||||
try {
|
||||
@ -106,7 +114,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
}
|
||||
}, [columnsConfig, handleUpdateCell]);
|
||||
|
||||
// Метаданные для таблицы
|
||||
const tableMeta = useMemo(() => ({
|
||||
updateCell: handleUpdateCell,
|
||||
}), [handleUpdateCell]);
|
||||
@ -117,13 +124,17 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
...BASE_TABLE_CONFIG,
|
||||
columns,
|
||||
data: data,
|
||||
columnVirtualizerInstanceRef,
|
||||
enableRowVirtualization: false,
|
||||
enableRowVirtualization: true,
|
||||
rowVirtualizerOptions: {
|
||||
overscan: 50,
|
||||
scrollPaddingStart: 0,
|
||||
scrollPaddingEnd: 0,
|
||||
},
|
||||
enableRowSelection: true,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
onColumnSizingChange: setColumnSizing,
|
||||
onColumnPinningChange: setColumnPinning,
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
onGlobalFilterChange: handleGlobalFilterChange,
|
||||
state: {
|
||||
columnSizing,
|
||||
columnPinning,
|
||||
@ -133,7 +144,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
rowSelection
|
||||
},
|
||||
initialState: {
|
||||
hiddenColumn: []
|
||||
expanded: true
|
||||
},
|
||||
meta: tableMeta,
|
||||
muiTableHeadCellProps: ({ column }) => ({
|
||||
@ -151,22 +162,13 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
height: '1px',
|
||||
minHeight: '1px',
|
||||
maxHeight: '1px',
|
||||
opacity: 0,
|
||||
pointerEvents: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
'& th': {
|
||||
height: '1px',
|
||||
minHeight: '1px',
|
||||
maxHeight: '1px',
|
||||
},
|
||||
visibility: 'hidden',
|
||||
},
|
||||
},
|
||||
muiTablePaperProps: getTablePaperStyles(),
|
||||
muiTableContainerProps: {
|
||||
ref: containerRef,
|
||||
sx: {
|
||||
overflowX: 'auto',
|
||||
overflowY: 'auto',
|
||||
position: 'relative',
|
||||
contain: 'layout',
|
||||
minHeight: '100%',
|
||||
@ -196,21 +198,41 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
|
||||
const table = useMaterialReactTable(tableConfig);
|
||||
|
||||
useEffect(() => {
|
||||
if (table && data && columns.length !== 0) {
|
||||
setIsLoadingData(true)
|
||||
}
|
||||
}, [data, columns, table])
|
||||
|
||||
const handleAddRow = useCallback(() => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
|
||||
// Фильтруем выделенные строки
|
||||
const selectedRows = allRows.filter(row => rowSelection[row.id]);
|
||||
if (selectedRows.length === 0) {
|
||||
toasr.error('Выделите строку для вставки');
|
||||
toast.error('Выделите строку для вставки');
|
||||
return;
|
||||
}
|
||||
const row = selectedRows[0];
|
||||
const expense_item_id = row.original.data.header.expense_item_id;
|
||||
contextAddRow({expense_item_id:expense_item_id})
|
||||
contextAddRow({ expense_item_id: expense_item_id })
|
||||
}, [rowSelection, table])
|
||||
|
||||
const handleDeleteRow = useCallback(() => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
|
||||
const selectedRows = allRows.filter(row => rowSelection[row.id]);
|
||||
if (selectedRows.length === 0) {
|
||||
toast.error('Выделите строку для удаления');
|
||||
return;
|
||||
}
|
||||
const row = selectedRows[0];
|
||||
const expense_item_id = row.original.data.line_id;
|
||||
contextDeleteRow(expense_item_id)
|
||||
setRowSelection({});
|
||||
}, [rowSelection, table])
|
||||
|
||||
if (!data) {
|
||||
return <div>Loading...</div>;
|
||||
return <div>Загрузка...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -223,10 +245,12 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
onToggleColumnVisibility={handleToggleColumnVisibility}
|
||||
columnVisibility={columnVisibility}
|
||||
onChangeSizeMult={setSizeMult}
|
||||
onGlobalFilterChange={setGlobalFilter}
|
||||
onGlobalFilterChange={handleGlobalFilterChange}
|
||||
sizeMult={sizeMult}
|
||||
onChangeShowColumnFilters={setShowColumnFilters}
|
||||
onAddRow={handleAddRow}
|
||||
onDeleteRow={handleDeleteRow}
|
||||
isLoadingData={isLoadingData}
|
||||
/>
|
||||
|
||||
<div style={tableWrapperStyle}>
|
||||
@ -250,6 +274,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
onHeaderSelect={setSelectedColumn}
|
||||
selectHeader={selectedColumn}
|
||||
onChangeWidth={handleSetColumnWidth}
|
||||
isLoadingData={isLoadingData}
|
||||
/>,
|
||||
headerPortalRef.current,
|
||||
)}
|
||||
|
||||
@ -30,6 +30,8 @@ const SettingPanel = ({
|
||||
sizeMult,
|
||||
onChangeShowColumnFilters,
|
||||
onAddRow,
|
||||
onDeleteRow,
|
||||
isLoadingData,
|
||||
}) => {
|
||||
const [isPinned, setIsPinned] = useState(false);
|
||||
const [columnTree, setColumnTree] = useState();
|
||||
@ -40,7 +42,7 @@ const SettingPanel = ({
|
||||
if (!table) return;
|
||||
setColumnTree(table.getAllColumns());
|
||||
setShowColumnFilters(table.getState().showColumnFilters);
|
||||
}, [table]);
|
||||
}, [table, isLoadingData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedColumn) return;
|
||||
@ -55,6 +57,7 @@ const SettingPanel = ({
|
||||
);
|
||||
}, [columnVisibility]);
|
||||
|
||||
//TO DO: сделать
|
||||
const { addColorForCells, addFormulaForCells, deleteFormula } = {};
|
||||
|
||||
const handleChangeShowColumnFilters = () => {
|
||||
@ -100,7 +103,7 @@ const SettingPanel = ({
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Удалить строку">
|
||||
<IconButton variant="outlined">
|
||||
<IconButton onClick={onDeleteRow} variant="outlined">
|
||||
<Minus />
|
||||
<RemoveLine />
|
||||
</IconButton>
|
||||
|
||||
@ -131,7 +131,6 @@ const HeaderCell = ({ header, table, onClick, hightlight, onChangeWidth }) => {
|
||||
// Если колонка не закреплена, текст должен начинаться после всех закрепленных колонок
|
||||
const getTextLeftOffset = () => {
|
||||
if (isPinned) {
|
||||
// Для закрепленных колонок текст остается на месте
|
||||
return '0';
|
||||
}
|
||||
// Для незакрепленных колонок текст должен начинаться после всех закрепленных
|
||||
@ -215,7 +214,7 @@ const FilterRow = ({ table }) => {
|
||||
style={{
|
||||
display: 'flex',
|
||||
position: 'sticky',
|
||||
top: '1.5rem', // После строки с номерами колонок
|
||||
top: '1.5rem',
|
||||
zIndex: 20,
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
@ -338,11 +337,16 @@ export const TableHead = ({
|
||||
onHeaderSelect,
|
||||
selectHeader,
|
||||
onChangeWidth,
|
||||
isLoadingData,
|
||||
}) => {
|
||||
const handleHeaderClick = (header) => {
|
||||
onHeaderSelect(header);
|
||||
};
|
||||
|
||||
if (!isLoadingData) {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -57,7 +57,7 @@ export const RealtimeProvider = ({
|
||||
|
||||
case "row_deleted":
|
||||
if (data.result && onRowDeleteRef.current) {
|
||||
onRowDeleteRef.current(data.result);
|
||||
onRowDeleteRef.current(data);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -194,6 +194,9 @@ export const RealtimeProvider = ({
|
||||
const message = {
|
||||
event: "row_deleted",
|
||||
data: { row_id: rowId },
|
||||
sheet: sheetName,
|
||||
form_id: formId,
|
||||
direction: direction,
|
||||
};
|
||||
|
||||
return sendMessage(message);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { FormsSheetApi } from "../../../api/form_sheet";
|
||||
import { useRealtime } from "../contexts/RealtimeContext";
|
||||
import { insertCell, updateCells } from "../utils/cellUtils";
|
||||
import { deleteRow, insertCell, updateCells } from "../utils/cellUtils";
|
||||
|
||||
const useRealtimeData = (formId, sheetName, direction) => {
|
||||
const [data, setData] = useState([]);
|
||||
@ -86,15 +86,8 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxDepth = data.reduce(
|
||||
(prev, current) => {
|
||||
return (prev.depth || 0) > (current.depth || 0) ? prev : current;
|
||||
},
|
||||
{ depth: 0 },
|
||||
).depth;
|
||||
|
||||
const formattedData = formatedToSubRows(maxDepth, data);
|
||||
setData(formattedData);
|
||||
const hierarchicalData = buildHierarchy(data);
|
||||
setData(hierarchicalData);
|
||||
} catch (err) {
|
||||
console.error("Failed to load initial data:", err);
|
||||
setError(err.message);
|
||||
@ -104,7 +97,54 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
};
|
||||
|
||||
loadInitialData();
|
||||
}, [formId, sheetName, formatedToSubRows]);
|
||||
}, [formId, sheetName]);
|
||||
|
||||
const buildHierarchy = (items) => {
|
||||
const result = [];
|
||||
const stack = [];
|
||||
|
||||
const getTypeLevel = (type) => {
|
||||
const levels = {
|
||||
ROOT: 0,
|
||||
GROUP: 1,
|
||||
ITEM: 2,
|
||||
SUB_ITEM: 3,
|
||||
INPUT: 4,
|
||||
};
|
||||
return levels[type] ?? 999;
|
||||
};
|
||||
|
||||
for (const item of items) {
|
||||
const currentLevel = getTypeLevel(item.row_type);
|
||||
|
||||
while (
|
||||
stack.length > 0 &&
|
||||
getTypeLevel(stack[stack.length - 1].row_type) >= currentLevel
|
||||
) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
const parent = stack.length > 0 ? stack[stack.length - 1] : null;
|
||||
|
||||
const newNode = { ...item, subRows: [] };
|
||||
|
||||
if (parent) {
|
||||
if (!parent.subRows) {
|
||||
parent.subRows = [];
|
||||
}
|
||||
parent.subRows.push(newNode);
|
||||
} else {
|
||||
result.push(newNode);
|
||||
}
|
||||
|
||||
// Добавляем текущий узел в стек, если он может иметь детей
|
||||
if (item.row_type !== "INPUT") {
|
||||
stack.push(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setIsConnected(wsConnected);
|
||||
@ -132,34 +172,35 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
});
|
||||
|
||||
const unsubscribeRowAdds = subscribeToRowAdds((data) => {
|
||||
const updates = data.result;
|
||||
const updates = data.result.data;
|
||||
const expense_item_id = data.data.expense_item_id;
|
||||
const updatedCells = updates.slice(0, -1);
|
||||
const newRow = updates.at(-1);
|
||||
const newLineId = data.result.new_line_id;
|
||||
const [updatedCells, newRow] = updates.reduce(
|
||||
(res, row) => {
|
||||
if (row[3]?.line_id === newLineId) {
|
||||
res[1] = row;
|
||||
} else {
|
||||
res[0].push(row);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
[[], null],
|
||||
);
|
||||
setData((prevData) => {
|
||||
const updateData = updateCells(prevData, updatedCells);
|
||||
|
||||
const newData = insertCell(updateData, newRow, expense_item_id);
|
||||
console.log(newData)
|
||||
return newData;
|
||||
});
|
||||
});
|
||||
|
||||
const unsubscribeRowDeletes = subscribeToRowDeletes((deletedRowId) => {
|
||||
const unsubscribeRowDeletes = subscribeToRowDeletes((data) => {
|
||||
const updatedCells = data.result;
|
||||
const lineIdForDelete = data.data.row_id;
|
||||
setData((prevData) => {
|
||||
// Функция для удаления строки из иерархии
|
||||
const deleteInHierarchy = (items) => {
|
||||
return items
|
||||
.filter((item) => (item.id || item.line_id) !== deletedRowId)
|
||||
.map((item) => {
|
||||
if (item.subRows && item.subRows.length > 0) {
|
||||
return { ...item, subRows: deleteInHierarchy(item.subRows) };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
return deleteInHierarchy(prevData);
|
||||
const updateData = updateCells(prevData, updatedCells);
|
||||
const newData = deleteRow(updateData, lineIdForDelete);
|
||||
console.log(newData);
|
||||
return newData;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -9,7 +9,8 @@ function EditCellPortal({
|
||||
EditCell,
|
||||
onSaveStart,
|
||||
onSaveEnd,
|
||||
onError
|
||||
onError,
|
||||
isEditable
|
||||
}) {
|
||||
const ref = useRef(null);
|
||||
const [refTbody, setRefTbody] = useState(false);
|
||||
@ -25,17 +26,7 @@ function EditCellPortal({
|
||||
|
||||
const handleChange = async (val) => {
|
||||
table.setEditingCell(null);
|
||||
if (isSaving) return;
|
||||
|
||||
setIsSaving(true);
|
||||
onSaveStart?.();
|
||||
|
||||
// Сохраняем старое значение на случай ошибки
|
||||
const oldValue = cell.getValue();
|
||||
|
||||
try {
|
||||
|
||||
// Используем updateCell из контекста (передается через meta)
|
||||
if (table.options.meta?.updateCell) {
|
||||
const success = await table.options.meta.updateCell(
|
||||
cell.row,
|
||||
@ -44,32 +35,15 @@ function EditCellPortal({
|
||||
);
|
||||
if (success) {
|
||||
console.log('Cell updated successfully via WebSocket');
|
||||
onSaveEnd?.(true);
|
||||
table.setEditingCell(null);
|
||||
} else {
|
||||
console.error('Failed to update cell via WebSocket');
|
||||
const errorMsg = 'Не удалось сохранить изменение';
|
||||
onError?.(errorMsg);
|
||||
onSaveEnd?.(false);
|
||||
// Возвращаем предыдущее значение
|
||||
|
||||
}
|
||||
} else {
|
||||
// Если updateCell нет в meta, используем onCellUpdate из пропсов (fallback)
|
||||
console.warn('WebSocket updateCell not configured in meta');
|
||||
if (table.options.meta?.onCellUpdate) {
|
||||
table.options.meta.onCellUpdate(cell.row.id, cell.column.id, val);
|
||||
}
|
||||
onSaveEnd?.(true);
|
||||
table.setEditingCell(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating cell:', error);
|
||||
onError?.(error.message);
|
||||
onSaveEnd?.(false);
|
||||
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -88,7 +62,7 @@ function EditCellPortal({
|
||||
refCell={ref}
|
||||
cell={cell}
|
||||
value={cell.getValue()}
|
||||
disabled={isDisabled || isSaving}
|
||||
disabled={!isEditable}
|
||||
onChange={handleChange}
|
||||
/>,
|
||||
refTbody,
|
||||
@ -131,8 +105,9 @@ export const getTableColumns = ({
|
||||
column={column}
|
||||
cell={cell}
|
||||
globalFilter={
|
||||
table.getState().globalFilter || column.getFilterValue()
|
||||
table.getState().globalFilter
|
||||
}
|
||||
isEditable={row.original?.row_type === 'INPUT' || false}
|
||||
backgroundColor={columnColors[column.id]?.color_type?.[row.original?.row_type || row.row_type]}
|
||||
color={columnColors[column.id]?.color_type?.['COLOR']}
|
||||
isUpdating={table.options.meta?.updatingCells?.[`${row.id}_${column.id}`]}
|
||||
@ -144,20 +119,8 @@ export const getTableColumns = ({
|
||||
cell={cell}
|
||||
table={table}
|
||||
EditCell={EditCell}
|
||||
onSaveStart={() => {
|
||||
if (table.options.meta?.setUpdatingCell) {
|
||||
table.options.meta.setUpdatingCell(row.id, column.id, true);
|
||||
}
|
||||
}}
|
||||
onSaveEnd={(success) => {
|
||||
if (table.options.meta?.setUpdatingCell) {
|
||||
table.options.meta.setUpdatingCell(row.id, column.id, false);
|
||||
}
|
||||
if (!success && onCellUpdateError) {
|
||||
onCellUpdateError(row.id, column.id, 'Failed to save');
|
||||
}
|
||||
}}
|
||||
onError={onCellUpdateError}
|
||||
isEditable={row.original?.row_type === 'INPUT' || false}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -177,10 +140,9 @@ export const getTableColumns = ({
|
||||
enableEditing: false,
|
||||
enablePinning: true,
|
||||
Cell: ({ cell, row }) => {
|
||||
return <span
|
||||
|
||||
onClick={() => {console.log('TEST', row.id); onCellNumberClick(row.id)}}
|
||||
>{cell.getValue()}</span>;
|
||||
return <button onClick={() => { console.log('TEST', row.id); onCellNumberClick(row.id) }}>
|
||||
{cell.getValue()}
|
||||
</button>
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export function setNewValueToData(data, colId, rowId, value) {
|
||||
// 1. Находим целевой объект по rowId(индексы вложенных subRows)
|
||||
let targetObject = data;
|
||||
if (rowId && rowId.length) {
|
||||
const indices = rowId.split(".").map(Number);
|
||||
@ -71,7 +70,7 @@ export function updateCells(data, newRows) {
|
||||
}
|
||||
});
|
||||
}
|
||||
return true; // Нашли и обновили
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
@ -125,3 +124,26 @@ export function insertCell(data, newRow, expense_item_id) {
|
||||
findAndUpdate(updatedRows);
|
||||
return updatedRows;
|
||||
}
|
||||
|
||||
export function deleteRow(data, lineId) {
|
||||
const updatedRows = JSON.parse(JSON.stringify(data));
|
||||
|
||||
function findAndDelete(rows) {
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
|
||||
if (row.data.line_id == lineId) {
|
||||
rows.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (row.subRows && Array.isArray(row.subRows) && row.subRows.length > 0) {
|
||||
const found = findAndDelete(row.subRows);
|
||||
if (found) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
findAndDelete(updatedRows);
|
||||
return updatedRows;
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ const TableCard = ({ table, onNavigate }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div key={table}>
|
||||
<div key={table.sheet + '_' + table?.direction}>
|
||||
<Section onClick={handleClick}>
|
||||
<SectionStartPart>
|
||||
<div className="for-img-table">
|
||||
|
||||
@ -26,7 +26,7 @@ const TablesList = ({
|
||||
|
||||
return (
|
||||
<TableCard
|
||||
key={table.sheet}
|
||||
key={table.sheet+"_"+table?.direction}
|
||||
onNavigate={() => handleNavigate(path)}
|
||||
table={table}
|
||||
/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user