'use client'; import { Sparkline } from '@/components/charts/sparkline.js'; import { AnimatedNumber } from '@/components/dashboard/animated-number.js'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card.js'; import { cn } from '@/lib/utils.js'; import type { ReactNode } from 'react'; import { useCallback } from 'react'; type AnimatedFormat = 'dollar' | 'compact' | 'integer'; const FORMAT_FNS: Record string> = { dollar: (n: number) => `$${n.toFixed(2)}`, compact: (n: number) => { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; return n.toFixed(1); }, integer: (n: number) => Math.round(n).toLocaleString(), }; interface MetricCardProps { title: string; value: string; /** When provided with animatedFormat, the value animates via spring physics on change. */ numericValue?: number; /** Named format preset for the animated value. */ animatedFormat?: AnimatedFormat; unit?: string; icon: ReactNode; className?: string; sparklineData?: { value: number }[]; sparklineColor?: string; } export function MetricCard({ title, value, numericValue, animatedFormat, unit, icon, className, sparklineData, sparklineColor, }: MetricCardProps) { const formatFn = useCallback( (n: number) => (animatedFormat ? FORMAT_FNS[animatedFormat](n) : n.toFixed(2)), [animatedFormat], ); return ( {icon} {title}
{numericValue !== undefined && animatedFormat ? ( ) : ( {value} )} {unit && {unit}}
{sparklineData && sparklineData.length >= 2 && (
)}
); }