From 633c36787dd59f4b1a3033d938508c0c6d10fdfe Mon Sep 17 00:00:00 2001 From: PotapovaA Date: Thu, 1 Oct 2026 15:43:26 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B2=D0=BE=D0=B4=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/api/projects.js | 6 + web/src/app/Routes.jsx | 2 + .../ProjectPages/NavigationProjectPage.jsx | 13 +- .../ProjectPages/ProjectSummariesPage.jsx | 261 ++++++++++++++++++ web/src/pages/SvodPage/SummaryTable.jsx | 163 +++++++++++ web/src/pages/SvodPage/SvodFilters.jsx | 4 + web/src/pages/SvodPage/SvodPage.jsx | 157 +---------- 7 files changed, 451 insertions(+), 155 deletions(-) create mode 100644 web/src/pages/ProjectPages/ProjectSummariesPage.jsx create mode 100644 web/src/pages/SvodPage/SummaryTable.jsx diff --git a/web/src/api/projects.js b/web/src/api/projects.js index 9d92132..c3f0f44 100644 --- a/web/src/api/projects.js +++ b/web/src/api/projects.js @@ -38,6 +38,12 @@ export const ProjectsApi = { getSummary: (id) => { return api.get(`/projects/${id}/summary`).then((r) => r.data); }, + getAggregateSummary: (kind, year, branchIds = [], projectType = '') => { + const params = { year }; + if (branchIds.length) params.branch_id = branchIds.join(','); + if (projectType) params.project_type = projectType; + return api.get(`/projects/${kind}-summary`, { params }).then((r) => r.data); + }, export: (id, year, sheetName) => { return api.get(`/export/project/${id}/report/${year}/${sheetName}`, { responseType: 'blob', diff --git a/web/src/app/Routes.jsx b/web/src/app/Routes.jsx index c4cbdfc..1cf7196 100644 --- a/web/src/app/Routes.jsx +++ b/web/src/app/Routes.jsx @@ -9,6 +9,7 @@ import DictsNavigatorPage from '../pages/DictsNavigatorPage/DictsNavigatorPage.j import LoginPage from '../pages/LoginPage'; import NewFormTablePage from '../pages/NewFormTablePage.jsx'; import NavigationProjectPage from '../pages/ProjectPages/NavigationProjectPage.jsx'; +import ProjectSummariesPage from '../pages/ProjectPages/ProjectSummariesPage.jsx'; import Svod2Page from '../pages/Svod2Page/Svod2Page.jsx'; import { SvodProvider } from '../pages/SvodPage/SvodContext.jsx'; import SvodPage from '../pages/SvodPage/SvodPage.jsx'; @@ -38,6 +39,7 @@ export const AppRoutes = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/web/src/pages/ProjectPages/NavigationProjectPage.jsx b/web/src/pages/ProjectPages/NavigationProjectPage.jsx index b303ec7..29c29f2 100644 --- a/web/src/pages/ProjectPages/NavigationProjectPage.jsx +++ b/web/src/pages/ProjectPages/NavigationProjectPage.jsx @@ -462,9 +462,16 @@ const NavigationProjectPage = () => { selectedStatus={selectedStatus} onStatusChange={handleStatusChange} /> - setCreateModalOpen(true)} startIcon={} sx={{ height: '2.5rem' }}> - Создать проект - + + navigate(branchIdsParam ? `/projects/summaries?branch_ids=${branchIdsParam}` : '/projects/summaries')} + sx={{ height: '2.5rem', whiteSpace: 'nowrap' }}> + Перейти к сводам + + setCreateModalOpen(true)} startIcon={} sx={{ height: '2.5rem', whiteSpace: 'nowrap' }}> + Создать проект + + { + if (value === null || value === undefined || value === '') return '—'; + const numericValue = typeof value === 'number' ? value : Number(value); + if (numericValue === 0) return '-'; + return Number.isNaN(numericValue) ? value : numberFormatter.format(numericValue); +}; +const valueColumn = (id, header, getValue) => ({ + id, + header, + accessorFn: getValue, + Cell: ({ cell }) => formatValue(cell.getValue()), + size: 112, + muiTableBodyCellProps: { align: 'right' }, + muiTableHeadCellProps: { align: 'right' }, +}); +const headerColumns = [ + { id: 'section_code', header: '№ п/п', accessorFn: (row) => row.data?.header?.section_code, size: 92 }, + { + id: 'name', + header: 'Перечень расходов и затрат', + accessorFn: (row) => row.data?.header?.name, + size: 330, + Cell: ({ cell, row }) => ( + + {cell.getValue() || 'Без названия'} + + ), + }, +]; +const metricColumns = (metric, label, periods) => ({ + id: metric, + header: label, + columns: [ + { key: 'limit', label: 'Лимит' }, + { key: 'current', label: 'Текущие расходы' }, + { key: 'total', label: 'Итого' }, + ].map((group) => ({ + id: `${metric}.${group.key}`, + header: group.label, + columns: periods.map((period) => + valueColumn( + `${metric}.${group.key}.${period.key}`, + period.label, + (row) => row.data?.[metric]?.[group.key]?.[period.key], + ), + ), + })), +}); +const createColumns = (sheet) => { + if (sheet === 'plan') { + return [ + ...headerColumns, + metricColumns('base', 'Базовый план', QUARTERS), + metricColumns('correction', 'Корректировка плана', QUARTERS.filter(({ key }) => key !== 'q1')), + ]; + } + if (sheet === 'fact') { + return [...headerColumns, metricColumns('fact', 'Факт', QUARTERS)]; + } + return [ + ...headerColumns, + ...QUARTERS.filter(({ key }) => key !== 'year').map((quarter) => ({ + id: `detail.${quarter.key}`, + header: quarter.label, + columns: DETAIL_FIELDS.map((field) => + valueColumn( + `detail.${quarter.key}.${field.key}`, + field.label, + (row) => row.data?.detail?.[quarter.key]?.[field.key], + ), + ), + })), + valueColumn('detail.year', 'Факт за год', (row) => row.data?.detail?.year), + ]; +}; +const parseBranchIds = (value) => + value + .split(',') + .map(Number) + .filter((id) => Number.isInteger(id) && id > 0); +const COLUMNS_BY_SHEET = Object.fromEntries(SHEETS.map(({ value }) => [value, createColumns(value)])); +const EMPTY_SUMMARY = { rows: [], tree: [] }; +const createCacheKey = (year, branchIds, projectType) => JSON.stringify([year, branchIds.map(String).sort(), projectType]); +const getSheetCache = (cache, sheet) => { + if (!cache.has(sheet)) cache.set(sheet, new Map()); + return cache.get(sheet); +}; + +export default function ProjectSummariesPage() { + const [searchParams] = useSearchParams(); + const initialBranchIds = useMemo(() => parseBranchIds(searchParams.get('branch_ids') || ''), [searchParams]); + const { organizations } = useSummaryOrganizations(); + const [sheet, setSheet] = useState('plan'); + const [filters, setFilters] = useState({ + year: new Date().getFullYear(), + selectedOrganizations: [], + search: '', + }); + const [projectType, setProjectType] = useState(''); + const [summary, setSummary] = useState(EMPTY_SUMMARY); + const { rows, tree } = summary; + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const requestRef = useRef(0); + const summaryCacheRef = useRef(new Map()); + const initialBranchesAppliedRef = useRef(false); + const { year, selectedOrganizations, search } = filters; + const branchIds = useMemo( + () => selectedOrganizations.map(({ id }) => id), + [selectedOrganizations], + ); + const updateFilters = useCallback((changes) => setFilters((current) => ({ ...current, ...changes })), []); + + useEffect( + () => () => { + summaryCacheRef.current.clear(); + }, + [], + ); + + useEffect(() => { + if (initialBranchesAppliedRef.current || !organizations.length) return; + initialBranchesAppliedRef.current = true; + if (initialBranchIds.length) { + setFilters((current) => ({ + ...current, + selectedOrganizations: organizations.filter(({ id }) => initialBranchIds.includes(id)), + })); + } + }, [initialBranchIds, organizations]); + + const getSummary = useCallback((kind, selectedYear, selectedBranchIds, selectedProjectType) => { + const sheetCache = getSheetCache(summaryCacheRef.current, kind); + const cacheKey = createCacheKey(selectedYear, selectedBranchIds, selectedProjectType); + const cachedRequest = sheetCache.get(cacheKey); + if (cachedRequest) return cachedRequest; + + const request = ProjectsApi.getAggregateSummary(kind, selectedYear, selectedBranchIds, selectedProjectType) + .then((response) => { + const rows = [...(response.result || [])].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)); + const preparedSummary = { rows, tree: buildSummaryTree(rows) }; + if (sheetCache.get(cacheKey) === request) sheetCache.set(cacheKey, Promise.resolve(preparedSummary)); + return preparedSummary; + }) + .catch((requestError) => { + if (sheetCache.get(cacheKey) === request) sheetCache.delete(cacheKey); + throw requestError; + }); + + sheetCache.set(cacheKey, request); + return request; + }, []); + + const loadSummary = useCallback(async () => { + const requestId = ++requestRef.current; + setLoading(true); + setError(''); + try { + const requestedBranchIds = initialBranchesAppliedRef.current ? branchIds : initialBranchIds; + const response = await getSummary(sheet, year, requestedBranchIds, projectType); + if (requestId !== requestRef.current) return; + setSummary(response); + } catch (requestError) { + if (requestId !== requestRef.current) return; + setSummary(EMPTY_SUMMARY); + setError(requestError.response?.data?.detail || 'Не удалось загрузить свод проектов. Попробуйте ещё раз.'); + } finally { + if (requestId === requestRef.current) setLoading(false); + } + }, [sheet, year, branchIds, initialBranchIds, projectType, getSummary]); + + const refreshSummary = useCallback(() => { + const requestedBranchIds = initialBranchesAppliedRef.current ? branchIds : initialBranchIds; + summaryCacheRef.current.get(sheet)?.delete(createCacheKey(year, requestedBranchIds, projectType)); + loadSummary(); + }, [sheet, year, branchIds, initialBranchIds, projectType, loadSummary]); + + useEffect(() => { + loadSummary(); + return () => { requestRef.current += 1; }; + }, [loadSummary]); + + const filteredRows = useMemo(() => { + const query = search.trim().toLowerCase(); + return query ? filterSummaryTree(tree, query) : tree; + }, [tree, search]); + const columns = COLUMNS_BY_SHEET[sheet]; + const filteredCount = useMemo(() => countSummaryTree(filteredRows), [filteredRows]); + + return ( + + + + setSheet(value)} sx={{ px: '0.5rem', minHeight: '3rem', borderBottom: '0.0625rem solid #edf0f2' }}> + {SHEETS.map(({ value, label }) => )} + + setProjectType('')} + searchPlaceholder='Код или статья расходов' + additionalFilters={ + setProjectType(event.target.value)} sx={{ minWidth: '13rem' }}> + Все типы + {PROJECT_TYPES.map((type) => {type})} + + } + > + {!loading && filteredCount !== rows.length && ( + + {`Найдено ${filteredCount.toLocaleString('ru-RU')} из ${rows.length.toLocaleString('ru-RU')} строк`} + + )} + } disabled={loading} onClick={refreshSummary} sx={{ ml: 'auto', height: '2.5rem', flexShrink: 0 }}> + Обновить + + + + {error ? ( + Повторить}>{error} + ) : ( + + )} + + ); +} diff --git a/web/src/pages/SvodPage/SummaryTable.jsx b/web/src/pages/SvodPage/SummaryTable.jsx new file mode 100644 index 0000000..a88c357 --- /dev/null +++ b/web/src/pages/SvodPage/SummaryTable.jsx @@ -0,0 +1,163 @@ +import { MaterialReactTable, useMaterialReactTable } from 'material-react-table'; +import { memo } from 'react'; +import { blueColumn } from '../../components/RealtimeTable/constants/columnColors'; +import { sectionCodeColor } from '../../components/RealtimeTable/constants/columnConfig'; + +const tableSize = (rem) => rem * 16; +const ROW_VIRTUALIZER_OPTIONS = { + overscan: 8, + estimateSize: () => 37, + measureElement: (element) => element?.offsetHeight || 37, +}; + +const getSummaryRowColor = (row) => { + const rowType = row.original?.row_type || row.row_type; + const sectionCodeValue = row.original?.data?.section_code ?? row.original?.data?.header?.section_code; + if (sectionCodeValue === null || sectionCodeValue === undefined || sectionCodeValue === '') return undefined; + const sectionCode = String(sectionCodeValue)[0]; + return (sectionCodeColor[sectionCode] || blueColumn)[rowType]; +}; + +const getTextColor = (backgroundColor) => { + if (!backgroundColor?.startsWith('#') || backgroundColor.length !== 7) return '#000'; + const red = Number.parseInt(backgroundColor.slice(1, 3), 16); + const green = Number.parseInt(backgroundColor.slice(3, 5), 16); + const blue = Number.parseInt(backgroundColor.slice(5, 7), 16); + return 0.299 * red + 0.587 * green + 0.114 * blue < 128 ? '#fff' : '#000'; +}; + +export const buildSummaryTree = (sourceRows) => { + const roots = []; + const parents = []; + + for (const [index, sourceRow] of sourceRows.entries()) { + const row = { + ...sourceRow, + _rowId: `${sourceRow.sort_order ?? index}-${sourceRow.data?.section_code ?? sourceRow.data?.header?.section_code ?? index}`, + subRows: [], + }; + + while (parents.length > row.depth) parents.pop(); + if (row.depth > 0 && parents[row.depth - 1]) parents[row.depth - 1].subRows.push(row); + else roots.push(row); + + parents[row.depth] = row; + parents.length = row.depth + 1; + } + + return roots; +}; + +export const filterSummaryTree = (tree, query) => + tree.flatMap((row) => { + const matches = [row.data?.section_code, row.data?.name, row.data?.header?.section_code, row.data?.header?.name].some((value) => + String(value || '') + .toLowerCase() + .includes(query), + ); + + if (matches) return [row]; + + const subRows = filterSummaryTree(row.subRows, query); + return subRows.length ? [{ ...row, subRows }] : []; + }); + +export const countSummaryTree = (tree) => tree.reduce((count, row) => count + 1 + countSummaryTree(row.subRows), 0); + +export const SummaryTable = memo(function SummaryTable({ columns, data, loading }) { + const table = useMaterialReactTable({ + columns, + data, + enableExpanding: true, + enableExpandAll: true, + getSubRows: (row) => row.subRows, + getRowId: (row) => row._rowId, + enableColumnActions: false, + enableColumnFilters: false, + enableDensityToggle: false, + enableFullScreenToggle: false, + enableHiding: false, + enablePagination: false, + enableRowVirtualization: true, + rowVirtualizerOptions: ROW_VIRTUALIZER_OPTIONS, + enableTopToolbar: false, + enableBottomToolbar: false, + enableSorting: false, + displayColumnDefOptions: { + 'mrt-row-expand': { + size: tableSize(3.75), + minSize: tableSize(3.75), + maxSize: tableSize(3.75), + grow: false, + }, + }, + state: { isLoading: loading }, + initialState: { columnPinning: { left: ['mrt-row-expand', 'section_code', 'name'] }, expanded: true }, + muiTablePaperProps: { + elevation: 0, + sx: { + border: '0.0625rem solid #e5e7eb', + borderRadius: '0.75rem', + overflow: 'hidden', + flex: 1, + minHeight: 0, + width: '100%', + display: 'flex', + flexDirection: 'column', + }, + }, + muiTableContainerProps: { + sx: { + flex: 1, + overflow: 'auto', + minHeight: 0, + height: '100%', + '& thead': { + position: 'sticky', + top: 0, + zIndex: 10, + }, + }, + }, + muiTableHeadCellProps: { + sx: { + position: 'sticky', + top: 0, + boxShadow: 'none', + backgroundColor: '#f8f9fa', + borderRight: '0.0625rem solid #e5e7eb', + color: '#4a5565', + fontSize: '0.75rem', + fontWeight: 700, + lineHeight: 1.2, + px: '0.75rem', + py: '0.5rem', + whiteSpace: 'nowrap', + }, + }, + muiTableHeadRowProps: { + sx: { + boxShadow: 'none', + }, + }, + muiTableBodyCellProps: ({ row, column }) => { + const backgroundColor = getSummaryRowColor(row); + return { + sx: { + backgroundColor, + borderRight: '0.0625rem solid #edf0f2', + color: getTextColor(backgroundColor), + fontSize: '0.8125rem', + px: column.id === 'mrt-row-expand' ? '0.25rem' : '0.75rem', + py: column.id === 'mrt-row-expand' ? 0 : '0.5rem', + whiteSpace: 'nowrap', + textAlign: ['section_code', 'name'].includes(column.id) ? 'left' : 'right', + }, + }; + }, + muiExpandButtonProps: { sx: { width: '1.25rem', height: '1.25rem', p: 0, '& .MuiSvgIcon-root': { fontSize: '1.125rem' } } }, + localization: { noRecordsToDisplay: 'Нет данных для выбранных параметров' }, + }); + + return ; +}); diff --git a/web/src/pages/SvodPage/SvodFilters.jsx b/web/src/pages/SvodPage/SvodFilters.jsx index 3a149c2..59c3811 100644 --- a/web/src/pages/SvodPage/SvodFilters.jsx +++ b/web/src/pages/SvodPage/SvodFilters.jsx @@ -53,6 +53,8 @@ export function SvodFilters({ searchPlaceholder = 'Код или статья расходов', emptyOrganizationsPlaceholder = 'Все доступные', allowSelectAll = false, + onReset, + additionalFilters, children, }) { const { organizations, organizationsLoading } = useSummaryOrganizations(); @@ -147,10 +149,12 @@ export function SvodFilters({ }, }} /> + {additionalFilters} { setSearchInput(''); onChange({ year: new Date().getFullYear(), selectedOrganizations: [], search: '' }); + onReset?.(); }} /> {children} diff --git a/web/src/pages/SvodPage/SvodPage.jsx b/web/src/pages/SvodPage/SvodPage.jsx index 48c036c..e98cbd0 100644 --- a/web/src/pages/SvodPage/SvodPage.jsx +++ b/web/src/pages/SvodPage/SvodPage.jsx @@ -1,14 +1,12 @@ import RefreshRoundedIcon from '@mui/icons-material/RefreshRounded'; import { Alert, Box, Button, Paper, Tab, Tabs, Typography } from '@mui/material'; -import { MaterialReactTable, useMaterialReactTable } from 'material-react-table'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { HeaderSwitch } from '../../components/HeaderMenu/HeaderSwitch'; -import { blueColumn } from '../../components/RealtimeTable/constants/columnColors'; -import { sectionCodeColor } from '../../components/RealtimeTable/constants/columnConfig'; +import { PrimaryButton, PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons'; +import { SummaryTable, buildSummaryTree, countSummaryTree, filterSummaryTree } from './SummaryTable'; import { useSvod } from './SvodContext'; import { SvodFilters } from './SvodFilters'; -import { PrimaryButton, PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons'; const SHEETS = [ { value: 'MAIN', label: 'Общий свод' }, @@ -162,60 +160,6 @@ const createColumns = (sheet) => { ]; }; -const getSummaryRowColor = (row) => { - const rowType = row.original?.row_type || row.row_type; - const sectionCodeValue = row.original?.data?.section_code ?? row.original?.data?.header?.section_code; - if (sectionCodeValue === null || sectionCodeValue === undefined || sectionCodeValue === '') return undefined; - const sectionCode = String(sectionCodeValue)[0]; - return (sectionCodeColor[sectionCode] || blueColumn)[rowType]; -}; - -const getTextColor = (backgroundColor) => { - if (!backgroundColor?.startsWith('#') || backgroundColor.length !== 7) return '#000'; - const red = Number.parseInt(backgroundColor.slice(1, 3), 16); - const green = Number.parseInt(backgroundColor.slice(3, 5), 16); - const blue = Number.parseInt(backgroundColor.slice(5, 7), 16); - return 0.299 * red + 0.587 * green + 0.114 * blue < 128 ? '#fff' : '#000'; -}; - -const buildSummaryTree = (sourceRows) => { - const roots = []; - const parents = []; - - for (const [index, sourceRow] of sourceRows.entries()) { - const row = { - ...sourceRow, - _rowId: `${sourceRow.sort_order ?? index}-${sourceRow.data?.section_code ?? index}`, - subRows: [], - }; - - while (parents.length > row.depth) parents.pop(); - if (row.depth > 0 && parents[row.depth - 1]) parents[row.depth - 1].subRows.push(row); - else roots.push(row); - - parents[row.depth] = row; - parents.length = row.depth + 1; - } - - return roots; -}; - -const filterSummaryTree = (tree, query) => - tree.flatMap((row) => { - const matches = [row.data?.section_code, row.data?.name].some((value) => - String(value || '') - .toLowerCase() - .includes(query), - ); - - if (matches) return [row]; - - const subRows = filterSummaryTree(row.subRows, query); - return subRows.length ? [{ ...row, subRows }] : []; - }); - -const countSummaryTree = (tree) => tree.reduce((count, row) => count + 1 + countSummaryTree(row.subRows), 0); - export default function SvodPage() { const navigate = useNavigate(); const { getSheetFilters, updateSheetFilters, getSummary, invalidateSummary } = useSvod(); @@ -286,104 +230,13 @@ export default function SvodPage() { navigate(query ? `/projects?${query}` : '/projects'); }, [navigate, selectedOrganizations]); + const tree = useMemo(() => buildSummaryTree(rows), [rows]); const filteredRows = useMemo(() => { const normalizedSearch = search.trim().toLowerCase(); - const tree = buildSummaryTree(rows); return normalizedSearch ? filterSummaryTree(tree, normalizedSearch) : tree; - }, [rows, search]); + }, [tree, search]); const columns = useMemo(() => createColumns(sheet), [sheet]); const filteredRowsCount = useMemo(() => countSummaryTree(filteredRows), [filteredRows]); - const table = useMaterialReactTable({ - columns, - data: filteredRows, - enableExpanding: true, - enableExpandAll: true, - getSubRows: (row) => row.subRows, - getRowId: (row) => row._rowId, - enableColumnActions: false, - enableColumnFilters: false, - enableDensityToggle: false, - enableFullScreenToggle: false, - enableHiding: false, - enablePagination: false, - enableTopToolbar: false, - enableBottomToolbar: false, - enableSorting: false, - displayColumnDefOptions: { - 'mrt-row-expand': { - size: tableSize(3.75), - minSize: tableSize(3.75), - maxSize: tableSize(3.75), - grow: false, - }, - }, - state: { isLoading: loading }, - initialState: { columnPinning: { left: ['mrt-row-expand', 'section_code', 'name'] }, expanded: true }, - muiTablePaperProps: { - elevation: 0, - sx: { - border: '0.0625rem solid #e5e7eb', - borderRadius: '0.75rem', - overflow: 'hidden', - flex: 1, - minHeight: 0, - width: '100%', - display: 'flex', - flexDirection: 'column', - }, - }, - muiTableContainerProps: { - sx: { - flex: 1, - overflow: 'auto', - minHeight: 0, - height: '100%', - '& thead': { - position: 'sticky', - top: 0, - zIndex: 10, - }, - }, - }, - muiTableHeadCellProps: { - sx: { - position: 'sticky', - top: 0, - boxShadow: 'none', - backgroundColor: '#f8f9fa', - borderRight: '0.0625rem solid #e5e7eb', - color: '#4a5565', - fontSize: '0.75rem', - fontWeight: 700, - lineHeight: 1.2, - px: '0.75rem', - py: '0.5rem', - whiteSpace: 'nowrap', - }, - }, - muiTableHeadRowProps: { - sx: { - boxShadow: 'none', - }, - }, - muiTableBodyCellProps: ({ row, column }) => { - const backgroundColor = getSummaryRowColor(row); - return { - sx: { - backgroundColor, - borderRight: '0.0625rem solid #edf0f2', - color: getTextColor(backgroundColor), - fontSize: '0.8125rem', - px: column.id === 'mrt-row-expand' ? '0.25rem' : '0.75rem', - py: column.id === 'mrt-row-expand' ? 0 : '0.5rem', - whiteSpace: 'nowrap', - textAlign: ['section_code', 'name'].includes(column.id) ? 'left' : 'right', - }, - }; - }, - muiExpandButtonProps: { sx: { width: '1.25rem', height: '1.25rem', p: 0, '& .MuiSvgIcon-root': { fontSize: '1.125rem' } } }, - localization: { noRecordsToDisplay: 'Нет данных для выбранных параметров' }, - }); return ( ) : ( - + )} );