166 lines
8.3 KiB
TypeScript
166 lines
8.3 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
import {
|
||
DollarSign, CalendarCheck, UserPlus, Star,
|
||
Clock, TrendingUp, Eye
|
||
} from 'lucide-react';
|
||
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||
import PageLayout from '@/components/PageLayout';
|
||
import StatCard from '@/components/StatCard';
|
||
import StatusBadge from '@/components/StatusBadge';
|
||
import { dashboardStats, weeklyRevenue, bookings } from '@/data/demoData';
|
||
|
||
export default function Home() {
|
||
const [animateStats, setAnimateStats] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => setAnimateStats(true), 100);
|
||
return () => clearTimeout(timer);
|
||
}, []);
|
||
|
||
const stats = [
|
||
{ label: 'الإيرادات', value: `${animateStats ? dashboardStats.totalRevenue.toLocaleString() : '0'} ر.س`, change: dashboardStats.revenueChange, icon: DollarSign, color: 'bg-wine' },
|
||
{ label: 'الحجوزات', value: animateStats ? dashboardStats.totalBookings.toString() : '0', change: dashboardStats.bookingsChange, icon: CalendarCheck, color: 'bg-wine-light' },
|
||
{ label: 'عملاء جدد', value: animateStats ? dashboardStats.newClients.toString() : '0', change: dashboardStats.clientsChange, icon: UserPlus, color: 'bg-success' },
|
||
{ label: 'التقييم', value: animateStats ? dashboardStats.averageRating.toString() : '0', change: dashboardStats.ratingChange, icon: Star, color: 'bg-gold' },
|
||
];
|
||
|
||
const recentBookings = bookings.slice(0, 6);
|
||
|
||
const popularServices = [
|
||
{ name: 'صبغ الشعر', count: 45, percent: 90 },
|
||
{ name: 'قص الشعر', count: 38, percent: 76 },
|
||
{ name: 'تنظيف بشرة', count: 32, percent: 64 },
|
||
{ name: 'مانيكير', count: 28, percent: 56 },
|
||
{ name: 'مساج', count: 22, percent: 44 },
|
||
{ name: 'ميكاب', count: 18, percent: 36 },
|
||
];
|
||
|
||
return (
|
||
<PageLayout title="لوحة التحكم" subtitle="نظرة عامة على أداء صالونك">
|
||
{/* Welcome Banner */}
|
||
<div className="wine-gradient rounded-2xl p-6 mb-6 text-white relative overflow-hidden">
|
||
<div className="absolute left-0 top-0 w-40 h-40 bg-white/5 rounded-full -translate-x-10 -translate-y-10" />
|
||
<div className="absolute left-20 bottom-0 w-24 h-24 bg-white/5 rounded-full translate-y-10" />
|
||
<div className="relative z-10">
|
||
<h3 className="text-xl font-bold mb-1">أهلاً بكِ في BeautyHub 👋</h3>
|
||
<p className="text-white/80 text-sm">لديك 5 حجوزات اليوم وطلب توظيف جديد</p>
|
||
<div className="flex items-center gap-3 mt-4">
|
||
<div className="bg-white/15 rounded-lg px-4 py-2 text-sm backdrop-blur">
|
||
<span className="text-white/60">اليوم: </span>
|
||
<span className="font-semibold">5 حجوزات</span>
|
||
</div>
|
||
<div className="bg-white/15 rounded-lg px-4 py-2 text-sm backdrop-blur">
|
||
<span className="text-white/60">إيرادات اليوم: </span>
|
||
<span className="font-semibold">2,400 ر.س</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Stats Row */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||
{stats.map((s) => (
|
||
<StatCard key={s.label} label={s.label} value={s.value} change={s.change} icon={s.icon} iconColor={s.color} />
|
||
))}
|
||
</div>
|
||
|
||
{/* Two Column: Recent Bookings + Popular Services */}
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||
{/* Recent Bookings */}
|
||
<div className="bg-white rounded-2xl shadow-card border border-border-color/50 overflow-hidden">
|
||
<div className="flex items-center justify-between px-5 py-4 border-b border-border-color/50">
|
||
<h3 className="font-bold text-text-primary">أحدث الحجوزات</h3>
|
||
<button className="flex items-center gap-1 text-xs text-wine hover:text-wine-dark font-medium transition-colors">
|
||
<Eye className="w-3.5 h-3.5" />
|
||
عرض الكل
|
||
</button>
|
||
</div>
|
||
<div className="divide-y divide-border-color/30">
|
||
{recentBookings.map((b) => (
|
||
<div key={b.id} className="flex items-center gap-4 px-5 py-3.5 hover:bg-cream/50 transition-colors">
|
||
<div className="w-9 h-9 rounded-full bg-wine-100 flex items-center justify-center text-wine text-sm font-bold flex-shrink-0">
|
||
{b.clientName.charAt(0)}
|
||
</div>
|
||
<div className="flex-1 min-w-0">
|
||
<p className="text-sm font-semibold text-text-primary truncate">{b.clientName}</p>
|
||
<p className="text-xs text-text-muted">{b.services.join(' + ')}</p>
|
||
</div>
|
||
<div className="text-left flex-shrink-0">
|
||
<p className="text-sm font-bold text-gold-dark">{b.amount} ر.س</p>
|
||
<StatusBadge status={b.status} />
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Popular Services */}
|
||
<div className="bg-white rounded-2xl shadow-card border border-border-color/50 overflow-hidden">
|
||
<div className="flex items-center justify-between px-5 py-4 border-b border-border-color/50">
|
||
<h3 className="font-bold text-text-primary">الخدمات الأكثر طلباً</h3>
|
||
<div className="flex items-center gap-1 text-xs text-text-muted">
|
||
<TrendingUp className="w-3.5 h-3.5" />
|
||
هذا الشهر
|
||
</div>
|
||
</div>
|
||
<div className="px-5 py-4 space-y-4">
|
||
{popularServices.map((s) => (
|
||
<div key={s.name}>
|
||
<div className="flex items-center justify-between mb-1.5">
|
||
<span className="text-sm font-medium text-text-primary">{s.name}</span>
|
||
<span className="text-xs text-text-muted">{s.count} حجز</span>
|
||
</div>
|
||
<div className="w-full h-2 bg-cream rounded-full overflow-hidden">
|
||
<div
|
||
className="h-full rounded-full bg-gradient-to-l from-wine to-wine-light transition-all duration-1000"
|
||
style={{ width: `${s.percent}%` }}
|
||
/>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Weekly Revenue Chart */}
|
||
<div className="bg-white rounded-2xl shadow-card border border-border-color/50 p-5">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h3 className="font-bold text-text-primary">الإيرادات الأسبوعية</h3>
|
||
<div className="flex items-center gap-1 text-xs text-text-muted">
|
||
<Clock className="w-3.5 h-3.5" />
|
||
آخر 7 أيام
|
||
</div>
|
||
</div>
|
||
<div className="h-64">
|
||
<ResponsiveContainer width="100%" height="100%">
|
||
<AreaChart data={weeklyRevenue} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
|
||
<defs>
|
||
<linearGradient id="colorRevenue" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="5%" stopColor="#7B2D4B" stopOpacity={0.15} />
|
||
<stop offset="95%" stopColor="#7B2D4B" stopOpacity={0} />
|
||
</linearGradient>
|
||
</defs>
|
||
<CartesianGrid strokeDasharray="3 3" stroke="#E5DDD6" vertical={false} />
|
||
<XAxis dataKey="day" axisLine={false} tickLine={false} tick={{ fontSize: 12, fill: '#6B5B63' }} />
|
||
<YAxis axisLine={false} tickLine={false} tick={{ fontSize: 12, fill: '#6B5B63' }} />
|
||
<Tooltip
|
||
contentStyle={{ borderRadius: '12px', border: '1px solid #E5DDD6', boxShadow: '0 4px 12px rgba(0,0,0,0.08)' }}
|
||
formatter={(value: number) => [`${value.toLocaleString()} ر.س`, 'الإيرادات']}
|
||
/>
|
||
<Area
|
||
type="monotone"
|
||
dataKey="revenue"
|
||
stroke="#7B2D4B"
|
||
strokeWidth={2.5}
|
||
fill="url(#colorRevenue)"
|
||
dot={{ fill: '#D4A853', strokeWidth: 2, r: 4, stroke: '#fff' }}
|
||
activeDot={{ r: 6, fill: '#D4A853', stroke: '#7B2D4B', strokeWidth: 2 }}
|
||
/>
|
||
</AreaChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
</div>
|
||
</PageLayout>
|
||
);
|
||
}
|