import React, { useState, useEffect } from 'react';
// Simple SVG Icons (replacing lucide-react)
const Icons = {
Users: () => <svg className="w-6 h-6" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round"
strokeLinejoin="round" strokeWidth={2}
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0
0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /></svg>,
Eye: () => <svg className="w-6 h-6" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round"
strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round"
strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5
12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064
7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>,
Globe: () => <svg className="w-6 h-6" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round"
strokeWidth={2} d="M3.055 11H5a2 2 0 012 2v1a2 2 0
002 2 2 2 0 012 2v2.945M8 3.935V5.5A2 2 0 009.5 8h2a2
2 0 002 2 2 2 0 002-2h1.064M15 20.488V18a2 2 0 013-1.736" />
<circle cx="12" cy="12" r="10" stroke="currentColor"
strokeWidth="2" fill="none"/></svg>,
Clock: () => <svg className="w-6 h-6" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" strokeWidth={2}/>
<polyline points="12 6 12 12 16 14" strokeWidth={2}/></svg>,
TrendingUp: () => <svg className="w-6 h-6" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"
strokeWidth={2}/><polyline points="17 6 23 12 23 6"
strokeWidth={2}/></svg>,
ExternalLink: () => <svg className="w-6 h-6" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round"
strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0
002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>,
Loader2: () => <svg className="w-16 h-16 animate-spin"
fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12"
r="10" stroke="currentColor" strokeWidth="4" opacity="0.25"/>
<path d="M4 12a8 8 0 018-8" stroke="currentColor"
strokeWidth="4" strokeLinecap="round"/></svg>,
};
উপরের জিনিস গুলো হলো আইকন। SVG দিয়ে icon বানানো হয়েছে <Icons.Eye /> বা <Icons.Users /> এভাবে ব্যবহার
// Animated Counter
const AnimatedCounter = ({ value, suffix = '' }) => {
const [count, setCount] = useState(0);
useEffect(() => {
let start = 0;
const end = parseInt(value);
const duration = 2000;
const increment = end / (duration / 16);
const timer = setInterval(() => {
start += increment;
if (start > end) {
setCount(end);
clearInterval(timer);
} else setCount(Math.floor(start));
}, 16);
return () => clearInterval(timer);
}, [value]);
return <span className="text-4xl font-bold bg-gradient-to-r
from-blue-600 to-purple-600 bg-clip-text text-transparent">
{count.toLocaleString()}{suffix}</span>;
};
AnimatedCounter কী?
AnimatedCounter হলো একটি React component যা কোনো সংখ্যাকে ০ থেকে ধীরে ধীরে বাড়িয়ে নির্দিষ্ট মান পর্যন্ত এনিমেশনসহ দেখায়।
ধরো, value=1000 দিলে—এটা সরাসরি 1000 দেখাবে না, বরং 0 → 1 → 2 → … → 1000 এইভাবে বাড়তে থাকবে (animation effect)।
// Stats Card
const StatsCard = ({ title, value, Icon, trend, color = 'blue' })
=> {
const bg = {
blue: 'from-blue-500 to-blue-600',
green: 'from-emerald-500 to-emerald-600',
purple: 'from-purple-500 to-purple-600',
orange: 'from-orange-500 to-orange-600',
}[color];
<div className="grid grid-cols-1 sm:grid-cols-2
lg:grid-cols-4 gap-8 mb-12">
<StatsCard title="Total Visitors" value={125420}
Icon={Icons.Users} trend={12} color="blue" />
<StatsCard title="Unique Visitors" value={89234}
Icon={Icons.Eye} trend={8} color="green" />
<StatsCard title="New Visit" value={742105}
Icon={Icons.TrendingUp} trend={18} color="purple" />
<StatsCard title="Avg. Time (mins)" value={3}
Icon={Icons.Clock} trend={-5} color="orange" />
</div>
উপরের যে চারটি কার্ড সেগুলো কে কন্টল বানানোর জন্য এই কোডটি বানানো হয়।
চারটি গ্রিড এর জন্য বানানো হয়েছে। তাই এখানে flex justify-between items-start এটা ব্যবহার করা
হয়েছে। এখন Icon className="text-white" এটা দিয়ে আইকন এর কালার বানানো হয়েছে। ${trend > 0 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'} এটা দিয়ে শূন্য এর বেশি হলে bg-green-100 text-green-700 এটি কাজ করবে আর এর কম হলে অন্যটি যেমন bg-red-100 text-red-700 কাজ করবে।{trend !== undefined && ( আর বিশেষ করে যদি
trend এর ভ্যালু থাকে তাহলে (undefined না হয়) তাহলেই <span> দেখাবে। {trend > 0 ? 'Up' : 'Down'} {Math.abs(trend)}% trend return (
<div className="bg-white rounded-2xl shadow-xl
p-6 hover:shadow-2xl transition-all border
border-gray-100 w-[100%]">
<div className="flex justify-between items-start mb-4">
<div className={`p-4 rounded-xl bg-gradient-to-br ${bg}`}>
<Icon className="text-white" />
</div>
{trend !== undefined && (
<span className={`px-3 py-1 rounded-full text-sm
font-bold
${trend > 0 ? 'bg-green-100 text-green-700' :
'bg-red-100 text-red-700'}`}>
{trend > 0 ? 'Up' : 'Down'} {Math.abs(trend)}%
</span>
)}
</div>
<p className="text-gray-600 text-sm">{title}</p>
<div className="mt-2"><AnimatedCounter
value={value} suffix={title.includes('Time') ? 'm' : ''} />
</div>
</div>
);
};


0 Comments