392 lines
9.8 KiB
JavaScript
392 lines
9.8 KiB
JavaScript
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;
|