import { WarningOutlined } from '@ant-design/icons'; import { Button, Modal, Segmented, Tooltip } from 'antd'; import { useState } from 'react'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; import type { CounterPairOut, OsvSummaryOut } from '../../types/api'; import type { ConsStatus } from '../../types/consolidations/common_types'; import { counterPairsWarning, rememberCounterPairsShown, reportTabNavigation, shouldWarnCounterPairs } from '../../utils/consolidations'; import { pairsViewSearch } from './counterPairs'; const TAB_CONFIG = [ { 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: 'Будет в следующих срезах', }, ]; type Props = { /** Сводка ОСВ: есть пары без встречных записей — переход «ОСВ» → «Шахматка» через модалку. */ summary: OsvSummaryOut | null; versionId: string; status: ConsStatus | undefined; }; /** `sessionStorage` может бросать (приватный режим) — тогда `null`. */ const safeSessionStorage = (): Storage | null => { try { return window.sessionStorage; } catch { return null; } }; /** Ряд вкладок шагов консолидации (макет [30]); вкладка — сегмент адреса `/versions/:version_id/:tab`. */ export function OsvTabs({ summary, versionId, status }: Props) { const navigate = useNavigate(); const location = useLocation(); const { tab } = useParams<{ tab?: string }>(); const [pending, setPending] = useState(null); const activeKey = tab || 'osv'; const go = (newKey: string) => { const { to, options } = reportTabNavigation(location.pathname, activeKey, newKey); navigate(to, options); }; const handleTabChange = (value: string | number) => { const newKey = String(value); if (newKey === activeKey) return; const pairs = summary?.counter_pairs ?? []; if (activeKey === 'osv' && newKey === 'chess' && shouldWarnCounterPairs(versionId, status, pairs, safeSessionStorage())) { rememberCounterPairsShown(versionId, pairs, safeSessionStorage()); setPending(newKey); } else { go(newKey); } }; const options = TAB_CONFIG.map((tabItem) => { const isActive = tabItem.key === activeKey; const label = ( {tabItem.label} ); return { value: tabItem.key, disabled: tabItem.disabled, label: tabItem.tooltip ? {label} : label, }; }); return ( <> { setPending(null); navigate( { pathname: location.pathname, search: pairsViewSearch({ view: 'pairs', holder: null, missing: null }) }, { replace: true }, ); }} onStay={() => setPending(null)} onGo={() => { const next = pending; setPending(null); if (next) go(next); }} /> ); } /** Модалка перед шахматкой: у части участников нет встречных записей (`05-domain-rules.md` §3 п. 9). */ export function CounterPairsModal({ open, pairs, unmappedTotal, onShow, onStay, onGo, }: { open: boolean; pairs: readonly CounterPairOut[]; unmappedTotal: number; onShow: () => void; onStay: () => void; onGo: () => void; }) { const { headline, details } = counterPairsWarning(pairs, unmappedTotal); return ( Показать пары , , , ]}>
{headline}
{details}
); }