web сокеты для начала/конца редактирования ячейки
This commit is contained in:
parent
ca11d55228
commit
aa18a8e19c
@ -25,13 +25,14 @@ import { toast } from 'react-toastify';
|
||||
import { CircularProgress } from '@mui/material';
|
||||
|
||||
const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
const { data, setData, isLoading: isTableLoading } = useRealtimeData(formId, sheetName, direction);
|
||||
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction);
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
||||
const [selectedColumn, setSelectedColumn] = useState();
|
||||
const [rowSelection, setRowSelection] = useState({});
|
||||
const [isLoadingData, setIsLoadingData] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [editingCell, setEditingCell] = useState(null);
|
||||
|
||||
const handleGlobalFilterChange = (value) => {
|
||||
startTransition(() => {
|
||||
@ -42,13 +43,14 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
const {
|
||||
isConnected,
|
||||
isAuthenticated,
|
||||
subscribeToCellUpdates,
|
||||
subscribeToErrors,
|
||||
subscribeToAuthSuccess,
|
||||
error: wsError,
|
||||
updateCell: contextUpdateCell,
|
||||
addRow: contextAddRow,
|
||||
deleteRow: contextDeleteRow,
|
||||
startEditing: contextStartEditing,
|
||||
endEditing: contextEndEditing,
|
||||
} = useRealtime();
|
||||
|
||||
const {
|
||||
@ -144,6 +146,14 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
scrollPaddingStart: 0,
|
||||
scrollPaddingEnd: 0,
|
||||
},
|
||||
onEditingCellChange: (cell) => {
|
||||
console.log(cell, 'CELL')
|
||||
if (cell) {
|
||||
contextStartEditing?.(cell.row, cell.column);
|
||||
} else {
|
||||
contextEndEditing?.(editingCell.row, editingCell.column);
|
||||
}
|
||||
},
|
||||
columnVirtualizerOptions: ({ table }) => ({
|
||||
overscan: 5,
|
||||
measureElement: (el) => {
|
||||
@ -163,6 +173,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
globalFilter,
|
||||
showColumnFilters,
|
||||
rowSelection,
|
||||
editingCell,
|
||||
},
|
||||
initialState: {
|
||||
expanded: true,
|
||||
@ -214,6 +225,27 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
|
||||
const table = useMaterialReactTable(tableConfig);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dataEditingCells.line_id) {
|
||||
setEditingCell(null);
|
||||
return;
|
||||
}
|
||||
if (editingCell) return;
|
||||
const rowId = dataEditingCells.line_id;
|
||||
const columnId = dataEditingCells.column;
|
||||
try {
|
||||
const row = table.getRow(rowId);
|
||||
const column = table.getColumn(columnId);
|
||||
const cell = row?.getVisibleCells().find(c => c.column.id === columnId);
|
||||
if (cell) {
|
||||
setEditingCell(cell);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Ошибка редактирования ячейки');
|
||||
}
|
||||
}, [dataEditingCells, table]);
|
||||
|
||||
useEffect(() => {
|
||||
if (table && data && columns.length !== 0) {
|
||||
setIsLoadingData(true)
|
||||
|
||||
@ -28,7 +28,8 @@ export const RealtimeProvider = ({
|
||||
userId,
|
||||
}) => {
|
||||
// Формируем URL (кастомные секреты на фронте пока недоступны, делаем через REACT_APP_ROOT_PATH)
|
||||
const wsUrl = `${(process.env.REACT_APP_API_URL || process.env.REACT_APP_API_URL || process.env.REACT_APP_ROOT_PATH || "ws://localhost:8000").replace(/^http/, "ws")}/api/v1/ws/form/${formId}/sheet/${sheetName}${direction ? `?direction=${direction}` : ""}`; console.log()
|
||||
const wsUrl = `${(process.env.REACT_APP_API_URL || process.env.REACT_APP_API_URL || process.env.REACT_APP_ROOT_PATH || "ws://localhost:8000").replace(/^http/, "ws")}/api/v1/ws/form/${formId}/sheet/${sheetName}${direction ? `?direction=${direction}` : ""}`;
|
||||
|
||||
const handleMessage = useCallback((data) => {
|
||||
// Обработка ошибок
|
||||
if (data.error) {
|
||||
@ -38,8 +39,21 @@ export const RealtimeProvider = ({
|
||||
}
|
||||
|
||||
switch (data.event) {
|
||||
case "cell_edit_start":
|
||||
console.log("Cell edit started:", data);
|
||||
if (onCellEditStartRef.current) {
|
||||
onCellEditStartRef.current(data);
|
||||
}
|
||||
break;
|
||||
|
||||
case "cell_edit_end":
|
||||
console.log("Cell edit ended:", data);
|
||||
if ( onCellEditEndRef.current) {
|
||||
onCellEditEndRef.current(data);
|
||||
}
|
||||
break;
|
||||
|
||||
case "cell_updated":
|
||||
console.log(onCellUpdateRef.current);
|
||||
if (data.result && onCellUpdateRef.current) {
|
||||
const updatedCells = Array.isArray(data.result)
|
||||
? data.result
|
||||
@ -82,6 +96,8 @@ export const RealtimeProvider = ({
|
||||
const onRowDeleteRef = useRef(null);
|
||||
const onErrorRef = useRef(null);
|
||||
const onAuthSuccessRef = useRef(null);
|
||||
const onCellEditStartRef = useRef(null);
|
||||
const onCellEditEndRef = useRef(null);
|
||||
const authAttemptedRef = useRef(false);
|
||||
const reconnectTimerRef = useRef(null);
|
||||
|
||||
@ -134,6 +150,70 @@ export const RealtimeProvider = ({
|
||||
}
|
||||
}, [isConnected]);
|
||||
|
||||
// Функция для начала редактирования ячейки
|
||||
const startEditing = useCallback(
|
||||
async (row, column) => {
|
||||
const columnId = column.id;
|
||||
const rowId = row.id;
|
||||
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("WebSocket не подключен");
|
||||
return false;
|
||||
}
|
||||
|
||||
const lineId = row.id || null;
|
||||
const colId = columnId.slice(5); // убираем префикс "data."
|
||||
|
||||
const message = {
|
||||
event: "cell_edit_start",
|
||||
data: {
|
||||
line_id: lineId,
|
||||
column: colId,
|
||||
},
|
||||
};
|
||||
|
||||
const sent = sendMessage(message);
|
||||
if (!sent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[isConnected, sendMessage],
|
||||
);
|
||||
|
||||
// Функция для завершения редактирования ячейки
|
||||
const endEditing = useCallback(
|
||||
async (row, column) => {
|
||||
const columnId = column.id;
|
||||
const rowId = row.id;
|
||||
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("WebSocket не подключен");
|
||||
return false;
|
||||
}
|
||||
|
||||
const lineId = row.id || null;
|
||||
const colId = columnId.slice(5);
|
||||
|
||||
const message = {
|
||||
event: "cell_edit_end",
|
||||
data: {
|
||||
line_id: lineId,
|
||||
column: colId,
|
||||
},
|
||||
};
|
||||
|
||||
const sent = sendMessage(message);
|
||||
if (!sent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
[isConnected, sendMessage],
|
||||
);
|
||||
|
||||
const updateCell = useCallback(
|
||||
async (row, column, value) => {
|
||||
const columnId = column.id;
|
||||
@ -180,7 +260,7 @@ export const RealtimeProvider = ({
|
||||
};
|
||||
return sendMessage(message);
|
||||
},
|
||||
[isConnected, sendMessage],
|
||||
[isConnected, sendMessage, sheetName, formId, direction],
|
||||
);
|
||||
|
||||
const deleteRow = useCallback(
|
||||
@ -200,7 +280,7 @@ export const RealtimeProvider = ({
|
||||
|
||||
return sendMessage(message);
|
||||
},
|
||||
[isConnected, sendMessage],
|
||||
[isConnected, sendMessage, sheetName, formId, direction],
|
||||
);
|
||||
|
||||
const subscribeToCellUpdates = useCallback((callback) => {
|
||||
@ -238,6 +318,21 @@ export const RealtimeProvider = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const subscribeToCellEditStart = useCallback((callback) => {
|
||||
console.log('edit')
|
||||
onCellEditStartRef.current = callback;
|
||||
return () => {
|
||||
onCellEditStartRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const subscribeToCellEditEnd = useCallback((callback) => {
|
||||
onCellEditEndRef.current = callback;
|
||||
return () => {
|
||||
onCellEditEndRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const retryLogin = useCallback(() => {
|
||||
if (isConnected) {
|
||||
authAttemptedRef.current = false;
|
||||
@ -250,6 +345,8 @@ export const RealtimeProvider = ({
|
||||
value={{
|
||||
isConnected,
|
||||
error: lastError,
|
||||
startEditing,
|
||||
endEditing,
|
||||
updateCell,
|
||||
addRow,
|
||||
deleteRow,
|
||||
@ -262,6 +359,8 @@ export const RealtimeProvider = ({
|
||||
subscribeToRowDeletes,
|
||||
subscribeToErrors,
|
||||
subscribeToAuthSuccess,
|
||||
subscribeToCellEditStart,
|
||||
subscribeToCellEditEnd,
|
||||
retryLogin,
|
||||
releaseAllLocks: () => releaseAllLocks(userId),
|
||||
}}
|
||||
@ -269,4 +368,4 @@ export const RealtimeProvider = ({
|
||||
{children}
|
||||
</RealtimeContext.Provider>
|
||||
);
|
||||
};
|
||||
};
|
||||
@ -8,6 +8,7 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [editingCells, setEditingCells] = useState({});
|
||||
const dataRef = useRef(data);
|
||||
|
||||
const {
|
||||
@ -17,9 +18,10 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
subscribeToRowAdds,
|
||||
subscribeToRowDeletes,
|
||||
subscribeToErrors,
|
||||
subscribeToCellEditStart,
|
||||
subscribeToCellEditEnd,
|
||||
} = useRealtime();
|
||||
|
||||
// Ваша существующая функция форматирования
|
||||
const formatedToSubRows = useCallback((depth, oldData) => {
|
||||
if (depth === 0) return oldData;
|
||||
|
||||
@ -204,6 +206,100 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
});
|
||||
});
|
||||
|
||||
// Обработчик начала редактирования ячейки
|
||||
const unsubscribeCellEditStart = subscribeToCellEditStart((data) => {
|
||||
console.log(data);
|
||||
console.log('START EDIT');
|
||||
const dataCell = data.data;
|
||||
dataCell.column = 'data.'+ dataCell.column;
|
||||
setEditingCells(data.data)
|
||||
|
||||
// const { line_id, column } = data.data;
|
||||
// const cellKey = `${line_id}_${column}`;
|
||||
|
||||
// // Сохраняем информацию о том, кто начал редактирование
|
||||
// setEditingCells((prev) => ({
|
||||
// ...prev,
|
||||
// [cellKey]: {
|
||||
// userId: data.user_id || "unknown",
|
||||
// timestamp: Date.now(),
|
||||
// },
|
||||
// }));
|
||||
|
||||
// // Можно также добавить визуальную индикацию в данных
|
||||
// setData((prevData) => {
|
||||
// // Функция для обновления статуса редактирования в данных
|
||||
// const updateCellEditingStatus = (items) => {
|
||||
// return items.map((item) => {
|
||||
// if (item.line_id === line_id) {
|
||||
// // Находим нужную колонку и добавляем флаг редактирования
|
||||
// const updatedItem = { ...item };
|
||||
// if (updatedItem.columns && updatedItem.columns[column]) {
|
||||
// updatedItem.columns[column] = {
|
||||
// ...updatedItem.columns[column],
|
||||
// isEditing: true,
|
||||
// editingBy: data.user_id,
|
||||
// };
|
||||
// }
|
||||
// return updatedItem;
|
||||
// }
|
||||
// if (item.subRows) {
|
||||
// return {
|
||||
// ...item,
|
||||
// subRows: updateCellEditingStatus(item.subRows),
|
||||
// };
|
||||
// }
|
||||
// return item;
|
||||
// });
|
||||
// };
|
||||
// return updateCellEditingStatus(prevData);
|
||||
// });
|
||||
|
||||
// console.log(`Cell ${cellKey} editing started by user ${data.user_id}`);
|
||||
});
|
||||
|
||||
// Обработчик завершения редактирования ячейки
|
||||
const unsubscribeCellEditEnd = subscribeToCellEditEnd((data) => {
|
||||
setEditingCells(null);
|
||||
const { line_id, column } = data.data;
|
||||
const cellKey = `${line_id}_${column}`;
|
||||
|
||||
// Удаляем информацию о редактировании
|
||||
setEditingCells((prev) => {
|
||||
const newState = { ...prev };
|
||||
delete newState[cellKey];
|
||||
return newState;
|
||||
});
|
||||
|
||||
// Убираем визуальную индикацию в данных
|
||||
setData((prevData) => {
|
||||
const updateCellEditingStatus = (items) => {
|
||||
return items.map((item) => {
|
||||
if (item.line_id === line_id) {
|
||||
const updatedItem = { ...item };
|
||||
if (updatedItem.columns && updatedItem.columns[column]) {
|
||||
// Удаляем флаги редактирования
|
||||
const { isEditing, editingBy, ...restColumns } =
|
||||
updatedItem.columns[column];
|
||||
updatedItem.columns[column] = restColumns;
|
||||
}
|
||||
return updatedItem;
|
||||
}
|
||||
if (item.subRows) {
|
||||
return {
|
||||
...item,
|
||||
subRows: updateCellEditingStatus(item.subRows),
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
return updateCellEditingStatus(prevData);
|
||||
});
|
||||
|
||||
console.log(`Cell ${cellKey} editing ended`);
|
||||
});
|
||||
|
||||
const unsubscribeErrors = subscribeToErrors((err) => {
|
||||
console.error("WebSocket error:", err);
|
||||
setError(err);
|
||||
@ -215,6 +311,8 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
unsubscribeRowAdds();
|
||||
unsubscribeRowDeletes();
|
||||
unsubscribeErrors();
|
||||
unsubscribeCellEditStart();
|
||||
unsubscribeCellEditEnd();
|
||||
};
|
||||
}, [
|
||||
wsConnected,
|
||||
@ -222,6 +320,8 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
subscribeToRowAdds,
|
||||
subscribeToRowDeletes,
|
||||
subscribeToErrors,
|
||||
subscribeToCellEditStart,
|
||||
subscribeToCellEditEnd,
|
||||
formatedToSubRows,
|
||||
]);
|
||||
|
||||
@ -244,13 +344,33 @@ const useRealtimeData = (formId, sheetName, direction) => {
|
||||
[formatedToSubRows],
|
||||
);
|
||||
|
||||
// Функция для проверки, редактируется ли ячейка
|
||||
const isCellEditing = useCallback(
|
||||
(lineId, column) => {
|
||||
const cellKey = `${lineId}_${column}`;
|
||||
return !!editingCells[cellKey];
|
||||
},
|
||||
[editingCells],
|
||||
);
|
||||
|
||||
// Функция для получения информации о том, кто редактирует ячейку
|
||||
const getCellEditingInfo = useCallback(
|
||||
(lineId, column) => {
|
||||
const cellKey = `${lineId}_${column}`;
|
||||
return editingCells[cellKey] || null;
|
||||
},
|
||||
[editingCells],
|
||||
);
|
||||
|
||||
return {
|
||||
data,
|
||||
setData,
|
||||
// setData: updateData,
|
||||
isConnected,
|
||||
isLoading,
|
||||
error,
|
||||
editingCells,
|
||||
isCellEditing,
|
||||
getCellEditingInfo,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -97,22 +97,13 @@ export const getTableColumns = ({
|
||||
EditCell,
|
||||
columnsConfig,
|
||||
onCellUpdateError,
|
||||
onCellNumberClick
|
||||
onCellNumberClick,
|
||||
}) => {
|
||||
const columnColors = { ...columnsConfig.colors };
|
||||
const columns = [...columnsConfig.columns];
|
||||
|
||||
const processColumns = (columns, EditCell, Cell) => {
|
||||
columns.forEach((col) => {
|
||||
if (col.header.length > 15) {
|
||||
col.size = 250;
|
||||
}
|
||||
if(col.header.length >= 20){
|
||||
col.size = 350;
|
||||
}
|
||||
if(col.header.length >= 30){
|
||||
col.size = 400;
|
||||
}
|
||||
col.Cell = ({ cell, table, column, row }) => (
|
||||
<Cell
|
||||
row={row}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user