import { MaterialReactTable, useMaterialReactTable } from 'material-react-table'; import { memo, useMemo } from 'react'; import DenominationSelect from '../../components/common/DenominationSelect'; import useDenomination from '../../components/common/useDenomination'; import { blueColumn, greenColumn, orangeColumn } from '../../components/RealtimeTable/constants/columnColors'; import { sectionCodeColor } from '../../components/RealtimeTable/constants/columnConfig'; import { buildRowHierarchy } from '../../components/RealtimeTable/utils/rowUtils'; const tableSize = (rem) => rem * 16; const ROW_VIRTUALIZER_OPTIONS = { overscan: 8, estimateSize: () => 37, measureElement: (element) => element?.offsetHeight || 37, }; const SOURCE_COLORS = { ahr: greenColumn, oper: blueColumn, cap: orangeColumn }; const getSummaryRowColor = (row, colorBySource) => { const rowType = row.original?.row_type || row.row_type; if (colorBySource) { const source = row.original?.data?.header?.source?.toLowerCase(); if (SOURCE_COLORS[source]) return SOURCE_COLORS[source][rowType]; } 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) => buildRowHierarchy(sourceRows, (row, index) => ({ ...row, _rowId: `${row.sort_order ?? index}-${row.data?.section_code ?? row.data?.header?.section_code ?? index}`, })); 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); const denominationKey = (tableKey) => `summary_${tableKey}_denomination`; const scaleColumns = (columns, divisor) => columns.map((column) => { const children = column.columns ? scaleColumns(column.columns, divisor) : undefined; const originalCell = column.Cell; const isAmount = !['section_code', 'name', 'mrt-row-expand'].includes(column.id) && /plan|approved|fact|corrected|detail|total|quarter|residual|actual|adj|base|correction/i.test(column.id || ''); return { ...column, ...(children ? { columns: children } : {}), ...(isAmount ? { Cell: ({ cell }) => { const value = cell.getValue(); if (value === null || value === undefined || value === '') return originalCell ? originalCell({ cell }) : '—'; const numeric = Number(value); if (!Number.isFinite(numeric)) return originalCell ? originalCell({ cell }) : value; if (numeric === 0) return '-'; return new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(numeric / divisor); } } : {}), }; }); export const SummaryTable = memo(function SummaryTable({ columns, data, loading, colorBySource = false, tableKey = 'default' }) { const storageKey = denominationKey(tableKey); const { divisor } = useDenomination(storageKey); const scaledColumns = useMemo(() => scaleColumns(columns, divisor), [columns, divisor]); const table = useMaterialReactTable({ columns: scaledColumns, 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, colorBySource); 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 ; }); export function SummaryDenominationControl({ tableKey = 'default' }) { const storageKey = denominationKey(tableKey); const { denomination, setDenomination } = useDenomination(storageKey); return ; }