diff --git a/web/src/api/form_sheet.js b/web/src/api/form_sheet.js
index 0c1771b..9213a35 100644
--- a/web/src/api/form_sheet.js
+++ b/web/src/api/form_sheet.js
@@ -1,10 +1,9 @@
import api from './client';
export const FormsSheetApi = {
- get(formId, sheetName) {
+ get(formId, sheetName, params) {
return api
- .get(`/form/${formId}/sheet/${sheetName}`)
- // .get(`/forms/${form}/sheet/${sheetName}`)
+ .get(`/form/${formId}/sheet/${sheetName}`,params)
.then((r) => r.data);
},
};
diff --git a/web/src/app/Routes.jsx b/web/src/app/Routes.jsx
index 74414ab..4c26684 100644
--- a/web/src/app/Routes.jsx
+++ b/web/src/app/Routes.jsx
@@ -50,7 +50,7 @@ export const AppRoutes = () => {
} />
} />
} />
- } />
+ } />
diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx
index 063dbd3..1cdf94b 100644
--- a/web/src/components/RealtimeTable/RealtimeTable.jsx
+++ b/web/src/components/RealtimeTable/RealtimeTable.jsx
@@ -21,10 +21,9 @@ 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);
+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);
@@ -39,7 +38,8 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
subscribeToErrors,
subscribeToAuthSuccess,
error: wsError,
- updateCell: contextUpdateCell
+ updateCell: contextUpdateCell,
+ addRow: contextAddRow,
} = useRealtime();
const {
@@ -196,6 +196,19 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
const table = useMaterialReactTable(tableConfig);
+ const handleAddRow = useCallback(() => {
+ const allRows = table.getRowModel().rows;
+
+ // Фильтруем выделенные строки
+ const selectedRows = allRows.filter(row => rowSelection[row.id]);
+ if (selectedRows.length === 0) {
+ toasr.error('Выделите строку для вставки');
+ return;
+ }
+ const row = selectedRows[0];
+ const expense_item_id = row.original.data.header.expense_item_id;
+ contextAddRow({expense_item_id:expense_item_id})
+ }, [rowSelection, table])
if (!data) {
return
Loading...
;
}
@@ -213,6 +226,7 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
onGlobalFilterChange={setGlobalFilter}
sizeMult={sizeMult}
onChangeShowColumnFilters={setShowColumnFilters}
+ onAddRow={handleAddRow}
/>
diff --git a/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx b/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx
index 7b4b12a..225e5fe 100644
--- a/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx
+++ b/web/src/components/RealtimeTable/SettingPanel/SettingPanel.jsx
@@ -29,6 +29,7 @@ const SettingPanel = ({
onGlobalFilterChange,
sizeMult,
onChangeShowColumnFilters,
+ onAddRow,
}) => {
const [isPinned, setIsPinned] = useState(false);
const [columnTree, setColumnTree] = useState();
@@ -54,7 +55,7 @@ const SettingPanel = ({
);
}, [columnVisibility]);
- const { addRow, addColorForCells, addFormulaForCells, deleteFormula } = {};
+ const { addColorForCells, addFormulaForCells, deleteFormula } = {};
const handleChangeShowColumnFilters = () => {
const show = table.getState().showColumnFilters;
@@ -62,14 +63,14 @@ const SettingPanel = ({
onChangeShowColumnFilters(!show);
};
- const handleClickAddFormula = () => {};
+ const handleClickAddFormula = () => { };
- const handleClickDeleteFormula = () => {};
+ const handleClickDeleteFormula = () => { };
return (
<>
-
+
@@ -92,7 +93,7 @@ const SettingPanel = ({
-
+
diff --git a/web/src/components/RealtimeTable/constants/FORM_1/AHR_P.js b/web/src/components/RealtimeTable/constants/FORM_1/AHR.js
similarity index 100%
rename from web/src/components/RealtimeTable/constants/FORM_1/AHR_P.js
rename to web/src/components/RealtimeTable/constants/FORM_1/AHR.js
diff --git a/web/src/components/RealtimeTable/contexts/RealtimeContext.js b/web/src/components/RealtimeTable/contexts/RealtimeContext.js
index 500ee91..b7bd552 100644
--- a/web/src/components/RealtimeTable/contexts/RealtimeContext.js
+++ b/web/src/components/RealtimeTable/contexts/RealtimeContext.js
@@ -33,15 +33,14 @@ export const RealtimeProvider = ({
const handleMessage = useCallback((data) => {
// Обработка ошибок
if (data.error) {
- toast.error("Server error:", data.error)
+ toast.error("Server error:", data.error);
onErrorRef.current?.(data.error);
return;
}
- // Обработка событий
switch (data.event) {
case "cell_updated":
- console.log(onCellUpdateRef.current)
+ console.log(onCellUpdateRef.current);
if (data.result && onCellUpdateRef.current) {
const updatedCells = Array.isArray(data.result)
? data.result
@@ -52,7 +51,7 @@ export const RealtimeProvider = ({
case "row_added":
if (data.result && onRowAddRef.current) {
- onRowAddRef.current(data.result);
+ onRowAddRef.current(data);
}
break;
@@ -145,7 +144,6 @@ export const RealtimeProvider = ({
return false;
}
-
const lineId = row.original?.data?.line_id || null;
const colId = columnId.slice(5);
const message = {
@@ -156,7 +154,6 @@ export const RealtimeProvider = ({
value: value,
},
};
- console.log(message);
const sent = sendMessage(message);
if (!sent) {
@@ -165,10 +162,7 @@ export const RealtimeProvider = ({
return true;
},
- [
- isConnected,
- sendMessage,
- ],
+ [isConnected, sendMessage],
);
const addRow = useCallback(
@@ -181,8 +175,10 @@ export const RealtimeProvider = ({
const message = {
event: "row_added",
data: rowData,
+ sheet: sheetName,
+ form_id: formId,
+ direction: direction,
};
-
return sendMessage(message);
},
[isConnected, sendMessage],
@@ -190,7 +186,7 @@ export const RealtimeProvider = ({
const deleteRow = useCallback(
async (rowId) => {
- if (!isConnected ) {
+ if (!isConnected) {
onErrorRef.current?.("Нет подключения или авторизации");
return false;
}
diff --git a/web/src/components/RealtimeTable/hooks/useRealtimeData.js b/web/src/components/RealtimeTable/hooks/useRealtimeData.js
index 857f434..efa64df 100644
--- a/web/src/components/RealtimeTable/hooks/useRealtimeData.js
+++ b/web/src/components/RealtimeTable/hooks/useRealtimeData.js
@@ -1,9 +1,9 @@
import { useState, useEffect, useCallback, useRef } from "react";
import { FormsSheetApi } from "../../../api/form_sheet";
import { useRealtime } from "../contexts/RealtimeContext";
-import { updateExistingStructure } from "../utils/cellUtils";
+import { insertCell, updateCells } from "../utils/cellUtils";
-const useRealtimeData = (formId, sheetName) => {
+const useRealtimeData = (formId, sheetName, direction) => {
const [data, setData] = useState([]);
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState(null);
@@ -71,7 +71,14 @@ const useRealtimeData = (formId, sheetName) => {
const loadInitialData = async () => {
try {
setIsLoading(true);
- const res = await FormsSheetApi.get(formId, sheetName);
+ let res;
+ if (direction !== null) {
+ res = await FormsSheetApi.get(formId, sheetName, {
+ params: { direction: direction },
+ });
+ } else {
+ res = await FormsSheetApi.get(formId, sheetName);
+ }
const data = res.result;
if (!data || data.length === 0) {
@@ -119,8 +126,22 @@ const useRealtimeData = (formId, sheetName) => {
if (!updatedCells || updatedCells.length === 0) return;
setData((prevData) => {
- const newData = updateExistingStructure(prevData, updatedCells)
- return newData
+ const newData = updateCells(prevData, updatedCells);
+ return newData;
+ });
+ });
+
+ const unsubscribeRowAdds = subscribeToRowAdds((data) => {
+ const updates = data.result;
+ const expense_item_id = data.data.expense_item_id;
+ const updatedCells = updates.slice(0, -1);
+ const newRow = updates.at(-1);
+ setData((prevData) => {
+ const updateData = updateCells(prevData, updatedCells);
+
+ const newData = insertCell(updateData, newRow, expense_item_id);
+ console.log(newData)
+ return newData;
});
});
@@ -150,7 +171,7 @@ const useRealtimeData = (formId, sheetName) => {
return () => {
unsubscribeCellUpdates();
- // unsubscribeRowAdds();
+ unsubscribeRowAdds();
unsubscribeRowDeletes();
unsubscribeErrors();
};
diff --git a/web/src/components/RealtimeTable/utils/cellUtils.js b/web/src/components/RealtimeTable/utils/cellUtils.js
index 64b2e3d..1f948e5 100644
--- a/web/src/components/RealtimeTable/utils/cellUtils.js
+++ b/web/src/components/RealtimeTable/utils/cellUtils.js
@@ -42,10 +42,10 @@ export function setNewValueToData(data, colId, rowId, value) {
return true;
}
-export function updateExistingStructure(existingRows, updateItems) {
- const updatedRows = JSON.parse(JSON.stringify(existingRows));
+export function updateCells(data, newRows) {
+ const updatedRows = JSON.parse(JSON.stringify(data));
- updateItems.forEach((update) => {
+ newRows.forEach((update) => {
const [rowType, depth, sortOrder, newData] = update;
// Рекурсивная функция для поиска и обновления строки
@@ -53,20 +53,20 @@ export function updateExistingStructure(existingRows, updateItems) {
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]};
+ if (typeof row.data[key] === "object") {
+ row.data[key] = { ...row.data[key], ...newData[key] };
+ } else {
+ row.data[key] = newData[key];
+ }
} else if (row[key]) {
- // Если поле на уровне самой строки (не в data)
row[key] = newData[key];
}
});
@@ -74,7 +74,6 @@ export function updateExistingStructure(existingRows, updateItems) {
return true; // Нашли и обновили
}
- // Рекурсивно ищем в subRows
if (
row.subRows &&
Array.isArray(row.subRows) &&
@@ -92,3 +91,37 @@ export function updateExistingStructure(existingRows, updateItems) {
return updatedRows;
}
+
+export function insertCell(data, newRow, expense_item_id) {
+ const updatedRows = JSON.parse(JSON.stringify(data));
+ const [row_type, depth, sort_order, dataRow] = newRow;
+ const newRowObject = {
+ row_type: row_type,
+ depth: depth,
+ sort_order: sort_order,
+ subRows: [],
+ data: dataRow,
+ };
+
+ function findAndUpdate(rows) {
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+
+ if (row.data.header.expense_item_id === expense_item_id) {
+ if (!("subRows" in row)) {
+ row.subRows = [];
+ }
+ row.subRows.push(newRowObject);
+ return true;
+ }
+
+ 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;
+}
diff --git a/web/src/components/common/TableCard/TableCard.jsx b/web/src/components/common/TableCard/TableCard.jsx
index db19e26..fa12a9d 100644
--- a/web/src/components/common/TableCard/TableCard.jsx
+++ b/web/src/components/common/TableCard/TableCard.jsx
@@ -22,9 +22,10 @@ const TableCard = ({ table, onNavigate }) => {
{table.name}
-
+ {table?.direction}
+ {/*
-
+ */}
diff --git a/web/src/components/common/TableList/TableList.jsx b/web/src/components/common/TableList/TableList.jsx
index 54a992a..28be971 100644
--- a/web/src/components/common/TableList/TableList.jsx
+++ b/web/src/components/common/TableList/TableList.jsx
@@ -22,7 +22,7 @@ const TablesList = ({
if (!formInfo){
return;
}
- const path = `${basePath}/form/${formInfo.id}/form-type/${formInfo.form_type_code}/${table.sheet}`;
+ const path = `${basePath}/form/${formInfo.id}/form-type/${formInfo.form_type_code}/${table.sheet}/${table?.direction}`;
return (
{
};
export default function NewTablePage() {
- const { formId, formType, sheetName } = useParams()
+ const { formId, formType, sheetName, direction } = useParams()
const { user } = useAuth();
return (
@@ -34,8 +34,9 @@ export default function NewTablePage() {
formId={formId}
sheetName={sheetName}
userId={user.id}
+ direction={direction === 'null' ? null : direction}
>
-
+
)}
diff --git a/web/src/pages/TaskPage/TaskPage.jsx b/web/src/pages/TaskPage/TaskPage.jsx
index 1d84ba1..5b08735 100644
--- a/web/src/pages/TaskPage/TaskPage.jsx
+++ b/web/src/pages/TaskPage/TaskPage.jsx
@@ -63,7 +63,13 @@ export default function TaskPage() {
setIsLoading(true);
try {
const data = await TasksApi.getSheets(id);
- setTables(data.result.sheets.map(table => ({ name: SHEET_NAME[table], sheet: table })));
+ // Преобразуем данные с учетом direction
+ const mappedTables = data.result.all_sheets.map(sheet => ({
+ name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
+ sheet: sheet.sheet_name,
+ direction: sheet.direction
+ }));
+ setTables(mappedTables);
} catch (error) {
toast.error(
error?.response?.data?.detail || 'Ошибка получения данных задачи',
@@ -78,14 +84,14 @@ export default function TaskPage() {
}
}, [id]);
- useEffect(() => {
+ useEffect(() => {
const getFormInfo = async () => {
setIsLoading(true);
try {
- const data = await TasksApi.getById(id);
- setTask(data.result)
+ const data = await TasksApi.getById(id);
+ setTask(data.result);
} catch (error) {
-
+ // обработка ошибки
} finally {
setIsLoading(false);
}
@@ -97,13 +103,14 @@ export default function TaskPage() {
}, [id]);
const handleCreateProject = (projectData) => {
-
+ // логика создания проекта
};
- // Фильтруем таблицы по query
- const filteredTables = tables.filter((item) =>
- item.name.toLowerCase().includes(query.toLowerCase()),
- );
+ // Фильтруем таблицы по query (учитывая name и direction)
+ const filteredTables = tables.filter((item) => {
+ const searchString = `${item.name} ${item.direction || ''}`.toLowerCase();
+ return searchString.includes(query.toLowerCase());
+ });
if (isLoading) {
return (
@@ -124,7 +131,6 @@ export default function TaskPage() {
return (
<>
- {/* */}
-
-
setModalStageOpen(true)}
@@ -179,4 +183,4 @@ export default function TaskPage() {
>
);
-}
+}
\ No newline at end of file