// Nexa — Internal compliance dashboard (rebuild) // Single-file React app wired to https://api.usenexa.io const { useState, useEffect, useMemo, useCallback } = React; const API_BASE = window.location.origin; const RESET_KEY = "3F8869A9-609D-4892-B380-D0933E0C263B"; // ── Formatters ───────────────────────────────────────────────────────── const fmtMoney = (amt, ccy) => { if (amt == null) return "—"; const sym = { GBP: "£", USD: "$", EUR: "€", JPY: "¥", CAD: "C$", CHF: "CHF ", CNY: "¥" }[ccy] || ""; const n = Number(amt).toLocaleString("en-GB", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); return `${sym}${n}${sym ? "" : " " + (ccy || "")}`; }; const fmtCompact = (n) => { if (n == null) return "—"; if (Math.abs(n) >= 1e9) return (n / 1e9).toFixed(1) + "B"; if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(1) + "M"; if (Math.abs(n) >= 1e3) return (n / 1e3).toFixed(0) + "k"; return String(n); }; const fmtDateTime = (iso) => { if (!iso) return "—"; const d = new Date(iso); if (isNaN(d)) return iso; const date = d.toLocaleDateString("en-GB", { day: "2-digit", month: "short", timeZone: "UTC" }); const time = d.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", timeZone: "UTC" }); return `${date} · ${time}`; }; const fmtDate = (iso) => { if (!iso) return "—"; const d = new Date(iso); if (isNaN(d)) return iso; return d.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric", timeZone: "UTC" }); }; const fmtRelative = (iso) => { if (!iso) return "—"; const d = new Date(iso); if (isNaN(d)) return iso; const diff = (d - Date.now()) / 1000; const abs = Math.abs(diff); const sign = diff < 0 ? "ago" : "from now"; if (abs < 60) return `${Math.round(abs)}s ${sign}`; if (abs < 3600) return `${Math.round(abs / 60)}m ${sign}`; if (abs < 86400) return `${Math.round(abs / 3600)}h ${sign}`; return `${Math.round(abs / 86400)}d ${sign}`; }; // ── API helpers ──────────────────────────────────────────────────────── async function apiGet(path) { const headers = {}; if (path === "/internal/state" || path.startsWith("/internal/state?")) { headers["X-Internal-Key"] = RESET_KEY; } const r = await fetch(`${API_BASE}${path}`, { headers }); if (!r.ok) throw new Error(`${r.status} ${r.statusText}`); return r.json(); } async function apiPost(path, body) { const headers = { "Content-Type": "application/json" }; if (path.startsWith("/internal/")) { headers["X-Internal-Key"] = RESET_KEY; } const r = await fetch(`${API_BASE}${path}`, { method: "POST", headers, body: body ? JSON.stringify(body) : undefined, }); if (!r.ok) throw new Error(`${r.status} ${r.statusText}`); const text = await r.text(); return text ? JSON.parse(text) : null; } async function apiReset() { const r = await fetch(`${API_BASE}/internal/reset-data`, { method: "POST", headers: { "X-Internal-Key": RESET_KEY }, }); if (!r.ok) throw new Error(`${r.status} ${r.statusText}`); return r.json(); } // ── Toast bus ────────────────────────────────────────────────────────── let toastSetter = null; function toast(msg, opts = {}) { if (!toastSetter) return; toastSetter({ msg, error: !!opts.error, key: Date.now() }); setTimeout(() => toastSetter && toastSetter(null), 2400); } // ── Common cells ─────────────────────────────────────────────────────── const Badge = ({ kind, children }) => ( {children} ); const RiskBadge = ({ r }) => { const label = { mandatory_high: "mandatory high" }[r] || r || "—"; return {label}; }; const TxStatusBadge = ({ s }) => { const label = { pending_approval: "pending approval" }[s] || s; return {label}; }; const RfiStatusBadge = ({ status, overdue }) => { const label = overdue && status === "open" ? "open · overdue" : status; return {label}; }; const StageBadge = ({ s }) => { const label = { kyb_review: "kyb review", edd_required: "edd required", documents_pending: "docs pending", hard_blocked: "hard blocked" }[s] || s || "—"; return {label}; }; const FlagPills = ({ flags }) => { if (!flags || flags.length === 0) return ; return (
{flags.map((f, i) => { const label = typeof f === "string" ? f : f.label || f.type || "flag"; const crit = (typeof f !== "string" && (f.type === "sanctions_near_miss" || f.type === "agent_auto_suspended")) ? "is-critical" : ""; return {label}; })}
); }; const BizCell = ({ id, businesses }) => { const b = businesses?.[id]; if (!b) return {id}; return (
{b.name} {b.country || "—"} · {b.industry || "—"}
); }; // ── Side nav ─────────────────────────────────────────────────────────── const NAV_ITEMS = [ { id: "overview", label: "Overview", group: "Platform" }, { id: "monitoring", label: "Transactions", group: "Monitoring" }, { id: "rfis", label: "RFIs", group: "Monitoring" }, { id: "alerts", label: "Alerts", group: "Monitoring" }, { id: "businesses", label: "Businesses", group: "KYB" }, { id: "onboarding", label: "Onboarding queue", group: "KYB" }, { id: "reviews", label: "Periodic reviews", group: "KYB" }, { id: "audit", label: "Audit log", group: "Platform" }, ]; const NavIcon = ({ name }) => { const p = { className: "tc-nav-item-icon", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round" }; switch (name) { case "overview": return ; case "monitoring": return ; case "rfis": return ; case "alerts": return ; case "businesses": return ; case "onboarding": return ; case "reviews": return ; case "audit": return ; default: return null; } }; function SideNav({ page, onNav, counts, you }) { const groups = ["Platform", "Monitoring", "KYB"]; const alertish = new Set(["rfis", "alerts", "onboarding", "monitoring", "reviews"]); return ( ); } // ── Topbar ───────────────────────────────────────────────────────────── const PAGE_TITLES = { overview: { eyebrow: "Platform · live", h: "Compliance overview" }, monitoring: { eyebrow: "Monitoring · 48h window", h: "Transaction monitor" }, rfis: { eyebrow: "Monitoring", h: "Requests for information" }, alerts: { eyebrow: "Monitoring", h: "Alerts feed" }, businesses: { eyebrow: "KYB", h: "Onboarded businesses" }, onboarding: { eyebrow: "KYB", h: "Onboarding queue" }, reviews: { eyebrow: "KYB · ongoing monitoring", h: "Periodic client reviews" }, audit: { eyebrow: "Platform", h: "Audit log" }, }; function TopBar({ page, lastFetch, onRefresh }) { const t = PAGE_TITLES[page] || { eyebrow: "", h: page }; return (
{t.eyebrow} {t.h}
last fetch · {lastFetch ? fmtRelative(lastFetch) : "—"}
); } // ── Pages ────────────────────────────────────────────────────────────── function OverviewPage({ state, onNav, onOpen }) { const { businesses = [], biz_by_id = {}, transactions = [], rfis = [], alerts = [], applications = [] } = state; const today = new Date().toISOString().slice(0, 10); const activeBiz = businesses.filter(b => b.status === "active").length; const openRfis = rfis.filter(r => r.status === "open" || r.status === "responded").length; const overdueRfis = rfis.filter(r => r.overdue && r.status !== "resolved").length; const pendingApprovals = transactions.filter(t => t.status === "pending_approval").length; const flaggedToday = transactions.filter(t => (t.flags?.length > 0) && (t.ts || "").startsWith(today)).length; // Pipeline funnel const onboarding = applications.length ? applications : businesses.filter(b => b.status === "onboarding").map(b => ({ id: b.id, name: b.name, onboardingStage: b.onboardingStage, daysInQueue: b.daysInQueue, riskRating: b.riskRating, country: b.country, industry: b.industry, submittedDate: b.submittedDate, assignedAnalyst: b.assignedAnalyst, })); const stages = [ { k: "submitted", label: "Submitted" }, { k: "kyb_review", label: "KYB review" }, { k: "edd_required", label: "EDD required", flag: "is-edd" }, { k: "documents_pending", label: "Docs pending" }, { k: "hard_blocked", label: "Hard blocked", flag: "is-blocked" }, ]; const stageCount = (k) => onboarding.filter(a => a.onboardingStage === k).length; const recentAlerts = [...alerts].sort((a, b) => (b.ts || "").localeCompare(a.ts || "")).slice(0, 6); const recentTx = [...transactions].sort((a, b) => (b.ts || "").localeCompare(a.ts || "")).slice(0, 8); return ( <>
Active businesses{activeBiz}{businesses.length} total
Open RFIs{openRfis}awaiting / responded
Overdue RFIs{overdueRfis}past due date
Pending approvals{pendingApprovals}awaiting decision
Flagged today{flaggedToday}transactions

