"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 border-blue-100 bg-white shadow-sm hover:shadow-md";
case "success":
return "border border-green-100 bg-white shadow-sm hover:shadow-md";
case "warning":
return "border border-pink-100 bg-white shadow-sm hover:shadow-md";
case "danger":
return "border border-red-100 bg-white shadow-sm hover:shadow-md";
default:
return "border border-gray-100 bg-white shadow-sm hover:shadow-md";
}
};
const getIconClasses = () => {
return "bg-gray-50 text-gray-900 border-gray-100";
};
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 (
{title}
{description && (
{description}
)}
{icon && (
{icon}
)}
{value ?? "—"}
{trend && (
{getTrendIcon()}
{Math.abs(trend.value).toFixed(1)}%
{trend.label && ` ${trend.label}`}
)}
);
}