53 lines
1.4 KiB
TypeScript
53 lines
1.4 KiB
TypeScript
import { Card, CardContent } from '@kit/ui/card';
|
|
|
|
interface StatsCardProps {
|
|
title: string;
|
|
value: string | number;
|
|
icon?: React.ReactNode;
|
|
description?: string;
|
|
trend?: { value: number; label: string };
|
|
}
|
|
|
|
/**
|
|
* Reusable stat card with icon + value + label + optional trend.
|
|
* Used on dashboard and list pages.
|
|
*/
|
|
export function StatsCard({
|
|
title,
|
|
value,
|
|
icon,
|
|
description,
|
|
trend,
|
|
}: StatsCardProps) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-1">
|
|
<p className="text-muted-foreground text-sm font-medium">{title}</p>
|
|
<p className="text-2xl font-bold">{value}</p>
|
|
{description && (
|
|
<p className="text-muted-foreground text-xs">{description}</p>
|
|
)}
|
|
</div>
|
|
{icon && (
|
|
<div className="bg-primary/10 text-primary rounded-full p-3">
|
|
{icon}
|
|
</div>
|
|
)}
|
|
</div>
|
|
{trend && (
|
|
<div className="mt-2 flex items-center text-xs">
|
|
<span
|
|
className={trend.value >= 0 ? 'text-green-600' : 'text-red-600'}
|
|
>
|
|
{trend.value >= 0 ? '↑' : '↓'} {Math.abs(trend.value)}%
|
|
</span>
|
|
<span className="text-muted-foreground ml-1">{trend.label}</span>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|