import { Check } from "lucide-react"; import { cn } from "@/lib/utils"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Progress } from "@/components/ui/progress"; import { TaskCheckItem } from "@/components/ui/pipeline-primitives"; import { Spinner } from "@/components/ui/spinner"; /** * StageTimeline — WealthX DS (L4 Section) * * Progress bar + accordion list of pipeline stages with task checklists. * Used in the Tasks tab of OpportunityDetailsDrawer. * * Layout: * • Top: progress bar showing completed stages / total stages * • Below: accordion with one item per stage * * Data source: `listColumns()` + `listLoans()` → tasks per stage */ // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface StageTimelineTask { id: string; title: string; completed: boolean; aiAgentName?: string | null; /** When provided alongside `aiAgentName`, shows the auto-complete toggle switch. */ autoCompleteEnabled?: boolean; } export interface StageTimelineStage { id: string; name: string; tasks: StageTimelineTask[]; /** Stage the opportunity is currently in. */ isCurrentStage?: boolean; /** Stage has been fully passed through. */ isCompleted?: boolean; } export interface StageTimelineProps { stages: StageTimelineStage[]; /** Called when a task checkbox is toggled. */ onTaskToggle?: (stageId: string, taskId: string) => void; /** Called when the auto-complete switch on a task is toggled. */ onTaskAutoCompleteToggle?: (stageId: string, taskId: string) => void; /** Shows a loading skeleton instead of stages. */ isLoading?: boolean; className?: string; } // --------------------------------------------------------------------------- // StageTimeline // --------------------------------------------------------------------------- export function StageTimeline({ stages, onTaskToggle, onTaskAutoCompleteToggle, isLoading = false, className, }: StageTimelineProps) { if (isLoading) { return (

Loading stages…

); } if (stages.length === 0) { return (
No stages available.
); } const completedCount = stages.filter((s) => s.isCompleted).length; const progressPct = Math.round((completedCount / stages.length) * 100); // Default open: current stage (or first if none flagged) const defaultOpen = stages.find((s) => s.isCurrentStage)?.id ?? stages[0].id; return (
{/* Progress bar */}
Stage progress {completedCount} / {stages.length} completed
{/* Stage accordion */} {stages.map((stage) => { const completedTasks = stage.tasks.filter((t) => t.completed).length; const hasTasks = stage.tasks.length > 0; return ( svg]:size-4", stage.isCompleted ? "text-muted-foreground" : stage.isCurrentStage ? "font-semibold text-foreground" : "font-normal text-muted-foreground", )} disabled={!hasTasks} > {/* Stage status icon */} {stage.isCompleted ? ( ) : stage.isCurrentStage ? ( {hasTasks && ( {stage.tasks.map((task) => ( onTaskToggle(stage.id, task.id) : undefined } onAutoCompleteToggle={ onTaskAutoCompleteToggle ? () => onTaskAutoCompleteToggle(stage.id, task.id) : undefined } /> ))} )} ); })}
); }