diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx
index d322b4b..063dbd3 100644
--- a/web/src/components/RealtimeTable/RealtimeTable.jsx
+++ b/web/src/components/RealtimeTable/RealtimeTable.jsx
@@ -21,6 +21,7 @@ import {
getTablePaperStyles,
} from './constants/tableConfig';
import { useRealtime } from './contexts/RealtimeContext';
+import { setNewValueToData, updateExistingStructure } from './utils/cellUtils';
const RealtimeTable = ({ formType, formId, sheetName }) => {
const { data, setData } = useRealtimeData(formId, sheetName);
@@ -28,9 +29,8 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
const [globalFilter, setGlobalFilter] = useState('');
const [showColumnFilters, setShowColumnFilters] = useState(false);
const [selectedColumn, setSelectedColumn] = useState();
- const [updatingCells, setUpdatingCells] = useState({});
-
const columnVirtualizerInstanceRef = useRef(null);
+ const [rowSelection, setRowSelection] = useState({});
const {
isConnected,
@@ -39,7 +39,7 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
subscribeToErrors,
subscribeToAuthSuccess,
error: wsError,
- updateCell: contextUpdateCell
+ updateCell: contextUpdateCell
} = useRealtime();
const {
@@ -75,75 +75,16 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
loadConfig();
}, [formType, sheetName]);
- // Подписка на обновления от других пользователей
- useEffect(() => {
- const unsubscribe = subscribeToCellUpdates((updatedCells) => {
- console.log('Received updates from other users:', updatedCells);
-
- setData(prevData => {
- if (!prevData) return prevData;
- const newData = [...prevData];
- updatedCells.forEach(update => {
- const rowIndex = newData.findIndex(row => row.id === update.line_id);
- if (rowIndex !== -1) {
- newData[rowIndex] = {
- ...newData[rowIndex],
- [update.column]: update.value
- };
- }
- });
- return newData;
- });
- });
-
- return unsubscribe;
- }, [subscribeToCellUpdates, setData]);
-
- // Подписка на ошибки
- useEffect(() => {
- const unsubscribe = subscribeToErrors((errorMsg) => {
- console.error('WebSocket error:', errorMsg);
- // Можно добавить Toast уведомление
- });
-
- return unsubscribe;
- }, [subscribeToErrors]);
-
- // Подписка на успешную авторизацию
- useEffect(() => {
- const unsubscribe = subscribeToAuthSuccess(() => {
- console.log('Successfully authenticated with WebSocket');
- setIsLoading(false);
- });
-
- return unsubscribe;
- }, [subscribeToAuthSuccess]);
-
- // Функция для установки состояния обновления ячейки
- const setUpdatingCell = useCallback((rowId, columnId, isUpdating) => {
- setUpdatingCells(prev => ({
- ...prev,
- [`${rowId}_${columnId}`]: isUpdating
- }));
- }, []);
-
- const handleUpdateCell = useCallback(async (rowId, columnId, value) => {
- setData(prevData => {
- const newData = [...prevData];
- const rowIndex = newData.findIndex(row => row.id === rowId);
- if (rowIndex !== -1) {
- newData[rowIndex] = {
- ...newData[rowIndex],
- [columnId]: value
- };
- }
- return newData;
- });
-
- // Отправляем через WebSocket
- return await contextUpdateCell(rowId, columnId, value);
+ const handleUpdateCell = useCallback(async (row, column, value) => {
+ return await contextUpdateCell(row, column, value);
}, [contextUpdateCell, setData]);
+ const handleClickRowCell = useCallback((rowId) => {
+ setRowSelection((prev) => ({
+ [rowId]: !prev[rowId],
+ }));
+ }, [rowSelection])
+
// Создание колонок с интеграцией WebSocket
const columns = useMemo(() => {
if (!columnsConfig || !columnsConfig.columns) return [];
@@ -152,10 +93,11 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
Cell,
EditCell,
columnsConfig,
- onCellUpdate: handleUpdateCell, // Передаем функцию обновления
+ onCellUpdate: handleUpdateCell,
+ onCellNumberClick: handleClickRowCell,
onCellUpdateError: (rowId, columnId, error) => {
console.error(`Error updating cell ${rowId}_${columnId}:`, error);
- // Можно добавить Toast уведомление
+ toast.error('Ошибка обновления ячейки')
}
});
} catch (error) {
@@ -166,19 +108,19 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
// Метаданные для таблицы
const tableMeta = useMemo(() => ({
- updatingCells,
- setUpdatingCell,
updateCell: handleUpdateCell,
- }), [updatingCells, setUpdatingCell]);
+ }), [handleUpdateCell]);
// Конфигурация таблицы
const tableConfig = useMemo(
() => ({
...BASE_TABLE_CONFIG,
columns,
- data: data || [],
+ data: data,
columnVirtualizerInstanceRef,
enableRowVirtualization: false,
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
onColumnSizingChange: setColumnSizing,
onColumnPinningChange: setColumnPinning,
onGlobalFilterChange: setGlobalFilter,
@@ -188,6 +130,10 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
columnVisibility,
globalFilter,
showColumnFilters,
+ rowSelection
+ },
+ initialState: {
+ hiddenColumn: []
},
meta: tableMeta,
muiTableHeadCellProps: ({ column }) => ({
@@ -244,47 +190,18 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
showColumnFilters,
containerRef,
tableMeta,
+ rowSelection,
],
);
const table = useMaterialReactTable(tableConfig);
- // Индикатор подключения WebSocket
- const ConnectionStatus = () => (
-
- {console.log(isConnected ,isAuthenticated )}
-
- {isConnected && isAuthenticated ? '● Connected' : '○ Disconnected'}
- {wsError &&
{wsError}
}
-
- );
-
if (!data) {
return Loading...
;
}
return (
<>
-
{
- console.log(children, formId, sheetName, direction, userId);
- const [pendingUpdates, setPendingUpdates] = useState(new Map());
-
// Формируем URL
const wsUrl = `${process.env.REACT_APP_WS_URL || "ws://localhost:8000"}/api/v1/ws/form/${formId}/sheet/${sheetName}${direction ? `?direction=${direction}` : ""}`;
const handleMessage = useCallback((data) => {
- console.log("Received message:", data);
-
// Обработка ошибок
if (data.error) {
- console.error("Server error:", data.error);
+ toast.error("Server error:", data.error)
onErrorRef.current?.(data.error);
return;
}
@@ -45,6 +41,7 @@ export const RealtimeProvider = ({
// Обработка событий
switch (data.event) {
case "cell_updated":
+ console.log(onCellUpdateRef.current)
if (data.result && onCellUpdateRef.current) {
const updatedCells = Array.isArray(data.result)
? data.result
@@ -115,7 +112,6 @@ export const RealtimeProvider = ({
return false;
}
- console.log("Sending login message with token");
const loginMessage = {
event: "user_login",
data: { token },
@@ -132,7 +128,7 @@ export const RealtimeProvider = ({
sendLogin();
}, 100);
}
- }, [isConnected, sendLogin]);
+ }, [isConnected, sendLogin]);
useEffect(() => {
if (!isConnected) {
@@ -141,43 +137,29 @@ export const RealtimeProvider = ({
}, [isConnected]);
const updateCell = useCallback(
- async (rowId, columnId, value) => {
+ async (row, column, value) => {
+ const columnId = column.id;
+ const rowId = row.id;
if (!isConnected) {
onErrorRef.current?.("WebSocket не подключен");
return false;
}
- if (!tryLockCell(rowId, columnId, userId)) {
- const lockInfo = getCellLockInfo(rowId, columnId);
- onErrorRef.current?.(
- `Ячейка редактируется пользователем ${lockInfo?.userId}`,
- );
- return false;
- }
+ const lineId = row.original?.data?.line_id || null;
+ const colId = columnId.slice(5);
const message = {
event: "cell_updated",
data: {
- line_id: rowId,
- column: columnId,
+ line_id: lineId,
+ column: colId,
value: value,
},
};
-
- const updateKey = `${rowId}_${columnId}`;
- setPendingUpdates((prev) =>
- new Map(prev).set(updateKey, { value, timestamp: Date.now() }),
- );
-
+ console.log(message);
const sent = sendMessage(message);
if (!sent) {
- unlockCell(rowId, columnId, userId);
- setPendingUpdates((prev) => {
- const newMap = new Map(prev);
- newMap.delete(updateKey);
- return newMap;
- });
return false;
}
@@ -186,10 +168,6 @@ export const RealtimeProvider = ({
[
isConnected,
sendMessage,
- tryLockCell,
- unlockCell,
- getCellLockInfo,
- userId,
],
);
diff --git a/web/src/components/RealtimeTable/hooks/useRealtimeData.js b/web/src/components/RealtimeTable/hooks/useRealtimeData.js
index 319e38c..857f434 100644
--- a/web/src/components/RealtimeTable/hooks/useRealtimeData.js
+++ b/web/src/components/RealtimeTable/hooks/useRealtimeData.js
@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { FormsSheetApi } from "../../../api/form_sheet";
import { useRealtime } from "../contexts/RealtimeContext";
+import { updateExistingStructure } from "../utils/cellUtils";
const useRealtimeData = (formId, sheetName) => {
const [data, setData] = useState([]);
@@ -8,7 +9,7 @@ const useRealtimeData = (formId, sheetName) => {
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const dataRef = useRef(data);
-
+
const {
isConnected: wsConnected,
error: wsError,
@@ -21,21 +22,48 @@ const useRealtimeData = (formId, sheetName) => {
// Ваша существующая функция форматирования
const formatedToSubRows = useCallback((depth, oldData) => {
if (depth === 0) return oldData;
+
const dataFiltered = oldData.filter((d) => d.depth < depth);
for (let i = 0; i < dataFiltered.length; i++) {
const curRow = dataFiltered[i];
- if (curRow.depth !== depth - 1) continue;
- const nextRow = dataFiltered[i + 1] || null;
- const subRows = oldData.filter(
- (d) =>
- d.depth === depth &&
- d.sort_order > curRow.sort_order &&
- (!nextRow || d.sort_order < nextRow.sort_order),
- );
- curRow.subRows = subRows;
+
+ // Находим следующий элемент на том же или более высоком уровне
+ let nextRow = null;
+ for (let j = i + 1; j < dataFiltered.length; j++) {
+ if (dataFiltered[j].depth <= curRow.depth) {
+ nextRow = dataFiltered[j];
+ break;
+ }
+ }
+
+ // Находим всех потомков для текущей строки
+ const subRows = oldData.filter((d) => {
+ // Потомок должен иметь глубину больше чем у родителя
+ if (d.depth <= curRow.depth) return false;
+
+ // Потомок должен идти после родителя по sort_order
+ if (d.sort_order <= curRow.sort_order) return false;
+
+ // Если есть следующий элемент на том же уровне - потомок должен быть до него
+ if (nextRow && d.sort_order >= nextRow.sort_order) return false;
+
+ return true;
+ });
+
+ if (subRows.length > 0) {
+ curRow.subRows = subRows;
+ }
}
- return formatedToSubRows(depth - 1, dataFiltered);
+
+ // Рекурсивно обрабатываем следующий уровень
+ // Находим максимальную глубину в текущем dataFiltered
+ const maxDepth = Math.max(...dataFiltered.map((d) => d.depth));
+ if (maxDepth > 0) {
+ return formatedToSubRows(maxDepth, dataFiltered);
+ }
+
+ return dataFiltered;
}, []);
// Загрузка начальных данных
@@ -44,21 +72,24 @@ const useRealtimeData = (formId, sheetName) => {
try {
setIsLoading(true);
const res = await FormsSheetApi.get(formId, sheetName);
- const mockData = res.result;
-
- if (!mockData || mockData.length === 0) {
+ const data = res.result;
+
+ if (!data || data.length === 0) {
setData([]);
return;
}
-
- const maxDepth = mockData.reduce((prev, current) => {
- return (prev.depth || 0) > (current.depth || 0) ? prev : current;
- }, { depth: 0 }).depth;
- const formattedData = formatedToSubRows(maxDepth, mockData);
+ 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);
} catch (err) {
- console.error('Failed to load initial data:', err);
+ console.error("Failed to load initial data:", err);
setError(err.message);
} finally {
setIsLoading(false);
@@ -87,116 +118,78 @@ const useRealtimeData = (formId, sheetName) => {
const unsubscribeCellUpdates = subscribeToCellUpdates((updatedCells) => {
if (!updatedCells || updatedCells.length === 0) return;
- setData(prevData => {
- // Функция для обновления данных в иерархической структуре
- const updateInHierarchy = (items) => {
- return items.map(item => {
- let updatedItem = { ...item };
-
- // Обновляем ячейки текущего уровня
- updatedCells.forEach(cell => {
- if ((item.id || item.line_id) === cell.line_id) {
- updatedItem[cell.column] = cell.value;
- }
- });
-
- // Рекурсивно обновляем подстроки
- if (item.subRows && item.subRows.length > 0) {
- updatedItem.subRows = updateInHierarchy(item.subRows);
- }
-
- return updatedItem;
- });
- };
-
- return updateInHierarchy(prevData);
- });
- });
-
- const unsubscribeRowAdds = subscribeToRowAdds((newRow) => {
- setData(prevData => {
- // Преобразуем текущие данные в плоский массив
- const flatData = flattenHierarchy(prevData);
-
- // Добавляем новую строку
- const updatedFlatData = [...flatData, newRow];
-
- // Сортируем по sort_order
- updatedFlatData.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
-
- // Восстанавливаем иерархию
- const maxDepth = updatedFlatData.reduce((prev, current) => {
- return (prev.depth || 0) > (current.depth || 0) ? prev : current;
- }, { depth: 0 }).depth;
-
- return formatedToSubRows(maxDepth, updatedFlatData);
+ setData((prevData) => {
+ const newData = updateExistingStructure(prevData, updatedCells)
+ return newData
});
});
const unsubscribeRowDeletes = subscribeToRowDeletes((deletedRowId) => {
- setData(prevData => {
+ setData((prevData) => {
// Функция для удаления строки из иерархии
const deleteInHierarchy = (items) => {
return items
- .filter(item => (item.id || item.line_id) !== deletedRowId)
- .map(item => {
+ .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 unsubscribeErrors = subscribeToErrors((err) => {
- console.error('WebSocket error:', err);
+ console.error("WebSocket error:", err);
setError(err);
setTimeout(() => setError(null), 5000);
});
return () => {
unsubscribeCellUpdates();
- unsubscribeRowAdds();
+ // unsubscribeRowAdds();
unsubscribeRowDeletes();
unsubscribeErrors();
};
- }, [wsConnected, subscribeToCellUpdates, subscribeToRowAdds, subscribeToRowDeletes, subscribeToErrors, formatedToSubRows]);
+ }, [
+ wsConnected,
+ subscribeToCellUpdates,
+ subscribeToRowAdds,
+ subscribeToRowDeletes,
+ subscribeToErrors,
+ formatedToSubRows,
+ ]);
- // Вспомогательная функция для преобразования иерархических данных в плоский массив
- const flattenHierarchy = useCallback((items, result = []) => {
- for (const item of items) {
- const { subRows, ...rest } = item;
- result.push(rest);
- if (subRows && subRows.length > 0) {
- flattenHierarchy(subRows, result);
+ const updateData = useCallback(
+ (newData) => {
+ if (Array.isArray(newData) && !newData[0]?.subRows) {
+ const maxDepth = newData.reduce(
+ (prev, current) => {
+ return (prev.depth || 0) > (current.depth || 0) ? prev : current;
+ },
+ { depth: 0 },
+ ).depth;
+
+ const formattedData = formatedToSubRows(maxDepth, newData);
+ setData(formattedData);
+ } else {
+ setData(newData);
}
- }
- return result;
- }, []);
-
- const updateData = useCallback((newData) => {
- if (Array.isArray(newData) && !newData[0]?.subRows) {
- const maxDepth = newData.reduce((prev, current) => {
- return (prev.depth || 0) > (current.depth || 0) ? prev : current;
- }, { depth: 0 }).depth;
-
- const formattedData = formatedToSubRows(maxDepth, newData);
- setData(formattedData);
- } else {
- setData(newData);
- }
- }, [formatedToSubRows]);
+ },
+ [formatedToSubRows],
+ );
return {
data,
- setData: updateData,
+ setData,
+ // setData: updateData,
isConnected,
isLoading,
error,
};
};
-export default useRealtimeData;
\ No newline at end of file
+export default useRealtimeData;
diff --git a/web/src/components/RealtimeTable/hooks/useTableWebSocket.js b/web/src/components/RealtimeTable/hooks/useTableWebSocket.js
deleted file mode 100644
index 8e28018..0000000
--- a/web/src/components/RealtimeTable/hooks/useTableWebSocket.js
+++ /dev/null
@@ -1,165 +0,0 @@
-// hooks/useTableWebSocket.js
-import { useWebSocket } from './useWebSocket';
-import { useCallback, useEffect, useRef } from 'react';
-
-export const useTableWebSocket = (formId, sheet, direction = null) => {
- // Формируем URL WebSocket
- const wsUrl = `${process.env.REACT_APP_WS_URL || 'ws://localhost:8000'}/ws/form/${formId}/sheet/${sheet}${direction ? `?direction=${direction}` : ''}`;
-
- const pendingChanges = useRef(new Map()); // Храним ожидающие подтверждения изменения
- const callbacks = useRef(new Map()); // Храним колбэки для каждого изменения
-
- const handleWebSocketMessage = useCallback((data) => {
- console.log('Received WebSocket message:', data);
-
- // Обработка подтверждения изменения
- if (data.event === 'cell_updated' && data.result) {
- const changeKey = `${data.data.line_id}_${data.data.column}`;
- const callback = callbacks.current.get(changeKey);
-
- if (callback) {
- // Успешное обновление
- callback.success?.(data.result);
- callbacks.current.delete(changeKey);
- pendingChanges.current.delete(changeKey);
- }
- }
-
- // Обработка ошибок
- if (data.error) {
- const changeKey = `${data.data?.line_id}_${data.data?.column}`;
- const callback = callbacks.current.get(changeKey);
-
- if (callback) {
- callback.error?.(data.error);
- callbacks.current.delete(changeKey);
- pendingChanges.current.delete(changeKey);
- }
- }
-
- // Обработка обновлений от других пользователей
- if (data.event === 'cell_updated' && data.result) {
- // Обновляем таблицу данными от других пользователей
- if (window.dispatchEvent) {
- window.dispatchEvent(new CustomEvent('table-data-update', {
- detail: {
- lineId: data.data.line_id,
- column: data.data.column,
- value: data.data.value,
- sourceUserId: data.user_id
- }
- }));
- }
- }
- }, []);
-
- const { isConnected, error, sendMessage, login, disconnect } = useWebSocket(
- wsUrl,
- handleWebSocketMessage
- );
-
- // Отправка обновления ячейки
- const updateCell = useCallback((lineId, column, value, onSuccess, onError) => {
- if (!isConnected) {
- onError?.('WebSocket not connected');
- return false;
- }
-
- const message = {
- event: 'cell_updated',
- data: {
- line_id: lineId,
- column: column,
- value: value
- }
- };
-
- const changeKey = `${lineId}_${column}`;
-
- // Сохраняем колбэки
- callbacks.current.set(changeKey, { success: onSuccess, error: onError });
- pendingChanges.current.set(changeKey, message);
-
- // Отправляем сообщение
- const sent = sendMessage(message);
-
- if (!sent) {
- callbacks.current.delete(changeKey);
- pendingChanges.current.delete(changeKey);
- onError?.('Failed to send message');
- }
-
- return sent;
- }, [isConnected, sendMessage]);
-
- // Добавление строки
- const addRow = useCallback((rowData, onSuccess, onError) => {
- if (!isConnected) {
- onError?.('WebSocket not connected');
- return false;
- }
-
- const message = {
- event: 'row_added',
- data: rowData
- };
-
- const sent = sendMessage(message);
-
- if (!sent) {
- onError?.('Failed to send message');
- }
-
- return sent;
- }, [isConnected, sendMessage]);
-
- // Удаление строки
- const deleteRow = useCallback((rowId, onSuccess, onError) => {
- if (!isConnected) {
- onError?.('WebSocket not connected');
- return false;
- }
-
- const message = {
- event: 'row_deleted',
- data: {
- row_id: rowId
- }
- };
-
- const sent = sendMessage(message);
-
- if (!sent) {
- onError?.('Failed to send message');
- }
-
- return sent;
- }, [isConnected, sendMessage]);
-
- // Авторизация при подключении
- useEffect(() => {
- if (isConnected) {
- const token = localStorage.getItem('token'); // или откуда вы получаете токен
- if (token) {
- login(token);
- }
- }
- }, [isConnected, login]);
-
- // Очистка при размонтировании
- useEffect(() => {
- return () => {
- callbacks.current.clear();
- pendingChanges.current.clear();
- };
- }, []);
-
- return {
- isConnected,
- error,
- updateCell,
- addRow,
- deleteRow,
- disconnect
- };
-};
\ No newline at end of file
diff --git a/web/src/components/RealtimeTable/tableColumns.jsx b/web/src/components/RealtimeTable/tableColumns.jsx
index 7afd9c4..d6fc397 100644
--- a/web/src/components/RealtimeTable/tableColumns.jsx
+++ b/web/src/components/RealtimeTable/tableColumns.jsx
@@ -3,14 +3,13 @@ import { createPortal } from 'react-dom';
import { columns as mockColumns } from '../../mocks/mockTable';
import { config } from './constants/FORM_2/AHR';
-function EditCellPortal({
- cell,
- table,
- EditCell,
- onChange,
+function EditCellPortal({
+ cell,
+ table,
+ EditCell,
onSaveStart,
onSaveEnd,
- onError
+ onError
}) {
const ref = useRef(null);
const [refTbody, setRefTbody] = useState(false);
@@ -25,27 +24,24 @@ function EditCellPortal({
}, []);
const handleChange = async (val) => {
+ table.setEditingCell(null);
if (isSaving) return;
-
+
setIsSaving(true);
onSaveStart?.();
-
+
// Сохраняем старое значение на случай ошибки
const oldValue = cell.getValue();
- console.log(table.options.meta)
-
+
try {
- // Сначала обновляем локальное состояние (оптимистичное обновление)
- onChange(val);
-
+
// Используем updateCell из контекста (передается через meta)
if (table.options.meta?.updateCell) {
const success = await table.options.meta.updateCell(
- cell.row.id,
- cell.column.id,
+ cell.row,
+ cell.column,
val
);
-
if (success) {
console.log('Cell updated successfully via WebSocket');
onSaveEnd?.(true);
@@ -56,7 +52,7 @@ function EditCellPortal({
onError?.(errorMsg);
onSaveEnd?.(false);
// Возвращаем предыдущее значение
- onChange(oldValue);
+
}
} else {
// Если updateCell нет в meta, используем onCellUpdate из пропсов (fallback)
@@ -71,8 +67,7 @@ function EditCellPortal({
console.error('Error updating cell:', error);
onError?.(error.message);
onSaveEnd?.(false);
- // Возвращаем предыдущее значение
- onChange(oldValue);
+
} finally {
setIsSaving(false);
}
@@ -118,11 +113,12 @@ function EditCellPortal({
);
}
-export const getTableColumns = ({
- Cell,
- EditCell,
+export const getTableColumns = ({
+ Cell,
+ EditCell,
columnsConfig,
- onCellUpdateError
+ onCellUpdateError,
+ onCellNumberClick
}) => {
const columnColors = { ...columnsConfig.colors };
const columns = [...columnsConfig.columns];
@@ -142,18 +138,12 @@ export const getTableColumns = ({
isUpdating={table.options.meta?.updatingCells?.[`${row.id}_${column.id}`]}
/>
);
-
+
col.Edit = ({ cell, table, row, column }) => (
{
- // Локальное обновление кэша
- if (row._valuesCache) {
- row._valuesCache[column.id] = value;
- }
- }}
onSaveStart={() => {
if (table.options.meta?.setUpdatingCell) {
table.options.meta.setUpdatingCell(row.id, column.id, true);
@@ -186,8 +176,11 @@ export const getTableColumns = ({
size: 70,
enableEditing: false,
enablePinning: true,
- Cell: ({ cell }) => {
- return {cell.getValue()};
+ Cell: ({ cell, row }) => {
+ return {console.log('TEST', row.id); onCellNumberClick(row.id)}}
+ >{cell.getValue()};
},
};
diff --git a/web/src/components/RealtimeTable/utils/cellUtils.js b/web/src/components/RealtimeTable/utils/cellUtils.js
new file mode 100644
index 0000000..64b2e3d
--- /dev/null
+++ b/web/src/components/RealtimeTable/utils/cellUtils.js
@@ -0,0 +1,94 @@
+export function setNewValueToData(data, colId, rowId, value) {
+ // 1. Находим целевой объект по rowId(индексы вложенных subRows)
+ let targetObject = data;
+ if (rowId && rowId.length) {
+ const indices = rowId.split(".").map(Number);
+ for (let i = 0; i < indices.length; i++) {
+ const index = indices[i];
+ if (targetObject && targetObject[index] !== undefined) {
+ targetObject = targetObject[index];
+ } else {
+ console.error(`Индекс ${index} не найден на уровне ${i}`);
+ return false;
+ }
+
+ // Если это не последний индекс, переходим в subRows
+ if (i < indices.length - 1) {
+ if (targetObject.subRows && Array.isArray(targetObject.subRows)) {
+ targetObject = targetObject.subRows;
+ } else {
+ console.error(`Нет subRows для перехода на уровне ${i}`);
+ return false;
+ }
+ }
+ }
+ }
+
+ const keys = colId.split(".");
+ const lastKey = keys.pop();
+
+ // Идем по вложенности path
+ let current = targetObject;
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i];
+ if (!current[key]) {
+ current[key] = {};
+ }
+ current = current[key];
+ }
+
+ current[lastKey] = value;
+
+ return true;
+}
+
+export function updateExistingStructure(existingRows, updateItems) {
+ const updatedRows = JSON.parse(JSON.stringify(existingRows));
+
+ updateItems.forEach((update) => {
+ const [rowType, depth, sortOrder, newData] = update;
+
+ // Рекурсивная функция для поиска и обновления строки
+ function findAndUpdate(rows) {
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+
+ // Проверяем совпадение по всем ключам
+ if (
+ row.row_type === rowType &&
+ row.depth === depth &&
+ row.sort_order === sortOrder
+ ) {
+ // Обновляем только те поля, которые пришли в newData
+ if (newData && typeof newData === "object") {
+ Object.keys(newData).forEach((key) => {
+ if (row.data && row.data[key]) {
+ // Если поле существует в data, обновляем его
+ row.data[key] = {...row.data[key], ...newData[key]};
+ } else if (row[key]) {
+ // Если поле на уровне самой строки (не в data)
+ row[key] = newData[key];
+ }
+ });
+ }
+ return true; // Нашли и обновили
+ }
+
+ // Рекурсивно ищем в subRows
+ if (
+ row.subRows &&
+ Array.isArray(row.subRows) &&
+ row.subRows.length > 0
+ ) {
+ const found = findAndUpdate(row.subRows);
+ if (found) return true;
+ }
+ }
+ return false;
+ }
+
+ findAndUpdate(updatedRows);
+ });
+
+ return updatedRows;
+}