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

dd

parent 0c0bcb97
import { Outlet } from 'react-router-dom'
import { Header } from './Header'
import { BottomNav } from './BottomNav'
import { DecorativeBackground } from './DecorativeBackground'
import { ToastContainer } from '../ui/ToastContainer'
import { usePresence } from '../../hooks/usePresence'
import { useNotifications } from '../../hooks/useNotifications'
......@@ -11,15 +10,12 @@ export function AppShell() {
useNotifications()
return (
<div className="relative flex flex-col min-h-dvh overflow-hidden">
<DecorativeBackground />
<div className="relative z-10 flex flex-col min-h-dvh">
<Header />
<main className="flex-1 pb-28 overflow-y-auto">
<Outlet />
</main>
<BottomNav />
</div>
<div className="flex flex-col min-h-dvh bg-background">
<Header />
<main className="flex-1 overflow-y-auto">
<Outlet />
</main>
<BottomNav />
<ToastContainer />
</div>
)
......
import { motion } from 'framer-motion'
import { Home, Gamepad2, Trophy, Users, User } from 'lucide-react'
import { useLocation, useNavigate } from 'react-router-dom'
......@@ -15,38 +14,25 @@ export function BottomNav() {
const navigate = useNavigate()
return (
<nav className="fixed bottom-0 left-0 right-0 z-50 bg-surface-1 border-t border-border">
<div className="flex items-center justify-around px-4 py-2 max-w-[480px] mx-auto">
<nav className="fixed bottom-0 left-0 right-0 z-50 bg-background border-t border-border safe-area-pb">
<div className="flex items-stretch justify-around max-w-[480px] mx-auto">
{NAV_ITEMS.map((item) => {
const isActive = location.pathname === item.path
const Icon = item.icon
return (
<motion.button
<button
key={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"
whileTap={{ scale: 0.9 }}
transition={{ duration: 0.15 }}
className={`flex flex-col items-center justify-center gap-0.5 py-2 px-3 min-w-[56px] min-h-[56px] transition-colors ${
isActive ? 'text-gold' : 'text-text-muted'
}`}
>
<Icon
size={22}
className={isActive ? 'text-gold' : 'text-text-muted'}
strokeWidth={isActive ? 2.5 : 2}
/>
<span
className={`text-[11px] font-semibold ${isActive ? 'text-gold' : 'text-text-muted'}`}
>
<Icon size={22} strokeWidth={isActive ? 2.5 : 1.8} />
<span className={`text-[10px] ${isActive ? 'font-semibold' : 'font-normal'}`}>
{item.label}
</span>
{isActive && (
<motion.div
className="absolute -bottom-2 w-6 h-[3px] rounded-full bg-gold"
layoutId="nav-indicator"
transition={{ duration: 0.2 }}
/>
)}
</motion.button>
</button>
)
})}
</div>
......
import { motion } from 'framer-motion'
import { Bell, Coins, Gem } from 'lucide-react'
import { useNotificationStore } from '../../stores/notificationStore'
import { useAuthStore } from '../../stores/authStore'
......@@ -10,55 +9,49 @@ export function Header() {
const navigate = useNavigate()
return (
<header className="sticky top-0 z-50 bg-surface-1/95 backdrop-blur-sm border-b border-border">
<div className="app-container flex items-center justify-between h-14">
<header className="sticky top-0 z-50 bg-background border-b border-border">
<div className="flex items-center justify-between h-14 px-4 max-w-[1440px] mx-auto">
{/* Logo */}
<div className="flex items-center gap-2">
<span className="text-lg font-bold text-gold tracking-wide">
<div className="flex items-center gap-3">
<span className="text-xl font-bold tracking-tight text-text-primary">
EL3AB
</span>
{profile && (
<div className="flex items-center justify-center w-7 h-7 rounded-full bg-surface-2 border border-border">
<span className="text-[11px] font-bold text-gold">
{profile.level || 1}
</span>
</div>
<span className="text-xs font-medium text-text-muted bg-surface-2 px-2 py-0.5 rounded-[var(--radius-tiny)]">
Lv.{profile.level || 1}
</span>
)}
</div>
{/* Resources + Bell */}
<div className="flex items-center gap-3">
{/* Right: currency + notifications */}
<div className="flex items-center gap-2">
{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" />
<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>
{profile.gems > 0 && (
<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-[var(--radius-tiny)] bg-surface-2 border border-border">
{(profile.gems ?? 0) > 0 && (
<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" />
<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>
)}
</>
)}
<motion.button
className="relative flex items-center justify-center w-10 h-10 rounded-[var(--radius-tiny)] bg-surface-2 border border-border"
<button
className="relative flex items-center justify-center w-10 h-10 rounded-[var(--radius-tiny)] hover:bg-surface-2 transition-colors"
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 && (
<div className="absolute -top-1 -right-1 w-5 h-5 rounded-full bg-coral flex items-center justify-center">
<span className="text-[10px] font-bold text-white">
{unreadCount > 9 ? '9+' : unreadCount}
</span>
</div>
<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">
{unreadCount > 9 ? '9+' : unreadCount}
</span>
)}
</motion.button>
</button>
</div>
</div>
</header>
......
......@@ -9,7 +9,7 @@ interface PageTransitionProps {
export function PageTransition({ children, className = '' }: PageTransitionProps) {
return (
<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 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
......
......@@ -281,3 +281,7 @@ body {
.bg-surface-1 { background-color: var(--color-surface-1); }
.bg-surface-2 { background-color: var(--color-surface-2); }
.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 { 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 { useAuthStore } from '../stores/authStore'
import { PageTransition } from '../components/layout/PageTransition'
......@@ -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() {
const navigate = useNavigate()
const { profile } = useAuthStore()
......@@ -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 (
<PageTransition>
<DailyRewardModal
......@@ -53,122 +47,113 @@ export function HomePage() {
onClose={() => setShowDailyModal(false)}
/>
<motion.div
variants={stagger}
initial="hidden"
animate="show"
className="flex flex-col gap-6"
>
<div className="flex flex-col gap-8">
{/* Welcome */}
{profile && (
<motion.div variants={fadeUp} className="flex items-center gap-3">
<div className="w-12 h-12 rounded-[var(--radius-standard)] bg-surface-2 border border-border flex items-center justify-center">
<span className="text-lg font-bold text-gold">
{profile.display_name?.charAt(0) || 'L'}
</span>
<section className="flex items-center gap-4">
<div className="w-14 h-14 rounded-full bg-surface-2 flex items-center justify-center text-xl font-bold text-gold">
{profile.display_name?.charAt(0) || '?'}
</div>
<div>
<h2 className="text-xl font-bold text-text-primary">
{profile.display_name}
</h2>
<span className="text-sm text-text-muted">
المستوى {profile.level}
</span>
<div className="flex-1 min-w-0">
<h1 className="text-xl font-bold text-text-primary truncate">
اهلا {profile.display_name}
</h1>
<p className="text-sm text-text-muted">
المستوى {profile.level} · {profile.elo_blitz} تقييم
</p>
</div>
</motion.div>
</section>
)}
{/* Play Button */}
<motion.div variants={fadeUp}>
{/* Primary CTA */}
<section>
<motion.button
onClick={() => {
playSound('tap')
navigate('/play')
}}
className="btn-gold w-full py-6 rounded-[var(--radius-hero)] text-2xl font-bold"
whileTap={{ scale: 0.96, y: 2 }}
onClick={() => { playSound('tap'); navigate('/play') }}
className="w-full flex items-center justify-center gap-3 py-5 bg-gold rounded-[var(--radius-large)] text-background text-xl font-bold"
whileTap={{ scale: 0.97 }}
transition={{ duration: 0.15 }}
>
<Play size={32} fill="currentColor" />
<span>العب الان</span>
<Play size={28} fill="currentColor" />
العب الان
</motion.button>
</motion.div>
</section>
{/* Stats */}
{/* Stats row */}
{profile && (
<motion.div variants={fadeUp} className="grid grid-cols-3 gap-3">
<StatCard
icon={<TrendingUp size={18} className="text-gold" />}
value={profile.elo_blitz}
label="تقييم"
/>
<StatCard
icon={<Swords size={18} className="text-cyan" />}
value={profile.total_games_played}
label="مباراة"
/>
<StatCard
icon={<Flame size={18} className="text-coral" />}
value={profile.win_streak}
label="سلسلة فوز"
/>
</motion.div>
<section className="grid grid-cols-3 gap-3">
<div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 text-center">
<TrendingUp size={18} className="text-gold mx-auto mb-1" />
<p className="text-lg font-bold text-text-primary tabular-nums">{profile.elo_blitz}</p>
<p className="text-[11px] text-text-muted">تقييم</p>
</div>
<div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 text-center">
<Swords size={18} className="text-cyan mx-auto mb-1" />
<p className="text-lg font-bold text-text-primary tabular-nums">{profile.total_games_played}</p>
<p className="text-[11px] text-text-muted">مباراة</p>
</div>
<div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 text-center">
<Flame size={18} className="text-coral mx-auto mb-1" />
<p className="text-lg font-bold text-text-primary tabular-nums">{winRate}%</p>
<p className="text-[11px] text-text-muted">نسبة الفوز</p>
</div>
</section>
)}
{/* Quick Actions */}
<motion.div variants={fadeUp} className="grid grid-cols-2 gap-4">
<QuickAction
icon={<Bot size={24} className="text-purple" />}
title="العب ضد روبوت"
subtitle="تدريب وتحسين"
<section className="grid grid-cols-2 gap-3">
<button
onClick={() => { playSound('tap'); navigate('/bot-select') }}
/>
<QuickAction
icon={<Users size={24} className="text-gold" />}
title="تحدى صديق"
subtitle="ارسل دعوة"
className="bg-surface-2 rounded-[var(--radius-standard)] p-5 flex flex-col items-center gap-3 hover:bg-surface-3 transition-colors"
>
<Bot size={28} className="text-purple" />
<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('/friends') }}
/>
</motion.div>
className="bg-surface-2 rounded-[var(--radius-standard)] p-5 flex flex-col items-center gap-3 hover:bg-surface-3 transition-colors"
>
<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 */}
<motion.div variants={fadeUp} className="card">
<div className="flex items-center gap-2 mb-3">
<section className="bg-surface-2 rounded-[var(--radius-standard)] p-4">
<div className="flex items-center gap-2 mb-2">
<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>
<p className="text-sm text-text-secondary leading-relaxed">
{todayTip}
</p>
</motion.div>
</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>
</section>
</div>
</motion.button>
</PageTransition>
)
}
......@@ -13,16 +13,6 @@ const CATEGORIES = [
{ key: 'classical', label: 'كلاسيكي', icon: Hourglass },
] 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() {
const navigate = useNavigate()
const [selectedTC, setSelectedTC] = useState<string>('blitz_5_0')
......@@ -36,68 +26,56 @@ export function PlayPage() {
return (
<PageTransition>
<motion.div
variants={stagger}
initial="hidden"
animate="show"
className="flex flex-col gap-6"
>
<div className="flex flex-col gap-8">
{/* Page Title */}
<motion.div variants={fadeUp}>
<h1 className="text-2xl font-bold text-text-primary">اختر اللعبة</h1>
</motion.div>
<h1 className="text-2xl font-bold text-text-primary">العب</h1>
{/* Featured Game: Chess */}
<motion.div variants={fadeUp} className="card border-gold/20">
<div className="flex items-center gap-4">
<div className="w-14 h-14 rounded-[var(--radius-standard)] bg-gold/10 border border-gold/20 flex items-center justify-center">
<span className="text-2xl">♟️</span>
{/* Game Selection */}
<section className="flex flex-col gap-3">
{/* Chess - primary */}
<div className="bg-surface-2 rounded-[var(--radius-standard)] p-4 flex items-center gap-4 border border-gold/20">
<div className="w-12 h-12 rounded-[var(--radius-tiny)] bg-gold/10 flex items-center justify-center text-2xl">
♟️
</div>
<div className="flex-1">
<span className="text-lg font-bold text-text-primary">{chessGame.nameAr}</span>
<p className="text-sm text-text-secondary mt-0.5">
العب شطرنج اونلاين ضد لاعبين حقيقيين
</p>
<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>
<p className="font-semibold text-text-primary">{chessGame.nameAr}</p>
<div className="flex items-center gap-1.5 mt-1">
<span className="w-2 h-2 rounded-full bg-online" />
<span className="text-xs text-text-muted">متاح الان</span>
</div>
</div>
</div>
</motion.div>
{/* Other Games (locked) */}
<motion.div variants={fadeUp} className="grid grid-cols-2 gap-3">
{otherGames.map((game) => (
<div
key={game.key}
className="card flex items-center gap-2 py-3 px-3 opacity-50"
>
<Lock size={14} className="text-text-muted" />
<div>
<span className="text-sm font-medium text-text-muted block">{game.nameAr}</span>
<span className="text-[11px] text-text-muted">قريبا</span>
{/* Other games - locked row */}
<div className="grid grid-cols-4 gap-2">
{otherGames.map((game) => (
<div
key={game.key}
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" />
<span className="text-[10px] text-text-muted">{game.nameAr}</span>
</div>
</div>
))}
</motion.div>
))}
</div>
</section>
{/* Time Control */}
<motion.div variants={fadeUp} className="space-y-4">
<h2 className="text-lg font-bold text-text-primary">نظام الوقت</h2>
<section className="flex flex-col gap-4">
<h2 className="text-lg font-semibold text-text-primary">الوقت</h2>
{/* Category Tabs */}
<div className="flex gap-2">
{/* Category tabs */}
<div className="flex gap-2 overflow-x-auto">
{CATEGORIES.map((cat) => {
const isActive = activeCategory === cat.key
const Icon = cat.icon
return (
<button
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
? 'bg-gold text-background'
: 'bg-surface-2 text-text-muted border border-border'
: 'bg-surface-2 text-text-muted'
}`}
onClick={() => {
playSound('tap')
......@@ -112,39 +90,37 @@ export function PlayPage() {
})}
</div>
{/* Time Options Grid */}
<div className="grid grid-cols-3 gap-3">
{/* Time options */}
<div className="grid grid-cols-3 gap-2">
{Object.entries(TIME_CONTROLS)
.filter(([, v]) => v.category === activeCategory)
.map(([key, tc]) => {
const isSelected = selectedTC === key
return (
<motion.button
<button
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
? 'border-gold bg-gold/10 text-gold'
: 'border-border bg-surface-1 text-text-secondary'
? 'bg-gold/10 border-2 border-gold text-gold'
: 'bg-surface-2 border border-transparent text-text-secondary hover:bg-surface-3'
}`}
whileTap={{ scale: 0.95 }}
transition={{ duration: 0.15 }}
onClick={() => {
playSound('tap')
setSelectedTC(key)
}}
>
{tc.labelAr}
</motion.button>
</button>
)
})}
</div>
</motion.div>
</section>
{/* Action Buttons */}
<motion.div variants={fadeUp} className="flex flex-col gap-3 mt-2">
{/* Actions */}
<section className="flex flex-col gap-3">
<motion.button
className="btn-gold w-full py-5 rounded-[var(--radius-large)] text-lg font-bold"
whileTap={{ scale: 0.96, y: 2 }}
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.97 }}
transition={{ duration: 0.15 }}
onClick={() => {
playSound('tap')
......@@ -152,12 +128,12 @@ export function PlayPage() {
}}
>
<Swords size={22} />
<span>البحث عن خصم</span>
البحث عن خصم
</motion.button>
<motion.button
className="btn-ghost w-full py-4 rounded-[var(--radius-large)] text-base font-semibold"
whileTap={{ scale: 0.96 }}
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.97 }}
transition={{ duration: 0.15 }}
onClick={() => {
playSound('tap')
......@@ -165,10 +141,10 @@ export function PlayPage() {
}}
>
<Cpu size={20} className="text-purple" />
<span>العب ضد الروبوت</span>
ضد الروبوت
</motion.button>
</motion.div>
</motion.div>
</section>
</div>
</PageTransition>
)
}
import { useState } from 'react'
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 { useNotificationStore } from '../stores/notificationStore'
import { PageTransition } from '../components/layout/PageTransition'
import { Button } from '../components/ui/Button'
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() {
const { user, profile, setProfile } = useAuthStore()
const { showToast } = useNotificationStore()
......@@ -78,366 +66,161 @@ export function ProfilePage() {
const xpPercent = Math.min((currentXpInLevel / xpForNextLevel) * 100, 100)
const ratings = [
{ label: 'رصاصة', value: profile.elo_bullet, color: '#FF5252', borderColor: 'border-[#FF5252]' },
{ label: 'خاطف', value: profile.elo_blitz, color: '#FFC83D', borderColor: 'border-[#FFC83D]' },
{ label: 'سريع', value: profile.elo_rapid, color: '#00E5CC', borderColor: 'border-[#00E5CC]' },
{ label: 'كلاسيكي', value: profile.elo_classical, color: '#B44DFF', borderColor: 'border-[#B44DFF]' },
]
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' },
{ label: 'رصاصة', value: profile.elo_bullet, icon: '⚡' },
{ label: 'خاطف', value: profile.elo_blitz, icon: '🔥' },
{ label: 'سريع', value: profile.elo_rapid, icon: '⏱' },
{ label: 'كلاسيكي', value: profile.elo_classical, icon: '♟️' },
]
return (
<PageTransition>
<motion.div
className="flex flex-col gap-6"
variants={stagger}
initial="hidden"
animate="show"
>
{/* === PLAYER CARD / NAMEPLATE === */}
<motion.div variants={item}>
<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 className="flex flex-col gap-8">
{/* Profile Header */}
<section className="flex flex-col items-center gap-4">
{/* Avatar */}
<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">
{profile.display_name?.charAt(0) || '?'}
</span>
</div>
</motion.div>
{/* === RATINGS 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(255,200,60,0.4))' }} />
{/* Name + username */}
<div className="text-center">
<h1 className="text-2xl font-bold text-text-primary">{profile.display_name}</h1>
<p className="text-sm text-text-muted mt-0.5">@{profile.username}</p>
{profile.bio && (
<p className="text-sm text-text-secondary mt-2 max-w-[280px]">{profile.bio}</p>
)}
</div>
{/* 2x2 Rating grid */}
<div className="grid grid-cols-2 gap-3">
{ratings.map((rating, i) => (
{/* XP Bar */}
<div className="w-full max-w-[300px]">
<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
key={rating.label}
className="relative flex flex-col items-center justify-center overflow-hidden"
style={{
height: 90,
clipPath: HEX_CLIP,
border: `3px solid ${rating.color}`,
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))' }} />
className="h-full bg-gold rounded-full"
initial={{ width: 0 }}
animate={{ width: `${xpPercent}%` }}
transition={{ duration: 0.8, ease: 'easeOut' }}
/>
</div>
</div>
</section>
{/* 4 circular medal items */}
<div className="flex items-center justify-between gap-2">
{stats.map((stat, i) => (
<motion.div
key={stat.label}
className="flex flex-col items-center gap-1.5"
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ type: 'spring', stiffness: 400, damping: 22, delay: 0.25 + i * 0.07 }}
>
{/* 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>
{/* Ratings */}
<section>
<h2 className="text-lg font-semibold text-text-primary mb-3">التقييمات</h2>
<div className="grid grid-cols-2 gap-3">
{ratings.map((r) => (
<div key={r.label} className="bg-surface-2 rounded-[var(--radius-standard)] p-4 flex items-center gap-3">
<span className="text-xl">{r.icon}</span>
<div>
<p className="text-lg font-bold text-text-primary tabular-nums">{r.value}</p>
<p className="text-xs text-text-muted">{r.label}</p>
</div>
<span className="text-[9px] text-text-muted font-bold text-center leading-tight">{stat.label}</span>
</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 === */}
<motion.div className="flex flex-col items-center gap-3 mt-1" variants={item}>
{/* Edit profile 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)' }}
{/* Actions */}
<section className="flex flex-col gap-3">
<button
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]" />
<span>تعديل الملف الشخصي</span>
</motion.button>
<Pencil size={16} className="text-text-secondary" />
تعديل الملف الشخصي
</button>
{/* Logout 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 }}
<button
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} />
<span>تسجيل الخروج</span>
</motion.button>
</motion.div>
</motion.div>
<LogOut size={16} />
تسجيل الخروج
</button>
</section>
</div>
{/* === EDIT MODAL === */}
{/* Edit Modal */}
<AnimatePresence>
{editOpen && (
<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 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
>
<motion.div
className="absolute inset-0 bg-background/85 backdrop-blur-sm"
<div
className="absolute inset-0 bg-background/80 backdrop-blur-sm"
onClick={() => setEditOpen(false)}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
<motion.div
className="relative w-full max-w-[360px] flex flex-col gap-5 overflow-hidden"
style={{
borderRadius: 20,
border: '3px solid var(--color-border)',
background: 'var(--color-surface-1)',
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)',
}}
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 }}
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={{ boxShadow: 'var(--shadow-3)' }}
initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.95, opacity: 0 }}
transition={{ duration: 0.2 }}
>
{/* Ribbon banner header */}
<div
className="relative flex items-center justify-center py-3"
style={{
background: 'linear-gradient(to bottom, rgba(255,200,60,0.12), transparent)',
borderBottom: '2px solid var(--color-border)',
clipPath: 'polygon(0 0, 100% 0, 95% 100%, 5% 100%)',
}}
>
<Shield size={18} className="text-[#FFC83D] ml-2" />
<h2 className="text-lg font-black">تعديل الملف</h2>
<div className="flex items-center justify-between">
<h2 className="text-lg font-bold">تعديل الملف</h2>
<button
onClick={() => setEditOpen(false)}
className="p-2 rounded-[var(--radius-tiny)] hover:bg-surface-2"
>
<X size={16} className="text-text-muted" />
</button>
</div>
{/* Close button */}
<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-4">
<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
type="text"
value={editForm.display_name}
onChange={(e) => setEditForm({ ...editForm, display_name: e.target.value })}
className="px-4 py-3 rounded-xl text-sm font-bold outline-none 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)',
}}
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"
maxLength={30}
dir="rtl"
/>
</div>
<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
value={editForm.bio}
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"
style={{
background: 'var(--color-surface-3)',
border: '3px solid var(--color-border)',
boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.3)',
}}
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"
maxLength={150}
dir="rtl"
/>
</div>
<Button
onClick={handleSave}
loading={saving}
disabled={!editForm.display_name.trim()}
className="w-[80%] mx-auto mt-1"
>
حفظ التعديلات
</Button>
</div>
<Button
onClick={handleSave}
loading={saving}
disabled={!editForm.display_name.trim()}
className="w-full"
>
حفظ التعديلات
</Button>
</motion.div>
</motion.div>
)}
......@@ -445,3 +228,14 @@ export function ProfilePage() {
</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