"use client"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { cn } from "@/lib/utils"; import { TrendingUp, TrendingDown, Minus } from "lucide-react"; interface MetricCardProps { title: string; value: string | number | null | undefined; description?: string; icon?: React.ReactNode; trend?: { value: number; label?: string; isPositive?: boolean; }; variant?: "default" | "primary" | "success" | "warning" | "danger"; isLoading?: boolean; className?: string; } export default function MetricCard({ title, value, description, icon, trend, variant = "default", isLoading = false, className, }: MetricCardProps) { if (isLoading) { return (
); } const getVariantClasses = () => { switch (variant) { case "primary": return "border-primary/20 bg-gradient-to-br from-primary/5 to-primary/10"; case "success": return "border-green-200 bg-gradient-to-br from-green-50 to-green-100 dark:border-green-800 dark:from-green-950 dark:to-green-900"; case "warning": return "border-amber-200 bg-gradient-to-br from-amber-50 to-amber-100 dark:border-amber-800 dark:from-amber-950 dark:to-amber-900"; case "danger": return "border-red-200 bg-gradient-to-br from-red-50 to-red-100 dark:border-red-800 dark:from-red-950 dark:to-red-900"; default: return "border-border bg-gradient-to-br from-card to-muted/20"; } }; const getIconClasses = () => { switch (variant) { case "primary": return "bg-primary/10 text-primary border-primary/20"; case "success": return "bg-green-100 text-green-600 border-green-200 dark:bg-green-900 dark:text-green-400 dark:border-green-800"; case "warning": return "bg-amber-100 text-amber-600 border-amber-200 dark:bg-amber-900 dark:text-amber-400 dark:border-amber-800"; case "danger": return "bg-red-100 text-red-600 border-red-200 dark:bg-red-900 dark:text-red-400 dark:border-red-800"; default: return "bg-muted text-muted-foreground border-border"; } }; const getTrendIcon = () => { if (!trend) return null; if (trend.value === 0) { return ; } return trend.isPositive !== false ? ( ) : ( ); }; const getTrendColor = () => { if (!trend || trend.value === 0) return "text-muted-foreground"; return trend.isPositive !== false ? "text-green-600 dark:text-green-400" : "text-red-600 dark:text-red-400"; }; return ( {/* Subtle gradient overlay */}

{title}

{description && (

{description}

)}
{icon && (
{icon}
)}

{value ?? "—"}

{trend && ( {getTrendIcon()} {Math.abs(trend.value).toFixed(1)}% {trend.label && ` ${trend.label}`} )}
); }