arm-core/web/src/layouts/ConsolidationReportPage.tsx
2026-10-27 18:56:51 +04:00

125 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Button, Space, Tooltip, message } from 'antd';
import { type ReactNode, useEffect, useState } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { ApiError } from '../api/client';
import { useCurrentUser } from '../auth/MeContext';
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 { OsvSummaryOut } from '../types/api';
import type { ConsRussianStatus } from '../types/consolidations/common_types';
import type { VersionCard } from '../types/consolidations/version_members';
import { CONS_STATUS_LABEL, canChangeStatus, consStatusDescription, isLockedBy } from '../utils/consolidations';
import { quarterShortLabel } from '../utils/labels';
import { statusColorMapping } from '../widgets/ConsoleTable/consts';
import { ChessContent } from '../widgets/ConsolidatedChessContent/ConsolidatedChessContent';
import { OsvTabs } from '../widgets/ConsolidatedOsv/OsvTabs';
import { OsvContent } from '../widgets/ConsolidatedOsvContent/ConsolidatedOsvContent';
import ChangeStatusModal from '../widgets/StatusChangeModal/ChangeStatusModal';
function ReportContent() {
const [card, setCard] = useState<VersionCard | null>(null);
const [statusModalOpen, setModalOpen] = useState(false);
// сводка ОСВ (пары без встречных записей) — для модалки при уходе с вкладки «ОСВ»
const [osvSummary, setOsvSummary] = useState<OsvSummaryOut | null>(null);
const currentUser = useCurrentUser();
const { version_id, tab } = useParams<{ tab?: string; version_id: string }>();
const navigation = useNavigate();
const { pathname } = useLocation();
const TAB_COMPONENTS: Record<string, ReactNode> = {
osv: <OsvContent card={card} onSummaryChange={setOsvSummary} />,
chess: <ChessContent card={card} />,
};
const ActiveTab = TAB_COMPONENTS[tab as string] ?? null;
const russianStatus = card ? (CONS_STATUS_LABEL[card.status] ?? card.status) : '';
// под заголовком — пояснение статуса, не его название
const description = card ? consStatusDescription(card.status) : '';
const preset = statusColorMapping[russianStatus as ConsRussianStatus] ?? TAG_PRESETS.default;
const statusChangeAllowed = card ? canChangeStatus(card.status, card.lock, currentUser.id) : false;
const ownLock = card ? isLockedBy(card.lock, currentUser.id) : false;
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]);
useEffect(() => {
const isValidTab = !tab || tab === 'osv' || tab === 'chess';
if (!isValidTab) {
const pathParts = pathname.split('/');
pathParts[pathParts.length - 1] = 'osv';
const newPath = pathParts.join('/');
navigation(newPath, { replace: true });
}
}, [tab, navigation, pathname]);
return (
<div style={{ padding: 12, display: 'flex', flexDirection: 'column', height: '100vh', gap: 12 }}>
<div style={{ flexShrink: 0, display: 'flex', flexDirection: 'column', gap: 12 }}>
<PageTitle
backable
location={['Отчётность', groupName, period]}
header={card ? `${groupName} — ${card.year} ${quarterShortLabel(card.quarter)}` : 'Сводная ОСВ'}
description={description}
extra={
card && (
<Space size={8} style={{ display: 'flex', gap: 4 }}>
<span style={{ color: '#667085', fontSize: 12 }}>Статус отчёта</span>
<TableTag
style={{
padding: '3px 10px',
width: 158,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 11,
}}
text={russianStatus}
{...preset}
/>
{/* активна при своей блокировке и наличии ручных переходов из текущего статуса */}
<Tooltip title={ownLock ? undefined : 'Возьмите консолидацию в работу'}>
<Button
size='middle'
style={{ fontSize: 12 }}
type='default'
disabled={!statusChangeAllowed}
onClick={() => setModalOpen(true)}>
Изменить статус
</Button>
</Tooltip>
</Space>
)
}
/>
<OsvTabs summary={osvSummary} versionId={version_id ?? ''} status={card?.status} />
</div>
{ActiveTab}
{card && <ChangeStatusModal open={statusModalOpen} card={card} onSuccess={setCard} onClose={() => setModalOpen(false)} />}
</div>
);
}
export function ConsolidationReportPage() {
const { version_id = '' } = useParams<{ version_id: string }>();
// key: при смене версии всё состояние (карточка, фильтры, страница, данные) создаётся заново
return <ReportContent key={version_id} />;
}