import * as React from "react"; import { Check, Circle, CircleAlert, Play, RefreshCw, Square } from "lucide-react"; import { cn } from "@/lib/utils"; import { Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Spinner } from "@/components/ui/spinner"; import { TypingDots } from "@/components/ui/chat-widget-primitives"; /** * CopilotPlanCard — WealthX DS (Molecule) * * Renders an agentic plan the broker Copilot proposes: a named plan with a * description and an ordered list of steps that execute one-by-one. Each step * shows its own status (pending / running / done / failed) and the card carries * an "Execute Plan" affordance below the steps. * * Composes Card + Button + Badge + Spinner + TypingDots. Pure display: the host * wires onExecute to the real side-effect and drives `status` + per-step * `status` as execution progresses. */ export type CopilotPlanStepStatus = "pending" | "running" | "done" | "failed"; export interface CopilotPlanStep { /** Step label, e.g. "Read the loan application details from WealthX". */ label: string; /** Optional detail shown under the label. */ description?: string; /** Execution status of this step. Defaults to "pending". */ status?: CopilotPlanStepStatus; } export type CopilotPlanStatus = "idle" | "running" | "done" | "failed"; export interface CopilotPlanCardProps { /** Plan name shown as the card title, e.g. "Quickli Scenario Sync". */ title: string; /** What the plan does. */ description?: string; /** Ordered steps, executed one-by-one. */ steps: CopilotPlanStep[]; /** Overall plan lifecycle. Defaults to "idle". */ status?: CopilotPlanStatus; /** Runs the plan. When provided, renders the execute / retry button. */ onExecute?: () => void; /** * Stops an in-progress run. When provided, the running state swaps the * disabled "Executing…" button for a clickable Stop button so the host can * abort execution. */ onCancel?: () => void; /** Label for the idle execute button. Defaults to "Execute Plan". */ executeLabel?: string; /** Label while the plan is running. Defaults to "Executing…". */ runningLabel?: string; /** Label for the retry button after a failed run. Defaults to "Retry Plan". */ retryLabel?: string; /** Label for the stop button while running. Defaults to "Stop". */ cancelLabel?: string; className?: string; } const PLAN_BADGE: Record< Exclude, { label: string; variant: "warning" | "success" | "destructive" } > = { running: { label: "Running…", variant: "warning" }, done: { label: "Completed", variant: "success" }, failed: { label: "Failed", variant: "destructive" }, }; const STEP_STATUS: Record< CopilotPlanStepStatus, { srLabel: string; labelClass: string } > = { pending: { srLabel: "pending", labelClass: "text-muted-foreground" }, running: { srLabel: "in progress", labelClass: "text-foreground" }, done: { srLabel: "completed", labelClass: "text-muted-foreground" }, failed: { srLabel: "failed", labelClass: "text-foreground" }, }; /** Per-step status indicator, sized to align with the first line of the label. */ function PlanStepIcon({ status }: { status: CopilotPlanStepStatus }) { const slot = "mt-0.5 flex size-4 shrink-0 items-center justify-center"; switch (status) { case "done": return ( ); case "running": return ( ); case "failed": return ( ); default: return ( ); } } export function CopilotPlanCard({ title, description, steps, status = "idle", onExecute, onCancel, executeLabel = "Execute Plan", runningLabel = "Executing…", retryLabel = "Retry Plan", cancelLabel = "Stop", className, }: CopilotPlanCardProps) { const badge = status === "idle" ? null : PLAN_BADGE[status]; const isRunning = status === "running"; const showStop = isRunning && !!onCancel; const hasFooter = status !== "done" && (!!onExecute || showStop); return ( {title} {description && ( {description} )} {badge && ( {isRunning && } {badge.label} )}
    {steps.map((step, i) => { const stepStatus = step.status ?? "pending"; return (
  1. 0 && "border-t", )} >
    {step.label} {step.description && ( {step.description} )}
    {STEP_STATUS[stepStatus].srLabel}
  2. ); })}
{hasFooter && ( {status === "failed" && onExecute ? ( ) : showStop ? ( ) : onExecute ? ( ) : null} )}
); }