import { useState } from 'react'; import { ChevronDown, FileText, RefreshCw, TriangleAlert } from 'lucide-react'; import { cn } from '../../lib/cn'; import { Avatar } from '../core/Avatar'; import { ReasoningBody } from './ReasoningBody'; import type { Decision } from '../../types'; export interface AIDecisionStreamProps { decisions: Decision[]; loading?: boolean; /** Manual refresh handler. Shown as a refresh button in the header. */ onRefresh?: () => void; /** Autonomy threshold. @default 0.7 */ threshold?: number; className?: string; } /** * AI DECISION STREAM — the stacked-decision view. * A single-open accordion: each row is a scannable summary (employee, activity, * confidence, one-line reasoning); expanding reveals the full reasoning in a * height-capped, scrollable box so a wall of text never blows out the column. */ export function AIDecisionStream({ decisions, loading = false, onRefresh, threshold = 0.7, className, }: AIDecisionStreamProps) { // Single-open accordion; all collapsed by default (-1 = none open). const [openIdx, setOpenIdx] = useState(-1); const escalations = decisions.filter((d) => d.confidence < threshold); return (

AI decision stream

{decisions.length} action{decisions.length === 1 ? '' : 's'} {onRefresh && ( )}
{escalations.length > 0 && (
{escalations.length} escalation{escalations.length > 1 ? 's' : ''} need you
{escalations[0].employee} · confidence {Math.round(escalations[0].confidence * 100)}%.
)} {/* Placeholder only on first load; a manual refresh keeps the list visible (the header button spins) so the stream never blanks out. */} {loading && decisions.length === 0 && (
Loading decisions…
)} {!loading && decisions.length === 0 && (
No AI decisions for this lead yet.
)} {decisions.length > 0 && (
{decisions.map((d, i) => ( setOpenIdx((cur) => (cur === i ? -1 : i))} /> ))}
)}
); } function DecisionRow({ decision: d, threshold, open, onToggle, }: { decision: Decision; threshold: number; open: boolean; onToggle: () => void; }) { const autonomous = d.confidence >= threshold; const accent = autonomous ? 'var(--status-autonomous)' : 'var(--status-escalated)'; const accentSoft = autonomous ? 'var(--status-autonomous-soft)' : 'var(--status-escalated-soft)'; const pct = Math.round(d.confidence * 100); return (
{/* accent rail on the open row */} {open && ( )} {open && (d.reasoning || d.citations.length > 0) && (
{d.reasoning && (
)} {d.citations.length > 0 && (
Cited knowledge
{d.citations.map((c, i) => ( {c} ))}
)}
)}
); }