perf(dict-vsp): виртуализация строк, состояние модалок вынесено из таблицы
Модалки в DictVspModals (forwardRef), таблица в memo со стабильными колбэками, enableRowVirtualization, size/minSize/maxSize колонок выверены под grid-раскладку. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3ab229f6f8
commit
b0de763259
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { NameTask, TaskInfoContainer } from '../../components/common/SwitchFormTask/SwitchFormTask.style';
|
import { NameTask, TaskInfoContainer } from '../../components/common/SwitchFormTask/SwitchFormTask.style';
|
||||||
|
|
||||||
import { Box, CircularProgress, Stack } from '@mui/material';
|
import { Box, CircularProgress, Stack } from '@mui/material';
|
||||||
@ -11,6 +11,7 @@ import { HeaderPortal } from '../../components/common/HeaderPortal';
|
|||||||
import { ROLES_NAME_ID } from '../../constants/constants';
|
import { ROLES_NAME_ID } from '../../constants/constants';
|
||||||
import { PageContainer } from '../../components/common/PageContainer';
|
import { PageContainer } from '../../components/common/PageContainer';
|
||||||
import DictVspFilters from './components/DictVspFilters';
|
import DictVspFilters from './components/DictVspFilters';
|
||||||
|
import DictVspModals from './components/DictVspModals';
|
||||||
import TableDictVsp from './components/TableDictVsp';
|
import TableDictVsp from './components/TableDictVsp';
|
||||||
|
|
||||||
const DictVspInfo = () => {
|
const DictVspInfo = () => {
|
||||||
@ -30,6 +31,7 @@ function DictVspPage() {
|
|||||||
const [ssps, setSsps] = useState([]);
|
const [ssps, setSsps] = useState([]);
|
||||||
const [selectedSsp, setSelectedSsp] = useState(null);
|
const [selectedSsp, setSelectedSsp] = useState(null);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const modalsRef = useRef(null);
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
@ -68,7 +70,7 @@ function DictVspPage() {
|
|||||||
loadData();
|
loadData();
|
||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
const handleAdd = async (formData) => {
|
const handleAdd = useCallback(async (formData) => {
|
||||||
try {
|
try {
|
||||||
const response = await DictVspApi.create(formData);
|
const response = await DictVspApi.create(formData);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
@ -82,9 +84,9 @@ function DictVspPage() {
|
|||||||
toast.error(error.response?.data?.message || 'Ошибка создания ВСП');
|
toast.error(error.response?.data?.message || 'Ошибка создания ВСП');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleEdit = async (formData) => {
|
const handleEdit = useCallback(async (formData) => {
|
||||||
try {
|
try {
|
||||||
const response = await DictVspApi.update(formData.id, formData);
|
const response = await DictVspApi.update(formData.id, formData);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
@ -98,9 +100,9 @@ function DictVspPage() {
|
|||||||
toast.error(error.response?.data?.message || 'Ошибка обновления ВСП');
|
toast.error(error.response?.data?.message || 'Ошибка обновления ВСП');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleDelete = async (vspId) => {
|
const handleDelete = useCallback(async (vspId) => {
|
||||||
try {
|
try {
|
||||||
const response = await DictVspApi.delete(vspId);
|
const response = await DictVspApi.delete(vspId);
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
@ -114,7 +116,11 @@ function DictVspPage() {
|
|||||||
toast.error(error.response?.data?.message || 'Ошибка удаления ВСП');
|
toast.error(error.response?.data?.message || 'Ошибка удаления ВСП');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
const handleAddClick = useCallback(() => modalsRef.current?.openAdd(), []);
|
||||||
|
const handleEditClick = useCallback((item) => modalsRef.current?.openEdit(item), []);
|
||||||
|
const handleExportClick = useCallback(() => modalsRef.current?.openExport(), []);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
@ -139,12 +145,18 @@ function DictVspPage() {
|
|||||||
)}
|
)}
|
||||||
<TableDictVsp
|
<TableDictVsp
|
||||||
data={dictVspData}
|
data={dictVspData}
|
||||||
ssps={ssps}
|
|
||||||
selectedSsp={selectedSsp}
|
selectedSsp={selectedSsp}
|
||||||
|
onAddClick={handleAddClick}
|
||||||
|
onEditClick={handleEditClick}
|
||||||
|
onExportClick={handleExportClick}
|
||||||
|
loading={isLoading}
|
||||||
|
/>
|
||||||
|
<DictVspModals
|
||||||
|
ref={modalsRef}
|
||||||
|
ssps={ssps}
|
||||||
onAdd={handleAdd}
|
onAdd={handleAdd}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
loading={isLoading}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
65
web/src/pages/DictVspPage/components/DictVspModals.jsx
Normal file
65
web/src/pages/DictVspPage/components/DictVspModals.jsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { forwardRef, useCallback, useImperativeHandle, useState } from 'react';
|
||||||
|
import ModalEditAddVsp from './Modals/ModalEditAddVsp';
|
||||||
|
import ModalExport from './Modals/ModalExport';
|
||||||
|
|
||||||
|
// Состояние модалок живёт здесь, а не в таблице: открытие/закрытие не перерисовывает MRT.
|
||||||
|
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps }, ref) => {
|
||||||
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
const [modalExportOpen, setModalExportOpen] = useState(false);
|
||||||
|
const [editingItem, setEditingItem] = useState(null);
|
||||||
|
const [saveLoading, setSaveLoading] = useState(false);
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
ref,
|
||||||
|
() => ({
|
||||||
|
openAdd: () => {
|
||||||
|
setEditingItem(null);
|
||||||
|
setModalOpen(true);
|
||||||
|
},
|
||||||
|
openEdit: (item) => {
|
||||||
|
setEditingItem(item);
|
||||||
|
setModalOpen(true);
|
||||||
|
},
|
||||||
|
openExport: () => 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 closeEdit = useCallback(() => setModalOpen(false), []);
|
||||||
|
const closeExport = useCallback(() => setModalExportOpen(false), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ModalEditAddVsp
|
||||||
|
open={modalOpen}
|
||||||
|
onClose={closeEdit}
|
||||||
|
onSave={handleSave}
|
||||||
|
onDelete={onDelete}
|
||||||
|
initialData={editingItem}
|
||||||
|
loading={saveLoading}
|
||||||
|
ssps={ssps}
|
||||||
|
/>
|
||||||
|
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={ssps} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DictVspModals.displayName = 'DictVspModals';
|
||||||
|
|
||||||
|
export default DictVspModals;
|
||||||
@ -1,55 +1,19 @@
|
|||||||
import { Box, IconButton, Stack, Typography } from '@mui/material';
|
import { Box, IconButton, Stack, Typography } from '@mui/material';
|
||||||
import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
|
import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
|
||||||
import { useMemo, useRef, useState } from 'react';
|
import { memo, useMemo, useRef } from 'react';
|
||||||
import { useAuth } from '../../../app/context/AuthProvider';
|
import { useAuth } from '../../../app/context/AuthProvider';
|
||||||
import { PrimaryButton, PrimaryOutlinedButton } from '../../../components/common/Buttons/Buttons';
|
import { PrimaryButton, PrimaryOutlinedButton } from '../../../components/common/Buttons/Buttons';
|
||||||
import { EditPenSvg } from '../../../components/common/icons/icons';
|
import { EditPenSvg } from '../../../components/common/icons/icons';
|
||||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||||
import ModalEditAddVsp from './Modals/ModalEditAddVsp';
|
import { filterVspBySsp } from '../utils/filterVspBySsp';
|
||||||
import ModalExport from './Modals/ModalExport';
|
|
||||||
|
|
||||||
const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, selectedSsp }) => {
|
const ROW_ESTIMATED_HEIGHT = 54;
|
||||||
|
|
||||||
|
const TableDictVsp = ({ data, onEditClick, onAddClick, onExportClick, loading = false, selectedSsp }) => {
|
||||||
const { user } = useAuth();
|
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 tableContainerRef = useRef(null);
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => filterVspBySsp(data, selectedSsp), [data, selectedSsp]);
|
||||||
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(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@ -70,9 +34,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'system_code',
|
accessorKey: 'system_code',
|
||||||
header: 'Код РФ',
|
header: 'Код РФ',
|
||||||
size: 180,
|
size: 110,
|
||||||
minSize: 50,
|
minSize: 90,
|
||||||
maxSize: 100,
|
maxSize: 140,
|
||||||
Cell: ({ cell }) => (
|
Cell: ({ cell }) => (
|
||||||
<Typography variant='body2' component='div' sx={{ fontWeight: 'medium' }}>
|
<Typography variant='body2' component='div' sx={{ fontWeight: 'medium' }}>
|
||||||
{cell.getValue() || '-'}
|
{cell.getValue() || '-'}
|
||||||
@ -84,9 +48,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'registration_number',
|
accessorKey: 'registration_number',
|
||||||
header: 'Регистрационный номер ВСП',
|
header: 'Регистрационный номер ВСП',
|
||||||
size: 180,
|
size: 250,
|
||||||
minSize: 150,
|
minSize: 200,
|
||||||
maxSize: 200,
|
maxSize: 300,
|
||||||
Cell: ({ cell }) => (
|
Cell: ({ cell }) => (
|
||||||
<Typography variant='body2' component='div' sx={{ fontWeight: 'medium' }}>
|
<Typography variant='body2' component='div' sx={{ fontWeight: 'medium' }}>
|
||||||
{cell.getValue() || '-'}
|
{cell.getValue() || '-'}
|
||||||
@ -98,9 +62,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'regional_branch',
|
accessorKey: 'regional_branch',
|
||||||
header: 'Региональный филиал/ССП',
|
header: 'Региональный филиал/ССП',
|
||||||
size: 120,
|
size: 230,
|
||||||
minSize: 100,
|
minSize: 180,
|
||||||
maxSize: 150,
|
maxSize: 300,
|
||||||
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
@ -118,9 +82,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'address',
|
accessorKey: 'address',
|
||||||
header: 'Адрес объекта',
|
header: 'Адрес объекта',
|
||||||
size: 250,
|
size: 300,
|
||||||
minSize: 200,
|
minSize: 200,
|
||||||
maxSize: 350,
|
maxSize: 500,
|
||||||
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
@ -128,9 +92,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'approved_format',
|
accessorKey: 'approved_format',
|
||||||
header: 'Утверждённый формат',
|
header: 'Утверждённый формат',
|
||||||
size: 150,
|
size: 190,
|
||||||
minSize: 120,
|
minSize: 160,
|
||||||
maxSize: 180,
|
maxSize: 240,
|
||||||
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
@ -138,9 +102,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'open_date',
|
accessorKey: 'open_date',
|
||||||
header: 'Дата открытия ВСП',
|
header: 'Дата открытия ВСП',
|
||||||
size: 130,
|
size: 180,
|
||||||
minSize: 110,
|
minSize: 150,
|
||||||
maxSize: 150,
|
maxSize: 220,
|
||||||
Cell: ({ cell }) => {
|
Cell: ({ cell }) => {
|
||||||
const date = cell.getValue();
|
const date = cell.getValue();
|
||||||
return <Typography variant='body2'>{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}</Typography>;
|
return <Typography variant='body2'>{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}</Typography>;
|
||||||
@ -151,9 +115,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'close_date',
|
accessorKey: 'close_date',
|
||||||
header: 'Дата закрытия ВСП',
|
header: 'Дата закрытия ВСП',
|
||||||
size: 130,
|
size: 180,
|
||||||
minSize: 110,
|
minSize: 150,
|
||||||
maxSize: 150,
|
maxSize: 220,
|
||||||
Cell: ({ cell }) => {
|
Cell: ({ cell }) => {
|
||||||
const date = cell.getValue();
|
const date = cell.getValue();
|
||||||
return <Typography variant='body2'>{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}</Typography>;
|
return <Typography variant='body2'>{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}</Typography>;
|
||||||
@ -164,9 +128,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'placement_type',
|
accessorKey: 'placement_type',
|
||||||
header: 'Форма расположения ВСП',
|
header: 'Форма расположения ВСП',
|
||||||
size: 170,
|
size: 220,
|
||||||
minSize: 140,
|
minSize: 180,
|
||||||
maxSize: 200,
|
maxSize: 260,
|
||||||
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
@ -174,9 +138,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'staff_count',
|
accessorKey: 'staff_count',
|
||||||
header: 'Штатная численность ВСП',
|
header: 'Штатная численность ВСП',
|
||||||
size: 170,
|
size: 230,
|
||||||
minSize: 140,
|
minSize: 190,
|
||||||
maxSize: 200,
|
maxSize: 270,
|
||||||
Cell: ({ cell }) => {
|
Cell: ({ cell }) => {
|
||||||
const value = cell.getValue();
|
const value = cell.getValue();
|
||||||
return <Typography variant='body2'>{value !== undefined && value !== null ? value : '-'}</Typography>;
|
return <Typography variant='body2'>{value !== undefined && value !== null ? value : '-'}</Typography>;
|
||||||
@ -197,9 +161,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'rent_contract_num',
|
accessorKey: 'rent_contract_num',
|
||||||
header: 'Номер договора',
|
header: 'Номер договора',
|
||||||
size: 130,
|
size: 150,
|
||||||
minSize: 110,
|
minSize: 130,
|
||||||
maxSize: 160,
|
maxSize: 200,
|
||||||
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
Cell: ({ cell }) => <Typography variant='body2'>{cell.getValue() || '-'}</Typography>,
|
||||||
enableColumnActions: false,
|
enableColumnActions: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
@ -207,9 +171,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'rent_end_date',
|
accessorKey: 'rent_end_date',
|
||||||
header: 'Дата окончания договора',
|
header: 'Дата окончания договора',
|
||||||
size: 150,
|
size: 230,
|
||||||
minSize: 130,
|
minSize: 190,
|
||||||
maxSize: 170,
|
maxSize: 270,
|
||||||
Cell: ({ cell }) => {
|
Cell: ({ cell }) => {
|
||||||
const date = cell.getValue();
|
const date = cell.getValue();
|
||||||
return <Typography variant='body2'>{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}</Typography>;
|
return <Typography variant='body2'>{date ? new Date(date).toLocaleDateString('ru-RU') : '-'}</Typography>;
|
||||||
@ -220,9 +184,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
{
|
{
|
||||||
accessorKey: 'notes',
|
accessorKey: 'notes',
|
||||||
header: 'Примечания',
|
header: 'Примечания',
|
||||||
size: 200,
|
size: 300,
|
||||||
minSize: 150,
|
minSize: 200,
|
||||||
maxSize: 300,
|
maxSize: 500,
|
||||||
Cell: ({ cell }) => (
|
Cell: ({ cell }) => (
|
||||||
<Typography variant='body2' sx={{ whiteSpace: 'pre-wrap' }}>
|
<Typography variant='body2' sx={{ whiteSpace: 'pre-wrap' }}>
|
||||||
{cell.getValue() || '-'}
|
{cell.getValue() || '-'}
|
||||||
@ -245,6 +209,9 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
enableGlobalFilter: false,
|
enableGlobalFilter: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enablePagination: false,
|
enablePagination: false,
|
||||||
|
enableRowVirtualization: true,
|
||||||
|
// Высоту строк MRT измеряет сам (по умолчанию): «Примечания» многострочные (pre-wrap).
|
||||||
|
rowVirtualizerOptions: { overscan: 5, estimateSize: () => ROW_ESTIMATED_HEIGHT },
|
||||||
positionActionsColumn: 'last',
|
positionActionsColumn: 'last',
|
||||||
|
|
||||||
// Включаем изменение размера колонок для лучшего UX при горизонтальном скролле
|
// Включаем изменение размера колонок для лучшего UX при горизонтальном скролле
|
||||||
@ -279,8 +246,8 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
Справочник объектов
|
Справочник объектов
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack spacing={'.5rem'} direction={'row'}>
|
<Stack spacing={'.5rem'} direction={'row'}>
|
||||||
<PrimaryOutlinedButton onClick={handleExport}>Экспортировать</PrimaryOutlinedButton>
|
<PrimaryOutlinedButton onClick={onExportClick}>Экспортировать</PrimaryOutlinedButton>
|
||||||
{user.role_id == ROLES_NAME_ID.admin && <PrimaryButton onClick={handleAdd}>+ Добавить объект</PrimaryButton>}
|
{user.role_id == ROLES_NAME_ID.admin && <PrimaryButton onClick={onAddClick}>+ Добавить объект</PrimaryButton>}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
),
|
),
|
||||||
@ -325,7 +292,7 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
}}>
|
}}>
|
||||||
<IconButton
|
<IconButton
|
||||||
size='small'
|
size='small'
|
||||||
onClick={() => handleEdit(row.original)}
|
onClick={() => onEditClick(row.original)}
|
||||||
sx={{
|
sx={{
|
||||||
padding: '4px',
|
padding: '4px',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
@ -479,23 +446,10 @@ const TableDictVsp = ({ data, onEdit, onDelete, onAdd, loading = false, ssps, se
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ position: 'relative', width: '100%' }}>
|
||||||
<Box sx={{ position: 'relative', width: '100%' }}>
|
<MaterialReactTable table={table} />
|
||||||
<MaterialReactTable table={table} />
|
</Box>
|
||||||
</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;
|
export default memo(TableDictVsp);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user