409 lines
13 KiB
JavaScript
409 lines
13 KiB
JavaScript
import React, { useMemo, useState } from "react";
|
||
import {
|
||
useMaterialReactTable,
|
||
MaterialReactTable,
|
||
} from 'material-react-table';
|
||
import {
|
||
Box,
|
||
Paper,
|
||
Typography,
|
||
} from "@mui/material";
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { firstTableData, secondTableData } from "./mockData";
|
||
import { createFirstColumns, secondColumns } from "./columns";
|
||
import EditModal from "./components/EditModal/EditModal";
|
||
import TableFilters from "./components/TableFilters/TableFilters"; // Импортируем компонент фильтров
|
||
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; |