552 lines
14 KiB
JavaScript
552 lines
14 KiB
JavaScript
import { useMemo, useState, useRef } from 'react';
|
|
import {
|
|
MaterialReactTable,
|
|
useMaterialReactTable,
|
|
} from 'material-react-table';
|
|
import { Box, Typography, IconButton, Button, Stack } from '@mui/material';
|
|
import { EditPenSvg } from '../../../components/common/icons/icons';
|
|
import ModalEditAddVsp from './Modals/ModalEditAddVsp';
|
|
import {
|
|
PrimaryButton,
|
|
PrimaryOutlinedButton,
|
|
} from '../../../components/common/Buttons/Buttons';
|
|
import { useAuth } from '../../../app/context/AuthProvider';
|
|
import { ROLES_NAME_ID } from '../../../constants';
|
|
import ModalExport from './Modals/ModalExport';
|
|
|
|
const TableDictVsp = ({
|
|
data,
|
|
onEdit,
|
|
onDelete,
|
|
onAdd,
|
|
loading = false,
|
|
ssps,
|
|
selectedSsp,
|
|
}) => {
|
|
const { user } = useAuth();
|
|
const [modalOpen, setModalOpen] = useState(false);
|
|
const [modalExportOpen, setModalExportOpen] = useState(false);
|
|
const [editingItem, setEditingItem] = useState(null);
|
|
const [saveLoading, setSaveLoading] = useState(false);
|
|
const tableContainerRef = useRef(null);
|
|
|
|
const filteredData = useMemo(() => {
|
|
if (!selectedSsp) {
|
|
return data;
|
|
}
|
|
return data.filter((item) => item.ssp_id === selectedSsp.id);
|
|
}, [data, selectedSsp]);
|
|
|
|
const handleEdit = (item) => {
|
|
setEditingItem(item);
|
|
setModalOpen(true);
|
|
};
|
|
|
|
const handleAdd = () => {
|
|
setEditingItem(null);
|
|
setModalOpen(true);
|
|
};
|
|
|
|
const handleExport = () => [setModalExportOpen(true)];
|
|
|
|
const handleSave = async (formData) => {
|
|
setSaveLoading(true);
|
|
try {
|
|
if (editingItem) {
|
|
await onEdit?.({ ...formData, id: editingItem.id });
|
|
} else {
|
|
await onAdd?.(formData);
|
|
}
|
|
setModalOpen(false);
|
|
} catch (error) {
|
|
console.error('Error saving:', error);
|
|
} finally {
|
|
setSaveLoading(false);
|
|
}
|
|
};
|
|
|
|
const columns = useMemo(
|
|
() => [
|
|
{
|
|
accessorKey: 'index',
|
|
header: '№',
|
|
size: 60,
|
|
minSize: 50,
|
|
maxSize: 70,
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
Cell: ({ row }) => (
|
|
<Typography variant="body2" component="div" fontWeight="medium">
|
|
{row.index + 1}
|
|
</Typography>
|
|
),
|
|
},
|
|
{
|
|
accessorKey: 'system_code',
|
|
header: 'Код РФ',
|
|
size: 180,
|
|
minSize: 50,
|
|
maxSize: 100,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2" component="div" fontWeight="medium">
|
|
{cell.getValue() || '-'}
|
|
</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'registration_number',
|
|
header: 'Регистрационный номер ВСП',
|
|
size: 180,
|
|
minSize: 150,
|
|
maxSize: 200,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2" component="div" fontWeight="medium">
|
|
{cell.getValue() || '-'}
|
|
</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'ssp_id',
|
|
header: 'Региональный филиал/ССП',
|
|
size: 120,
|
|
minSize: 100,
|
|
maxSize: 150,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">
|
|
{cell.getValue()
|
|
? ssps.find((s) => s.id == cell.getValue())?.title
|
|
: '-'}
|
|
</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'vsp_type',
|
|
header: 'Тип ВСП',
|
|
size: 120,
|
|
minSize: 100,
|
|
maxSize: 150,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">{cell.getValue() || '-'}</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'address',
|
|
header: 'Адрес объекта',
|
|
size: 250,
|
|
minSize: 200,
|
|
maxSize: 350,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">{cell.getValue() || '-'}</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'approved_format',
|
|
header: 'Утверждённый формат',
|
|
size: 150,
|
|
minSize: 120,
|
|
maxSize: 180,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">{cell.getValue() || '-'}</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'open_date',
|
|
header: 'Дата открытия ВСП',
|
|
size: 130,
|
|
minSize: 110,
|
|
maxSize: 150,
|
|
Cell: ({ cell }) => {
|
|
const date = cell.getValue();
|
|
return (
|
|
<Typography variant="body2">
|
|
{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}
|
|
</Typography>
|
|
);
|
|
},
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'close_date',
|
|
header: 'Дата закрытия ВСП',
|
|
size: 130,
|
|
minSize: 110,
|
|
maxSize: 150,
|
|
Cell: ({ cell }) => {
|
|
const date = cell.getValue();
|
|
return (
|
|
<Typography variant="body2">
|
|
{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}
|
|
</Typography>
|
|
);
|
|
},
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'location_form',
|
|
header: 'Форма расположения ВСП',
|
|
size: 170,
|
|
minSize: 140,
|
|
maxSize: 200,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">{cell.getValue() || '-'}</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'numbers',
|
|
header: 'Штатная численность ВСП',
|
|
size: 170,
|
|
minSize: 140,
|
|
maxSize: 200,
|
|
Cell: ({ cell }) => {
|
|
const value = cell.getValue();
|
|
return (
|
|
<Typography variant="body2">
|
|
{value !== undefined && value !== null ? value : '-'}
|
|
</Typography>
|
|
);
|
|
},
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'area',
|
|
header: 'Площадь',
|
|
size: 100,
|
|
minSize: 80,
|
|
maxSize: 120,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">{cell.getValue() || '-'}</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'rent_contract_num',
|
|
header: 'Номер договора',
|
|
size: 130,
|
|
minSize: 110,
|
|
maxSize: 160,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2">{cell.getValue() || '-'}</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'rent_end_date',
|
|
header: 'Дата окончания договора',
|
|
size: 150,
|
|
minSize: 130,
|
|
maxSize: 170,
|
|
Cell: ({ cell }) => {
|
|
const date = cell.getValue();
|
|
return (
|
|
<Typography variant="body2">
|
|
{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}
|
|
</Typography>
|
|
);
|
|
},
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
{
|
|
accessorKey: 'notes',
|
|
header: 'Примечания',
|
|
size: 200,
|
|
minSize: 150,
|
|
maxSize: 300,
|
|
Cell: ({ cell }) => (
|
|
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
|
|
{cell.getValue() || '-'}
|
|
</Typography>
|
|
),
|
|
enableColumnActions: false,
|
|
enableSorting: false,
|
|
},
|
|
],
|
|
[],
|
|
);
|
|
|
|
const table = useMaterialReactTable({
|
|
columns,
|
|
data: filteredData,
|
|
enableEditing: true,
|
|
enableRowActions: true,
|
|
enableTopToolbar: true,
|
|
enableColumnFilters: false,
|
|
enableGlobalFilter: false,
|
|
enableSorting: false,
|
|
enablePagination: false,
|
|
positionActionsColumn: 'last',
|
|
|
|
// Включаем изменение размера колонок для лучшего UX при горизонтальном скролле
|
|
// enableColumnResizing: true,
|
|
columnResizeMode: 'onChange',
|
|
|
|
enableToolbarInternalActions: false,
|
|
|
|
initialState: {
|
|
columnPinning: {
|
|
left: ['index', 'system_code', 'registration_number'],
|
|
right: ['mrt-row-actions'],
|
|
},
|
|
},
|
|
|
|
renderTopToolbarCustomActions: () => (
|
|
<Box
|
|
justifyContent={'space-between'}
|
|
flexDirection={'row'}
|
|
display={'flex'}
|
|
width={'100%'}
|
|
>
|
|
<Typography
|
|
sx={{
|
|
fontWeight: 700,
|
|
fontSize: '1.25rem',
|
|
lineHeight: '140%',
|
|
color: '#2d3748',
|
|
}}
|
|
>
|
|
Справочник объектов
|
|
</Typography>
|
|
<Stack spacing={'.5rem'} direction={'row'}>
|
|
<PrimaryOutlinedButton onClick={handleExport}>
|
|
Экспортировать
|
|
</PrimaryOutlinedButton>
|
|
{user.role_id == ROLES_NAME_ID.admin && (
|
|
<PrimaryButton onClick={handleAdd}>+ Добавить объект</PrimaryButton>
|
|
)}
|
|
</Stack>
|
|
</Box>
|
|
),
|
|
|
|
muiTopToolbarProps: {
|
|
sx: {
|
|
backgroundColor: '#F9FAFB',
|
|
minHeight: 'auto',
|
|
padding: '1rem 1rem 0 1rem',
|
|
position: 'sticky',
|
|
top: 0,
|
|
zIndex: 10,
|
|
'& .MuiToolbar-root': {
|
|
padding: 0,
|
|
minHeight: 'auto',
|
|
},
|
|
},
|
|
},
|
|
|
|
displayColumnDefOptions: {
|
|
'mrt-row-actions': {
|
|
header: 'Действия',
|
|
size: 80,
|
|
muiTableHeadCellProps: {
|
|
align: 'center',
|
|
},
|
|
muiTableBodyCellProps: {
|
|
align: 'center',
|
|
},
|
|
},
|
|
},
|
|
|
|
renderRowActions: ({ row }) => (
|
|
<>
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 1,
|
|
}}
|
|
>
|
|
<IconButton
|
|
size="small"
|
|
onClick={() => handleEdit(row.original)}
|
|
sx={{
|
|
padding: '4px',
|
|
'&:hover': {
|
|
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
|
},
|
|
}}
|
|
title="Редактировать"
|
|
>
|
|
<EditPenSvg fill="#6FB246" />
|
|
</IconButton>
|
|
</Box>
|
|
</>
|
|
),
|
|
|
|
state: {
|
|
isLoading: loading,
|
|
},
|
|
|
|
muiTablePaperProps: {
|
|
sx: {
|
|
borderRadius: '.63rem',
|
|
border: '1px solid #f2f2f2',
|
|
padding: 0,
|
|
boxShadow: 'none',
|
|
overflow: 'hidden',
|
|
position: 'relative',
|
|
},
|
|
},
|
|
|
|
muiTableContainerProps: {
|
|
ref: tableContainerRef,
|
|
sx: {
|
|
maxHeight: 'calc(100vh - 450px)',
|
|
minHeight: '50rem',
|
|
maxWidth: '100rem',
|
|
overflowX: 'auto', // Включаем горизонтальный скролл
|
|
overflowY: 'auto', // Вертикальный скролл
|
|
position: 'relative',
|
|
// Добавляем индикатор скролла для лучшего UX
|
|
'&::-webkit-scrollbar': {
|
|
height: '8px',
|
|
width: '8px',
|
|
},
|
|
'&::-webkit-scrollbar-track': {
|
|
backgroundColor: '#f1f1f1',
|
|
borderRadius: '4px',
|
|
},
|
|
'&::-webkit-scrollbar-thumb': {
|
|
backgroundColor: '#c1c1c1',
|
|
borderRadius: '4px',
|
|
'&:hover': {
|
|
backgroundColor: '#a8a8a8',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// Обеспечиваем минимальную ширину таблицы для скролла
|
|
muiTableProps: {
|
|
sx: {
|
|
minWidth: 'max-content', // Таблица будет занимать минимум необходимую ширину
|
|
width: '100%',
|
|
},
|
|
},
|
|
|
|
muiTableBodyRowProps: {
|
|
sx: {
|
|
'& .MuiTableCell-root': {
|
|
padding: '0.75rem',
|
|
},
|
|
'&:hover': {
|
|
backgroundColor: 'white !important',
|
|
},
|
|
'&:hover td::after': {
|
|
opacity: '0 !important',
|
|
backgroundColor: 'transparent !important',
|
|
},
|
|
'&:hover .MuiTableCell-root': {
|
|
backgroundColor: 'white !important',
|
|
},
|
|
},
|
|
},
|
|
|
|
muiTableHeadProps: {
|
|
sx: {
|
|
position: 'sticky',
|
|
top: 0,
|
|
zIndex: 10,
|
|
},
|
|
},
|
|
|
|
muiTableHeadRowProps: {
|
|
sx: {
|
|
position: 'sticky',
|
|
backgroundColor: '#F9FAFB',
|
|
color: '#4A5565',
|
|
fontSize: '0.88rem',
|
|
'& .MuiTableCell-root': {
|
|
padding: '1rem 0.75rem',
|
|
fontWeight: 'bold',
|
|
backgroundColor: '#F9FAFB',
|
|
whiteSpace: 'nowrap', // Заголовки тоже не переносим
|
|
},
|
|
'& .Mui-TableHeadCell-Content': {
|
|
'& .Mui-TableHeadCell-Content-Labels': {
|
|
overflow: 'visible',
|
|
},
|
|
'& .MuiSvgIcon-root': {
|
|
fontSize: '1.2rem',
|
|
marginLeft: '8px',
|
|
overflow: 'visible !important',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
muiTableBodyCellProps: {
|
|
sx: {
|
|
padding: '0.75rem',
|
|
fontSize: '0.75rem',
|
|
lineHeight: '150%',
|
|
color: '#364153',
|
|
whiteSpace: 'nowrap', // Предотвращаем перенос текста
|
|
},
|
|
},
|
|
|
|
muiTableHeadCellProps: {
|
|
sx: {
|
|
padding: '1rem 0.75rem',
|
|
fontSize: 'inherit',
|
|
fontWeight: 'bold',
|
|
whiteSpace: 'nowrap', // Заголовки не переносим
|
|
'& .MuiSvgIcon-root': {
|
|
fontSize: '1.2rem',
|
|
marginLeft: '4px',
|
|
flexShrink: 0,
|
|
},
|
|
},
|
|
},
|
|
|
|
muiBottomToolbarProps: {
|
|
sx: {
|
|
display: 'none',
|
|
},
|
|
},
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<Box sx={{ position: 'relative' }}>
|
|
<MaterialReactTable table={table} />
|
|
</Box>
|
|
|
|
<ModalEditAddVsp
|
|
open={modalOpen}
|
|
onClose={() => setModalOpen(false)}
|
|
onSave={handleSave}
|
|
onDelete={onDelete}
|
|
initialData={editingItem}
|
|
loading={saveLoading}
|
|
ssps={ssps}
|
|
/>
|
|
<ModalExport
|
|
open={modalExportOpen}
|
|
onClose={() => setModalExportOpen(false)}
|
|
ssps={ssps}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default TableDictVsp;
|