2026-08-06 17:44:49 +03:00

392 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Box, Paper, Typography } from '@mui/material';
import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
import { useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { createFirstColumns, secondColumns } from './columns';
import EditModal from './components/EditModal/EditModal';
import TableFilters from './components/TableFilters/TableFilters'; // Импортируем компонент фильтров
import { firstTableData, secondTableData } from './mockData';
import { handleEditClick, handleNavigateClick, handleSaveEdit } from './utils/tableHandlers';
const SummaryPage = () => {
const navigate = useNavigate();
const [selectedRow, setSelectedRow] = useState(null);
const [editModalOpen, setEditModalOpen] = useState(false);
const [selectedRowData, setSelectedRowData] = useState(null);
const [tableData, setTableData] = useState(firstTableData);
// Состояния для фильтров
const [selectedBranch, setSelectedBranch] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const getFilteredData = () => {
if (!selectedBranch && !searchQuery) {
return tableData;
}
return tableData
.filter((item) => {
// Фильтр по филиалу (только для корневых)
if (selectedBranch) {
return item.org_unit_id === selectedBranch.id;
}
return true;
})
.map((item) => {
if (!searchQuery) {
return item;
}
const query = searchQuery.toLowerCase().trim();
// Проверяем, совпадает ли родитель
const parentMatches = item.project.toLowerCase().includes(query);
// Фильтруем дочерние элементы - оставляем только те, что совпадают с поиском
const filteredSubRows = item.subRows?.filter((subItem) => subItem.project.toLowerCase().includes(query)) || [];
// Возвращаем элемент с отфильтрованными дочерними элементами
return {
...item,
subRows: filteredSubRows,
};
})
.filter((item) => {
if (!searchQuery) {
return true;
}
const query = searchQuery.toLowerCase().trim();
const parentMatches = item.project.toLowerCase().includes(query);
const hasMatchingChildren = item.subRows && item.subRows.length > 0;
return parentMatches || hasMatchingChildren;
});
};
const filteredData = getFilteredData();
const handleRowClick = (row) => {
if (row.depth === 0 && row) {
setSelectedRow(row);
}
};
const isChildOfSelectedRow = (row) => {
if (!selectedRow) return false;
const parentRowId = row.parentId;
return parentRowId === selectedRow.id;
};
const onEdit = (row) => {
handleEditClick(row, setEditModalOpen, setSelectedRowData);
};
const onNavigate = (row) => {
handleNavigateClick(row, navigate);
};
const onSaveEdit = (formData) => {
handleSaveEdit(formData, updateTableData);
};
const updateTableData = (updatedData) => {
setTableData((prevData) => prevData.map((item) => (item.id === updatedData.id ? { ...item, ...updatedData } : item)));
};
const firstColumns = useMemo(() => createFirstColumns({ onEdit, onNavigate }), [onEdit, onNavigate]);
const tableHeadCellStyles = {
fontWeight: 700,
fontSize: '12px',
lineHeight: 1.15,
textAlign: 'left',
backgroundColor: 'rgb(248, 249, 250)',
borderRight: '1px solid #e0e0e0',
'&:last-child': {
borderRight: 'none',
},
whiteSpace: 'nowrap',
position: 'sticky',
top: 0,
boxShadow: 'none',
padding: '0.5rem 1rem',
'& .MuiTableSortLabel-icon': {
fontSize: '0.875rem',
},
'& .MuiTableSortLabel-iconDirectionDesc': {
fontSize: '0.875rem',
},
'& .MuiTableSortLabel-iconDirectionAsc': {
fontSize: '0.875rem',
},
};
const summaryProjectsTableOptions = {
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableSorting: true,
enableBottomToolbar: false,
enableTopToolbar: false,
enableExpanding: true,
getSubRows: (row) => row.subRows,
muiTableHeadCellProps: {
sx: tableHeadCellStyles,
},
muiExpandButtonProps: {
sx: {
width: '1.5rem',
height: '1.5rem',
minWidth: '1.5rem',
'& .MuiSvgIcon-root': {
fontSize: '0.875rem',
},
},
},
muiTableBodyRowProps: ({ row }) => {
const isSelected = selectedRow && row.id === selectedRow.id;
const isChild = isChildOfSelectedRow(row);
return {
sx: {
backgroundColor: '#ffffff',
'&:hover': {
backgroundColor: 'rgb(248, 250, 252)',
},
'&:hover td::after': {
backgroundColor: 'transparent !important',
},
...(row.depth > 0 && {
backgroundColor: 'rgb(252, 252, 253)',
}),
...((isSelected || isChild) && {
backgroundColor: 'rgb(234, 247, 236)',
'&:hover': {
backgroundColor: 'rgb(225, 242, 229) !important',
},
}),
},
onClick: () => handleRowClick(row),
};
},
muiTableBodyCellProps: ({ row, column }) => ({
sx: {
borderRight: '1px solid #e0e0e0',
'&:last-child': {
borderRight: 'none',
},
'&:hover td::after': {
backgroundColor: 'transparent !important',
},
textAlign: 'left',
fontSize: '0.875rem',
padding: '0.5rem 1rem',
...(column.id === 'mrt-row-expand' && {
width: '1.5rem',
maxWidth: '1.5rem',
minWidth: '1.5rem',
padding: '0.5rem 0.25rem',
}),
...(column.id === 'project' &&
row.depth > 0 && {
pl: `${2 + row.depth * 0.5}rem`,
}),
},
}),
muiTablePaperProps: {
elevation: 0,
sx: {
border: '1px solid #e0e0e0',
borderRadius: '1rem',
overflow: 'hidden',
height: '45vh',
display: 'flex',
flexDirection: 'column',
},
},
muiTableContainerProps: {
sx: {
flex: 1,
overflow: 'auto',
'& thead': {
position: 'sticky',
top: 0,
zIndex: 10,
},
},
},
};
const projectTableOptions = {
enableColumnActions: false,
enableColumnFilters: false,
enablePagination: false,
enableSorting: true,
enableBottomToolbar: false,
enableTopToolbar: false,
enableExpanding: false,
muiTableBodyRowProps: ({ row }) => ({
sx: {
backgroundColor: '#ffffff',
'&:hover': {
backgroundColor: 'rgb(248, 250, 252)',
},
'&:hover td::after': {
backgroundColor: 'transparent !important',
},
boxShadow: 'none',
},
}),
muiTableHeadCellProps: {
sx: tableHeadCellStyles,
},
muiTableBodyCellProps: ({ column }) => ({
sx: {
borderRight: '1px solid #e0e0e0',
'&:last-child': {
borderRight: 'none',
},
textAlign: 'left',
fontSize: '0.875rem',
padding: '0.5rem 1rem',
},
}),
muiTablePaperProps: {
elevation: 0,
sx: {
border: '1px solid #e0e0e0',
borderRadius: '0 0 1rem 1rem',
overflow: 'hidden',
height: '35vh',
display: 'flex',
flexDirection: 'column',
},
},
muiTableContainerProps: {
sx: {
flex: 1,
overflow: 'auto',
'& thead': {
position: 'sticky',
top: 0,
zIndex: 10,
},
},
},
initialState: {
columnPinning: { left: ['project'] },
},
};
const summaryProjectsTable = useMaterialReactTable({
columns: firstColumns,
data: filteredData,
...summaryProjectsTableOptions,
initialState: {
columnPinning: { right: ['actions'], left: ['mrt-row-expand', 'project'] },
},
});
const projectTable = useMaterialReactTable({
columns: secondColumns,
data: selectedRow ? secondTableData[selectedRow.original.id] : [],
...projectTableOptions,
});
const getSelectedProjectTitle = () => {
if (selectedRow && selectedRow.original.project) {
return selectedRow.original.project;
}
return 'Проект не выбран';
};
// Обработчики фильтров
const handleBranchChange = (value) => {
setSelectedBranch(value);
};
const handleSearchChange = (value) => {
setSearchQuery(value);
};
return (
<Box
sx={{
p: '1.5rem',
maxWidth: '1600px',
mx: 'auto',
height: '100vh',
display: 'flex',
flexDirection: 'column',
}}>
{/* Компонент фильтров */}
<TableFilters
data={tableData}
onFilterChange={() => {}} // Можно использовать для дополнительной логики
selectedBranch={selectedBranch}
onBranchChange={handleBranchChange}
searchQuery={searchQuery}
onSearchChange={handleSearchChange}
/>
{/* Первая таблица */}
<Paper
sx={{
mb: '2rem',
overflow: 'hidden',
flex: '0 0 45vh',
borderRadius: '1rem',
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)',
}}>
<MaterialReactTable table={summaryProjectsTable} />
</Paper>
{/* Вторая таблица */}
<Paper
sx={{
overflow: 'hidden',
flex: '0 0 35vh',
borderRadius: '1rem',
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.1)',
}}>
<Typography
variant='subtitle1'
sx={{
p: '0.75rem 1.5rem',
backgroundColor: 'white',
borderBottom: '1px solid #e0e0e0',
fontWeight: 'bold',
flexShrink: 0,
fontSize: '1.25rem',
}}>
{getSelectedProjectTitle()}
</Typography>
{selectedRow ? (
<Box sx={{ height: 'calc(35vh - 3rem)' }}>
<MaterialReactTable table={projectTable} />
</Box>
) : (
<Box
sx={{
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'rgb(152, 162, 179)',
fontSize: '1rem',
fontWeight: 600,
}}>
Разверните или выберите проект в верхней таблице
</Box>
)}
</Paper>
{/* Модалка редактирования */}
<EditModal open={editModalOpen} onClose={() => setEditModalOpen(false)} rowData={selectedRowData} onSave={onSaveEdit} />
</Box>
);
};
export default SummaryPage;