added consolidation chess table
This commit is contained in:
parent
681697f728
commit
a63701c8b2
@ -10,7 +10,6 @@ import {
|
|||||||
AuditLogging,
|
AuditLogging,
|
||||||
CatalogueGroup,
|
CatalogueGroup,
|
||||||
CatalogueJur,
|
CatalogueJur,
|
||||||
ConsolidatedOsvPage,
|
|
||||||
ConsolidationPage,
|
ConsolidationPage,
|
||||||
DocStorage,
|
DocStorage,
|
||||||
DocumentPage,
|
DocumentPage,
|
||||||
@ -20,6 +19,7 @@ import {
|
|||||||
} from './layouts/';
|
} from './layouts/';
|
||||||
import { store } from './store/store';
|
import { store } from './store/store';
|
||||||
import { Sidebar } from './widgets/Sidebar/Sidebar';
|
import { Sidebar } from './widgets/Sidebar/Sidebar';
|
||||||
|
import { ConsolidatioReportPage } from './layouts/ConsolidatioReportPage';
|
||||||
|
|
||||||
// Каркас: экраны по docs/context/10-user-journey.md добавляются срезами.
|
// Каркас: экраны по docs/context/10-user-journey.md добавляются срезами.
|
||||||
function App() {
|
function App() {
|
||||||
@ -37,7 +37,7 @@ function App() {
|
|||||||
<Route path='/groups'>
|
<Route path='/groups'>
|
||||||
<Route index element={<GroupsLayout />} />
|
<Route index element={<GroupsLayout />} />
|
||||||
<Route path=':group_id' element={<ConsolidationPage />} />
|
<Route path=':group_id' element={<ConsolidationPage />} />
|
||||||
<Route path=':group_id/versions/:version_id/osv' element={<ConsolidatedOsvPage />} />
|
<Route path=':group_id/versions/:version_id/:tab' element={<ConsolidatioReportPage/>} />
|
||||||
<Route path='monozaemchiki' element={<Mono />} />
|
<Route path='monozaemchiki' element={<Mono />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path='/catalogue/groups' element={<CatalogueGroup />} />
|
<Route path='/catalogue/groups' element={<CatalogueGroup />} />
|
||||||
|
|||||||
69
src/layouts/ConsolidatioReportPage.tsx
Normal file
69
src/layouts/ConsolidatioReportPage.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Space, message } from 'antd';
|
||||||
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { ApiError } from '../api/client';
|
||||||
|
import PageTitle from '../components/PageTitle';
|
||||||
|
import { TableTag } from '../components/TableTag/TableTag';
|
||||||
|
import { TAG_PRESETS } from '../components/TableTag/presets';
|
||||||
|
import { getVersionCard } from '../services/api/consolidations';
|
||||||
|
import type { ConsRussianStatus } from '../types/consolidations/common_types';
|
||||||
|
import type { VersionCard } from '../types/consolidations/version_members';
|
||||||
|
import { quarterShortLabel } from '../utils/labels';
|
||||||
|
import { statusColorMapping, statusMapping } from '../widgets/ConsoleTable/consts';
|
||||||
|
import { OsvTabs } from '../widgets/ConsolidatedOsv/OsvTabs';
|
||||||
|
import { OsvContent } from '../widgets/ConsolidatedOsvContent/ConsolidatedOsvContent';
|
||||||
|
import { ChessContent } from '../widgets/ConsolidatedChessContent/ConsolidatedChessContent';
|
||||||
|
|
||||||
|
export function ConsolidatioReportPage() {
|
||||||
|
const [card, setCard] = useState<VersionCard | null>(null);
|
||||||
|
const { version_id, tab } = useParams<{ tab?: string; version_id: string }>();
|
||||||
|
|
||||||
|
const TAB_COMPONENTS: Record<string, ReactNode> = {
|
||||||
|
osv: <OsvContent card={card} />,
|
||||||
|
|
||||||
|
chess: <ChessContent />,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ActiveTab = TAB_COMPONENTS[tab as string] ?? <></>;
|
||||||
|
|
||||||
|
const russianStatus = card ? (statusMapping[card.status] ?? card.status) : '';
|
||||||
|
const preset = statusColorMapping[russianStatus as ConsRussianStatus] ?? TAG_PRESETS.default;
|
||||||
|
const groupName = card?.company_group.name ?? '';
|
||||||
|
const period = card ? `${card.year} · ${quarterShortLabel(card.quarter)}` : '';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (version_id) {
|
||||||
|
let actual = true;
|
||||||
|
getVersionCard(version_id)
|
||||||
|
.then((c) => {
|
||||||
|
if (actual) setCard(c);
|
||||||
|
})
|
||||||
|
.catch((e: unknown) => {
|
||||||
|
if (actual) message.error(e instanceof ApiError ? e.message : 'Не удалось получить версию');
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
actual = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [version_id]);
|
||||||
|
return (
|
||||||
|
<div style={{ padding: 12, display: 'flex', flexDirection: 'column', height: '100vh', gap: 12 }}>
|
||||||
|
<PageTitle
|
||||||
|
backable
|
||||||
|
location={['Отчётность', groupName, period]}
|
||||||
|
header={card ? `${groupName} — ${card.year} ${quarterShortLabel(card.quarter)}` : 'Сводная ОСВ'}
|
||||||
|
description={russianStatus}
|
||||||
|
extra={
|
||||||
|
card && (
|
||||||
|
<Space size={8}>
|
||||||
|
<span style={{ color: '#667085', fontSize: 12 }}>Статус отчёта</span>
|
||||||
|
<TableTag text={russianStatus} {...preset} />
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<OsvTabs />
|
||||||
|
{ActiveTab}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -9,4 +9,4 @@ export { GroupsLayout } from './Groups';
|
|||||||
export { AuditLogging } from './AuditLogging';
|
export { AuditLogging } from './AuditLogging';
|
||||||
export { Admin } from './Admin';
|
export { Admin } from './Admin';
|
||||||
export { DocumentPage } from './DocumentPage';
|
export { DocumentPage } from './DocumentPage';
|
||||||
export { ConsolidatedOsvPage } from './ConsolidatedOsvPage';
|
export { ConsolidatioReportPage } from './ConsolidatioReportPage';
|
||||||
|
|||||||
4
src/services/consodilation/chess.ts
Normal file
4
src/services/consodilation/chess.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { api } from "../../api/client";
|
||||||
|
import { IntercompanyMatrixResponse } from "../../types/chess/chess";
|
||||||
|
|
||||||
|
export const fetchChessConsolidations = (id: string) => api.get<IntercompanyMatrixResponse>(`/consolidations/versions/${id}/intercompany-matrix/all`);
|
||||||
17
src/types/chess/chess.ts
Normal file
17
src/types/chess/chess.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface MatrixCompany {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MatrixCell {
|
||||||
|
company_a_id: string;
|
||||||
|
company_b_id: string;
|
||||||
|
amount_a: string;
|
||||||
|
amount_b: string;
|
||||||
|
variance_amount: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IntercompanyMatrixResponse {
|
||||||
|
companies: MatrixCompany[];
|
||||||
|
cells: MatrixCell[];
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
.empty-cell {
|
||||||
|
background-color: #f2f4f7;
|
||||||
|
}
|
||||||
|
.ant-table-tbody > tr:hover > td.empty-cell {
|
||||||
|
background-color: #f2f4f7 !important;
|
||||||
|
}
|
||||||
@ -0,0 +1,119 @@
|
|||||||
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { Table, Typography } from 'antd';
|
||||||
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { ApiError } from '../../api/client';
|
||||||
|
import { fetchChessConsolidations } from '../../services/consodilation/chess';
|
||||||
|
import type { IntercompanyMatrixResponse, MatrixCell, MatrixCompany } from '../../types/chess/chess';
|
||||||
|
import { useNotificationApi } from '../../utils/useNotification';
|
||||||
|
|
||||||
|
import './ConsolidatedChessContent.css';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
export const ChessContent = () => {
|
||||||
|
const { version_id } = useParams<{ version_id: string }>();
|
||||||
|
const notification = useNotificationApi();
|
||||||
|
|
||||||
|
const [data, setData] = useState<IntercompanyMatrixResponse | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const loadData = async (vId: string) => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await fetchChessConsolidations(vId);
|
||||||
|
setData(res);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ApiError) {
|
||||||
|
notification.error({ description: e.message });
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!version_id) return;
|
||||||
|
loadData(version_id);
|
||||||
|
}, [version_id]);
|
||||||
|
|
||||||
|
const matrixData = useMemo(() => {
|
||||||
|
if (!data?.cells) return {};
|
||||||
|
const result: Record<string, Record<string, MatrixCell>> = {};
|
||||||
|
|
||||||
|
data.cells.forEach((cell) => {
|
||||||
|
if (!result[cell.company_a_id]) result[cell.company_a_id] = {};
|
||||||
|
result[cell.company_a_id][cell.company_b_id] = cell;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [data?.cells]);
|
||||||
|
|
||||||
|
const columns: ColumnsType<MatrixCompany> = useMemo(() => {
|
||||||
|
if (!data?.companies) return [];
|
||||||
|
|
||||||
|
const firstColumn: ColumnsType<MatrixCompany>[number] = {
|
||||||
|
title: 'ДБ / КТ',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
fixed: 'left',
|
||||||
|
width: 200,
|
||||||
|
render: (text) => <Text strong>{text}</Text>,
|
||||||
|
onCell: () => ({ style: { background: '#fff', zIndex: 2 } }),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Внутри companyColumns
|
||||||
|
const companyColumns = data.companies.map((company) => ({
|
||||||
|
title: company.name,
|
||||||
|
dataIndex: company.id,
|
||||||
|
key: company.id,
|
||||||
|
width: 150,
|
||||||
|
align: 'right' as const,
|
||||||
|
|
||||||
|
onCell: (row: MatrixCompany) => {
|
||||||
|
const cell = matrixData[row.id]?.[company.id];
|
||||||
|
if (!cell) {
|
||||||
|
// Возвращаем класс через onCell, это работает надежнее
|
||||||
|
return { className: 'empty-cell' };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
render: (_: unknown, row: MatrixCompany) => {
|
||||||
|
const cell = matrixData[row.id]?.[company.id];
|
||||||
|
|
||||||
|
if (!cell) {
|
||||||
|
return <span style={{ color: '#bfbfbf' }}>—</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 2, fontSize: 13 }}>
|
||||||
|
<span>{cell.amount_a}</span>
|
||||||
|
<span>{cell.amount_b}</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: cell.variance_amount.startsWith('-') || cell.variance_amount.startsWith('(') ? 'red' : 'inherit',
|
||||||
|
}}>
|
||||||
|
{cell.variance_amount}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [firstColumn, ...companyColumns];
|
||||||
|
}, [data?.companies, matrixData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table<MatrixCompany>
|
||||||
|
dataSource={data?.companies || []}
|
||||||
|
columns={columns}
|
||||||
|
rowKey='id'
|
||||||
|
pagination={false}
|
||||||
|
loading={loading}
|
||||||
|
scroll={{ x: 'max-content', y: 'calc(100vh - 280px)' }}
|
||||||
|
size='small'
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,26 +1,79 @@
|
|||||||
import { Segmented, Tooltip } from 'antd';
|
import { Segmented, Tooltip } from 'antd';
|
||||||
|
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
const SOON = 'Будет в следующих срезах';
|
const TAB_CONFIG = [
|
||||||
const TABS = ['Шахматка', 'Таблица корректировки', 'Гудвилл', 'КФО', 'КФО Общий', 'Подтверждение КФО клиента'];
|
{ key: 'osv', label: 'ОСВ' },
|
||||||
|
{ key: 'chess', label: 'Шахматка' },
|
||||||
|
{
|
||||||
|
key: 'correction',
|
||||||
|
label: 'Таблица корректировки',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: 'Будет в следующих срезах',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'goodwill',
|
||||||
|
label: 'Гудвилл',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: 'Будет в следующих срезах',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'kfo',
|
||||||
|
label: 'КФО',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: 'Будет в следующих срезах',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'kfo_common',
|
||||||
|
label: 'КФО Общий',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: 'Будет в следующих срезах',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'kfo_confirm',
|
||||||
|
label: 'Подтверждение КФО клиента',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: 'Будет в следующих срезах',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/** Ряд вкладок шагов консолидации (макет [30]): активна только «ОСВ». */
|
|
||||||
export function OsvTabs() {
|
export function OsvTabs() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const { tab } = useParams<{ tab?: string }>();
|
||||||
|
|
||||||
|
const activeKey = tab || 'osv';
|
||||||
|
|
||||||
|
const handleTabChange = (newKey: string | number) => {
|
||||||
|
const newPath = location.pathname.replace(new RegExp(`/${activeKey}/?$`), `/${newKey}`);
|
||||||
|
navigate(newPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = TAB_CONFIG.map((tabItem) => {
|
||||||
|
const isActive = tabItem.key === activeKey;
|
||||||
|
|
||||||
|
const label = (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: isActive ? '#2a8c3a' : undefined,
|
||||||
|
fontWeight: isActive ? 600 : 400,
|
||||||
|
}}>
|
||||||
|
{tabItem.label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: tabItem.key,
|
||||||
|
disabled: tabItem.disabled,
|
||||||
|
label: tabItem.tooltip ? <Tooltip title={tabItem.tooltip}>{label}</Tooltip> : label,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Segmented
|
<Segmented
|
||||||
value='ОСВ'
|
value={activeKey}
|
||||||
|
onChange={handleTabChange}
|
||||||
style={{ alignSelf: 'flex-start', maxWidth: '100%', overflowX: 'auto' }}
|
style={{ alignSelf: 'flex-start', maxWidth: '100%', overflowX: 'auto' }}
|
||||||
options={[
|
options={options}
|
||||||
{ value: 'ОСВ', label: <span style={{ color: '#2a8c3a', fontWeight: 600 }}>ОСВ</span> },
|
|
||||||
...TABS.map((t) => ({
|
|
||||||
value: t,
|
|
||||||
disabled: true,
|
|
||||||
label: (
|
|
||||||
<Tooltip title={SOON}>
|
|
||||||
<span>{t}</span>
|
|
||||||
</Tooltip>
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,51 +1,33 @@
|
|||||||
import { DownloadOutlined, FilterOutlined } from '@ant-design/icons';
|
import { DownloadOutlined, FilterOutlined } from '@ant-design/icons';
|
||||||
import { Alert, Button, Space, message } from 'antd';
|
import { Alert, Button, message } from 'antd';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { ApiError } from '../api/client';
|
import { ApiError } from '../../api/client';
|
||||||
import PageTitle from '../components/PageTitle';
|
import { exportOsv } from '../../services/api/osv';
|
||||||
import { TableTag } from '../components/TableTag/TableTag';
|
import type { ConsolidatedRowOut } from '../../types/api';
|
||||||
import { TAG_PRESETS } from '../components/TableTag/presets';
|
import type { VersionCard } from '../../types/consolidations/version_members';
|
||||||
import { getVersionCard } from '../services/api/consolidations';
|
import { exportFilename, saveBlob } from '../../utils/download';
|
||||||
import { exportOsv } from '../services/api/osv';
|
import { CounterpartyMapModal } from '../ConsolidatedOsv/CounterpartyMapModal';
|
||||||
import type { ConsolidatedRowOut } from '../types/api';
|
import { OsvGaps } from '../ConsolidatedOsv/OsvGaps';
|
||||||
import type { ConsRussianStatus } from '../types/consolidations/common_types';
|
import { OsvTable } from '../ConsolidatedOsv/OsvTable';
|
||||||
import type { VersionCard } from '../types/consolidations/version_members';
|
import { mapEntryId } from '../ConsolidatedOsv/counterpartyMapping';
|
||||||
import { exportFilename, saveBlob } from '../utils/download';
|
import { OSV_PAGE_SIZE, useConsolidatedOsv } from '../ConsolidatedOsv/useConsolidatedOsv';
|
||||||
import { quarterShortLabel } from '../utils/labels';
|
|
||||||
import { statusColorMapping, statusMapping } from '../widgets/ConsoleTable/consts';
|
|
||||||
import { CounterpartyMapModal } from '../widgets/ConsolidatedOsv/CounterpartyMapModal';
|
|
||||||
import { OsvGaps } from '../widgets/ConsolidatedOsv/OsvGaps';
|
|
||||||
import { OsvTable } from '../widgets/ConsolidatedOsv/OsvTable';
|
|
||||||
import { OsvTabs } from '../widgets/ConsolidatedOsv/OsvTabs';
|
|
||||||
import { mapEntryId } from '../widgets/ConsolidatedOsv/counterpartyMapping';
|
|
||||||
import { OSV_PAGE_SIZE, useConsolidatedOsv } from '../widgets/ConsolidatedOsv/useConsolidatedOsv';
|
|
||||||
|
|
||||||
function OsvContent({ versionId }: { versionId: string }) {
|
export function OsvContent({ card }: { card: VersionCard | null }) {
|
||||||
const [card, setCard] = useState<VersionCard | null>(null);
|
|
||||||
const [exporting, setExporting] = useState(false);
|
const [exporting, setExporting] = useState(false);
|
||||||
|
const { version_id } = useParams<{ version_id: string }>();
|
||||||
|
|
||||||
// строка, для которой открыт выбор организации («Указать» или ИНН сопоставленного ЮЛ)
|
// строка, для которой открыт выбор организации («Указать» или ИНН сопоставленного ЮЛ)
|
||||||
const [mapping, setMapping] = useState<ConsolidatedRowOut | null>(null);
|
const [mapping, setMapping] = useState<ConsolidatedRowOut | null>(null);
|
||||||
const osv = useConsolidatedOsv(versionId);
|
const osv = useConsolidatedOsv(version_id ?? '');
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let actual = true;
|
|
||||||
getVersionCard(versionId)
|
|
||||||
.then((c) => {
|
|
||||||
if (actual) setCard(c);
|
|
||||||
})
|
|
||||||
.catch((e: unknown) => {
|
|
||||||
if (actual) message.error(e instanceof ApiError ? e.message : 'Не удалось получить версию');
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
actual = false;
|
|
||||||
};
|
|
||||||
}, [versionId]);
|
|
||||||
|
|
||||||
const download = async () => {
|
const download = async () => {
|
||||||
|
if (!version_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setExporting(true);
|
setExporting(true);
|
||||||
try {
|
try {
|
||||||
const { blob, filename } = await exportOsv(versionId, osv.filters);
|
const { blob, filename } = await exportOsv(version_id, osv.filters);
|
||||||
saveBlob(blob, filename ?? exportFilename(card?.company_group.name ?? 'ГК', card?.year ?? 0, card?.quarter ?? ''));
|
saveBlob(blob, filename ?? exportFilename(card?.company_group.name ?? 'ГК', card?.year ?? 0, card?.quarter ?? ''));
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
message.error(e instanceof ApiError ? e.message : 'Не удалось выгрузить свод');
|
message.error(e instanceof ApiError ? e.message : 'Не удалось выгрузить свод');
|
||||||
@ -57,27 +39,9 @@ function OsvContent({ versionId }: { versionId: string }) {
|
|||||||
const s = osv.summary;
|
const s = osv.summary;
|
||||||
const unmapped = s?.unmapped_total ?? 0;
|
const unmapped = s?.unmapped_total ?? 0;
|
||||||
const onlyUnmapped = osv.filters.unmapped === true;
|
const onlyUnmapped = osv.filters.unmapped === true;
|
||||||
const russianStatus = card ? (statusMapping[card.status] ?? card.status) : '';
|
|
||||||
const preset = statusColorMapping[russianStatus as ConsRussianStatus] ?? TAG_PRESETS.default;
|
|
||||||
const groupName = card?.company_group.name ?? '';
|
|
||||||
const period = card ? `${card.year} · ${quarterShortLabel(card.quarter)}` : '';
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: 12, display: 'flex', flexDirection: 'column', height: '100vh', gap: 12 }}>
|
<>
|
||||||
<PageTitle
|
|
||||||
backable
|
|
||||||
location={['Отчётность', groupName, period]}
|
|
||||||
header={card ? `${groupName} — ${card.year} ${quarterShortLabel(card.quarter)}` : 'Сводная ОСВ'}
|
|
||||||
description={russianStatus}
|
|
||||||
extra={
|
|
||||||
card && (
|
|
||||||
<Space size={8}>
|
|
||||||
<span style={{ color: '#667085', fontSize: 12 }}>Статус отчёта</span>
|
|
||||||
<TableTag text={russianStatus} {...preset} />
|
|
||||||
</Space>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<OsvTabs />
|
|
||||||
<main
|
<main
|
||||||
style={{
|
style={{
|
||||||
flex: '1 1 auto',
|
flex: '1 1 auto',
|
||||||
@ -135,21 +99,15 @@ function OsvContent({ versionId }: { versionId: string }) {
|
|||||||
onMap={setMapping}
|
onMap={setMapping}
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
{mapping && (
|
{mapping && version_id && (
|
||||||
<CounterpartyMapModal
|
<CounterpartyMapModal
|
||||||
key={mapEntryId(mapping)}
|
key={mapEntryId(mapping)}
|
||||||
versionId={versionId}
|
versionId={version_id}
|
||||||
row={mapping}
|
row={mapping}
|
||||||
onClose={() => setMapping(null)}
|
onClose={() => setMapping(null)}
|
||||||
onMapped={osv.refresh}
|
onMapped={osv.refresh}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConsolidatedOsvPage() {
|
|
||||||
const { version_id = '' } = useParams();
|
|
||||||
// key: при смене версии всё состояние (фильтры, страница, данные) создаётся заново
|
|
||||||
return <OsvContent key={version_id} versionId={version_id} />;
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user