// Customers view — list customers derived from transactions and show their alerts/cases/transactions.
const { useState: useCustState } = React;
function formatAmount(n) {
return "$" + Number(n || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function formatDate(iso) {
if (!iso) return "—";
const d = new Date(iso);
return d.toLocaleString(undefined, { month: "short", day: "numeric", year: "numeric", hour: "2-digit", minute: "2-digit" });
}
function CustomerDetail({ customer, onBack }) {
const [tab, setTab] = useCustState("transactions");
const [transactions, setTransactions] = useCustState([]);
const [alerts, setAlerts] = useCustState([]);
const [cases, setCases] = useCustState([]);
const [loading, setLoading] = useCustState(false);
useEffectC(() => {
let cancelled = false;
const load = async () => {
if (!customer?.customer_id || !window.mossadApi) return;
setLoading(true);
try {
const [txns, alrts, cs] = await Promise.all([
window.mossadApi.fetchCustomerTransactions(customer.customer_id, 100).catch(() => []),
window.mossadApi.fetchCustomerAlerts(customer.customer_id, 100).catch(() => []),
window.mossadApi.fetchCustomerCases(customer.customer_id, 100).catch(() => []),
]);
if (!cancelled) {
setTransactions(Array.isArray(txns) ? txns : []);
setAlerts(Array.isArray(alrts) ? alrts.map(window.mossadApi.mapAlert) : []);
setCases(Array.isArray(cs) ? cs.map(window.mossadApi.mapCase) : []);
}
} catch (err) {
console.error("Failed to load customer details:", err);
} finally {
if (!cancelled) setLoading(false);
}
};
load();
return () => { cancelled = true; };
}, [customer?.customer_id]);
return (
{customer.customer_id}
{customer.transaction_count} transactions
{formatAmount(customer.total_volume)} volume
Last seen {formatDate(customer.last_seen)}
{customer.alert_count || 0} alerts
{customer.case_count || 0} cases
{["transactions", "alerts", "cases"].map(t => (
))}
{loading &&
Loading…
}
{!loading && tab === "transactions" && (
| Txn ID |
Timestamp |
Type |
Originator |
Beneficiary |
Amount |
{transactions.map(t => (
| {t.external_id} |
{formatDate(t.timestamp)} |
{t.transaction_type} |
{t.originator_id} |
{t.beneficiary_id} |
{formatAmount(t.amount)} |
))}
)}
{!loading && tab === "alerts" && (
| Alert ID |
Severity |
Typology |
Amount |
Status |
{alerts.map(a => (
| {a.external_id} |
{a.severity} |
{a.typology} |
{formatAmount(a.amount)} |
{a.status} |
))}
)}
{!loading && tab === "cases" && (
| Case ID |
Status |
Alerts |
Amount |
{cases.map(c => (
| {c.external_id} |
|
{c.alert_count} |
{formatAmount(c.total_amount)} |
))}
)}
);
}
function CaseStatusPill({ status }) {
const map = {
open: { cls: "pill info", label: "Open" },
in_review: { cls: "pill med", label: "In review" },
escalated: { cls: "pill high", label: "Escalated" },
closed: { cls: "pill low", label: "Closed" },
};
const m = map[status] || { cls: "pill", label: status };
return {m.label};
}
function CustomersView({ data, initialQuery }) {
const initialCustomers = data?.customers || window.CUSTOMERS_API || [];
const [customers, setCustomers] = useCustState(initialCustomers);
const [search, setSearch] = useCustState("");
const [loading, setLoading] = useCustState(false);
const [selected, setSelected] = useCustState(null);
useEffectC(() => {
setCustomers(initialCustomers);
}, [initialCustomers]);
useEffectC(() => {
if (initialQuery) setSearch(initialQuery);
}, [initialQuery]);
useEffectC(() => {
let cancelled = false;
const timer = setTimeout(async () => {
if (!window.mossadApi) return;
setLoading(true);
try {
const res = await window.mossadApi.fetchCustomers(500, search.trim());
if (!cancelled) setCustomers(Array.isArray(res) ? res : []);
} catch (err) {
console.error("Failed to search customers:", err);
if (!cancelled) setCustomers([]);
} finally {
if (!cancelled) setLoading(false);
}
}, 250);
return () => {
cancelled = true;
clearTimeout(timer);
};
}, [search]);
if (selected) {
return setSelected(null)} />;
}
return (
| Customer ID |
Transactions |
Total volume |
Last seen |
Alerts |
Cases |
{customers.map(c => (
setSelected(c)} style={{ cursor: "pointer" }}>
| {c.customer_id} |
{c.transaction_count.toLocaleString()} |
{formatAmount(c.total_volume)} |
{formatDate(c.last_seen)} |
{(c.alert_count ?? 0).toLocaleString()} |
{(c.case_count ?? 0).toLocaleString()} |
))}
{customers.length === 0 && (
No customers found.
)}
);
}
Object.assign(window, { CustomersView });