/** * StatsCard - Statistics display card component */ import clsx from 'clsx'; import type { ReactNode } from 'react'; export interface StatsCardProps { /** Card title/label */ title: string; /** Main value to display */ value: string | number; /** Optional description or subtitle */ description?: string; /** Icon to display */ icon?: ReactNode; /** Trend indicator */ trend?: { value: number; label?: string; isPositive?: boolean; }; /** Color variant */ variant?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'; /** Simple color prop (maps to variant for backward compatibility) */ color?: string; /** Loading state */ loading?: boolean; /** Additional CSS classes */ className?: string; } const variantClasses = { default: { icon: 'bg-background-muted dark:bg-background-muted text-foreground-muted', accent: '', }, primary: { icon: 'bg-primary/10 dark:bg-primary/20 text-primary', accent: 'border-l-4 border-l-primary', }, success: { icon: 'bg-green-100 dark:bg-green-900/30 text-green-600 dark:text-green-400', accent: 'border-l-4 border-l-green-500', }, warning: { icon: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-600 dark:text-yellow-400', accent: 'border-l-4 border-l-yellow-500', }, danger: { icon: 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400', accent: 'border-l-4 border-l-red-500', }, info: { icon: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400', accent: 'border-l-4 border-l-blue-500', }, }; export function StatsCard({ title, value, description, icon, trend, variant = 'default', color, loading = false, className, }: StatsCardProps) { // Map color prop to variant for backward compatibility const colorToVariant: Record = { blue: 'info', green: 'success', yellow: 'warning', red: 'danger', orange: 'warning', purple: 'primary', indigo: 'primary', }; const effectiveVariant = color ? (colorToVariant[color] || 'default') : variant; const styles = variantClasses[effectiveVariant]; if (loading) { return (
); } return (

{title}

{value}

{description && (

{description}

)} {trend && (
{trend.isPositive ? '↑' : '↓'} {Math.abs(trend.value)}% {trend.label && ( {trend.label} )}
)}
{icon && (
{icon}
)}
); } /** * StatsCardGrid - Grid layout for multiple stats cards */ export function StatsCardGrid({ children, cols = 4, className, }: { children: ReactNode; cols?: 2 | 3 | 4; className?: string; }) { const colClasses = { 2: 'grid-cols-1 sm:grid-cols-2', 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3', 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4', }; return (
{children}
); } export default StatsCard;