45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
import type { ReactNode } from 'react';
|
|
import { ArrowUp, ArrowDown, Minus } from 'lucide-react';
|
|
|
|
interface MetricCardProps {
|
|
key?: string | number;
|
|
label: string;
|
|
value: string | number;
|
|
subtext?: string;
|
|
icon?: ReactNode;
|
|
trend?: 'up' | 'down' | 'neutral';
|
|
}
|
|
|
|
const trendConfig = {
|
|
up: { icon: ArrowUp, color: 'text-emerald-500' },
|
|
down: { icon: ArrowDown, color: 'text-red-500' },
|
|
neutral: { icon: Minus, color: 'text-slate-400' },
|
|
};
|
|
|
|
export function MetricCard({ label, value, subtext, icon, trend }: MetricCardProps) {
|
|
return (
|
|
<div className="rounded-2xl border border-slate-100 shadow-sm bg-white p-5 relative">
|
|
{icon && (
|
|
<div className="absolute top-4 right-4 text-slate-300">
|
|
{icon}
|
|
</div>
|
|
)}
|
|
<p className="text-sm text-slate-500 mb-1">{label}</p>
|
|
<div className="flex items-end gap-2">
|
|
<span className="text-3xl font-bold text-[#0A1128]">{value}</span>
|
|
{trend && (
|
|
<span className={`${trendConfig[trend].color} mb-1`}>
|
|
{(() => {
|
|
const TrendIcon = trendConfig[trend].icon;
|
|
return <TrendIcon size={18} />;
|
|
})()}
|
|
</span>
|
|
)}
|
|
</div>
|
|
{subtext && (
|
|
<p className="text-xs text-slate-400 mt-1">{subtext}</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|