Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
el3ab-Player
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
el3ab-Player
Commits
0c0bcb97
Commit
0c0bcb97
authored
May 26, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
paswe 0
parent
a1d2298a
Changes
23
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
1302 additions
and
1202 deletions
+1302
-1202
V2_REBUILD_PLAN.md
V2_REBUILD_PLAN.md
+812
-0
index.html
index.html
+3
-3
DailyRewardModal.tsx
src/components/DailyRewardModal.tsx
+21
-36
BottomNav.tsx
src/components/layout/BottomNav.tsx
+34
-109
DecorativeBackground.tsx
src/components/layout/DecorativeBackground.tsx
+6
-76
Header.tsx
src/components/layout/Header.tsx
+42
-88
PageTransition.tsx
src/components/layout/PageTransition.tsx
+5
-5
Button.tsx
src/components/ui/Button.tsx
+14
-15
Card.tsx
src/components/ui/Card.tsx
+7
-14
GamePanel.tsx
src/components/ui/GamePanel.tsx
+9
-41
GameProgressBar.tsx
src/components/ui/GameProgressBar.tsx
+10
-20
RibbonHeader.tsx
src/components/ui/RibbonHeader.tsx
+5
-57
ShieldBadge.tsx
src/components/ui/ShieldBadge.tsx
+8
-55
index.css
src/index.css
+165
-219
BotSelectPage.tsx
src/pages/BotSelectPage.tsx
+2
-2
FriendsPage.tsx
src/pages/FriendsPage.tsx
+7
-7
GamePage.tsx
src/pages/GamePage.tsx
+2
-2
HomePage.tsx
src/pages/HomePage.tsx
+74
-224
MatchmakingPage.tsx
src/pages/MatchmakingPage.tsx
+3
-3
PlayPage.tsx
src/pages/PlayPage.tsx
+62
-215
SettingsPage.tsx
src/pages/SettingsPage.tsx
+4
-4
ShopPage.tsx
src/pages/ShopPage.tsx
+3
-3
TournamentsPage.tsx
src/pages/TournamentsPage.tsx
+4
-4
No files found.
V2_REBUILD_PLAN.md
0 → 100644
View file @
0c0bcb97
This diff is collapsed.
Click to expand it.
index.html
View file @
0c0bcb97
...
...
@@ -4,15 +4,15 @@
<meta
charset=
"UTF-8"
/>
<link
rel=
"icon"
type=
"image/svg+xml"
href=
"/favicon.svg"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta
name=
"theme-color"
content=
"#0
A0A14
"
/>
<meta
name=
"theme-color"
content=
"#0
71120
"
/>
<meta
name=
"apple-mobile-web-app-capable"
content=
"yes"
/>
<meta
name=
"apple-mobile-web-app-status-bar-style"
content=
"black-translucent"
/>
<title>
EL3AB - العب
</title>
<link
rel=
"preconnect"
href=
"https://fonts.googleapis.com"
/>
<link
rel=
"preconnect"
href=
"https://fonts.gstatic.com"
crossorigin
/>
<link
href=
"https://fonts.googleapis.com/css2?family=
Cairo:wght@400;
600;700;900&display=swap"
rel=
"stylesheet"
/>
<link
href=
"https://fonts.googleapis.com/css2?family=
IBM+Plex+Sans+Arabic:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Cairo:wght@
600;700;900&display=swap"
rel=
"stylesheet"
/>
</head>
<body
class=
"bg-background text-text-primary
font-cairo
antialiased overflow-x-hidden"
>
<body
class=
"bg-background text-text-primary antialiased overflow-x-hidden"
>
<div
id=
"root"
></div>
<script
type=
"module"
src=
"/src/main.tsx"
></script>
</body>
...
...
src/components/DailyRewardModal.tsx
View file @
0c0bcb97
import
{
motion
,
AnimatePresence
}
from
'framer-motion'
import
{
Gift
,
Coins
,
Flame
,
Sparkles
}
from
'lucide-react'
import
{
Gift
,
Coins
,
Flame
}
from
'lucide-react'
import
{
Button
}
from
'./ui/Button'
interface
DailyRewardModalProps
{
...
...
@@ -20,60 +20,45 @@ export function DailyRewardModal({ open, streak, reward, loading, onClaim, onClo
initial=
{
{
opacity
:
0
}
}
animate=
{
{
opacity
:
1
}
}
exit=
{
{
opacity
:
0
}
}
transition=
{
{
duration
:
0.2
}
}
onClick=
{
onClose
}
>
<
motion
.
div
className=
"w-full max-w-[340px] rounded-3xl bg-surface-1 border-3 border-border p-7 flex flex-col items-center gap-5 relative overflow-hidden"
initial=
{
{
scale
:
0.8
,
opacity
:
0
}
}
className=
"w-full max-w-[340px] rounded-[var(--radius-hero)] bg-surface-1 border border-border p-6 flex flex-col items-center gap-5"
style=
{
{
boxShadow
:
'var(--shadow-3)'
}
}
initial=
{
{
scale
:
0.9
,
opacity
:
0
}
}
animate=
{
{
scale
:
1
,
opacity
:
1
}
}
exit=
{
{
scale
:
0.
8
,
opacity
:
0
}
}
transition=
{
{
type
:
'spring'
,
stiffness
:
300
,
damping
:
25
}
}
exit=
{
{
scale
:
0.
9
,
opacity
:
0
}
}
transition=
{
{
duration
:
0.2
}
}
onClick=
{
(
e
)
=>
e
.
stopPropagation
()
}
>
<
div
className=
"absolute top-0 left-0 w-full h-32 bg-gradient-to-b from-gold/8 to-transparent"
/>
<
motion
.
div
className=
"w-20 h-20 rounded-full bg-gradient-to-br from-gold/20 to-gold/5 border-3 border-gold/50 flex items-center justify-center relative"
animate=
{
{
rotate
:
[
0
,
5
,
-
5
,
0
]
}
}
transition=
{
{
duration
:
3
,
repeat
:
Infinity
}
}
>
<
Gift
size=
{
36
}
className=
"text-gold"
/>
<
motion
.
div
className=
"absolute -top-1 -right-1"
animate=
{
{
scale
:
[
1
,
1.3
,
1
],
opacity
:
[
1
,
0.6
,
1
]
}
}
transition=
{
{
duration
:
1.5
,
repeat
:
Infinity
}
}
>
<
Sparkles
size=
{
16
}
className=
"text-gold"
/>
</
motion
.
div
>
</
motion
.
div
>
<
div
className=
"w-16 h-16 rounded-[var(--radius-large)] bg-gold/10 border border-gold/30 flex items-center justify-center"
>
<
Gift
size=
{
32
}
className=
"text-gold"
/>
</
div
>
<
div
className=
"text-center
z-10
"
>
<
h3
className=
"text-xl font-b
lack
text-text-primary"
>
المكافأة اليومية
</
h3
>
<
div
className=
"text-center"
>
<
h3
className=
"text-xl font-b
old
text-text-primary"
>
المكافأة اليومية
</
h3
>
<
p
className=
"text-sm text-text-muted mt-1"
>
ادخل كل يوم واحصل على مكافآت اكثر
</
p
>
</
div
>
<
div
className=
"flex items-center gap-3 bg-surface-2 rounded-
2xl px-5
py-3 border border-border"
>
<
Flame
size=
{
20
}
className=
"text-coral"
/>
<
span
className=
"text-sm font-
bold
text-text-secondary"
>
سلسلة:
</
span
>
<
span
className=
"text-lg font-b
lack
text-coral"
>
{
streak
}
</
span
>
<
div
className=
"flex items-center gap-3 bg-surface-2 rounded-
[var(--radius-standard)] px-4
py-3 border border-border"
>
<
Flame
size=
{
18
}
className=
"text-coral"
/>
<
span
className=
"text-sm font-
medium
text-text-secondary"
>
سلسلة:
</
span
>
<
span
className=
"text-lg font-b
old
text-coral"
>
{
streak
}
</
span
>
<
span
className=
"text-sm text-text-muted"
>
يوم
</
span
>
</
div
>
<
motion
.
div
className=
"flex items-center gap-3 bg-gold/10 rounded-2xl px-6 py-4 border border-gold/30"
animate=
{
{
scale
:
[
1
,
1.02
,
1
]
}
}
transition=
{
{
duration
:
2
,
repeat
:
Infinity
}
}
>
<
Coins
size=
{
28
}
className=
"text-gold"
/>
<
span
className=
"text-3xl font-black text-gold"
>
+
{
reward
}
</
span
>
</
motion
.
div
>
<
div
className=
"flex items-center gap-3 bg-gold/10 rounded-[var(--radius-standard)] px-5 py-4 border border-gold/20"
>
<
Coins
size=
{
24
}
className=
"text-gold"
/>
<
span
className=
"text-2xl font-bold text-gold"
>
+
{
reward
}
</
span
>
</
div
>
<
Button
variant=
"gold"
size=
"lg"
onClick=
{
onClaim
}
loading=
{
loading
}
className=
"w-
[80%] mx-auto
"
className=
"w-
full
"
>
استلم المكافأة
</
Button
>
...
...
src/components/layout/BottomNav.tsx
View file @
0c0bcb97
...
...
@@ -10,121 +10,46 @@ const NAV_ITEMS = [
{
path
:
'/profile'
,
icon
:
User
,
label
:
'حسابي'
},
]
const
CENTER_INDEX
=
2
export
function
BottomNav
()
{
const
location
=
useLocation
()
const
navigate
=
useNavigate
()
return
(
<
nav
className=
"fixed bottom-0 left-0 right-0 z-50 px-4 pb-3 pt-1"
>
<
div
className=
"app-container !p-0"
>
{
/* Decorative corner accents */
}
<
div
className=
"relative"
>
{
/* Left L-accent */
}
<
div
className=
"absolute -top-1 left-1 w-3 h-3 border-t-2 border-l-2 border-gold/40 rounded-tl-sm pointer-events-none"
/>
{
/* Right L-accent */
}
<
div
className=
"absolute -top-1 right-1 w-3 h-3 border-t-2 border-r-2 border-gold/40 rounded-tr-sm pointer-events-none"
/>
{
/* Main bar */
}
<
div
className=
"game-panel border-4 border-border bg-surface-2 shadow-[0_8px_32px_rgba(0,0,0,0.6),0_2px_8px_rgba(0,0,0,0.4)]"
>
<
div
className=
"flex items-center justify-around px-2 py-2 relative"
>
{
NAV_ITEMS
.
map
((
item
,
index
)
=>
{
<
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_ITEMS
.
map
((
item
)
=>
{
const
isActive
=
location
.
pathname
===
item
.
path
const
isCenter
=
index
===
CENTER_INDEX
const
Icon
=
item
.
icon
return
(
<
motion
.
button
key=
{
item
.
path
}
onClick=
{
()
=>
navigate
(
item
.
path
)
}
className=
"relative flex flex-col items-center gap-0.5 px-2 py-1"
whileTap=
{
{
scale
:
0.8
}
}
>
{
/* Slot background */
}
<
div
className=
{
`relative flex items-center justify-center rounded-xl transition-colors duration-200 ${
isCenter ? 'w-12 h-12' : 'w-10 h-10'
} ${
isActive
? 'bg-gold/15'
: 'bg-surface-3/60'
} ${
isCenter ? 'border-2 border-gold/20' : 'border border-border/50'
}`
}
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
}
}
>
{
/* Hex outline for center item */
}
{
isCenter
&&
(
<
div
className=
"absolute inset-0 border border-gold/15"
style=
{
{
clipPath
:
'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)'
,
}
}
/>
)
}
<
motion
.
div
animate=
{
isActive
?
{
y
:
-
14
,
scale
:
1.15
}
:
{
y
:
0
,
scale
:
1
}
}
transition=
{
{
type
:
'spring'
,
stiffness
:
400
,
damping
:
22
}
}
>
{
/* Glowing badge behind active icon */
}
{
isActive
&&
(
<
motion
.
div
className=
"absolute inset-0 -m-2 rounded-full bg-gold/20 blur-sm"
initial=
{
{
opacity
:
0
}
}
animate=
{
{
opacity
:
1
}
}
transition=
{
{
duration
:
0.2
}
}
/>
)
}
<
Icon
size=
{
isCenter
?
24
:
20
}
className=
{
isActive
?
'text-gold drop-shadow-[0_0_6px_rgba(255,200,60,0.6)]'
:
'text-text-muted'
}
strokeWidth=
{
isActive
?
2.8
:
2
}
size=
{
22
}
className=
{
isActive
?
'text-gold'
:
'text-text-muted'
}
strokeWidth=
{
isActive
?
2.5
:
2
}
/>
</
motion
.
div
>
</
div
>
{
/* Label: shows when active */
}
<
motion
.
span
className=
"text-[9px] font-bold text-gold"
initial=
{
false
}
animate=
{
{
opacity
:
isActive
?
1
:
0
,
y
:
isActive
?
0
:
4
,
}
}
transition=
{
{
duration
:
0.2
}
}
<
span
className=
{
`text-[11px] font-semibold ${isActive ? 'text-gold' : 'text-text-muted'}`
}
>
{
item
.
label
}
</
motion
.
span
>
{
/* Active glow indicator */
}
</
span
>
{
isActive
&&
(
<
motion
.
div
className=
"absolute -bottom-1 w-5
h-[3px] rounded-full bg-gold"
className=
"absolute -bottom-2 w-6
h-[3px] rounded-full bg-gold"
layoutId=
"nav-indicator"
style=
{
{
boxShadow
:
'0 0 8px rgba(255, 200, 60, 0.7)'
}
}
transition=
{
{
type
:
'spring'
,
stiffness
:
500
,
damping
:
30
}
}
transition=
{
{
duration
:
0.2
}
}
/>
)
}
</
motion
.
button
>
)
})
}
</
div
>
</
div
>
{
/* Bottom L-accents */
}
<
div
className=
"absolute -bottom-1 left-1 w-3 h-3 border-b-2 border-l-2 border-gold/40 rounded-bl-sm pointer-events-none"
/>
<
div
className=
"absolute -bottom-1 right-1 w-3 h-3 border-b-2 border-r-2 border-gold/40 rounded-br-sm pointer-events-none"
/>
</
div
>
</
div
>
</
nav
>
)
}
src/components/layout/DecorativeBackground.tsx
View file @
0c0bcb97
import
{
motion
}
from
'framer-motion'
const
particles
=
[
{
x
:
'12%'
,
delay
:
0
,
duration
:
4.5
},
{
x
:
'28%'
,
delay
:
1.2
,
duration
:
5.2
},
{
x
:
'45%'
,
delay
:
0.5
,
duration
:
3.8
},
{
x
:
'62%'
,
delay
:
2.1
,
duration
:
4.8
},
{
x
:
'78%'
,
delay
:
0.8
,
duration
:
5.5
},
{
x
:
'88%'
,
delay
:
1.6
,
duration
:
4.2
},
{
x
:
'35%'
,
delay
:
2.8
,
duration
:
5.0
},
]
function
generateRayLines
()
{
const
lines
=
[]
for
(
let
i
=
0
;
i
<
12
;
i
++
)
{
const
angle
=
(
i
*
30
*
Math
.
PI
)
/
180
const
x2
=
50
+
45
*
Math
.
cos
(
angle
)
const
y2
=
50
+
45
*
Math
.
sin
(
angle
)
lines
.
push
(
<
line
key=
{
i
}
x1=
"50"
y1=
"50"
x2=
{
x2
}
y2=
{
y2
}
stroke=
"rgba(255, 200, 60, 0.04)"
strokeWidth=
"0.5"
/>
)
}
return
lines
}
export
function
DecorativeBackground
()
{
return
(
<
div
className=
"fixed inset-0 pointer-events-none z-0 overflow-hidden"
>
{
/* Arena gradient base */
}
<
div
className=
"absolute inset-0 bg-arena"
/>
{
/* Rotating ray pattern */
}
<
motion
.
div
className=
"absolute inset-0 flex items-center justify-center"
animate=
{
{
rotate
:
360
}
}
transition=
{
{
duration
:
90
,
repeat
:
Infinity
,
ease
:
'linear'
,
}
}
>
<
svg
viewBox=
"0 0 100 100"
className=
"w-[140vmax] h-[140vmax] opacity-60"
xmlns=
"http://www.w3.org/2000/svg"
>
{
generateRayLines
()
}
</
svg
>
</
motion
.
div
>
{
/* Floating gold particles */
}
{
particles
.
map
((
particle
,
i
)
=>
(
<
motion
.
div
key=
{
i
}
className=
"absolute w-1 h-1 rounded-full bg-gold/30"
<
div
className=
"fixed inset-0 pointer-events-none z-0"
>
<
div
className=
"absolute inset-0"
style=
{
{
left
:
particle
.
x
,
top
:
'80%'
,
}
}
animate=
{
{
y
:
[
0
,
-
window
.
innerHeight
*
0.7
,
-
window
.
innerHeight
],
opacity
:
[
0
,
0.8
,
0
],
}
}
transition=
{
{
duration
:
particle
.
duration
,
repeat
:
Infinity
,
delay
:
particle
.
delay
,
ease
:
'linear'
,
background
:
'radial-gradient(ellipse at 50% 0%, rgba(23, 37, 60, 0.5) 0%, transparent 60%)'
,
}
}
/>
))
}
</
div
>
)
}
src/components/layout/Header.tsx
View file @
0c0bcb97
...
...
@@ -2,7 +2,6 @@ import { motion } from 'framer-motion'
import
{
Bell
,
Coins
,
Gem
}
from
'lucide-react'
import
{
useNotificationStore
}
from
'../../stores/notificationStore'
import
{
useAuthStore
}
from
'../../stores/authStore'
import
{
GoldCrown
}
from
'../icons/GoldCrown'
import
{
useNavigate
}
from
'react-router-dom'
export
function
Header
()
{
...
...
@@ -11,102 +10,57 @@ export function Header() {
const
navigate
=
useNavigate
()
return
(
<
header
className=
"sticky top-0 z-50 px-4 pt-3 pb-2"
>
<
div
className=
"app-container !p-0"
>
{
/* HUD Bar with diagonal bottom edge */
}
<
div
className=
"game-panel relative border-4 border-border bg-surface-2"
style=
{
{
clipPath
:
'polygon(0 0, 100% 0, 100% 85%, 98% 100%, 2% 100%, 0% 85%)'
,
}
}
>
<
div
className=
"flex items-center justify-between px-4 py-2.5"
>
{
/* Logo section (right side in RTL) */
}
<
div
className=
"flex items-center gap-2.5"
>
{
/* Shield-shaped container */
}
<
div
className=
"relative flex items-center justify-center w-11 h-11 bg-surface-3 border-2 border-gold/40"
style=
{
{
clipPath
:
'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)'
,
}
}
>
<
GoldCrown
size=
{
22
}
animate=
{
false
}
/>
</
div
>
<
span
className=
"text-lg font-black text-gold tracking-widest"
style=
{
{
textShadow
:
'0 2px 4px rgba(0,0,0,0.5), 0 0 12px rgba(255,200,60,0.3)'
,
}
}
>
<
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"
>
{
/* Logo */
}
<
div
className=
"flex items-center gap-2"
>
<
span
className=
"text-lg font-bold text-gold tracking-wide"
>
EL3AB
</
span
>
</
div
>
{
/* Center: Level indicator */
}
{
profile
&&
(
<
div
className=
"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
>
<
div
className=
"w-8 h-8 rounded-full bg-surface-3 border-2 border-gold/50 flex items-center justify-center shadow-[0_0_8px_rgba(255,200,60,0.2)]"
>
<
span
className=
"text-[11px] font-black text-gold"
>
<
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
>
</
div
>
)
}
</
div
>
{
/* Resources section (left side in RTL)
*/
}
<
div
className=
"flex items-center gap-2
"
>
{
/* Resources + Bell
*/
}
<
div
className=
"flex items-center gap-3
"
>
{
profile
&&
(
<>
{
/* Coins capsule */
}
<
motion
.
div
className=
"flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-gold/10 border-2 border-gold/40 shadow-[inset_0_0_8px_rgba(255,200,60,0.15)]"
whileTap=
{
{
scale
:
0.9
}
}
>
<
div
className=
"flex items-center gap-1.5 px-3 py-1.5 rounded-[var(--radius-tiny)] bg-surface-2 border border-border"
>
<
Coins
size=
{
14
}
className=
"text-gold"
/>
<
span
className=
"text-xs font-black text-gold
"
>
{
profile
.
coins
}
</
span
>
</
motion
.
div
>
<
span
className=
"text-xs font-semibold text-text-primary
"
>
{
profile
.
coins
}
</
span
>
</
div
>
{
/* Gems capsule */
}
{
profile
.
gems
>
0
&&
(
<
motion
.
div
className=
"flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-purple/10 border-2 border-purple/40 shadow-[inset_0_0_8px_rgba(180,77,255,0.15)]"
whileTap=
{
{
scale
:
0.9
}
}
>
<
div
className=
"flex items-center gap-1.5 px-3 py-1.5 rounded-[var(--radius-tiny)] bg-surface-2 border border-border"
>
<
Gem
size=
{
12
}
className=
"text-purple"
/>
<
span
className=
"text-xs font-black text-purple
"
>
{
profile
.
gems
}
</
span
>
</
motion
.
div
>
<
span
className=
"text-xs font-semibold text-text-primary
"
>
{
profile
.
gems
}
</
span
>
</
div
>
)
}
</>
)
}
{
/* Bell: hexagonal button */
}
<
motion
.
button
className=
"relative flex items-center justify-center w-10 h-10 bg-surface-3 border-2 border-border"
style=
{
{
clipPath
:
'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)'
,
}
}
className=
"relative flex items-center justify-center w-10 h-10 rounded-[var(--radius-tiny)] bg-surface-2 border border-border"
onClick=
{
()
=>
navigate
(
'/notifications'
)
}
whileTap=
{
{
scale
:
0.85
}
}
whileHover=
{
{
scale
:
1.0
5
}
}
whileTap=
{
{
scale
:
0.9
}
}
transition=
{
{
duration
:
0.1
5
}
}
>
<
Bell
size=
{
16
}
className=
"text-text-secondary"
/>
<
Bell
size=
{
18
}
className=
"text-text-secondary"
/>
{
unreadCount
>
0
&&
(
<
motion
.
div
className=
"absolute -top-1 -right-1 w-5 h-5 rounded-full bg-coral border-2 border-surface-2 flex items-center justify-center animate-pulse-glow"
initial=
{
{
scale
:
0
}
}
animate=
{
{
scale
:
1
}
}
transition=
{
{
type
:
'spring'
,
stiffness
:
500
,
damping
:
15
}
}
>
<
span
className=
"text-[9px] font-black text-white"
>
<
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
>
</
motion
.
div
>
</
div
>
)
}
</
motion
.
button
>
</
div
>
</
div
>
</
div
>
</
div
>
</
header
>
)
}
src/components/layout/PageTransition.tsx
View file @
0c0bcb97
...
...
@@ -9,11 +9,11 @@ interface PageTransitionProps {
export
function
PageTransition
({
children
,
className
=
''
}:
PageTransitionProps
)
{
return
(
<
motion
.
div
className=
{
`app-container py-
8 flex flex-col gap-7
${className}`
}
initial=
{
{
opacity
:
0
,
y
:
24
,
scale
:
0.97
}
}
animate=
{
{
opacity
:
1
,
y
:
0
,
scale
:
1
}
}
exit=
{
{
opacity
:
0
,
y
:
-
16
,
scale
:
0.98
}
}
transition=
{
{
type
:
'spring'
,
stiffness
:
300
,
damping
:
28
}
}
className=
{
`app-container py-
6 pb-24
${className}`
}
initial=
{
{
opacity
:
0
}
}
animate=
{
{
opacity
:
1
}
}
exit=
{
{
opacity
:
0
}
}
transition=
{
{
duration
:
0.2
}
}
>
{
children
}
</
motion
.
div
>
...
...
src/components/ui/Button.tsx
View file @
0c0bcb97
...
...
@@ -5,7 +5,7 @@ import { Loader2 } from 'lucide-react'
interface
ButtonProps
{
children
:
ReactNode
onClick
?:
()
=>
void
variant
?:
'gold'
|
'ghost'
|
'
coral'
|
'cyan'
|
'purple
'
variant
?:
'gold'
|
'ghost'
|
'
blue'
|
'cyan'
|
'danger
'
size
?:
'sm'
|
'md'
|
'lg'
disabled
?:
boolean
loading
?:
boolean
...
...
@@ -13,18 +13,18 @@ interface ButtonProps {
type
?:
'button'
|
'submit'
}
const
variants
=
{
gold
:
'b
g-gradient-to-b from-gold-light to-gold text-background font-black shadow-lg shadow-gold/30 border-b-4 border-gold-mute
d'
,
ghost
:
'bg-surface-2 border-2 border-border text-text-primary font-bold hover:border-gold/50 hover:bg-surface-3
'
,
c
oral
:
'bg-gradient-to-b from-[#FF7070] to-coral text-white font-black shadow-lg shadow-coral/25 border-b-4 border-[#B83A3A]
'
,
cyan
:
'bg-gradient-to-b from-[#33FFE5] to-cyan text-background font-black shadow-lg shadow-cyan/25 border-b-4 border-[#009E8E]
'
,
purple
:
'bg-gradient-to-b from-[#CC70FF] to-purple text-white font-black shadow-lg shadow-purple/25 border-b-4 border-[#6B3D8F]
'
,
const
variant
Style
s
=
{
gold
:
'b
tn-gol
d'
,
blue
:
'btn-blue
'
,
c
yan
:
'btn-cyan
'
,
ghost
:
'btn-ghost
'
,
danger
:
'btn-danger
'
,
}
const
sizes
=
{
sm
:
'px-
5 py-2 text-sm rounded-xl gap-2 min-w-[90
px]'
,
md
:
'px-
7 py-3 text-base rounded-2xl gap-2.5 min-w-[120
px]'
,
lg
:
'px-
10 py-4 text-lg rounded-2xl gap-3 min-w-[160
px]'
,
const
size
Style
s
=
{
sm
:
'px-
4 py-2 text-sm min-h-[36
px]'
,
md
:
'px-
6 py-3 text-base min-h-[44
px]'
,
lg
:
'px-
8 py-4 text-lg min-h-[52
px]'
,
}
export
function
Button
({
...
...
@@ -42,10 +42,9 @@ export function Button({
type=
{
type
}
onClick=
{
onClick
}
disabled=
{
disabled
||
loading
}
className=
{
`inline-flex items-center justify-center transition-all uppercase tracking-wide ${variants[variant]} ${sizes[size]} ${disabled ? 'opacity-50 cursor-not-allowed saturate-50' : ''} ${className}`
}
whileTap=
{
!
disabled
?
{
scale
:
0.92
,
y
:
2
}
:
undefined
}
whileHover=
{
!
disabled
?
{
scale
:
1.03
}
:
undefined
}
transition=
{
{
type
:
'spring'
,
stiffness
:
600
,
damping
:
20
}
}
className=
{
`btn ${variantStyles[variant]} ${sizeStyles[size]} ${disabled ? 'opacity-50 cursor-not-allowed' : ''} ${className}`
}
whileTap=
{
!
disabled
?
{
scale
:
0.96
,
y
:
2
}
:
undefined
}
transition=
{
{
duration
:
0.15
}
}
>
{
loading
&&
<
Loader2
size=
{
18
}
className=
"animate-spin"
/>
}
{
children
}
...
...
src/components/ui/Card.tsx
View file @
0c0bcb97
...
...
@@ -4,27 +4,20 @@ import type { ReactNode } from 'react'
interface
CardProps
{
children
:
ReactNode
className
?:
string
glow
?:
boolean
onClick
?:
()
=>
void
variant
?:
'default'
|
'
gold'
|
'
elevated'
variant
?:
'default'
|
'elevated'
}
export
function
Card
({
children
,
className
=
''
,
glow
=
false
,
onClick
,
variant
=
'default'
}:
CardProps
)
{
const
base
=
'rounded-2xl p-5 border-3 transition-all'
const
variantStyles
=
{
default
:
`bg-surface-1 border-border
${
glow
?
'border-gold shadow-[0_0_20px_rgba(255,200,60,0.2)]'
:
''
}
`
,
gold
:
'bg-gradient-to-br from-surface-2 to-surface-1 border-gold/60 shadow-[0_0_16px_rgba(255,200,60,0.15)]'
,
elevated
:
'bg-surface-2 border-border shadow-xl shadow-black/40'
,
}
export
function
Card
({
children
,
className
=
''
,
onClick
,
variant
=
'default'
}:
CardProps
)
{
const
base
=
variant
===
'elevated'
?
'card card-elevated'
:
'card'
return
(
<
motion
.
div
className=
{
`${base} ${
variantStyles[variant]} ${
onClick ? 'cursor-pointer' : ''} ${className}`
}
whileHover=
{
onClick
?
{
y
:
-
4
,
scale
:
1.01
,
boxShadow
:
'0 12px 32px rgba(0,0,0,0.5)'
}
:
undefined
}
whileTap=
{
onClick
?
{
scale
:
0.9
7
}
:
undefined
}
className=
{
`${base} ${onClick ? 'cursor-pointer' : ''} ${className}`
}
whileHover=
{
onClick
?
{
y
:
-
2
}
:
undefined
}
whileTap=
{
onClick
?
{
scale
:
0.9
8
}
:
undefined
}
onClick=
{
onClick
}
transition=
{
{
type
:
'spring'
,
stiffness
:
400
,
damping
:
22
}
}
transition=
{
{
duration
:
0.15
}
}
>
{
children
}
</
motion
.
div
>
...
...
src/components/ui/GamePanel.tsx
View file @
0c0bcb97
...
...
@@ -4,8 +4,7 @@ import type { ReactNode } from 'react'
interface
GamePanelProps
{
children
:
ReactNode
className
?:
string
variant
?:
'default'
|
'gold'
|
'legendary'
|
'recessed'
rivets
?:
boolean
variant
?:
'default'
|
'elevated'
onClick
?:
()
=>
void
}
...
...
@@ -13,52 +12,21 @@ export function GamePanel({
children
,
className
=
''
,
variant
=
'default'
,
rivets
=
false
,
onClick
,
}:
GamePanelProps
)
{
const
baseClass
=
variant
===
'recessed'
?
'game-panel-recessed'
:
'game-panel'
const
goldClass
=
variant
===
'gold'
||
variant
===
'legendary'
?
'game-panel-gold'
:
''
const
styles
=
variant
===
'elevated'
?
'bg-surface-2 border border-border-strong shadow-[var(--shadow-2)]'
:
'bg-surface-1 border border-border shadow-[var(--shadow-1)]'
return
(
<
motion
.
div
className=
{
`
${baseClass} ${goldClass} p-5 relative overflow-hidden
${onClick ? 'cursor-pointer' : ''} ${className}`
}
whileHover=
{
onClick
?
{
y
:
-
4
,
scale
:
1.01
}
:
undefined
}
whileTap=
{
onClick
?
{
scale
:
0.9
7
}
:
undefined
}
className=
{
`
rounded-[var(--radius-large)] p-4 md:p-6 relative ${styles}
${onClick ? 'cursor-pointer' : ''} ${className}`
}
whileHover=
{
onClick
?
{
y
:
-
2
}
:
undefined
}
whileTap=
{
onClick
?
{
scale
:
0.9
8
}
:
undefined
}
onClick=
{
onClick
}
transition=
{
{
type
:
'spring'
,
stiffness
:
400
,
damping
:
22
}
}
transition=
{
{
duration
:
0.15
}
}
>
{
/* Legendary shimmer overlay */
}
{
variant
===
'legendary'
&&
(
<
motion
.
div
className=
"absolute inset-0 pointer-events-none"
style=
{
{
background
:
'linear-gradient(105deg, transparent 40%, rgba(255, 200, 60, 0.12) 45%, rgba(255, 200, 60, 0.2) 50%, rgba(255, 200, 60, 0.12) 55%, transparent 60%)'
,
}
}
animate=
{
{
x
:
[
'-100%'
,
'200%'
],
}
}
transition=
{
{
duration
:
3
,
repeat
:
Infinity
,
ease
:
'easeInOut'
,
repeatDelay
:
1.5
,
}
}
/>
)
}
{
/* Rivets at 4 corners */
}
{
rivets
&&
(
<>
<
span
className=
"absolute top-3 left-3 w-[6px] h-[6px] rounded-full bg-surface-3 shadow-[inset_0_1px_2px_rgba(0,0,0,0.6),0_1px_0_rgba(255,255,255,0.05)]"
/>
<
span
className=
"absolute top-3 right-3 w-[6px] h-[6px] rounded-full bg-surface-3 shadow-[inset_0_1px_2px_rgba(0,0,0,0.6),0_1px_0_rgba(255,255,255,0.05)]"
/>
<
span
className=
"absolute bottom-3 left-3 w-[6px] h-[6px] rounded-full bg-surface-3 shadow-[inset_0_1px_2px_rgba(0,0,0,0.6),0_1px_0_rgba(255,255,255,0.05)]"
/>
<
span
className=
"absolute bottom-3 right-3 w-[6px] h-[6px] rounded-full bg-surface-3 shadow-[inset_0_1px_2px_rgba(0,0,0,0.6),0_1px_0_rgba(255,255,255,0.05)]"
/>
</>
)
}
{
/* Content */
}
<
div
className=
"relative z-10"
>
{
children
}
</
div
>
{
children
}
</
motion
.
div
>
)
}
src/components/ui/GameProgressBar.tsx
View file @
0c0bcb97
interface
GameProgressBarProps
{
value
:
number
max
:
number
color
?:
'gold'
|
'cyan'
|
'green'
|
'
purpl
e'
color
?:
'gold'
|
'cyan'
|
'green'
|
'
blu
e'
showLabel
?:
boolean
}
const
fillColor
Clas
s
:
Record
<
string
,
string
>
=
{
gold
:
'
progress-game-fill
'
,
cyan
:
'
progress-game-fill progress-game-fill
-cyan'
,
green
:
'
progress-game-fill progress-game-fill
-green'
,
purple
:
'progress-game-fill progress-game-fill-purpl
e'
,
const
fillColors
:
Record
<
string
,
string
>
=
{
gold
:
'
bg-gold
'
,
cyan
:
'
bg
-cyan'
,
green
:
'
bg
-green'
,
blue
:
'bg-royal-blu
e'
,
}
export
function
GameProgressBar
({
...
...
@@ -22,25 +22,15 @@ export function GameProgressBar({
return
(
<
div
className=
"w-full"
>
<
div
className=
"progress-game relative"
>
{
/* Fill bar */
}
<
div
className=
"h-3 rounded-[var(--radius-tiny)] bg-surface-3 overflow-hidden"
>
<
div
className=
{
`
${fillColorClass[color]} relative
`
}
className=
{
`
h-full rounded-[var(--radius-tiny)] ${fillColors[color]} transition-all duration-300
`
}
style=
{
{
width
:
`${percentage}%`
}
}
>
{
/* Segmented overlay */
}
<
div
className=
"absolute inset-0 opacity-20"
style=
{
{
background
:
'repeating-linear-gradient(90deg, transparent 0px, transparent 8px, rgba(0,0,0,0.3) 8px, rgba(0,0,0,0.3) 10px)'
,
}
}
/>
</
div
>
</
div
>
{
showLabel
&&
(
<
div
className=
"flex justify-between mt-1"
>
<
span
className=
"text-xs text-text-muted font-
bold
"
>
{
value
}
</
span
>
<
span
className=
"text-xs text-text-muted font-
medium
"
>
{
value
}
</
span
>
<
span
className=
"text-xs text-text-muted"
>
{
max
}
</
span
>
</
div
>
)
}
...
...
src/components/ui/RibbonHeader.tsx
View file @
0c0bcb97
...
...
@@ -3,66 +3,14 @@ import type { ComponentType } from 'react'
interface
RibbonHeaderProps
{
text
:
string
icon
?:
ComponentType
<
{
size
?:
number
;
className
?:
string
}
>
color
?:
'gold'
|
'cyan'
|
'purple'
|
'coral'
size
?:
'sm'
|
'md'
className
?:
string
}
const
colorStyles
=
{
gold
:
{
gradient
:
'linear-gradient(135deg, #FFC83D 0%, #C9972E 100%)'
,
border
:
'rgba(255, 200, 60, 0.6)'
,
text
:
'#1a1a2e'
,
},
cyan
:
{
gradient
:
'linear-gradient(135deg, #00E5CC 0%, #009E8C 100%)'
,
border
:
'rgba(0, 229, 204, 0.6)'
,
text
:
'#0a1a1a'
,
},
purple
:
{
gradient
:
'linear-gradient(135deg, #B44DFF 0%, #7B2EBF 100%)'
,
border
:
'rgba(180, 77, 255, 0.6)'
,
text
:
'#ffffff'
,
},
coral
:
{
gradient
:
'linear-gradient(135deg, #FF5252 0%, #BF2E2E 100%)'
,
border
:
'rgba(255, 82, 82, 0.6)'
,
text
:
'#ffffff'
,
},
}
const
sizeStyles
=
{
sm
:
{
padding
:
'6px 24px'
,
fontSize
:
'0.8rem'
,
iconSize
:
14
,
},
md
:
{
padding
:
'10px 36px'
,
fontSize
:
'0.95rem'
,
iconSize
:
18
,
},
}
export
function
RibbonHeader
({
text
,
icon
:
Icon
,
color
=
'gold'
,
size
=
'md'
}:
RibbonHeaderProps
)
{
const
colors
=
colorStyles
[
color
]
const
sizes
=
sizeStyles
[
size
]
export
function
RibbonHeader
({
text
,
icon
:
Icon
,
className
=
''
}:
RibbonHeaderProps
)
{
return
(
<
div
className=
"flex justify-center w-full"
>
<
div
className=
"ribbon-banner inline-flex items-center gap-2 font-bold"
style=
{
{
background
:
colors
.
gradient
,
color
:
colors
.
text
,
padding
:
sizes
.
padding
,
fontSize
:
sizes
.
fontSize
,
borderBottom
:
`3px solid ${colors.border}`
,
boxShadow
:
`0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2)`
,
}
}
>
{
Icon
&&
<
Icon
size=
{
sizes
.
iconSize
}
className=
"flex-shrink-0"
/>
}
<
span
>
{
text
}
</
span
>
</
div
>
<
div
className=
{
`flex items-center gap-2 mb-4 ${className}`
}
>
{
Icon
&&
<
Icon
size=
{
20
}
className=
"text-gold"
/>
}
<
h2
className=
"text-lg font-bold text-text-primary"
>
{
text
}
</
h2
>
</
div
>
)
}
src/components/ui/ShieldBadge.tsx
View file @
0c0bcb97
...
...
@@ -2,9 +2,8 @@ import type { ReactNode } from 'react'
interface
ShieldBadgeProps
{
children
:
ReactNode
size
?:
'sm'
|
'md'
|
'lg'
|
'xl'
color
?:
'gold'
|
'cyan'
|
'purple'
|
'coral'
|
'default'
glow
?:
boolean
size
?:
'sm'
|
'md'
|
'lg'
color
?:
'gold'
|
'cyan'
|
'blue'
|
'default'
className
?:
string
}
...
...
@@ -12,72 +11,26 @@ const sizeMap = {
sm
:
'w-10 h-10'
,
md
:
'w-14 h-14'
,
lg
:
'w-20 h-20'
,
xl
:
'w-28 h-28'
,
}
const
colorMap
=
{
default
:
{
bg
:
'bg-surface-2'
,
ring
:
'rgba(61, 69, 112, 0.8)'
,
glow
:
'none'
,
},
gold
:
{
bg
:
'bg-gradient-to-br from-[#FFC83D] to-[#C9972E]'
,
ring
:
'rgba(255, 200, 60, 0.8)'
,
glow
:
'0 0 20px rgba(255, 200, 60, 0.4)'
,
},
cyan
:
{
bg
:
'bg-gradient-to-br from-[#00E5CC] to-[#009E8C]'
,
ring
:
'rgba(0, 229, 204, 0.8)'
,
glow
:
'0 0 20px rgba(0, 229, 204, 0.4)'
,
},
purple
:
{
bg
:
'bg-gradient-to-br from-[#B44DFF] to-[#7B2EBF]'
,
ring
:
'rgba(180, 77, 255, 0.8)'
,
glow
:
'0 0 20px rgba(180, 77, 255, 0.4)'
,
},
coral
:
{
bg
:
'bg-gradient-to-br from-[#FF5252] to-[#BF2E2E]'
,
ring
:
'rgba(255, 82, 82, 0.8)'
,
glow
:
'0 0 20px rgba(255, 82, 82, 0.4)'
,
},
default
:
'bg-surface-2 border-border'
,
gold
:
'bg-surface-2 border-gold/40'
,
cyan
:
'bg-surface-2 border-cyan/40'
,
blue
:
'bg-surface-2 border-royal-blue/40'
,
}
export
function
ShieldBadge
({
children
,
size
=
'md'
,
color
=
'default'
,
glow
=
false
,
className
=
''
,
}:
ShieldBadgeProps
)
{
const
sizeClass
=
sizeMap
[
size
]
const
colorConfig
=
colorMap
[
color
]
return
(
<
div
className=
{
`relative inline-flex items-center justify-center ${sizeClass} ${className}`
}
style=
{
{
boxShadow
:
glow
?
colorConfig
.
glow
:
'none'
,
}
}
className=
{
`inline-flex items-center justify-center rounded-[var(--radius-standard)] border-2 ${sizeMap[size]} ${colorMap[color]} ${className}`
}
>
{
/* Outer ring */
}
<
div
className=
"absolute inset-0 clip-shield"
style=
{
{
background
:
colorConfig
.
ring
,
}
}
/>
{
/* Inner shield body */
}
<
div
className=
{
`absolute clip-shield ${colorConfig.bg} flex items-center justify-center`
}
style=
{
{
inset
:
'3px'
,
}
}
/>
{
/* Content */
}
<
div
className=
"relative z-10 flex items-center justify-center text-white font-bold"
>
{
children
}
</
div
>
</
div
>
)
}
src/index.css
View file @
0c0bcb97
This diff is collapsed.
Click to expand it.
src/pages/BotSelectPage.tsx
View file @
0c0bcb97
...
...
@@ -86,7 +86,7 @@ export function BotSelectPage() {
>
<
motion
.
div
onClick=
{
()
=>
setSelectedBot
(
bot
.
id
)
}
className=
{
`
game-panel
!p-4 flex items-center gap-4 relative overflow-hidden cursor-pointer transition-all ${
className=
{
`
card
!p-4 flex items-center gap-4 relative overflow-hidden cursor-pointer transition-all ${
isSelected ? '!border-[#FFC83D]' : ''
}`
}
style=
{
{
...
...
@@ -192,7 +192,7 @@ export function BotSelectPage() {
<
motion
.
button
onClick=
{
startGame
}
whileTap=
{
{
scale
:
0.95
}
}
className=
"
btn-3d
w-[80%] py-4 rounded-2xl bg-gradient-to-b from-[#FFC83D] to-[#E5A800] text-background text-lg font-black flex items-center justify-center gap-2"
className=
"w-[80%] py-4 rounded-2xl bg-gradient-to-b from-[#FFC83D] to-[#E5A800] text-background text-lg font-black flex items-center justify-center gap-2"
style=
{
{
boxShadow
:
'0 6px 0 #B8860B, 0 8px 20px rgba(255,200,61,0.3)'
}
}
>
<
Swords
size=
{
20
}
/>
...
...
src/pages/FriendsPage.tsx
View file @
0c0bcb97
...
...
@@ -128,7 +128,7 @@ export function FriendsPage() {
const
alreadySent
=
sentIds
.
has
(
result
.
id
)
return
(
<
motion
.
div
key=
{
result
.
id
}
variants=
{
item
}
>
<
div
className=
"
game-panel
!p-3.5 flex items-center gap-3"
>
<
div
className=
"
card
!p-3.5 flex items-center gap-3"
>
<
div
className=
"w-10 h-10 flex items-center justify-center shrink-0"
style=
{
{
clipPath
:
'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)'
}
}
>
...
...
@@ -152,7 +152,7 @@ export function FriendsPage() {
<
motion
.
button
whileTap=
{
{
scale
:
0.9
}
}
onClick=
{
()
=>
handleSendRequest
(
result
.
id
)
}
className=
"
btn-3d
px-4 py-2 rounded-xl bg-[#FFC83D] text-background text-xs font-black"
className=
"px-4 py-2 rounded-xl bg-[#FFC83D] text-background text-xs font-black"
>
اضافة
</
motion
.
button
>
...
...
@@ -180,7 +180,7 @@ export function FriendsPage() {
</
h2
>
<
div
className=
"flex flex-col gap-2.5"
>
{
pendingReceived
.
map
((
req
)
=>
(
<
div
key=
{
req
.
id
}
className=
"
game-panel-gol
d !p-3.5 flex items-center gap-3"
>
<
div
key=
{
req
.
id
}
className=
"
car
d !p-3.5 flex items-center gap-3"
>
<
div
className=
"w-11 h-11 flex items-center justify-center shrink-0"
style=
{
{
clipPath
:
'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)'
}
}
>
...
...
@@ -202,14 +202,14 @@ export function FriendsPage() {
<
motion
.
button
whileTap=
{
{
scale
:
0.85
}
}
onClick=
{
()
=>
acceptRequest
(
req
.
id
)
}
className=
"
btn-3d
px-3.5 py-2 rounded-xl bg-[#00E5CC] text-background text-xs font-black"
className=
"px-3.5 py-2 rounded-xl bg-[#00E5CC] text-background text-xs font-black"
>
قبول
</
motion
.
button
>
<
motion
.
button
whileTap=
{
{
scale
:
0.85
}
}
onClick=
{
()
=>
rejectRequest
(
req
.
id
)
}
className=
"
btn-3d
px-3.5 py-2 rounded-xl bg-[#FF5252] text-white text-xs font-black"
className=
"px-3.5 py-2 rounded-xl bg-[#FF5252] text-white text-xs font-black"
>
رفض
</
motion
.
button
>
...
...
@@ -229,7 +229,7 @@ export function FriendsPage() {
</
h2
>
<
div
className=
"flex flex-col gap-2.5"
>
{
onlineFriends
.
map
((
friend
)
=>
(
<
div
key=
{
friend
.
id
}
className=
"
game-panel
!p-3 flex items-center gap-3 relative overflow-hidden"
>
<
div
key=
{
friend
.
id
}
className=
"
card
!p-3 flex items-center gap-3 relative overflow-hidden"
>
{
/* Green accent stripe */
}
<
div
className=
"absolute right-0 top-0 bottom-0 w-[4px] bg-[#4ADE80] rounded-r-full"
/>
...
...
@@ -283,7 +283,7 @@ export function FriendsPage() {
</
h2
>
<
div
className=
"flex flex-col gap-2.5"
>
{
offlineFriends
.
map
((
friend
)
=>
(
<
div
key=
{
friend
.
id
}
className=
"
game-panel
!p-3 flex items-center gap-3 opacity-60 relative overflow-hidden"
>
<
div
key=
{
friend
.
id
}
className=
"
card
!p-3 flex items-center gap-3 opacity-60 relative overflow-hidden"
>
{
/* Gray accent stripe */
}
<
div
className=
"absolute right-0 top-0 bottom-0 w-[4px] bg-text-muted/30 rounded-r-full"
/>
...
...
src/pages/GamePage.tsx
View file @
0c0bcb97
...
...
@@ -423,7 +423,7 @@ function MultiplayerGameView({ matchId }: { matchId: string }) {
{
/* Action buttons */
}
<
div
className=
"flex items-center gap-4 mt-5"
>
<
Button
variant=
"
coral
"
variant=
"
danger
"
size=
"md"
onClick=
{
resign
}
disabled=
{
gameOver
}
...
...
@@ -913,7 +913,7 @@ function BotGameView({ botId }: { botId: string | undefined }) {
{
/* Action buttons */
}
<
div
className=
"flex items-center gap-4 mt-5"
>
<
Button
variant=
"
coral
"
variant=
"
danger
"
size=
"md"
onClick=
{
()
=>
{
setGameOver
(
true
);
setResult
(
'استسلام'
);
playSound
(
'lose'
)
}
}
>
...
...
src/pages/HomePage.tsx
View file @
0c0bcb97
This diff is collapsed.
Click to expand it.
src/pages/MatchmakingPage.tsx
View file @
0c0bcb97
...
...
@@ -178,9 +178,9 @@ export function MatchmakingPage() {
جاري البحث عن خصم
</
motion
.
h2
>
{
/* Timer in a
game-panel
mini-bar */
}
{
/* Timer in a
card
mini-bar */
}
<
motion
.
div
className=
"mt-4
game-panel
!py-2 !px-6 inline-flex items-center justify-center"
className=
"mt-4
card
!py-2 !px-6 inline-flex items-center justify-center"
initial=
{
{
opacity
:
0
}
}
animate=
{
{
opacity
:
1
}
}
transition=
{
{
delay
:
0.4
}
}
...
...
@@ -217,7 +217,7 @@ export function MatchmakingPage() {
<
motion
.
button
whileTap=
{
{
scale
:
0.93
}
}
onClick=
{
handleCancel
}
className=
"
btn-3d
px-8 py-3 rounded-xl bg-surface-2 border-3 border-border text-text-muted text-sm font-black"
className=
"px-8 py-3 rounded-xl bg-surface-2 border-3 border-border text-text-muted text-sm font-black"
style=
{
{
boxShadow
:
'0 4px 0 rgba(0,0,0,0.3)'
}
}
>
الغاء
...
...
src/pages/PlayPage.tsx
View file @
0c0bcb97
This diff is collapsed.
Click to expand it.
src/pages/SettingsPage.tsx
View file @
0c0bcb97
...
...
@@ -25,7 +25,7 @@ export function SettingsPage() {
<
div
className=
"flex flex-col gap-3"
>
{
/* Sound toggle */
}
<
div
className=
"
game-panel
!p-4 flex items-center justify-between"
>
<
div
className=
"
card
!p-4 flex items-center justify-between"
>
<
div
className=
"flex items-center gap-3"
>
{
soundEnabled
?
(
<
div
className=
"w-10 h-10 rounded-xl bg-[#00E5CC]/15 border-2 border-[#00E5CC]/30 flex items-center justify-center"
>
...
...
@@ -63,7 +63,7 @@ export function SettingsPage() {
</
div
>
{
/* Report bug */
}
<
div
className=
"
game-panel
!p-4 flex items-center gap-3"
>
<
div
className=
"
card
!p-4 flex items-center gap-3"
>
<
div
className=
"w-10 h-10 rounded-xl bg-[#B44DFF]/15 border-2 border-[#B44DFF]/30 flex items-center justify-center"
>
<
Bug
size=
{
18
}
className=
"text-[#B44DFF]"
/>
</
div
>
...
...
@@ -74,7 +74,7 @@ export function SettingsPage() {
</
div
>
{
/* Version info - recessed panel */
}
<
div
className=
"
game-panel
!p-4 flex items-center gap-3"
<
div
className=
"
card
!p-4 flex items-center gap-3"
style=
{
{
boxShadow
:
'inset 0 3px 8px rgba(0,0,0,0.3), inset 0 1px 2px rgba(0,0,0,0.2)'
}
}
>
<
div
className=
"w-10 h-10 rounded-xl bg-[#FFC83D]/15 border-2 border-[#FFC83D]/30 flex items-center justify-center"
>
...
...
@@ -92,7 +92,7 @@ export function SettingsPage() {
<
motion
.
button
whileTap=
{
{
scale
:
0.93
}
}
onClick=
{
handleLogout
}
className=
"
btn-3d
flex items-center justify-center gap-2.5 w-[70%] px-6 py-3.5 rounded-2xl bg-[#FF5252] text-white font-black text-sm"
className=
"flex items-center justify-center gap-2.5 w-[70%] px-6 py-3.5 rounded-2xl bg-[#FF5252] text-white font-black text-sm"
>
<
LogOut
size=
{
16
}
/>
<
span
>
تسجيل الخروج
</
span
>
...
...
src/pages/ShopPage.tsx
View file @
0c0bcb97
...
...
@@ -242,7 +242,7 @@ export function ShopPage() {
onClick=
{
()
=>
!
purchasing
&&
setSelectedItem
(
null
)
}
>
<
motion
.
div
className=
"w-full max-w-[340px]
game-panel
!p-0 flex flex-col items-center relative overflow-hidden"
className=
"w-full max-w-[340px]
card
!p-0 flex flex-col items-center relative overflow-hidden"
initial=
{
{
scale
:
0.7
,
opacity
:
0
,
rotate
:
-
3
}
}
animate=
{
{
scale
:
1
,
opacity
:
1
,
rotate
:
0
}
}
exit=
{
{
scale
:
0.7
,
opacity
:
0
,
rotate
:
3
}
}
...
...
@@ -333,7 +333,7 @@ export function ShopPage() {
whileTap=
{
{
scale
:
0.93
}
}
onClick=
{
handleEquip
}
disabled=
{
purchasing
}
className=
"
btn-3d
w-full py-3 rounded-xl bg-[#00E5CC] text-background text-sm font-black disabled:opacity-50"
className=
"w-full py-3 rounded-xl bg-[#00E5CC] text-background text-sm font-black disabled:opacity-50"
>
{
purchasing
?
'جاري...'
:
'تفعيل'
}
</
motion
.
button
>
...
...
@@ -365,7 +365,7 @@ export function ShopPage() {
?
(
profile
?.
gems
||
0
)
<
(
selectedItem
.
price_gems
||
0
)
:
(
profile
?.
coins
||
0
)
<
(
selectedItem
.
price_coins
||
0
))
}
className=
"
btn-3d
w-full py-3 rounded-xl bg-gradient-to-b from-[#FFC83D] to-[#E5A800] text-background text-sm font-black disabled:opacity-50 disabled:grayscale"
className=
"w-full py-3 rounded-xl bg-gradient-to-b from-[#FFC83D] to-[#E5A800] text-background text-sm font-black disabled:opacity-50 disabled:grayscale"
>
{
purchasing
?
'جاري...'
:
'شراء'
}
</
motion
.
button
>
...
...
src/pages/TournamentsPage.tsx
View file @
0c0bcb97
...
...
@@ -84,7 +84,7 @@ export function TournamentsPage() {
{
loading
?
(
<
div
className=
"flex flex-col gap-4"
>
{
[
1
,
2
,
3
].
map
((
i
)
=>
(
<
div
key=
{
i
}
className=
"
game-panel
animate-pulse !p-5"
>
<
div
key=
{
i
}
className=
"
card
animate-pulse !p-5"
>
<
div
className=
"w-36 h-5 rounded bg-surface-3 mb-3"
/>
<
div
className=
"w-24 h-3 rounded bg-surface-3"
/>
</
div
>
...
...
@@ -122,7 +122,7 @@ export function TournamentsPage() {
exit=
{
{
opacity
:
0
,
y
:
-
16
}
}
transition=
{
{
type
:
'spring'
,
stiffness
:
400
,
damping
:
24
,
delay
:
i
*
0.06
}
}
>
<
div
className=
"
game-panel
relative overflow-hidden !p-5"
>
<
div
className=
"
card
relative overflow-hidden !p-5"
>
{
/* Status ribbon - diagonal corner badge */
}
<
div
className=
"absolute top-0 left-0 w-28 h-7 flex items-center justify-center"
...
...
@@ -207,7 +207,7 @@ export function TournamentsPage() {
<
motion
.
button
whileTap=
{
{
scale
:
0.93
}
}
onClick=
{
()
=>
unregister
(
t
.
id
)
}
className=
"
btn-3d
px-3.5 py-2 rounded-xl bg-[#FF5252] text-white text-xs font-black"
className=
"px-3.5 py-2 rounded-xl bg-[#FF5252] text-white text-xs font-black"
>
إلغاء
</
motion
.
button
>
...
...
@@ -216,7 +216,7 @@ export function TournamentsPage() {
<
motion
.
button
whileTap=
{
{
scale
:
0.93
}
}
onClick=
{
()
=>
register
(
t
.
id
)
}
className=
"
btn-3d
w-[75%] py-3 rounded-xl bg-gradient-to-b from-[#FFC83D] to-[#E5A800] text-background text-sm font-black"
className=
"w-[75%] py-3 rounded-xl bg-gradient-to-b from-[#FFC83D] to-[#E5A800] text-background text-sm font-black"
>
سجل الان
</
motion
.
button
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment