40091-vm/src/pages/Home.tsx
2026-05-26 10:54:22 +00:00

166 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}