import type { TaskDetail } from "../api"; interface Props { detail: TaskDetail | null; loading: boolean; error: string | null; onClose: () => void; } function fmtDuration(s: number | null): string { if (s == null) return "—"; if (s < 60) return `${s.toFixed(1)}s`; if (s < 3600) return `${(s / 60).toFixed(1)}m`; return `${(s / 3600).toFixed(1)}h`; } function fmtCost(usd: number): string { return usd > 0 ? `$${usd.toFixed(4)}` : "—"; } // Right-hand drawer: a task's journey through the pipeline (vertical timeline of // task_transitions) plus Q&A, review verdicts, plan summary, and total spend. export function TaskDrawer({ detail, loading, error, onClose }: Props) { if (!detail && !loading && !error) return null; return (

Task history

{loading &&
Loading…
} {error &&
{error}
} {detail && detail.ok && ( <>
{detail.task}
{detail.status} {detail.short_id} spend {fmtCost(detail.total_usd)}
{detail.partial && (
Partial history — recorded since the v3 ledger; earlier steps are reconstructed.
)}
    {detail.timeline.map((step, i) => { const open = step.exited_at == null; return (
  1. {step.to_phase}
    {step.entered_at ?? "—"} {open ? "in progress" : fmtDuration(step.duration_s)}
    {step.status && {step.status}} {fmtCost(step.cost_usd)} {step.note && {step.note}}
  2. ); })} {detail.timeline.length === 0 && (
  3. No transitions recorded.
  4. )}
{detail.qa_history.length > 0 && (
Q&A ({detail.qa_history.length})
{JSON.stringify(detail.qa_history, null, 2)}
)} {detail.review_verdicts.length > 0 && (
Review verdicts ({detail.review_verdicts.length})
{JSON.stringify(detail.review_verdicts, null, 2)}
)} {detail.plan && (
Plan
{JSON.stringify(detail.plan, null, 2)}
)} )} {detail && !detail.ok && (
{detail.error ?? "Could not load task."}
)}
); }