Commit 932ff403 authored by Mahmoud Aglan's avatar Mahmoud Aglan

dd

parent 0c0bcb97
import { Outlet } from 'react-router-dom' import { Outlet } from 'react-router-dom'
import { Header } from './Header' import { Header } from './Header'
import { BottomNav } from './BottomNav' import { BottomNav } from './BottomNav'
import { DecorativeBackground } from './DecorativeBackground'
import { ToastContainer } from '../ui/ToastContainer' import { ToastContainer } from '../ui/ToastContainer'
import { usePresence } from '../../hooks/usePresence' import { usePresence } from '../../hooks/usePresence'
import { useNotifications } from '../../hooks/useNotifications' import { useNotifications } from '../../hooks/useNotifications'
...@@ -11,15 +10,12 @@ export function AppShell() { ...@@ -11,15 +10,12 @@ export function AppShell() {
useNotifications() useNotifications()
return ( return (
<div className="relative flex flex-col min-h-dvh overflow-hidden"> <div className="flex flex-col min-h-dvh bg-background">
<DecorativeBackground /> <Header />
<div className="relative z-10 flex flex-col min-h-dvh"> <main className="flex-1 overflow-y-auto">
<Header /> <Outlet />
<main className="flex-1 pb-28 overflow-y-auto"> </main>
<Outlet /> <BottomNav />
</main>
<BottomNav />
</div>
<ToastContainer /> <ToastContainer />
</div> </div>
) )
......
import { motion } from 'framer-motion'
import { Home, Gamepad2, Trophy, Users, User } from 'lucide-react' import { Home, Gamepad2, Trophy, Users, User } from 'lucide-react'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
...@@ -15,38 +14,25 @@ export function BottomNav() { ...@@ -15,38 +14,25 @@ export function BottomNav() {
const navigate = useNavigate() const navigate = useNavigate()
return ( return (
<nav className="fixed bottom-0 left-0 right-0 z-50 bg-surface-1 border-t border-border"> <nav className="fixed bottom-0 left-0 right-0 z-50 bg-background border-t border-border safe-area-pb">
<div className="flex items-center justify-around px-4 py-2 max-w-[480px] mx-auto"> <div className="flex items-stretch justify-around max-w-[480px] mx-auto">
{NAV_ITEMS.map((item) => { {NAV_ITEMS.map((item) => {
const isActive = location.pathname === item.path const isActive = location.pathname === item.path
const Icon = item.icon const Icon = item.icon
return ( return (
<motion.button <button
key={item.path} key={item.path}
onClick={() => navigate(item.path)} onClick={() => navigate(item.path)}
className="relative flex flex-col items-center gap-1 py-1 min-w-[44px] min-h-[44px] justify-center" className={`flex flex-col items-center justify-center gap-0.5 py-2 px-3 min-w-[56px] min-h-[56px] transition-colors ${
whileTap={{ scale: 0.9 }} isActive ? 'text-gold' : 'text-text-muted'
transition={{ duration: 0.15 }} }`}
> >
<Icon <Icon size={22} strokeWidth={isActive ? 2.5 : 1.8} />
size={22} <span className={`text-[10px] ${isActive ? 'font-semibold' : 'font-normal'}`}>
className={isActive ? 'text-gold' : 'text-text-muted'}
strokeWidth={isActive ? 2.5 : 2}
/>
<span
className={`text-[11px] font-semibold ${isActive ? 'text-gold' : 'text-text-muted'}`}
>
{item.label} {item.label}
</span> </span>
{isActive && ( </button>
<motion.div
className="absolute -bottom-2 w-6 h-[3px] rounded-full bg-gold"
layoutId="nav-indicator"
transition={{ duration: 0.2 }}
/>
)}
</motion.button>
) )
})} })}
</div> </div>
......
import { motion } from 'framer-motion'
import { Bell, Coins, Gem } from 'lucide-react' import { Bell, Coins, Gem } from 'lucide-react'
import { useNotificationStore } from '../../stores/notificationStore' import { useNotificationStore } from '../../stores/notificationStore'
import { useAuthStore } from '../../stores/authStore' import { useAuthStore } from '../../stores/authStore'
...@@ -10,55 +9,49 @@ export function Header() { ...@@ -10,55 +9,49 @@ export function Header() {
const navigate = useNavigate() const navigate = useNavigate()
return ( return (
<header className="sticky top-0 z-50 bg-surface-1/95 backdrop-blur-sm border-b border-border"> <header className="sticky top-0 z-50 bg-background border-b border-border">
<div className="app-container flex items-center justify-between h-14"> <div className="flex items-center justify-between h-14 px-4 max-w-[1440px] mx-auto">
{/* Logo */} {/* Logo */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<span className="text-lg font-bold text-gold tracking-wide"> <span className="text-xl font-bold tracking-tight text-text-primary">
EL3AB EL3AB
</span> </span>
{profile && ( {profile && (
<div className="flex items-center justify-center w-7 h-7 rounded-full bg-surface-2 border border-border"> <span className="text-xs font-medium text-text-muted bg-surface-2 px-2 py-0.5 rounded-[var(--radius-tiny)]">
<span className="text-[11px] font-bold text-gold"> Lv.{profile.level || 1}
{profile.level || 1} </span>
</span>
</div>
)} )}
</div> </div>
{/* Resources + Bell */} {/* Right: currency + notifications */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-2">
{profile && ( {profile && (
<> <>
<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-[var(--radius-tiny)] bg-surface-2 border border-border"> <div className="flex items-center gap-1.5 px-2.5 py-1.5 bg-surface-2 rounded-[var(--radius-tiny)]">
<Coins size={14} className="text-gold" /> <Coins size={14} className="text-gold" />
<span className="text-xs font-semibold text-text-primary">{profile.coins}</span> <span className="text-xs font-semibold text-text-primary tabular-nums">{profile.coins}</span>
</div> </div>
{profile.gems > 0 && ( {(profile.gems ?? 0) > 0 && (
<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-[var(--radius-tiny)] bg-surface-2 border border-border"> <div className="flex items-center gap-1.5 px-2.5 py-1.5 bg-surface-2 rounded-[var(--radius-tiny)]">
<Gem size={12} className="text-purple" /> <Gem size={12} className="text-purple" />
<span className="text-xs font-semibold text-text-primary">{profile.gems}</span> <span className="text-xs font-semibold text-text-primary tabular-nums">{profile.gems}</span>
</div> </div>
)} )}
</> </>
)} )}
<motion.button <button
className="relative flex items-center justify-center w-10 h-10 rounded-[var(--radius-tiny)] bg-surface-2 border border-border" className="relative flex items-center justify-center w-10 h-10 rounded-[var(--radius-tiny)] hover:bg-surface-2 transition-colors"
onClick={() => navigate('/notifications')} onClick={() => navigate('/notifications')}
whileTap={{ scale: 0.9 }}
transition={{ duration: 0.15 }}
> >
<Bell size={18} className="text-text-secondary" /> <Bell size={20} className="text-text-secondary" />
{unreadCount > 0 && ( {unreadCount > 0 && (
<div className="absolute -top-1 -right-1 w-5 h-5 rounded-full bg-coral flex items-center justify-center"> <span className="absolute top-1 right-1 w-4 h-4 rounded-full bg-coral text-[9px] font-bold text-white flex items-center justify-center">
<span className="text-[10px] font-bold text-white"> {unreadCount > 9 ? '9+' : unreadCount}
{unreadCount > 9 ? '9+' : unreadCount} </span>
</span>
</div>
)} )}
</motion.button> </button>
</div> </div>
</div> </div>
</header> </header>
......
...@@ -9,7 +9,7 @@ interface PageTransitionProps { ...@@ -9,7 +9,7 @@ interface PageTransitionProps {
export function PageTransition({ children, className = '' }: PageTransitionProps) { export function PageTransition({ children, className = '' }: PageTransitionProps) {
return ( return (
<motion.div <motion.div
className={`app-container py-6 pb-24 ${className}`} className={`px-4 py-6 pb-28 max-w-[1440px] mx-auto w-full md:px-6 lg:px-8 ${className}`}
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
......
...@@ -281,3 +281,7 @@ body { ...@@ -281,3 +281,7 @@ body {
.bg-surface-1 { background-color: var(--color-surface-1); } .bg-surface-1 { background-color: var(--color-surface-1); }
.bg-surface-2 { background-color: var(--color-surface-2); } .bg-surface-2 { background-color: var(--color-surface-2); }
.bg-surface-3 { background-color: var(--color-surface-3); } .bg-surface-3 { background-color: var(--color-surface-3); }
.safe-area-pb {
padding-bottom: env(safe-area-inset-bottom, 0px);
}
import { useState } from 'react' import { useState } from 'react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { Play, TrendingUp, Swords, Flame, Bot, Users, Lightbulb } from 'lucide-react' import { Play, TrendingUp, Swords, Flame, Bot, Users, Lightbulb, Trophy } from 'lucide-react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useAuthStore } from '../stores/authStore' import { useAuthStore } from '../stores/authStore'
import { PageTransition } from '../components/layout/PageTransition' import { PageTransition } from '../components/layout/PageTransition'
...@@ -18,16 +18,6 @@ const dailyTips = [ ...@@ -18,16 +18,6 @@ const dailyTips = [
'فكر في خطة خصمك قبل ان تلعب - لا تركز فقط على هجومك', 'فكر في خطة خصمك قبل ان تلعب - لا تركز فقط على هجومك',
] ]
const stagger = {
hidden: {},
show: { transition: { staggerChildren: 0.06 } },
}
const fadeUp = {
hidden: { opacity: 0, y: 12 },
show: { opacity: 1, y: 0, transition: { duration: 0.2 } },
}
export function HomePage() { export function HomePage() {
const navigate = useNavigate() const navigate = useNavigate()
const { profile } = useAuthStore() const { profile } = useAuthStore()
...@@ -42,6 +32,10 @@ export function HomePage() { ...@@ -42,6 +32,10 @@ export function HomePage() {
} }
} }
const winRate = profile && profile.total_games_played > 0
? Math.round((profile.total_wins / profile.total_games_played) * 100)
: 0
return ( return (
<PageTransition> <PageTransition>
<DailyRewardModal <DailyRewardModal
...@@ -53,122 +47,113 @@ export function HomePage() { ...@@ -53,122 +47,113 @@ export function HomePage() {
onClose={() => setShowDailyModal(false)} onClose={() => setShowDailyModal(false)}
/> />
<motion.div <div className="flex flex-col gap-8">
variants={stagger}
initial="hidden"
animate="show"
className="flex flex-col gap-6"
>
{/* Welcome */} {/* Welcome */}
{profile && ( {profile && (
<motion.div variants={fadeUp} className="flex items-center gap-3"> <section className="flex items-center gap-4">
<div className="w-12 h-12 rounded-[var(--radius-standard)] bg-surface-2 border border-border flex items-center justify-center"> <div className="w-14 h-14 rounded-full bg-surface-2 flex items-center justify-center text-xl font-bold text-gold">
<span className="text-lg font-bold text-gold"> {profile.display_name?.charAt(0) || '?'}
{profile.display_name?.charAt(0) || 'L'}
</span>
</div> </div>
<div> <div className="flex-1 min-w-0">
<h2 className="text-xl font-bold text-text-primary"> <h1 className="text-xl font-bold text-text-primary truncate">
{profile.display_name} اهلا {profile.display_name}
</h2> </h1>
<span className="text-sm text-text-muted"> <p className="text-sm text-text-muted">
المستوى {profile.level} المستوى {profile.level} · {profile.elo_blitz} تقييم
</span> </p>
</div> </div>
</motion.div> </section>
)} )}
{/* Play Button */} {/* Primary CTA */}
<motion.div variants={fadeUp}> <section>
<motion.button <motion.button
onClick={() => { onClick={() => { playSound('tap'); navigate('/play') }}
playSound('tap') className="w-full flex items-center justify-center gap-3 py-5 bg-gold rounded-[var(--radius-large)] text-background text-xl font-bold"
navigate('/play') whileTap={{ scale: 0.97 }}
}}
className="btn-gold w-full py-6 rounded-[var(--radius-hero)] text-2xl font-bold"
whileTap={{ scale: 0.96, y: 2 }}
transition={{ duration: 0.15 }} transition={{ duration: 0.15 }}
> >
<Play size={32} fill="currentColor" /> <Play size={28} fill="currentColor" />
<span>العب الان</span> العب الان
</motion.button> </motion.button>
</motion.div> </section>
{/* Stats */} {/* Stats row */}
{profile && ( {profile && (
<motion.div variants={fadeUp} className="grid grid-cols-3 gap-3"> <section className="grid grid-cols-3 gap-3">
<StatCard <div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 text-center">
icon={<TrendingUp size={18} className="text-gold" />} <TrendingUp size={18} className="text-gold mx-auto mb-1" />
value={profile.elo_blitz} <p className="text-lg font-bold text-text-primary tabular-nums">{profile.elo_blitz}</p>
label="تقييم" <p className="text-[11px] text-text-muted">تقييم</p>
/> </div>
<StatCard <div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 text-center">
icon={<Swords size={18} className="text-cyan" />} <Swords size={18} className="text-cyan mx-auto mb-1" />
value={profile.total_games_played} <p className="text-lg font-bold text-text-primary tabular-nums">{profile.total_games_played}</p>
label="مباراة" <p className="text-[11px] text-text-muted">مباراة</p>
/> </div>
<StatCard <div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 text-center">
icon={<Flame size={18} className="text-coral" />} <Flame size={18} className="text-coral mx-auto mb-1" />
value={profile.win_streak} <p className="text-lg font-bold text-text-primary tabular-nums">{winRate}%</p>
label="سلسلة فوز" <p className="text-[11px] text-text-muted">نسبة الفوز</p>
/> </div>
</motion.div> </section>
)} )}
{/* Quick Actions */} {/* Quick Actions */}
<motion.div variants={fadeUp} className="grid grid-cols-2 gap-4"> <section className="grid grid-cols-2 gap-3">
<QuickAction <button
icon={<Bot size={24} className="text-purple" />}
title="العب ضد روبوت"
subtitle="تدريب وتحسين"
onClick={() => { playSound('tap'); navigate('/bot-select') }} onClick={() => { playSound('tap'); navigate('/bot-select') }}
/> className="bg-surface-2 rounded-[var(--radius-standard)] p-5 flex flex-col items-center gap-3 hover:bg-surface-3 transition-colors"
<QuickAction >
icon={<Users size={24} className="text-gold" />} <Bot size={28} className="text-purple" />
title="تحدى صديق" <div className="text-center">
subtitle="ارسل دعوة" <p className="text-sm font-semibold text-text-primary">ضد الروبوت</p>
<p className="text-[11px] text-text-muted">تدريب</p>
</div>
</button>
<button
onClick={() => { playSound('tap'); navigate('/friends') }} onClick={() => { playSound('tap'); navigate('/friends') }}
/> className="bg-surface-2 rounded-[var(--radius-standard)] p-5 flex flex-col items-center gap-3 hover:bg-surface-3 transition-colors"
</motion.div> >
<Users size={28} className="text-royal-blue" />
<div className="text-center">
<p className="text-sm font-semibold text-text-primary">تحدى صديق</p>
<p className="text-[11px] text-text-muted">دعوة</p>
</div>
</button>
<button
onClick={() => { playSound('tap'); navigate('/tournaments') }}
className="bg-surface-2 rounded-[var(--radius-standard)] p-5 flex flex-col items-center gap-3 hover:bg-surface-3 transition-colors"
>
<Trophy size={28} className="text-gold" />
<div className="text-center">
<p className="text-sm font-semibold text-text-primary">البطولات</p>
<p className="text-[11px] text-text-muted">تنافس</p>
</div>
</button>
<button
onClick={() => { playSound('tap'); navigate('/leaderboard') }}
className="bg-surface-2 rounded-[var(--radius-standard)] p-5 flex flex-col items-center gap-3 hover:bg-surface-3 transition-colors"
>
<TrendingUp size={28} className="text-cyan" />
<div className="text-center">
<p className="text-sm font-semibold text-text-primary">المتصدرين</p>
<p className="text-[11px] text-text-muted">ترتيب</p>
</div>
</button>
</section>
{/* Daily Tip */} {/* Daily Tip */}
<motion.div variants={fadeUp} className="card"> <section className="bg-surface-2 rounded-[var(--radius-standard)] p-4">
<div className="flex items-center gap-2 mb-3"> <div className="flex items-center gap-2 mb-2">
<Lightbulb size={16} className="text-gold" /> <Lightbulb size={16} className="text-gold" />
<span className="text-sm font-semibold text-gold">نصيحة اليوم</span> <span className="text-sm font-semibold text-text-secondary">نصيحة اليوم</span>
</div> </div>
<p className="text-sm text-text-secondary leading-relaxed"> <p className="text-sm text-text-secondary leading-relaxed">
{todayTip} {todayTip}
</p> </p>
</motion.div> </section>
</motion.div>
</PageTransition>
)
}
function StatCard({ icon, value, label }: { icon: React.ReactNode; value: number; label: string }) {
return (
<div className="card flex flex-col items-center gap-1 py-3">
{icon}
<span className="text-lg font-bold text-text-primary">{value}</span>
<span className="text-[11px] text-text-muted">{label}</span>
</div>
)
}
function QuickAction({ icon, title, subtitle, onClick }: { icon: React.ReactNode; title: string; subtitle: string; onClick: () => void }) {
return (
<motion.button
className="card flex flex-col items-center gap-2 py-5 cursor-pointer"
onClick={onClick}
whileTap={{ scale: 0.96 }}
transition={{ duration: 0.15 }}
>
{icon}
<div className="text-center">
<p className="text-sm font-semibold text-text-primary">{title}</p>
<p className="text-[11px] text-text-muted mt-0.5">{subtitle}</p>
</div> </div>
</motion.button> </PageTransition>
) )
} }
...@@ -13,16 +13,6 @@ const CATEGORIES = [ ...@@ -13,16 +13,6 @@ const CATEGORIES = [
{ key: 'classical', label: 'كلاسيكي', icon: Hourglass }, { key: 'classical', label: 'كلاسيكي', icon: Hourglass },
] as const ] as const
const stagger = {
hidden: {},
show: { transition: { staggerChildren: 0.06 } },
}
const fadeUp = {
hidden: { opacity: 0, y: 12 },
show: { opacity: 1, y: 0, transition: { duration: 0.2 } },
}
export function PlayPage() { export function PlayPage() {
const navigate = useNavigate() const navigate = useNavigate()
const [selectedTC, setSelectedTC] = useState<string>('blitz_5_0') const [selectedTC, setSelectedTC] = useState<string>('blitz_5_0')
...@@ -36,68 +26,56 @@ export function PlayPage() { ...@@ -36,68 +26,56 @@ export function PlayPage() {
return ( return (
<PageTransition> <PageTransition>
<motion.div <div className="flex flex-col gap-8">
variants={stagger}
initial="hidden"
animate="show"
className="flex flex-col gap-6"
>
{/* Page Title */} {/* Page Title */}
<motion.div variants={fadeUp}> <h1 className="text-2xl font-bold text-text-primary">العب</h1>
<h1 className="text-2xl font-bold text-text-primary">اختر اللعبة</h1>
</motion.div>
{/* Featured Game: Chess */} {/* Game Selection */}
<motion.div variants={fadeUp} className="card border-gold/20"> <section className="flex flex-col gap-3">
<div className="flex items-center gap-4"> {/* Chess - primary */}
<div className="w-14 h-14 rounded-[var(--radius-standard)] bg-gold/10 border border-gold/20 flex items-center justify-center"> <div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 flex items-center gap-4 border border-gold/20">
<span className="text-2xl">♟️</span> <div className="w-12 h-12 rounded-[var(--radius-tiny)] bg-gold/10 flex items-center justify-center text-2xl">
♟️
</div> </div>
<div className="flex-1"> <div className="flex-1">
<span className="text-lg font-bold text-text-primary">{chessGame.nameAr}</span> <p className="font-semibold text-text-primary">{chessGame.nameAr}</p>
<p className="text-sm text-text-secondary mt-0.5"> <div className="flex items-center gap-1.5 mt-1">
العب شطرنج اونلاين ضد لاعبين حقيقيين <span className="w-2 h-2 rounded-full bg-online" />
</p> <span className="text-xs text-text-muted">متاح الان</span>
<div className="flex items-center gap-2 mt-2">
<div className="w-2 h-2 rounded-full bg-online animate-pulse-soft" />
<span className="text-xs text-online font-medium">اونلاين</span>
</div> </div>
</div> </div>
</div> </div>
</motion.div>
{/* Other Games (locked) */} {/* Other games - locked row */}
<motion.div variants={fadeUp} className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-4 gap-2">
{otherGames.map((game) => ( {otherGames.map((game) => (
<div <div
key={game.key} key={game.key}
className="card flex items-center gap-2 py-3 px-3 opacity-50" className="bg-surface-2 rounded-[var(--radius-tiny)] py-3 flex flex-col items-center gap-1 opacity-40"
> >
<Lock size={14} className="text-text-muted" /> <Lock size={14} className="text-text-muted" />
<div> <span className="text-[10px] text-text-muted">{game.nameAr}</span>
<span className="text-sm font-medium text-text-muted block">{game.nameAr}</span>
<span className="text-[11px] text-text-muted">قريبا</span>
</div> </div>
</div> ))}
))} </div>
</motion.div> </section>
{/* Time Control */} {/* Time Control */}
<motion.div variants={fadeUp} className="space-y-4"> <section className="flex flex-col gap-4">
<h2 className="text-lg font-bold text-text-primary">نظام الوقت</h2> <h2 className="text-lg font-semibold text-text-primary">الوقت</h2>
{/* Category Tabs */} {/* Category tabs */}
<div className="flex gap-2"> <div className="flex gap-2 overflow-x-auto">
{CATEGORIES.map((cat) => { {CATEGORIES.map((cat) => {
const isActive = activeCategory === cat.key const isActive = activeCategory === cat.key
const Icon = cat.icon const Icon = cat.icon
return ( return (
<button <button
key={cat.key} key={cat.key}
className={`flex items-center gap-1.5 px-4 py-2 rounded-[var(--radius-tiny)] text-sm font-semibold transition-all ${ className={`flex items-center gap-1.5 px-3 py-2 rounded-[var(--radius-tiny)] text-sm font-medium whitespace-nowrap transition-colors ${
isActive isActive
? 'bg-gold text-background' ? 'bg-gold text-background'
: 'bg-surface-2 text-text-muted border border-border' : 'bg-surface-2 text-text-muted'
}`} }`}
onClick={() => { onClick={() => {
playSound('tap') playSound('tap')
...@@ -112,39 +90,37 @@ export function PlayPage() { ...@@ -112,39 +90,37 @@ export function PlayPage() {
})} })}
</div> </div>
{/* Time Options Grid */} {/* Time options */}
<div className="grid grid-cols-3 gap-3"> <div className="grid grid-cols-3 gap-2">
{Object.entries(TIME_CONTROLS) {Object.entries(TIME_CONTROLS)
.filter(([, v]) => v.category === activeCategory) .filter(([, v]) => v.category === activeCategory)
.map(([key, tc]) => { .map(([key, tc]) => {
const isSelected = selectedTC === key const isSelected = selectedTC === key
return ( return (
<motion.button <button
key={key} key={key}
className={`py-4 px-3 text-center font-bold rounded-[var(--radius-standard)] border transition-all ${ className={`py-4 text-center font-semibold rounded-[var(--radius-standard)] transition-all ${
isSelected isSelected
? 'border-gold bg-gold/10 text-gold' ? 'bg-gold/10 border-2 border-gold text-gold'
: 'border-border bg-surface-1 text-text-secondary' : 'bg-surface-2 border border-transparent text-text-secondary hover:bg-surface-3'
}`} }`}
whileTap={{ scale: 0.95 }}
transition={{ duration: 0.15 }}
onClick={() => { onClick={() => {
playSound('tap') playSound('tap')
setSelectedTC(key) setSelectedTC(key)
}} }}
> >
{tc.labelAr} {tc.labelAr}
</motion.button> </button>
) )
})} })}
</div> </div>
</motion.div> </section>
{/* Action Buttons */} {/* Actions */}
<motion.div variants={fadeUp} className="flex flex-col gap-3 mt-2"> <section className="flex flex-col gap-3">
<motion.button <motion.button
className="btn-gold w-full py-5 rounded-[var(--radius-large)] text-lg font-bold" className="w-full flex items-center justify-center gap-3 py-5 bg-gold rounded-[var(--radius-large)] text-background text-lg font-bold"
whileTap={{ scale: 0.96, y: 2 }} whileTap={{ scale: 0.97 }}
transition={{ duration: 0.15 }} transition={{ duration: 0.15 }}
onClick={() => { onClick={() => {
playSound('tap') playSound('tap')
...@@ -152,12 +128,12 @@ export function PlayPage() { ...@@ -152,12 +128,12 @@ export function PlayPage() {
}} }}
> >
<Swords size={22} /> <Swords size={22} />
<span>البحث عن خصم</span> البحث عن خصم
</motion.button> </motion.button>
<motion.button <motion.button
className="btn-ghost w-full py-4 rounded-[var(--radius-large)] text-base font-semibold" className="w-full flex items-center justify-center gap-3 py-4 bg-surface-2 border border-border rounded-[var(--radius-large)] text-text-primary text-base font-medium"
whileTap={{ scale: 0.96 }} whileTap={{ scale: 0.97 }}
transition={{ duration: 0.15 }} transition={{ duration: 0.15 }}
onClick={() => { onClick={() => {
playSound('tap') playSound('tap')
...@@ -165,10 +141,10 @@ export function PlayPage() { ...@@ -165,10 +141,10 @@ export function PlayPage() {
}} }}
> >
<Cpu size={20} className="text-purple" /> <Cpu size={20} className="text-purple" />
<span>العب ضد الروبوت</span> ضد الروبوت
</motion.button> </motion.button>
</motion.div> </section>
</motion.div> </div>
</PageTransition> </PageTransition>
) )
} }
import { useState } from 'react' import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion' import { motion, AnimatePresence } from 'framer-motion'
import { Trophy, Target, Flame, TrendingUp, Pencil, X, LogOut, Star, Shield } from 'lucide-react' import { Trophy, Target, Flame, TrendingUp, Pencil, X, LogOut, Swords } from 'lucide-react'
import { useAuthStore } from '../stores/authStore' import { useAuthStore } from '../stores/authStore'
import { useNotificationStore } from '../stores/notificationStore' import { useNotificationStore } from '../stores/notificationStore'
import { PageTransition } from '../components/layout/PageTransition' import { PageTransition } from '../components/layout/PageTransition'
import { Button } from '../components/ui/Button' import { Button } from '../components/ui/Button'
import { supabase } from '../lib/supabase' import { supabase } from '../lib/supabase'
const stagger = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { staggerChildren: 0.08 } },
}
const item = {
hidden: { opacity: 0, y: 16, scale: 0.93 },
show: { opacity: 1, y: 0, scale: 1, transition: { type: 'spring' as const, stiffness: 380, damping: 22 } },
}
const HEX_CLIP = 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)'
export function ProfilePage() { export function ProfilePage() {
const { user, profile, setProfile } = useAuthStore() const { user, profile, setProfile } = useAuthStore()
const { showToast } = useNotificationStore() const { showToast } = useNotificationStore()
...@@ -78,366 +66,161 @@ export function ProfilePage() { ...@@ -78,366 +66,161 @@ export function ProfilePage() {
const xpPercent = Math.min((currentXpInLevel / xpForNextLevel) * 100, 100) const xpPercent = Math.min((currentXpInLevel / xpForNextLevel) * 100, 100)
const ratings = [ const ratings = [
{ label: 'رصاصة', value: profile.elo_bullet, color: '#FF5252', borderColor: 'border-[#FF5252]' }, { label: 'رصاصة', value: profile.elo_bullet, icon: '⚡' },
{ label: 'خاطف', value: profile.elo_blitz, color: '#FFC83D', borderColor: 'border-[#FFC83D]' }, { label: 'خاطف', value: profile.elo_blitz, icon: '🔥' },
{ label: 'سريع', value: profile.elo_rapid, color: '#00E5CC', borderColor: 'border-[#00E5CC]' }, { label: 'سريع', value: profile.elo_rapid, icon: '⏱' },
{ label: 'كلاسيكي', value: profile.elo_classical, color: '#B44DFF', borderColor: 'border-[#B44DFF]' }, { label: 'كلاسيكي', value: profile.elo_classical, icon: '♟️' },
]
const stats = [
{ icon: <TrendingUp size={20} className="text-[#B44DFF]" />, value: profile.total_games_played, label: 'مباريات', ringColor: '#B44DFF' },
{ icon: <Trophy size={20} className="text-[#FFC83D]" />, value: profile.total_wins, label: 'انتصارات', ringColor: '#FFC83D' },
{ icon: <Target size={20} className="text-[#00E5CC]" />, value: `${winRate}%`, label: 'نسبة الفوز', ringColor: '#00E5CC' },
{ icon: <Flame size={20} className="text-[#FF5252]" />, value: profile.best_win_streak, label: 'افضل سلسلة', ringColor: '#FF5252' },
] ]
return ( return (
<PageTransition> <PageTransition>
<motion.div <div className="flex flex-col gap-8">
className="flex flex-col gap-6" {/* Profile Header */}
variants={stagger} <section className="flex flex-col items-center gap-4">
initial="hidden" {/* Avatar */}
animate="show" <div className="w-20 h-20 rounded-full bg-surface-2 border-2 border-border flex items-center justify-center">
> <span className="text-3xl font-bold text-gold">
{/* === PLAYER CARD / NAMEPLATE === */} {profile.display_name?.charAt(0) || '?'}
<motion.div variants={item}> </span>
<div
className="relative overflow-hidden rounded-[20px] p-5"
style={{
border: '3px solid var(--color-border)',
background: 'linear-gradient(135deg, rgba(255,200,60,0.05) 0%, rgba(180,77,255,0.05) 100%)',
boxShadow: 'inset 0 2px 4px rgba(255,255,255,0.04), inset 0 -2px 6px rgba(0,0,0,0.3), 0 6px 20px rgba(0,0,0,0.4)',
}}
>
{/* Decorative corner stars */}
<Star size={24} className="absolute top-3 left-3 text-[#FFC83D] opacity-[0.08]" />
<Star size={18} className="absolute top-5 left-10 text-[#FFC83D] opacity-[0.06]" />
<Star size={20} className="absolute bottom-4 right-4 text-[#B44DFF] opacity-[0.07]" />
<Star size={14} className="absolute bottom-6 right-10 text-[#B44DFF] opacity-[0.05]" />
{/* Subtle background pattern */}
<div
className="absolute inset-0 opacity-[0.02]"
style={{
backgroundImage: 'repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(255,200,60,0.5) 10px, rgba(255,200,60,0.5) 11px)',
}}
/>
<div className="relative flex flex-col items-center gap-3">
{/* Hexagonal Avatar */}
<div className="relative">
<motion.div
className="flex items-center justify-center"
style={{
width: 80,
height: 80,
clipPath: HEX_CLIP,
background: 'linear-gradient(135deg, rgba(255,200,60,0.25), rgba(180,77,255,0.15))',
border: 'none',
}}
initial={{ scale: 0, rotate: -30 }}
animate={{ scale: 1, rotate: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 18 }}
>
<div
className="flex items-center justify-center"
style={{
width: 74,
height: 74,
clipPath: HEX_CLIP,
background: 'linear-gradient(135deg, var(--color-surface-2), var(--color-surface-3))',
}}
>
<span className="text-3xl font-black text-[#FFC83D]">
{profile.display_name?.charAt(0) || '?'}
</span>
</div>
</motion.div>
{/* Gold border hex overlay */}
<div
className="absolute inset-0 pointer-events-none"
style={{
clipPath: HEX_CLIP,
border: '3px solid #FFC83D',
width: 80,
height: 80,
}}
/>
{/* Level badge */}
<motion.div
className="absolute -bottom-2 left-1/2 -translate-x-1/2 w-7 h-7 rounded-full flex items-center justify-center"
style={{
background: 'linear-gradient(to bottom, #FFE066, #FFC83D)',
border: '2px solid var(--color-background)',
boxShadow: '0 2px 8px rgba(255,200,60,0.4)',
}}
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 500, damping: 20, delay: 0.2 }}
>
<span className="text-[10px] font-black text-[#0B0E1A]">{profile.level}</span>
</motion.div>
</div>
{/* Player name + username */}
<div className="text-center mt-1">
<h1 className="text-2xl font-black tracking-tight">{profile.display_name}</h1>
<p className="text-sm text-text-muted font-bold">@{profile.username}</p>
{profile.bio && (
<p className="text-xs text-text-secondary mt-1.5 max-w-[240px] mx-auto line-clamp-2 leading-relaxed">{profile.bio}</p>
)}
</div>
{/* XP Progress bar */}
<div className="w-full mt-2">
<div className="flex items-center justify-between mb-1.5">
<span className="text-xs font-black text-text-secondary">المستوى {profile.level}</span>
<span className="text-xs font-black text-[#FFC83D]">XP {currentXpInLevel}</span>
</div>
<div
className="w-full overflow-hidden"
style={{
height: 16,
borderRadius: 10,
border: '2px solid var(--color-border)',
background: 'var(--color-surface-3)',
boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.4)',
}}
>
<motion.div
className="h-full relative"
style={{
borderRadius: 8,
background: 'linear-gradient(to left, #FFC83D, #FFE066)',
backgroundImage: 'repeating-linear-gradient(90deg, transparent, transparent 8px, rgba(255,255,255,0.1) 8px, rgba(255,255,255,0.1) 9px)',
}}
initial={{ width: 0 }}
animate={{ width: `${xpPercent}%` }}
transition={{ duration: 1.2, ease: 'easeOut', delay: 0.4 }}
/>
</div>
</div>
</div>
</div> </div>
</motion.div>
{/* === RATINGS SECTION === */} {/* Name + username */}
<motion.div variants={item} className="flex flex-col gap-3"> <div className="text-center">
{/* Section header */} <h1 className="text-2xl font-bold text-text-primary">{profile.display_name}</h1>
<div className="flex items-center gap-3"> <p className="text-sm text-text-muted mt-0.5">@{profile.username}</p>
<h2 className="text-lg font-black">التقييمات</h2> {profile.bio && (
<div className="flex-1 h-[2px]" style={{ background: 'linear-gradient(to left, transparent, rgba(255,200,60,0.4))' }} /> <p className="text-sm text-text-secondary mt-2 max-w-[280px]">{profile.bio}</p>
)}
</div> </div>
{/* 2x2 Rating grid */} {/* XP Bar */}
<div className="grid grid-cols-2 gap-3"> <div className="w-full max-w-[300px]">
{ratings.map((rating, i) => ( <div className="flex items-center justify-between mb-1.5">
<span className="text-xs text-text-muted">المستوى {profile.level}</span>
<span className="text-xs text-text-muted tabular-nums">{currentXpInLevel}/{xpForNextLevel} XP</span>
</div>
<div className="h-2 bg-surface-3 rounded-full overflow-hidden">
<motion.div <motion.div
key={rating.label} className="h-full bg-gold rounded-full"
className="relative flex flex-col items-center justify-center overflow-hidden" initial={{ width: 0 }}
style={{ animate={{ width: `${xpPercent}%` }}
height: 90, transition={{ duration: 0.8, ease: 'easeOut' }}
clipPath: HEX_CLIP, />
border: `3px solid ${rating.color}`, </div>
background: `linear-gradient(to bottom, ${rating.color}10, var(--color-surface-1))`,
boxShadow: `inset 0 2px 6px rgba(0,0,0,0.3), 0 0 12px ${rating.color}15`,
}}
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ type: 'spring', stiffness: 400, damping: 22, delay: 0.2 + i * 0.07 }}
>
{/* Inner hex to create border effect */}
<div
className="absolute inset-0 flex flex-col items-center justify-center"
style={{
borderTop: `4px solid ${rating.color}`,
}}
>
<span className="text-2xl font-black mt-2">{rating.value}</span>
<span className="text-[10px] font-bold text-text-muted">{rating.label}</span>
</div>
</motion.div>
))}
</div>
</motion.div>
{/* === STATS SECTION === */}
<motion.div variants={item} className="flex flex-col gap-3">
{/* Section header */}
<div className="flex items-center gap-3">
<h2 className="text-lg font-black">الاحصائيات</h2>
<div className="flex-1 h-[2px]" style={{ background: 'linear-gradient(to left, transparent, rgba(0,229,204,0.4))' }} />
</div> </div>
</section>
{/* 4 circular medal items */} {/* Ratings */}
<div className="flex items-center justify-between gap-2"> <section>
{stats.map((stat, i) => ( <h2 className="text-lg font-semibold text-text-primary mb-3">التقييمات</h2>
<motion.div <div className="grid grid-cols-2 gap-3">
key={stat.label} {ratings.map((r) => (
className="flex flex-col items-center gap-1.5" <div key={r.label} className="bg-surface-2 rounded-[var(--radius-standard)] p-4 flex items-center gap-3">
initial={{ opacity: 0, y: 12 }} <span className="text-xl">{r.icon}</span>
animate={{ opacity: 1, y: 0 }} <div>
transition={{ type: 'spring', stiffness: 400, damping: 22, delay: 0.25 + i * 0.07 }} <p className="text-lg font-bold text-text-primary tabular-nums">{r.value}</p>
> <p className="text-xs text-text-muted">{r.label}</p>
{/* Circular badge with ring */}
<div
className="relative flex items-center justify-center"
style={{
width: 64,
height: 64,
borderRadius: '50%',
border: `3px solid ${stat.ringColor}`,
background: `linear-gradient(135deg, ${stat.ringColor}12, var(--color-surface-2))`,
boxShadow: `inset 0 2px 6px rgba(0,0,0,0.3), 0 0 8px ${stat.ringColor}15`,
}}
>
<div className="flex flex-col items-center gap-0.5">
{stat.icon}
<span className="text-xs font-black mt-0.5">{stat.value}</span>
</div>
</div> </div>
<span className="text-[9px] text-text-muted font-bold text-center leading-tight">{stat.label}</span> </div>
</motion.div>
))} ))}
</div> </div>
</motion.div> </section>
{/* Stats */}
<section>
<h2 className="text-lg font-semibold text-text-primary mb-3">الاحصائيات</h2>
<div className="grid grid-cols-4 gap-2">
<StatItem icon={<Swords size={18} className="text-text-secondary" />} value={profile.total_games_played} label="مباريات" />
<StatItem icon={<Trophy size={18} className="text-gold" />} value={profile.total_wins} label="فوز" />
<StatItem icon={<Target size={18} className="text-cyan" />} value={`${winRate}%`} label="نسبة" />
<StatItem icon={<Flame size={18} className="text-coral" />} value={profile.best_win_streak} label="سلسلة" />
</div>
</section>
{/* === ACTION BUTTONS === */} {/* Actions */}
<motion.div className="flex flex-col items-center gap-3 mt-1" variants={item}> <section className="flex flex-col gap-3">
{/* Edit profile button */} <button
<motion.button
className="w-[75%] flex items-center justify-center gap-2.5 py-3.5 rounded-2xl font-black text-sm"
style={{
border: '3px solid var(--color-border)',
background: 'var(--color-surface-2)',
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.04), inset 0 -2px 4px rgba(0,0,0,0.2), 0 4px 12px rgba(0,0,0,0.3)',
}}
whileTap={{ scale: 0.94, y: 2 }}
whileHover={{ scale: 1.02, borderColor: 'rgba(255,200,60,0.5)' }}
onClick={openEdit} onClick={openEdit}
className="w-full flex items-center justify-center gap-2 py-3 bg-surface-2 border border-border rounded-[var(--radius-standard)] text-sm font-medium text-text-primary hover:bg-surface-3 transition-colors"
> >
<Pencil size={16} className="text-[#FFC83D]" /> <Pencil size={16} className="text-text-secondary" />
<span>تعديل الملف الشخصي</span> تعديل الملف الشخصي
</motion.button> </button>
{/* Logout button */} <button
<motion.button
className="w-[60%] flex items-center justify-center gap-2 py-2.5 rounded-xl font-bold text-xs"
style={{
border: '2px solid rgba(255,82,82,0.4)',
background: 'rgba(255,82,82,0.08)',
color: '#FF5252',
boxShadow: '0 2px 8px rgba(255,82,82,0.15)',
}}
whileTap={{ scale: 0.92, y: 1 }}
onClick={handleLogout} onClick={handleLogout}
className="w-full flex items-center justify-center gap-2 py-3 rounded-[var(--radius-standard)] text-sm font-medium text-coral hover:bg-coral/10 transition-colors"
> >
<LogOut size={14} /> <LogOut size={16} />
<span>تسجيل الخروج</span> تسجيل الخروج
</motion.button> </button>
</motion.div> </section>
</motion.div> </div>
{/* === EDIT MODAL === */} {/* Edit Modal */}
<AnimatePresence> <AnimatePresence>
{editOpen && ( {editOpen && (
<motion.div <motion.div
className="fixed inset-0 z-50 flex items-center justify-center px-6" className="fixed inset-0 z-50 flex items-center justify-center px-4"
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
> >
<motion.div <div
className="absolute inset-0 bg-background/85 backdrop-blur-sm" className="absolute inset-0 bg-background/80 backdrop-blur-sm"
onClick={() => setEditOpen(false)} onClick={() => setEditOpen(false)}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/> />
<motion.div <motion.div
className="relative w-full max-w-[360px] flex flex-col gap-5 overflow-hidden" className="relative w-full max-w-[360px] bg-surface-1 rounded-[var(--radius-hero)] border border-border p-6 flex flex-col gap-5"
style={{ style={{ boxShadow: 'var(--shadow-3)' }}
borderRadius: 20, initial={{ scale: 0.95, opacity: 0 }}
border: '3px solid var(--color-border)', animate={{ scale: 1, opacity: 1 }}
background: 'var(--color-surface-1)', exit={{ scale: 0.95, opacity: 0 }}
boxShadow: 'inset 0 2px 4px rgba(255,255,255,0.03), inset 0 -2px 8px rgba(0,0,0,0.3), 0 16px 48px rgba(0,0,0,0.6)', transition={{ duration: 0.2 }}
}}
initial={{ scale: 0.85, opacity: 0, y: 30 }}
animate={{ scale: 1, opacity: 1, y: 0 }}
exit={{ scale: 0.85, opacity: 0, y: 30 }}
transition={{ type: 'spring', stiffness: 400, damping: 22 }}
> >
{/* Ribbon banner header */} <div className="flex items-center justify-between">
<div <h2 className="text-lg font-bold">تعديل الملف</h2>
className="relative flex items-center justify-center py-3" <button
style={{ onClick={() => setEditOpen(false)}
background: 'linear-gradient(to bottom, rgba(255,200,60,0.12), transparent)', className="p-2 rounded-[var(--radius-tiny)] hover:bg-surface-2"
borderBottom: '2px solid var(--color-border)', >
clipPath: 'polygon(0 0, 100% 0, 95% 100%, 5% 100%)', <X size={16} className="text-text-muted" />
}} </button>
>
<Shield size={18} className="text-[#FFC83D] ml-2" />
<h2 className="text-lg font-black">تعديل الملف</h2>
</div> </div>
{/* Close button */} <div className="flex flex-col gap-4">
<motion.button
whileTap={{ scale: 0.85 }}
onClick={() => setEditOpen(false)}
className="absolute top-3 left-3 p-2 rounded-xl"
style={{
background: 'var(--color-surface-3)',
border: '2px solid var(--color-border)',
}}
>
<X size={14} className="text-text-muted" />
</motion.button>
{/* Form */}
<div className="flex flex-col gap-4 px-5 pb-5">
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<label className="text-xs font-black text-text-secondary">الاسم</label> <label className="text-xs font-medium text-text-secondary">الاسم</label>
<input <input
type="text" type="text"
value={editForm.display_name} value={editForm.display_name}
onChange={(e) => setEditForm({ ...editForm, display_name: e.target.value })} onChange={(e) => setEditForm({ ...editForm, display_name: e.target.value })}
className="px-4 py-3 rounded-xl text-sm font-bold outline-none transition-colors" className="px-4 py-3 bg-surface-2 border border-border rounded-[var(--radius-standard)] text-sm outline-none focus:border-gold/50 transition-colors"
style={{
background: 'var(--color-surface-3)',
border: '3px solid var(--color-border)',
boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.3)',
}}
maxLength={30} maxLength={30}
dir="rtl" dir="rtl"
/> />
</div> </div>
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<label className="text-xs font-black text-text-secondary">نبذة</label> <label className="text-xs font-medium text-text-secondary">نبذة</label>
<textarea <textarea
value={editForm.bio} value={editForm.bio}
onChange={(e) => setEditForm({ ...editForm, bio: e.target.value })} onChange={(e) => setEditForm({ ...editForm, bio: e.target.value })}
className="px-4 py-3 rounded-xl text-sm font-bold outline-none transition-colors resize-none h-20" className="px-4 py-3 bg-surface-2 border border-border rounded-[var(--radius-standard)] text-sm outline-none focus:border-gold/50 transition-colors resize-none h-20"
style={{
background: 'var(--color-surface-3)',
border: '3px solid var(--color-border)',
boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.3)',
}}
maxLength={150} maxLength={150}
dir="rtl" dir="rtl"
/> />
</div> </div>
<Button
onClick={handleSave}
loading={saving}
disabled={!editForm.display_name.trim()}
className="w-[80%] mx-auto mt-1"
>
حفظ التعديلات
</Button>
</div> </div>
<Button
onClick={handleSave}
loading={saving}
disabled={!editForm.display_name.trim()}
className="w-full"
>
حفظ التعديلات
</Button>
</motion.div> </motion.div>
</motion.div> </motion.div>
)} )}
...@@ -445,3 +228,14 @@ export function ProfilePage() { ...@@ -445,3 +228,14 @@ export function ProfilePage() {
</PageTransition> </PageTransition>
) )
} }
function StatItem({ icon, value, label }: { icon: React.ReactNode; value: number | string; label: string }) {
return (
<div className="bg-surface-2 rounded-[var(--radius-tiny)] p-3 flex flex-col items-center gap-1">
{icon}
<span className="text-sm font-bold text-text-primary tabular-nums">{value}</span>
<span className="text-[10px] text-text-muted">{label}</span>
</div>
)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment