import { useState } from 'react' import { formatINR } from '@sims/domain' import { Badge, Button, ConfirmDialog, DataTable, EmptyState, ErrorState, Notice, PageHeader, Skeleton, StatCard, Stats, Toolbar, useToast, } from '@sims/ui' import { commitApexImport, getImportStatus, stageApexCsv } from '../api' import { useData } from './Clients' /** * APEX cutover importer (D18 WS-B; owner-only). Stage the two CSV exports → * every row carries its own problem list → verify the totals → Commit, which the * server refuses while ANY problem remains. Re-stage as many times as needed; * after the final commit APEX goes read-only (no double entry, spec §4). */ export function ImportApex() { const toast = useToast() const status = useData(getImportStatus, []) const [clientsCsv, setClientsCsv] = useState() const [invoicesCsv, setInvoicesCsv] = useState() const [busy, setBusy] = useState(false) const [confirming, setConfirming] = useState(false) const [done, setDone] = useState() const readFile = (set: (t: string) => void) => (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file === undefined) return const reader = new FileReader() reader.onload = () => set(String(reader.result ?? '')) reader.onerror = () => toast.err(`Could not read ${file.name}`) reader.readAsText(file) } const stage = () => { if (busy) return if (clientsCsv === undefined && invoicesCsv === undefined) { toast.err('Pick at least one CSV first'); return } setBusy(true); setDone(undefined) stageApexCsv({ ...(clientsCsv !== undefined ? { clientsCsv } : {}), ...(invoicesCsv !== undefined ? { invoicesCsv } : {}), }) .then(() => { toast.ok('Staged — review the report below'); status.reload() }) .catch((e: Error) => toast.err(e.message)) .finally(() => setBusy(false)) } const commit = () => { setBusy(true) commitApexImport() .then((r) => { setDone(`Committed: ${r.clients} clients, ${r.invoices} invoices` + (r.seeded !== null ? ` · INVOICE series seeded past ${r.seeded.fy} #${r.seeded.lastSeq}` : '')) toast.ok('Import committed — APEX is now read-only reference') status.reload() }) .catch((e: Error) => toast.err(e.message)) .finally(() => { setBusy(false); setConfirming(false) }) } const s = status.data const problems = (s?.report.clients.problems ?? 0) + (s?.report.invoices.problems ?? 0) const staged = (s?.report.clients.staged ?? 0) + (s?.report.invoices.staged ?? 0) return (

1 · Stage the CSV exports

2 · Verification report

{status.error !== undefined ? : s === undefined ? : staged === 0 ? Nothing staged yet. : ( <> 0 ? { hint: `${s.report.clients.problems} with problems`, hintTone: 'err' as const } : { hint: 'clean' })} /> 0 ? { hint: `${s.report.invoices.problems} with problems`, hintTone: 'err' as const } : { hint: 'clean' })} />

First clients: {s.report.samples.firstClients.join(' · ') || '—'}
Last invoices: {s.report.samples.lastInvoices.join(' · ') || '—'}

{s.problemClients.length > 0 && ( <>

Client rows with problems (first 50 of {s.report.clients.problems})

({ row: String(p.row_no), code: p.code ?? '—', name: p.name ?? '—', problems: (JSON.parse(p.problems) as string[]).join('; '), }))} /> )} {s.problemInvoices.length > 0 && ( <>

Invoice rows with problems (first 50 of {s.report.invoices.problems})

({ row: String(p.row_no), client: p.client_code ?? '—', docNo: p.doc_no ?? '—', problems: (JSON.parse(p.problems) as string[]).join('; '), }))} /> )} )}

3 · Commit

{problems > 0 && {problems} staged row(s) still carry problems — fix the CSVs and re-stage. Commit stays locked.} {done !== undefined && {done}} {confirming && ( setConfirming(false)} onConfirm={commit} title="Commit the APEX import" body="This writes the staged clients and invoices for real and seeds the INVOICE series past the legacy numbers. After the final cutover, APEX goes read-only — no double entry. Commit?" confirmLabel="Commit import" tone="danger" /> )}
) }