67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
import React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { ArrowUpRight, ArrowDownRight, LucideIcon } from 'lucide-react';
|
|
|
|
interface StatCardProps {
|
|
icon: LucideIcon;
|
|
label: string;
|
|
value: string;
|
|
change?: string;
|
|
positive?: boolean;
|
|
color?: string;
|
|
bg?: string;
|
|
loading?: boolean;
|
|
delay?: number;
|
|
}
|
|
|
|
export default function StatCard({
|
|
icon: Icon,
|
|
label,
|
|
value,
|
|
change,
|
|
positive,
|
|
color = 'text-brand-400',
|
|
bg = 'bg-brand-500/10',
|
|
loading,
|
|
delay = 0,
|
|
}: StatCardProps) {
|
|
if (loading) {
|
|
return (
|
|
<div className="bg-white/[0.02] border border-white/5 p-6 rounded-2xl animate-pulse">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="w-11 h-11 rounded-xl bg-white/5" />
|
|
<div className="w-14 h-4 rounded bg-white/5" />
|
|
</div>
|
|
<div className="w-20 h-3 rounded bg-white/5 mb-2" />
|
|
<div className="w-28 h-8 rounded bg-white/5" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay }}
|
|
className="bg-white/[0.02] border border-white/5 p-6 rounded-2xl group hover:border-white/10 hover:shadow-glow-sm transition-all duration-300 relative overflow-hidden"
|
|
>
|
|
{/* Ambient glow */}
|
|
<div className={`absolute top-0 right-0 w-24 h-24 ${bg} blur-[50px] rounded-full translate-x-1/2 -translate-y-1/2 group-hover:opacity-100 opacity-50 transition-opacity duration-500`} />
|
|
|
|
<div className="flex items-center justify-between mb-4 relative">
|
|
<div className={`${bg} ${color} p-3 rounded-xl border border-white/5`}>
|
|
<Icon size={20} />
|
|
</div>
|
|
{change && (
|
|
<div className={`flex items-center gap-1 text-xs font-bold ${positive ? 'text-emerald-400' : 'text-red-400'}`}>
|
|
{positive ? <ArrowUpRight size={14} /> : <ArrowDownRight size={14} />}
|
|
{change}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<p className="text-slate-500 text-xs font-bold uppercase tracking-wider relative">{label}</p>
|
|
<h3 className="text-3xl font-black text-white mt-1 tracking-tight relative">{value}</h3>
|
|
</motion.div>
|
|
);
|
|
}
|