'use client'; import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; import { CardSkeleton } from '@/components/shared/loading-skeleton'; import { EmptyState } from '@/components/shared/empty-state'; import { useIsMobile } from '@/hooks/use-is-mobile'; import { STAGE_SHORT_LABELS, safeStage, stageLabel } from '@/lib/constants'; import { ChartCard } from './chart-card'; import { useFunnel } from './use-analytics'; import type { DateRange } from '@/lib/services/analytics.service'; interface Props { range: DateRange; } export function PipelineFunnelChart({ range }: Props) { const { data, isLoading } = useFunnel(range); const isMobile = useIsMobile(); const stages = data?.stages ?? []; // Use short labels on mobile so the rotated axis isn't a wall of overlap. const chartData = stages.map((s) => ({ stage: isMobile ? STAGE_SHORT_LABELS[safeStage(s.stage)] : stageLabel(s.stage), count: s.count, conversionPct: s.conversionPct, })); const allZero = stages.every((s) => s.count === 0); function toCsv(): string | null { if (!stages.length) return null; const header = 'stage,count,conversion_pct'; const rows = stages.map((s) => `${s.stage},${s.count},${s.conversionPct}`); return [header, ...rows].join('\n'); } return ( {isLoading ? ( ) : allZero ? ( ) : ( { const pct = (item?.payload as { conversionPct?: number } | undefined) ?.conversionPct; return [`${value} (${pct ?? 0}%)`, 'Count']; }} /> )} ); }