добавление строки
This commit is contained in:
parent
52de655149
commit
99e7a445cf
@ -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);
|
||||
},
|
||||
};
|
||||
|
||||
@ -50,7 +50,7 @@ export const AppRoutes = () => {
|
||||
<Route path="/new-table" element={<NewTablePage />} />
|
||||
<Route path="/tables-new" element={<TablesTest />} />
|
||||
<Route path="/tables/:form/:sheetName" element={<NewFormTablePage />} />
|
||||
<Route path="/table/form/:formId/form-type/:formType/:sheetName" element={<NewFormTablePage />} />
|
||||
<Route path="/table/form/:formId/form-type/:formType/:sheetName/:direction" element={<NewFormTablePage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
@ -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 <div>Loading...</div>;
|
||||
}
|
||||
@ -213,6 +226,7 @@ const RealtimeTable = ({ formType, formId, sheetName }) => {
|
||||
onGlobalFilterChange={setGlobalFilter}
|
||||
sizeMult={sizeMult}
|
||||
onChangeShowColumnFilters={setShowColumnFilters}
|
||||
onAddRow={handleAddRow}
|
||||
/>
|
||||
|
||||
<div style={tableWrapperStyle}>
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<Panel>
|
||||
<Stack direction="row" sx={{gap:'1.25rem'}}>
|
||||
<Stack direction="row" sx={{ gap: '1.25rem' }}>
|
||||
<GroupByObject title="Отображение">
|
||||
<ColorPickerButton onColorApply={addColorForCells} />
|
||||
|
||||
@ -92,7 +93,7 @@ const SettingPanel = ({
|
||||
|
||||
<GroupByObject title="Строки">
|
||||
<Tooltip title="Добавить строку">
|
||||
<IconButton onClick={addRow} variant="outlined">
|
||||
<IconButton onClick={onAddRow} variant="outlined">
|
||||
<Plus />
|
||||
<AddLine />
|
||||
</IconButton>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -22,9 +22,10 @@ const TableCard = ({ table, onNavigate }) => {
|
||||
</div>
|
||||
<SectionTitle>
|
||||
<h2>{table.name}</h2>
|
||||
<UsersList>
|
||||
<h2>{table?.direction}</h2>
|
||||
{/* <UsersList>
|
||||
<div className="img-user"></div>
|
||||
</UsersList>
|
||||
</UsersList> */}
|
||||
</SectionTitle>
|
||||
</SectionStartPart>
|
||||
<Right />
|
||||
|
||||
@ -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 (
|
||||
<TableCard
|
||||
|
||||
@ -24,7 +24,7 @@ const TableInfo = () => {
|
||||
};
|
||||
|
||||
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}
|
||||
>
|
||||
<RealtimeTable formType={formType} formId={formId} sheetName={sheetName} />
|
||||
<RealtimeTable formType={formType} formId={formId} sheetName={sheetName} direction={direction === 'null' ? null : direction} />
|
||||
|
||||
</RealtimeProvider>
|
||||
)}
|
||||
|
||||
@ -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 || 'Ошибка получения данных задачи',
|
||||
@ -83,9 +89,9 @@ export default function TaskPage() {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const data = await TasksApi.getById(id);
|
||||
setTask(data.result)
|
||||
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 (
|
||||
<>
|
||||
{/* <TaskInfo task={task} /> */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@ -145,14 +151,12 @@ export default function TaskPage() {
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'space-between' }}>
|
||||
<SearchComponent
|
||||
placeholder="Поиск по названию таблицы"
|
||||
placeholder="Поиск по названию или направлению"
|
||||
value={query}
|
||||
onChange={setQuery}
|
||||
/>
|
||||
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end' }}>
|
||||
|
||||
<ExportWithTextButton onClick={handleExport} />
|
||||
|
||||
<PrimaryOutlinedButton
|
||||
variant="outlined"
|
||||
onClick={() => setModalStageOpen(true)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user