import * as React from "react"; import { cn } from "@/lib/utils"; import { useThemeVars } from "@/lib/theme-provider"; import { formatCurrencyAbbrev } from "@/lib/format-currency"; import { pipelinePrimaryColor } from "@/lib/pipeline-colors"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./tooltip"; /** * PipelineChart — WealthX DS (L4 Section) * * Horizontal stacked bar chart showing a total split by stage. Each segment is * proportional to its stage's value. Hover a segment to see the stage name and * the formatted value. * * Values are abbreviated currency by default, which suits the loan pipeline it * was built for. Pass `formatValue` when the stages count things instead — a * task board rendering "$3" for three cards is the case this exists for. * * Colors: each stage supplies its own `color` (e.g. `accentColor` from * `KanbanColumnStage`). Falls back to the design system's primary token. * * Data source: `listColumns()` → `Stage[]` in `loan-crm.ts` */ // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface PipelineChartStage { id: string; name: string; /** The stage's value — dollars by default, or whatever `formatValue` renders. */ value: number; /** CSS color string — use the stage's `accentColor`. Falls back to primary. */ color?: string; } export interface PipelineChartProps { stages: PipelineChartStage[]; /** Height of the stacked bar in pixels. Default 32. */ barHeight?: number; /** * Renders a stage's value in the tooltip and its accessible label. Defaults to * abbreviated currency. */ formatValue?: (value: number) => string; className?: string; } // --------------------------------------------------------------------------- // PipelineChart // --------------------------------------------------------------------------- export function PipelineChart({ stages, barHeight = 32, formatValue = formatCurrencyAbbrev, className, }: PipelineChartProps) { const themeVars = useThemeVars(); const [activeId, setActiveId] = React.useState(null); const nonEmpty = stages.filter((s) => s.value > 0); const total = nonEmpty.reduce((sum, s) => sum + s.value, 0); if (total === 0 || stages.length === 0) { return (
No pipeline data.
); } const n = nonEmpty.length; return (
{/* Stacked bar — TooltipContent uses a portal so it is never clipped */}
{nonEmpty.map((stage, i) => { const pct = (stage.value / total) * 100; const bgColor = stage.color ?? pipelinePrimaryColor(i, n); const isActive = activeId === stage.id; return ( setActiveId(stage.id)} onMouseLeave={() => setActiveId(null)} aria-label={`${stage.name}: ${formatValue(stage.value)}`} /> } /> {stage.name} {" — "} {formatValue(stage.value)} ); })}
{/* Legend */}
{nonEmpty.map((stage, i) => (
))}
); }