/** * ActiveUsersTable — admin-facing customer outreach view. * * Each row is a user active in the selected window: * user_id_hash · top_topic · interest_score · last_activity · contact-ready badge * * Clicking "Inspect" sets the user as the inspected user on the page, jumping * to their cognitive-facet cards above. */ export default function ActiveUsersTable({ rows, onInspect, windowLabel = "", activeHash = "" }) { if (!rows || rows.length === 0) { return (
No users active in window {windowLabel || "—"}.
); } return ( {rows.map((u) => { const isActive = activeHash === u.user_id_hash; const rowClass = [ u.contact_ready ? "ready" : "", isActive ? "row-active" : "", ].filter(Boolean).join(" "); return ( ); })}
User Top topic Interest Turns Topics Pos / total rewards Last seen Status
{u.display_name || shortHash(u.user_id_hash)}
{shortHash(u.user_id_hash)} {u.intents?.length > 0 && (
{u.intents.slice(0, 3).map((i) => ( {i} ))}
)}
{u.top_topic || "—"} {u.topics?.length > 1 && ( +{u.topics.length - 1} more )} {fmt(u.top_interest_score)} {u.turn_count} {u.topic_count} {u.positive_rewards} / {u.applied_rewards} {relativeTime(u.last_activity_ts)} {u.do_not_contact ? ( ⊘ Do not contact ) : !u.compliance_eligible ? ( ⊘ Compliance block ) : u.contact_ready ? ( ● Contact ready ) : ( ○ Below threshold )} {u.recommendation_reason && (
{u.recommendation_reason}
)}
); } function shortHash(h) { if (!h) return "—"; return h.length > 10 ? `${h.slice(0, 10)}…` : h; } function interestClass(v) { const x = Number(v) || 0; if (x >= 0.7) return "pos"; if (x <= 0.3) return "zero"; return ""; } function fmt(v) { if (v == null) return "—"; const x = Number(v); if (!Number.isFinite(x)) return "—"; return x.toFixed(2); } function relativeTime(iso) { if (!iso) return "—"; const d = new Date(iso); if (Number.isNaN(d.getTime())) return iso; const diffMs = Date.now() - d.getTime(); const m = Math.round(diffMs / 60000); if (m < 1) return "just now"; if (m < 60) return `${m}m ago`; const h = Math.round(m / 60); if (h < 24) return `${h}h ago`; const days = Math.round(h / 24); if (days < 30) return `${days}d ago`; return d.toLocaleDateString(); }