import * as React from 'react'; import { TrendingUp, TrendingDown, Minus } from 'lucide-react'; import { cn } from '../../shared/utils'; import { Card, CardContent } from '../card'; interface StatsCardProps extends React.HTMLAttributes { title: string; value: string | number; description?: string; trend?: { value: number; label?: string; }; icon?: React.ReactNode; /** * Tailwind class for the icon text/foreground color. * @default "text-muted-foreground" */ iconColor?: string; /** * Tailwind class for the icon background color. * @default "bg-muted" */ iconBg?: string; } /** * KPI metric card for dashboard overview pages. * * @description * Standardized layout for displaying a metric title, a large value, an * optional icon, and an optional trend indicator with automatic up/down/neutral * icon and color. Built on top of ``. * * @ai-rules * 1. Provide the `icon` as a Lucide React element with `className="size-5"`. * 2. `trend.value` is a raw number (e.g., `20.1` for +20.1%) — the component * displays `Math.abs(value)%` automatically. Do NOT pre-format as string. * 3. `value` IS a pre-formatted display string: `"$45,231"`, `"3.6%"`, etc. * 4. `trend.label` takes precedence over `description` for below-value text. */ const StatsCard = React.forwardRef( ({ className, title, value, description, trend, icon, iconColor, iconBg, ...props }, ref) => { const getTrendIcon = () => { if (!trend) return null; if (trend.value > 0) { return ; } else if (trend.value < 0) { return ; } else { return ; } }; const getTrendColor = () => { if (!trend) return ''; if (trend.value > 0) { return 'text-success'; } else if (trend.value < 0) { return 'text-destructive'; } else { return 'text-muted-foreground'; } }; return (

{title}

{value}

{trend && (
{getTrendIcon()} {Math.abs(trend.value)}%
)}
{(description || trend?.label) && (

{trend?.label || description}

)}
{icon && (
{icon}
)}
); } ); StatsCard.displayName = 'StatsCard'; export { StatsCard }; export type { StatsCardProps };