import { Input, Table, Tooltip } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import type { ColumnOut, DocumentType, ValueOut } from '../../types/api'; import { formatAmount, parseAmountInput } from '../../utils/money'; import { type LayoutRow, formLayout } from './formLayout'; import { columnTitle } from './reviewView'; import { type GridRow, cellFlags, cellKey } from './statementGrid'; type Props = { documentType: DocumentType; columns: ColumnOut[]; rows: GridRow[]; editable: boolean; draft: Record; onDraftChange: (key: string, text: string, originalAmount: string | null) => void; }; const ATTENTION_BG = '#fff7e6'; const MODIFIED_BG = '#e6f4ff'; function cellBackground(value: ValueOut | undefined): string | undefined { const flags = cellFlags(value); if (flags.modified) { return MODIFIED_BG; } return flags.attention ? ATTENTION_BG : undefined; } function cellTip(value: ValueOut): string | undefined { if (value.is_modified) { return `Исходное: ${formatAmount(value.original_amount, { fraction: 'auto', empty: '-' })}`; } return value.confidence ? `Уверенность: ${value.confidence}%` : undefined; } const SECTION_BG = '#f2f4f7'; const GRAND_BG = '#f9f0ff'; // строки «БАЛАНС»: фиолетовый не занят (серый — разделы, синий — исправлено, оранжевый — внимание) function lineWeight(r: LayoutRow): number | undefined { if (r.kind !== 'line') return undefined; if (r.grand) return 700; return r.total ? 600 : undefined; } const lineBackground = (r: LayoutRow) => (r.kind === 'line' && r.grand ? GRAND_BG : undefined); export function StatementTable({ documentType, columns, rows, editable, draft, onDraftChange }: Props) { const layout = formLayout(documentType, rows); const span = columns.length + 2; // строка-заголовок раздела занимает всю ширину: первая ячейка colSpan = все колонки, остальные скрыты const sectionSpan = (r: LayoutRow, first: boolean) => (r.kind === 'section' ? { colSpan: first ? span : 0 } : {}); const tableColumns: ColumnsType = [ { key: 'name', title: 'Наименование показателя', onCell: (r) => ({ ...sectionSpan(r, true), style: r.kind === 'section' ? { background: SECTION_BG, textAlign: 'center', fontWeight: r.level === 1 ? 700 : 600 } : { background: lineBackground(r) }, }), render: (_: unknown, r: LayoutRow) => { if (r.kind === 'section') return r.title; const text = {r.displayName || '—'}; return r.fullName ? {text} : text; }, }, { key: 'code', title: 'Код', width: 60, align: 'center' as const, onCell: (r) => (r.kind === 'section' ? sectionSpan(r, false) : { style: { background: lineBackground(r) } }), render: (_: unknown, r: LayoutRow) => (r.kind === 'line' ? {r.row.code} : null), }, ...columns.map((col) => ({ key: col.id, title: columnTitle(col), width: 112, align: 'right' as const, onCell: (r: LayoutRow) => r.kind === 'section' ? sectionSpan(r, false) : { style: { background: cellBackground(r.row.cells[col.id]) ?? lineBackground(r), fontWeight: lineWeight(r) } }, render: (_: unknown, r: LayoutRow) => { if (r.kind === 'section') return null; const row = r.row; const value = row.cells[col.id]; if (!value) { return null; } const key = cellKey(row.key, col.id); const tip = cellTip(value); if (!editable) { return {formatAmount(value.amount, { fraction: 'auto', empty: '-' })}; } const text = draft[key] ?? value.amount ?? ''; const invalid = key in draft && !parseAmountInput(text).ok; return ( onDraftChange(key, e.target.value, value.amount)} style={{ textAlign: 'right' }} /> ); }, })), ]; return size='small' bordered rowKey='key' columns={tableColumns} dataSource={layout} pagination={false} />; }