Onboarding pipeline

onNav("onboarding")} style={{ cursor: "pointer" }}>view queue →
{stages.map(s => (
{s.label} {stageCount(s.k)} applications
))}

Recent alerts

onNav("alerts")} style={{ cursor: "pointer" }}>all alerts →
{recentAlerts.length === 0 && } {recentAlerts.map(a => ( ))}
SeverityTriggerBusinessWhen
no alerts
{a.severity} {a.trigger} {fmtRelative(a.ts)}

Recent transactions

onNav("monitoring")} style={{ cursor: "pointer" }}>monitor →
{recentTx.length === 0 && } {recentTx.map(t => ( ))}
TimeCounterpartyAmountStatus
no transactions
{fmtRelative(t.ts)} {t.counterparty || "—"} {fmtMoney(t.amount, t.ccy)}
); } function MonitoringPage({ state, refresh, onOpenBiz }) { const { transactions = [], biz_by_id = {} } = state; const [filter, setFilter] = useState("all"); const [search, setSearch] = useState(""); const [busy, setBusy] = useState(null); const rows = useMemo(() => { return transactions .filter(t => filter === "all" || t.status === filter) .filter(t => !search || (t.counterparty || "").toLowerCase().includes(search.toLowerCase()) || (t.id || "").toLowerCase().includes(search.toLowerCase())) .sort((a, b) => (b.ts || "").localeCompare(a.ts || "")); }, [transactions, filter, search]); async function doApproval(t, action) { setBusy(t.id); try { // Find approval id for this tx const list = await apiGet(`/approvals?business_id=${encodeURIComponent(t.bizId)}`); const approval = (Array.isArray(list) ? list : list?.approvals || []).find(a => a.tx_id === t.id || a.transaction_id === t.id || a.id === t.id); const apId = approval?.id || approval?.approval_id || t.id; await apiPost(`/approvals/${encodeURIComponent(apId)}/${action}`); toast(`${action === "approve" ? "Approved" : "Rejected"} ${t.id}`); refresh(); } catch (e) { toast(`${action} failed: ${e.message}`, { error: true }); } finally { setBusy(null); } } return (
setSearch(e.target.value)} placeholder="Search counterparty or tx id…" /> {rows.length} of {transactions.length}
{rows.length === 0 && } {rows.map(t => ( ))}
Time (UTC) Business Agent Op Counterparty Amount Status Flags
no transactions match filters
{fmtDateTime(t.ts)}
{t.id}
onOpenBiz?.(t.bizId)} style={{ cursor: "pointer" }}> {t.agent || "—"} {t.op || "—"} {t.counterparty || "—"}
{t.reason}
{fmtMoney(t.amount, t.ccy)} {t.status === "pending_approval" && (
)}
); } function RfisPage({ state, onOpenRfi }) { const { rfis = [], biz_by_id = {} } = state; const [filter, setFilter] = useState("all"); const rows = useMemo(() => { return rfis .filter(r => filter === "all" || r.status === filter || (filter === "overdue" && r.overdue)) .sort((a, b) => { // overdue first, then by createdAt desc if (a.overdue !== b.overdue) return a.overdue ? -1 : 1; return (b.createdAt || "").localeCompare(a.createdAt || ""); }); }, [rfis, filter]); return (
{rows.length} of {rfis.length}
{rows.length === 0 && } {rows.map(r => ( onOpenRfi(r.id)}> ))}
RFI Business Status Urgency Created Due
no RFIs
{r.id}
{(r.questions || []).length} question{(r.questions || []).length === 1 ? "" : "s"}
{r.urgency || "standard"} {fmtDateTime(r.createdAt)} {fmtDate(r.dueAt)}{r.overdue && overdue}
); } function AlertsPage({ state, onOpenRfi }) { const { alerts = [], biz_by_id = {} } = state; const [sev, setSev] = useState("all"); const rows = useMemo(() => alerts .filter(a => sev === "all" || a.severity === sev) .sort((a, b) => (b.ts || "").localeCompare(a.ts || "")), [alerts, sev]); return (
{rows.length} of {alerts.length}
{rows.length === 0 && } {rows.map(a => ( ))}
AlertSeverityTriggerBusinessRFIWhen
no alerts
{a.id}
{a.type}
{a.severity} {a.trigger} {a.relatedRfi ? onOpenRfi(a.relatedRfi)}>{a.relatedRfi} : } {fmtDateTime(a.ts)}
); } function BusinessesPage({ state, onOpenBiz }) { const { businesses = [] } = state; const [search, setSearch] = useState(""); const [risk, setRisk] = useState("all"); const [status, setStatus] = useState("all"); const rows = useMemo(() => businesses .filter(b => b.status !== "onboarding") .filter(b => risk === "all" || b.riskRating === risk) .filter(b => status === "all" || b.status === status) .filter(b => !search || (b.name || "").toLowerCase().includes(search.toLowerCase())) .sort((a, b) => (b.volume30d || 0) - (a.volume30d || 0)), [businesses, search, risk, status]); return (
setSearch(e.target.value)} placeholder="Search businesses…" /> {rows.length} of {businesses.length}
{rows.length === 0 && } {rows.map(b => ( onOpenBiz(b.id)}> ))}
BusinessStatusRiskActive agents30d volumeOpen RFIs
no businesses
{b.name}{b.id} · {b.country || "—"} · {b.industry || "—"}
{b.status?.replace("_", " ")} {b.activeAgents ?? 0}{b.suspendedAgents ? +{b.suspendedAgents} susp : null} {fmtMoney(b.volume30d, b.volumeCcy)} {b.openRfis || 0}
); } function OnboardingPage({ state, onOpenBiz }) { const { applications = [], businesses = [], analysts = [] } = state; const apps = applications.length ? applications : businesses.filter(b => b.status === "onboarding"); const analystName = (id) => analysts.find(a => a.id === id)?.name || (id ? id : "unassigned"); const rows = [...apps].sort((a, b) => (b.daysInQueue || 0) - (a.daysInQueue || 0)); return (

Onboarding queue

{rows.length} applications
{rows.length === 0 && } {rows.map(a => ( onOpenBiz(a.id)}> ))}
ApplicationStageRiskCountryDays in queueSubmittedAnalyst
no applications in queue
{a.name}{a.id} · {a.industry || "—"}
{a.country || "—"} {a.daysInQueue ?? "—"} {fmtDate(a.submittedDate)} {analystName(a.assignedAnalyst)}
); } function AuditPage({ state }) { const { audit = [], biz_by_id = {} } = state; const [search, setSearch] = useState(""); const rows = useMemo(() => audit .filter(e => !search || (e.type || "").includes(search.toLowerCase()) || (e.details || "").toLowerCase().includes(search.toLowerCase())) .sort((a, b) => (b.ts || "").localeCompare(a.ts || "")), [audit, search]); return (
setSearch(e.target.value)} placeholder="Filter by type or detail…" /> {rows.length} of {audit.length}
{rows.length === 0 && } {rows.map(e => ( ))}
Time (UTC)EventPrincipalBusinessDetail
no audit entries
{fmtDateTime(e.ts)}
{e.id}
{e.type} {e.principal || "—"} {e.details || "—"}
); } function ReviewsPage({ state, refresh }) { const { reviews_due = [], reviews_pending = [], biz_by_id = {} } = state; const [tab, setTab] = useState("pending"); const [reviewDrawer, setReviewDrawer] = useState(null); const pendingWithClient = reviews_pending.map(r => ({ ...r, company_name: (() => { // look up company name via client_id or business_id const cf = Object.values(biz_by_id).find(b => b.client_id === r.client_id) || biz_by_id[r.client_id]; return cf ? cf.name : r.client_id; })(), })); return (
{tab === "pending" && (
{pendingWithClient.length === 0 && } {pendingWithClient.map(r => { const total = r.checklist.length; const done = r.checklist.filter(i => i.checked).length; return ( setReviewDrawer(r)}> ); })}
ClientReview IDTypeOpenedAssigned toProgress
no in-progress periodic reviews
{r.company_name}
{r.client_id}
{r.review_id} {r.review_type} {fmtDateTime(r.opened_at)} {r.assigned_to || unassigned}
{done}/{total}
)} {tab === "due" && (
{reviews_due.length === 0 && } {reviews_due.map(c => { const overdue = c.next_review_date ? Math.max(0, Math.floor((Date.now() - new Date(c.next_review_date)) / 86400000)) : null; return ( ); })}
ClientRisk ratingReview dateDays overdue
no clients currently due for review
{c.company_name}
{c.client_id}
{fmtDate(c.next_review_date)} {overdue != null ? 30 ? "var(--crit)" : "var(--warn)" }}>{overdue}d : "—"}
)} {reviewDrawer && ( { setReviewDrawer(null); refresh(); }} refresh={refresh} /> )}
); } function ReviewDrawer({ review, onClose, refresh }) { const [checklist, setChecklist] = useState(() => (review.checklist || []).map(i => ({ ...i }))); const [decision, setDecision] = useState(""); const [notes, setNotes] = useState(""); const [nextDate, setNextDate] = useState(""); const [ratingAfter, setRatingAfter] = useState(""); const [busy, setBusy] = useState(false); const total = checklist.length; const done = checklist.filter(i => i.checked).length; async function saveChecklist(updated) { setChecklist(updated); try { await apiPost(`/internal/clients/${encodeURIComponent(review.client_id)}/reviews/${encodeURIComponent(review.review_id)}/checklist`, { items: updated }); } catch (e) { toast(`Checklist save failed: ${e.message}`, { error: true }); } } function toggleItem(idx) { const updated = checklist.map((item, i) => i === idx ? { ...item, checked: !item.checked } : item); saveChecklist(updated); } async function completeReview() { if (!decision) { toast("Select a decision", { error: true }); return; } setBusy(true); try { await apiPost(`/internal/clients/${encodeURIComponent(review.client_id)}/reviews/${encodeURIComponent(review.review_id)}/complete`, { decision, notes, risk_rating_after: ratingAfter || null, next_review_date: nextDate || null, }); toast("Review completed"); onClose(); } catch (e) { toast(`Complete failed: ${e.message}`, { error: true }); } finally { setBusy(false); } } return ( <>