Commit e8043f26 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: admin branding — English labels, effective color saving, full coverage

- All Arabic labels replaced with English descriptive labels
- API save_theme now also writes local theme.json (prevents stale overrides)
- Added Ludo Board Colors section (9 controls)
- Added Backgammon Board Colors section (14 controls)
- Page lang/dir switched from ar/rtl to en/ltr
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 9b2a459c
......@@ -13,9 +13,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['logout'])) {
}
if (!($_SESSION['branding_auth'] ?? false)) {
?><!DOCTYPE html><html lang="ar" dir="rtl"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>EL3AB Branding Admin</title>
?><!DOCTYPE html><html lang="en" dir="ltr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>EL3AB Branding Admin</title>
<style>body{background:#0a0a1a;color:#f8fafc;font-family:'IBM Plex Sans Arabic',sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}form{background:#1a1a2e;padding:40px;border-radius:16px;width:320px;text-align:center}input{width:100%;padding:12px;margin:8px 0;background:#2a2a4a;border:1px solid #333;border-radius:8px;color:#fff;font-size:14px}button{width:100%;padding:12px;margin-top:16px;background:linear-gradient(135deg,#E4AC38,#FFCC66);border:none;border-radius:8px;color:#1a1a1a;font-weight:700;font-size:15px;cursor:pointer}h2{color:#E4AC38;margin-bottom:16px}</style></head><body>
<form method="POST"><h2>EL3AB Branding</h2><input name="username" placeholder="Username" required><input name="password" type="password" placeholder="Password" required><button name="login" value="1">دخول</button></form></body></html><?php
<form method="POST"><h2>EL3AB Branding</h2><input name="username" placeholder="Username" required><input name="password" type="password" placeholder="Password" required><button name="login" value="1">Login</button></form></body></html><?php
exit;
}
......@@ -125,7 +125,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
}
?>
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
......@@ -159,34 +159,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</head>
<body>
<div class="container">
<form method="POST" style="display:inline;"><button class="logout" name="logout" value="1">خروج</button></form>
<form method="POST" style="display:inline;"><button class="logout" name="logout" value="1">Logout</button></form>
<h1>🎨 EL3AB Branding Admin</h1>
<p style="color:#64748b;font-size:13px;margin-bottom:24px;">إدارة الهوية البصرية — الألوان والأيقونات والأصول تظهر كافتراضي لكل اللاعبين</p>
<p style="color:#64748b;font-size:13px;margin-bottom:24px;">Visual identity management — colors, icons, and assets appear as defaults for all players</p>
<?php if (isset($_POST['save_theme'])): ?><div class="saved">تم حفظ التغييرات</div><?php endif; ?>
<?php if (isset($_POST['save_theme'])): ?><div class="saved">Changes saved</div><?php endif; ?>
<form method="POST">
<input type="hidden" name="save_theme" value="1">
<!-- COLORS -->
<h2>🎨 الألوان الأساسية</h2>
<h2>🎨 Core Colors</h2>
<div class="section">
<div class="grid">
<?php
$colors = [
['key' => 'bg_deep', 'label' => 'خلفية عميقة', 'default' => '#050810', 'hint' => 'أغمق طبقة في التطبيق'],
['key' => 'bg_base', 'label' => 'خلفية أساسية', 'default' => '#0A1020', 'hint' => 'خلفية الشاشات'],
['key' => 'bg_card', 'label' => 'خلفية البطاقات', 'default' => '#121A2E', 'hint' => 'سطح البطاقات'],
['key' => 'bg_elevated', 'label' => 'خلفية مرتفعة', 'default' => '#1A2440', 'hint' => 'الحقول والعناصر المرتفعة'],
['key' => 'gold', 'label' => 'الذهبي (العلامة التجارية)', 'default' => '#E4AC38', 'hint' => 'اللون الرئيسي، الأزرار، الجوائز'],
['key' => 'gold_soft', 'label' => 'ذهبي فاتح', 'default' => '#FFCC66', 'hint' => 'تدرج الأزرار الذهبية'],
['key' => 'blue', 'label' => 'الأزرق', 'default' => '#2082F0', 'hint' => 'الروابط، المنافسة'],
['key' => 'cyan', 'label' => 'السماوي', 'default' => '#00FFFF', 'hint' => 'تأثيرات التوهج'],
['key' => 'purple', 'label' => 'البنفسجي', 'default' => '#6834BE', 'hint' => 'النادر/الأسطوري'],
['key' => 'success', 'label' => 'النجاح/الفوز', 'default' => '#34D399', 'hint' => 'لون الفوز والنجاح'],
['key' => 'error', 'label' => 'الخطأ/الخسارة', 'default' => '#EF4444', 'hint' => 'لون الخسارة والخطأ'],
['key' => 'text_primary', 'label' => 'نص أساسي', 'default' => '#F8FAFC', 'hint' => 'لون النص الرئيسي'],
['key' => 'text_secondary', 'label' => 'نص ثانوي', 'default' => '#94A3B8', 'hint' => 'نص أقل أهمية'],
['key' => 'bg_deep', 'label' => 'Deep Background', 'default' => '#050810', 'hint' => 'Darkest layer in the app'],
['key' => 'bg_base', 'label' => 'Base Background', 'default' => '#0A1020', 'hint' => 'Screen background'],
['key' => 'bg_card', 'label' => 'Card Background', 'default' => '#121A2E', 'hint' => 'Card surfaces'],
['key' => 'bg_elevated', 'label' => 'Elevated Background', 'default' => '#1A2440', 'hint' => 'Inputs & elevated elements'],
['key' => 'gold', 'label' => 'Gold (Brand)', 'default' => '#E4AC38', 'hint' => 'Primary brand, buttons, rewards'],
['key' => 'gold_soft', 'label' => 'Gold Light', 'default' => '#FFCC66', 'hint' => 'Gold button gradient end'],
['key' => 'blue', 'label' => 'Blue', 'default' => '#2082F0', 'hint' => 'Links, competitive actions'],
['key' => 'cyan', 'label' => 'Cyan', 'default' => '#00FFFF', 'hint' => 'Glow effects'],
['key' => 'purple', 'label' => 'Purple', 'default' => '#6834BE', 'hint' => 'Rare/Legendary tier'],
['key' => 'success', 'label' => 'Success / Win', 'default' => '#34D399', 'hint' => 'Win & success states'],
['key' => 'error', 'label' => 'Error / Loss', 'default' => '#EF4444', 'hint' => 'Loss & error states'],
['key' => 'text_primary', 'label' => 'Primary Text', 'default' => '#F8FAFC', 'hint' => 'Main text color'],
['key' => 'text_secondary', 'label' => 'Secondary Text', 'default' => '#94A3B8', 'hint' => 'Muted/secondary text'],
];
foreach ($colors as $c):
$val = $theme[$c['key']] ?? $c['default'];
......@@ -204,19 +204,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- GAME COLORS -->
<h2>🎮 ألوان الألعاب</h2>
<h2>🎮 Game Colors</h2>
<div class="section">
<div class="grid">
<?php
$gameColors = [
['key' => 'chess_primary', 'label' => 'شطرنج — أساسي', 'default' => '#2563EB'],
['key' => 'chess_secondary', 'label' => 'شطرنج — ثانوي', 'default' => '#F5B731'],
['key' => 'domino_primary', 'label' => 'دومينو — أساسي', 'default' => '#10B981'],
['key' => 'domino_secondary', 'label' => 'دومينو — ثانوي', 'default' => '#06B6D4'],
['key' => 'ludo_primary', 'label' => 'لودو — أساسي', 'default' => '#8B5CF6'],
['key' => 'ludo_secondary', 'label' => 'لودو — ثانوي', 'default' => '#EC4899'],
['key' => 'backgammon_primary', 'label' => 'طاولة — أساسي', 'default' => '#F59E0B'],
['key' => 'backgammon_secondary', 'label' => 'طاولة — ثانوي', 'default' => '#EF4444'],
['key' => 'chess_primary', 'label' => 'Chess — Primary', 'default' => '#2563EB'],
['key' => 'chess_secondary', 'label' => 'Chess — Secondary', 'default' => '#F5B731'],
['key' => 'domino_primary', 'label' => 'Domino — Primary', 'default' => '#10B981'],
['key' => 'domino_secondary', 'label' => 'Domino — Secondary', 'default' => '#06B6D4'],
['key' => 'ludo_primary', 'label' => 'Ludo — Primary', 'default' => '#8B5CF6'],
['key' => 'ludo_secondary', 'label' => 'Ludo — Secondary', 'default' => '#EC4899'],
['key' => 'backgammon_primary', 'label' => 'Backgammon — Primary', 'default' => '#F59E0B'],
['key' => 'backgammon_secondary', 'label' => 'Backgammon — Secondary', 'default' => '#EF4444'],
];
foreach ($gameColors as $c):
$val = $theme[$c['key']] ?? $c['default'];
......@@ -233,16 +233,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- CHESS BOARD -->
<h2>ألوان رقعة الشطرنج</h2>
<h2>Chess Board Colors</h2>
<div class="section">
<div class="grid">
<?php
$boardColors = [
['key' => 'board_light', 'label' => 'مربع فاتح', 'default' => '#F0D9B5', 'hint' => 'لون المربعات الفاتحة'],
['key' => 'board_dark', 'label' => 'مربع غامق', 'default' => '#B58863', 'hint' => 'لون المربعات الغامقة'],
['key' => 'board_highlight_light', 'label' => 'تمييز فاتح', 'default' => '#CDD16A', 'hint' => 'آخر نقلة — مربع فاتح'],
['key' => 'board_highlight_dark', 'label' => 'تمييز غامق', 'default' => '#AAA23A', 'hint' => 'آخر نقلة — مربع غامق'],
['key' => 'board_check', 'label' => 'تهديد الملك', 'default' => '#FF6B6B', 'hint' => 'عندما يكون الملك مكشوف'],
['key' => 'board_light', 'label' => 'Light Square', 'default' => '#F0D9B5', 'hint' => 'Light squares on the board'],
['key' => 'board_dark', 'label' => 'Dark Square', 'default' => '#B58863', 'hint' => 'Dark squares on the board'],
['key' => 'board_highlight_light', 'label' => 'Move Highlight (Light)', 'default' => '#CDD16A', 'hint' => 'Last move — light square'],
['key' => 'board_highlight_dark', 'label' => 'Move Highlight (Dark)', 'default' => '#AAA23A', 'hint' => 'Last move — dark square'],
['key' => 'board_check', 'label' => 'King in Check', 'default' => '#FF6B6B', 'hint' => 'When the king is under attack'],
];
foreach ($boardColors as $c):
$val = $theme[$c['key']] ?? $c['default'];
......@@ -259,20 +259,87 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
</div>
<!-- LUDO BOARD COLORS -->
<h2>🎲 Ludo Board Colors</h2>
<div class="section">
<div class="grid">
<?php
$ludoBoardColors = [
['key' => 'ludo_red', 'label' => 'Ludo Red', 'default' => '#E53935', 'hint' => 'Red player zone'],
['key' => 'ludo_blue', 'label' => 'Ludo Blue', 'default' => '#1E88E5', 'hint' => 'Blue player zone'],
['key' => 'ludo_green', 'label' => 'Ludo Green', 'default' => '#43A047', 'hint' => 'Green player zone'],
['key' => 'ludo_yellow', 'label' => 'Ludo Yellow', 'default' => '#FDD835', 'hint' => 'Yellow player zone'],
['key' => 'ludo_board_bg', 'label' => 'Board Background', 'default' => '#FAFAFA', 'hint' => 'Main board surface'],
['key' => 'ludo_path', 'label' => 'Path Color', 'default' => '#FFFFFF', 'hint' => 'Walking path tiles'],
['key' => 'ludo_safe', 'label' => 'Safe Spot', 'default' => '#FFD700', 'hint' => 'Star/safe zone marker'],
['key' => 'ludo_piece_border', 'label' => 'Piece Border', 'default' => '#FFFFFF', 'hint' => 'Ring around pieces'],
['key' => 'ludo_grid_line', 'label' => 'Grid Lines', 'default' => '#E0E0E0', 'hint' => 'Board grid lines'],
];
foreach ($ludoBoardColors as $c):
$val = $theme[$c['key']] ?? $c['default'];
?>
<div class="field">
<label><?= $c['label'] ?></label>
<div class="color-row">
<input type="color" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
<input type="text" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
</div>
<div class="hint"><?= $c['hint'] ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- BACKGAMMON BOARD COLORS -->
<h2>🎯 Backgammon Board Colors</h2>
<div class="section">
<div class="grid">
<?php
$bgBoardColors = [
['key' => 'bg_board_border', 'label' => 'Board Border', 'default' => '#5C3A0E', 'hint' => 'Outer frame'],
['key' => 'bg_board_light', 'label' => 'Board Light', 'default' => '#3D6B1E', 'hint' => 'Light felt area'],
['key' => 'bg_board_dark', 'label' => 'Board Dark', 'default' => '#2D5016', 'hint' => 'Dark felt area'],
['key' => 'bg_bar_dark', 'label' => 'Bar Dark', 'default' => '#2A1800', 'hint' => 'Center bar dark'],
['key' => 'bg_bar_light', 'label' => 'Bar Light', 'default' => '#3D2B1A', 'hint' => 'Center bar light'],
['key' => 'bg_bearoff', 'label' => 'Bear-off Area', 'default' => '#1A0E00', 'hint' => 'Bear-off tray'],
['key' => 'bg_point_dark', 'label' => 'Dark Point', 'default' => '#8B4513', 'hint' => 'Dark triangle base'],
['key' => 'bg_point_dark_tip', 'label' => 'Dark Point Tip', 'default' => '#5C2D0A', 'hint' => 'Dark triangle tip'],
['key' => 'bg_point_light', 'label' => 'Light Point', 'default' => '#D2691E', 'hint' => 'Light triangle base'],
['key' => 'bg_point_light_tip', 'label' => 'Light Point Tip', 'default' => '#A0522D', 'hint' => 'Light triangle tip'],
['key' => 'bg_checker_white', 'label' => 'White Checker', 'default' => '#F5E6D3', 'hint' => 'White player pieces'],
['key' => 'bg_checker_black', 'label' => 'Black Checker', 'default' => '#2A2A2A', 'hint' => 'Black player pieces'],
['key' => 'bg_highlight', 'label' => 'Move Highlight', 'default' => '#10B981', 'hint' => 'Valid move indicator'],
['key' => 'bg_selected', 'label' => 'Selected Piece', 'default' => '#FFD700', 'hint' => 'Selected checker glow'],
];
foreach ($bgBoardColors as $c):
$val = $theme[$c['key']] ?? $c['default'];
?>
<div class="field">
<label><?= $c['label'] ?></label>
<div class="color-row">
<input type="color" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
<input type="text" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
</div>
<div class="hint"><?= $c['hint'] ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- CHESS PIECES -->
<h2>قطع الشطرنج</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع صور PNG أو SVG لكل قطعة — ستظهر على الرقعة مباشرة بدل الرسم الافتراضي</p>
<h2>Chess Pieces</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">Upload PNG or SVG images for each piece — they appear on the board immediately</p>
<div class="section">
<h3 style="color:#f8fafc;margin-bottom:12px;">القطع البيضاء</h3>
<h3 style="color:#f8fafc;margin-bottom:12px;">White Pieces</h3>
<div class="grid">
<?php
$whitePieces = [
['slot' => 'chess_piece_wK', 'label' => 'ملك أبيض ♔', 'w' => 128, 'h' => 128, 'hint' => 'White King'],
['slot' => 'chess_piece_wQ', 'label' => 'وزير أبيض ♕', 'w' => 128, 'h' => 128, 'hint' => 'White Queen'],
['slot' => 'chess_piece_wR', 'label' => 'قلعة بيضاء ♖', 'w' => 128, 'h' => 128, 'hint' => 'White Rook'],
['slot' => 'chess_piece_wB', 'label' => 'فيل أبيض ♗', 'w' => 128, 'h' => 128, 'hint' => 'White Bishop'],
['slot' => 'chess_piece_wN', 'label' => 'حصان أبيض ♘', 'w' => 128, 'h' => 128, 'hint' => 'White Knight'],
['slot' => 'chess_piece_wP', 'label' => 'بيدق أبيض ♙', 'w' => 128, 'h' => 128, 'hint' => 'White Pawn'],
['slot' => 'chess_piece_wK', 'label' => 'White King ♔', 'w' => 128, 'h' => 128, 'hint' => 'White King'],
['slot' => 'chess_piece_wQ', 'label' => 'White Queen ♕', 'w' => 128, 'h' => 128, 'hint' => 'White Queen'],
['slot' => 'chess_piece_wR', 'label' => 'White Rook ♖', 'w' => 128, 'h' => 128, 'hint' => 'White Rook'],
['slot' => 'chess_piece_wB', 'label' => 'White Bishop ♗', 'w' => 128, 'h' => 128, 'hint' => 'White Bishop'],
['slot' => 'chess_piece_wN', 'label' => 'White Knight ♘', 'w' => 128, 'h' => 128, 'hint' => 'White Knight'],
['slot' => 'chess_piece_wP', 'label' => 'White Pawn ♙', 'w' => 128, 'h' => 128, 'hint' => 'White Pawn'],
];
foreach ($whitePieces as $a):
$current = $theme['assets'][$a['slot']] ?? null;
......@@ -287,9 +354,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<input type="file" name="asset" accept=".svg,.png,.webp" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:64px;height:64px;object-fit:contain;background:#F0D9B5;border-radius:8px;padding:4px;"></div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">مرفوع</div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">Uploaded</div>
<?php else: ?>
📤 اضغط للرفع
📤 Click to upload
<?php endif; ?>
<div class="size-hint"><?= $a['w'] ?>×<?= $a['h'] ?>px — <?= $a['hint'] ?></div>
</div>
......@@ -298,16 +365,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<?php endforeach; ?>
</div>
<h3 style="color:#f8fafc;margin:20px 0 12px;">القطع السوداء</h3>
<h3 style="color:#f8fafc;margin:20px 0 12px;">Black Pieces</h3>
<div class="grid">
<?php
$blackPieces = [
['slot' => 'chess_piece_bK', 'label' => 'ملك أسود ♚', 'w' => 128, 'h' => 128, 'hint' => 'Black King'],
['slot' => 'chess_piece_bQ', 'label' => 'وزير أسود ♛', 'w' => 128, 'h' => 128, 'hint' => 'Black Queen'],
['slot' => 'chess_piece_bR', 'label' => 'قلعة سوداء ♜', 'w' => 128, 'h' => 128, 'hint' => 'Black Rook'],
['slot' => 'chess_piece_bB', 'label' => 'فيل أسود ♝', 'w' => 128, 'h' => 128, 'hint' => 'Black Bishop'],
['slot' => 'chess_piece_bN', 'label' => 'حصان أسود ♞', 'w' => 128, 'h' => 128, 'hint' => 'Black Knight'],
['slot' => 'chess_piece_bP', 'label' => 'بيدق أسود ♟', 'w' => 128, 'h' => 128, 'hint' => 'Black Pawn'],
['slot' => 'chess_piece_bK', 'label' => 'Black King ♚', 'w' => 128, 'h' => 128, 'hint' => 'Black King'],
['slot' => 'chess_piece_bQ', 'label' => 'Black Queen ♛', 'w' => 128, 'h' => 128, 'hint' => 'Black Queen'],
['slot' => 'chess_piece_bR', 'label' => 'Black Rook ♜', 'w' => 128, 'h' => 128, 'hint' => 'Black Rook'],
['slot' => 'chess_piece_bB', 'label' => 'Black Bishop ♝', 'w' => 128, 'h' => 128, 'hint' => 'Black Bishop'],
['slot' => 'chess_piece_bN', 'label' => 'Black Knight ♞', 'w' => 128, 'h' => 128, 'hint' => 'Black Knight'],
['slot' => 'chess_piece_bP', 'label' => 'Black Pawn ♟', 'w' => 128, 'h' => 128, 'hint' => 'Black Pawn'],
];
foreach ($blackPieces as $a):
$current = $theme['assets'][$a['slot']] ?? null;
......@@ -322,9 +389,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<input type="file" name="asset" accept=".svg,.png,.webp" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:64px;height:64px;object-fit:contain;background:#B58863;border-radius:8px;padding:4px;"></div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">مرفوع</div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">Uploaded</div>
<?php else: ?>
📤 اضغط للرفع
📤 Click to upload
<?php endif; ?>
<div class="size-hint"><?= $a['w'] ?>×<?= $a['h'] ?>px — <?= $a['hint'] ?></div>
</div>
......@@ -334,86 +401,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
</div>
<!-- LUDO BOARD -->
<h2>🎲 Ludo Board Colors</h2>
<div class="section">
<div class="grid">
<?php
$ludoColors = [
['key' => 'ludo_red', 'label' => 'Red player zone', 'default' => '#E53935', 'hint' => 'Red home zone + pieces'],
['key' => 'ludo_blue', 'label' => 'Blue player zone', 'default' => '#1E88E5', 'hint' => 'Blue home zone + pieces'],
['key' => 'ludo_green', 'label' => 'Green player zone', 'default' => '#43A047', 'hint' => 'Green home zone + pieces'],
['key' => 'ludo_yellow', 'label' => 'Yellow player zone', 'default' => '#FDD835', 'hint' => 'Yellow home zone + pieces'],
['key' => 'ludo_board_bg', 'label' => 'Board background', 'default' => '#FAFAFA', 'hint' => 'White board surface'],
['key' => 'ludo_path', 'label' => 'Path cells', 'default' => '#FFFFFF', 'hint' => 'Shared path square color'],
['key' => 'ludo_safe', 'label' => 'Safe square star', 'default' => '#F9A825', 'hint' => 'Star on safe positions'],
['key' => 'ludo_piece_border', 'label' => 'Piece border', 'default' => '#FFFFFF', 'hint' => 'White stroke around pieces'],
['key' => 'ludo_grid_line', 'label' => 'Grid lines', 'default' => '#DDDDDD', 'hint' => 'Grid lines on board'],
];
foreach ($ludoColors as $c):
$val = $theme[$c['key']] ?? $c['default'];
?>
<div class="field">
<label><?= $c['label'] ?></label>
<div class="color-row">
<input type="color" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
<input type="text" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
</div>
<div class="hint"><?= $c['hint'] ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- BACKGAMMON BOARD -->
<h2>🎲 ألوان لوحة الطاولة (Backgammon)</h2>
<div class="section">
<div class="grid">
<?php
$bgColors = [
['key' => 'bg_board_border', 'label' => 'إطار اللوح', 'default' => '#5C3A0E', 'hint' => 'الحافة الخارجية للبورد'],
['key' => 'bg_board_light', 'label' => 'سطح اللوح فاتح', 'default' => '#3D6B1E', 'hint' => 'تدرج سطح اللوحة — فاتح'],
['key' => 'bg_board_dark', 'label' => 'سطح اللوح غامق', 'default' => '#2D5016', 'hint' => 'تدرج سطح اللوحة — غامق'],
['key' => 'bg_bar_dark', 'label' => 'البار — غامق', 'default' => '#2A1800', 'hint' => 'الشريط الوسطي'],
['key' => 'bg_bar_light', 'label' => 'البار — فاتح', 'default' => '#3D2B1A', 'hint' => 'منتصف شريط البار'],
['key' => 'bg_bearoff', 'label' => 'منطقة التطليع', 'default' => '#1A0E00', 'hint' => 'Bear-off tray'],
['key' => 'bg_point_dark', 'label' => 'مثلث غامق', 'default' => '#8B4513', 'hint' => 'أطراف المثلثات الداكنة'],
['key' => 'bg_point_dark_tip', 'label' => 'رأس مثلث غامق', 'default' => '#5C2D0A', 'hint' => 'رأس المثلث الداكن (تدرج)'],
['key' => 'bg_point_light', 'label' => 'مثلث فاتح', 'default' => '#D2691E', 'hint' => 'أطراف المثلثات الفاتحة'],
['key' => 'bg_point_light_tip', 'label' => 'رأس مثلث فاتح', 'default' => '#A0522D', 'hint' => 'رأس المثلث الفاتح (تدرج)'],
['key' => 'bg_checker_white', 'label' => 'قطعة بيضاء', 'default' => '#F5E6D3', 'hint' => 'لون القطع البيضاء'],
['key' => 'bg_checker_black', 'label' => 'قطعة سوداء', 'default' => '#2A2A2A', 'hint' => 'لون القطع السوداء'],
['key' => 'bg_highlight', 'label' => 'تمييز الحركة', 'default' => '#10B981', 'hint' => 'لون الأماكن المتاحة'],
['key' => 'bg_selected', 'label' => 'القطعة المختارة', 'default' => '#FFD700', 'hint' => 'توهج القطعة المختارة'],
];
foreach ($bgColors as $c):
$val = $theme[$c['key']] ?? $c['default'];
?>
<div class="field">
<label><?= $c['label'] ?></label>
<div class="color-row">
<input type="color" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
<input type="text" name="theme[<?= $c['key'] ?>]" value="<?= $val ?>">
</div>
<div class="hint"><?= $c['hint'] ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- BACKGAMMON SETTINGS -->
<h2>🎲 إعدادات الطاولة</h2>
<h2>🎲 Backgammon Settings</h2>
<div class="section">
<div class="grid">
<?php
$bgSettings = [
['key' => 'bg_move_duration', 'label' => 'مدة حركة القطعة (ms)', 'default' => '320', 'hint' => 'سرعة انتقال القطعة (300-500)', 'type' => 'number'],
['key' => 'bg_dice_duration', 'label' => 'مدة رمي النرد (ms)', 'default' => '700', 'hint' => 'مدة أنيميشن النرد (500-900)', 'type' => 'number'],
['key' => 'bg_bot_delay', 'label' => 'تأخير البوت (ms)', 'default' => '700', 'hint' => 'تأخير قبل حركة البوت', 'type' => 'number'],
['key' => 'bg_particle_count', 'label' => 'عدد جسيمات الضرب', 'default' => '12', 'hint' => 'جسيمات عند ضرب قطعة (0=إلغاء)', 'type' => 'number'],
['key' => 'bg_arc_height', 'label' => 'ارتفاع قوس الحركة', 'default' => '0.4', 'hint' => 'نسبة ارتفاع القوس (0-1)', 'type' => 'text'],
['key' => 'bg_turn_timeout', 'label' => 'مهلة الدور (ثانية)', 'default' => '30', 'hint' => 'المهلة للعب أونلاين', 'type' => 'number'],
['key' => 'bg_confetti_count', 'label' => 'عدد جسيمات الفوز', 'default' => '30', 'hint' => 'عدد الكونفيتي عند الفوز', 'type' => 'number'],
['key' => 'bg_move_duration', 'label' => 'Move Duration (ms)', 'default' => '320', 'hint' => 'Piece travel speed (300-500)', 'type' => 'number'],
['key' => 'bg_dice_duration', 'label' => 'Dice Roll Duration (ms)', 'default' => '700', 'hint' => 'Dice animation length (500-900)', 'type' => 'number'],
['key' => 'bg_bot_delay', 'label' => 'Bot Delay (ms)', 'default' => '700', 'hint' => 'Delay before bot moves', 'type' => 'number'],
['key' => 'bg_particle_count', 'label' => 'Hit Particles', 'default' => '12', 'hint' => 'Particles on hit (0=disable)', 'type' => 'number'],
['key' => 'bg_arc_height', 'label' => 'Move Arc Height', 'default' => '0.4', 'hint' => 'Arc ratio (0-1)', 'type' => 'text'],
['key' => 'bg_turn_timeout', 'label' => 'Turn Timeout (sec)', 'default' => '30', 'hint' => 'Online play timeout', 'type' => 'number'],
['key' => 'bg_confetti_count', 'label' => 'Win Confetti Count', 'default' => '30', 'hint' => 'Confetti particles on win', 'type' => 'number'],
];
foreach ($bgSettings as $s):
$val = $theme[$s['key']] ?? $s['default'];
......@@ -428,17 +429,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- ANIMATIONS -->
<h2>الحركة والرسوم المتحركة</h2>
<h2>Animations & Motion</h2>
<div class="section">
<div class="grid">
<?php
$anims = [
['key' => 'anim_speed', 'label' => 'سرعة الانتقال (ms)', 'default' => '250', 'hint' => 'المدة الافتراضية للانتقالات', 'type' => 'number'],
['key' => 'anim_fast', 'label' => 'سرعة سريعة (ms)', 'default' => '150', 'hint' => 'أزرار، تغذية راجعة', 'type' => 'number'],
['key' => 'anim_slow', 'label' => 'سرعة بطيئة (ms)', 'default' => '400', 'hint' => 'انتقالات المشاهد', 'type' => 'number'],
['key' => 'btn_scale', 'label' => 'تصغير الزر عند الضغط', 'default' => '0.92', 'hint' => '0.9 = ضغط قوي, 0.97 = ضغط خفيف', 'type' => 'text'],
['key' => 'border_radius', 'label' => 'انحناء الزوايا (px)', 'default' => '12', 'hint' => 'نصف قطر الأزرار', 'type' => 'number'],
['key' => 'card_radius', 'label' => 'انحناء البطاقات (px)', 'default' => '20', 'hint' => 'نصف قطر البطاقات والنوافذ', 'type' => 'number'],
['key' => 'anim_speed', 'label' => 'Transition Speed (ms)', 'default' => '250', 'hint' => 'Default transition duration', 'type' => 'number'],
['key' => 'anim_fast', 'label' => 'Fast Speed (ms)', 'default' => '150', 'hint' => 'Buttons, feedback', 'type' => 'number'],
['key' => 'anim_slow', 'label' => 'Slow Speed (ms)', 'default' => '400', 'hint' => 'Scene transitions', 'type' => 'number'],
['key' => 'btn_scale', 'label' => 'Button Press Scale', 'default' => '0.92', 'hint' => '0.9 = strong press, 0.97 = soft press', 'type' => 'text'],
['key' => 'border_radius', 'label' => 'Border Radius (px)', 'default' => '12', 'hint' => 'Button corner radius', 'type' => 'number'],
['key' => 'card_radius', 'label' => 'Card Radius (px)', 'default' => '20', 'hint' => 'Card & modal corner radius', 'type' => 'number'],
];
foreach ($anims as $a):
$val = $theme[$a['key']] ?? $a['default'];
......@@ -453,18 +454,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- BUTTON SHAPES -->
<h2>🔘 أشكال الأزرار</h2>
<h2>🔘 Button Shapes</h2>
<div class="section">
<div class="grid">
<?php
$shapes = [
['key' => 'btn_radius', 'label' => 'انحناء الأزرار الأساسية (px)', 'default' => '9999', 'hint' => '9999 = حبة (pill), 12 = مستطيل ناعم, 0 = حاد', 'type' => 'number'],
['key' => 'btn_secondary_radius', 'label' => 'انحناء الأزرار الثانوية (px)', 'default' => '9999', 'hint' => 'أزرار الإلغاء والخيارات', 'type' => 'number'],
['key' => 'btn_height', 'label' => 'ارتفاع الزر (px)', 'default' => '44', 'hint' => 'الحد الأدنى لارتفاع الزر — 44 للمس', 'type' => 'number'],
['key' => 'btn_font_weight', 'label' => 'ثقل خط الزر', 'default' => '700', 'hint' => '400=عادي, 600=سميك, 700=عريض, 800=أعرض', 'type' => 'number'],
['key' => 'btn_shadow', 'label' => 'ظل الزر الأساسي', 'default' => '0 4px 16px rgba(228,172,56,0.3)', 'hint' => 'CSS box-shadow كامل', 'type' => 'text'],
['key' => 'card_border_width', 'label' => 'سمك حدود البطاقة (px)', 'default' => '1', 'hint' => '0 = بلا حدود, 1 = ناعم, 2 = واضح', 'type' => 'number'],
['key' => 'input_radius', 'label' => 'انحناء حقول الإدخال (px)', 'default' => '12', 'hint' => 'حقول النص وكلمة المرور', 'type' => 'number'],
['key' => 'btn_radius', 'label' => 'Primary Button Radius (px)', 'default' => '9999', 'hint' => '9999 = pill, 12 = soft rect, 0 = sharp', 'type' => 'number'],
['key' => 'btn_secondary_radius', 'label' => 'Secondary Button Radius (px)', 'default' => '9999', 'hint' => 'Cancel & option buttons', 'type' => 'number'],
['key' => 'btn_height', 'label' => 'Button Height (px)', 'default' => '44', 'hint' => 'Min button height — 44 for touch', 'type' => 'number'],
['key' => 'btn_font_weight', 'label' => 'Button Font Weight', 'default' => '700', 'hint' => '400=normal, 600=semi, 700=bold, 800=extra', 'type' => 'number'],
['key' => 'btn_shadow', 'label' => 'Primary Button Shadow', 'default' => '0 4px 16px rgba(228,172,56,0.3)', 'hint' => 'Full CSS box-shadow', 'type' => 'text'],
['key' => 'card_border_width', 'label' => 'Card Border Width (px)', 'default' => '1', 'hint' => '0 = none, 1 = subtle, 2 = visible', 'type' => 'number'],
['key' => 'input_radius', 'label' => 'Input Field Radius (px)', 'default' => '12', 'hint' => 'Text & password fields', 'type' => 'number'],
];
foreach ($shapes as $s):
$val = $theme[$s['key']] ?? $s['default'];
......@@ -478,23 +479,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
</div>
<button type="submit">💾 حفظ كل التغييرات</button>
<button type="submit">💾 Save All Changes</button>
</form>
<!-- JUICE SETTINGS -->
<h2>🧃 إعدادات الـ Juice (التأثيرات)</h2>
<h2>🧃 Juice Effects</h2>
<div class="section">
<div class="grid">
<?php
$juiceSettings = [
['key' => 'juice_particles', 'label' => 'تفعيل الجسيمات (Particles)', 'default' => '1', 'hint' => '1=مفعل, 0=معطل', 'type' => 'number'],
['key' => 'juice_shake_intensity', 'label' => 'شدة الاهتزاز', 'default' => '4', 'hint' => '0=بلا, 2=خفيف, 4=عادي, 8=قوي', 'type' => 'number'],
['key' => 'juice_haptic', 'label' => 'تفعيل الاهتزاز (Haptic)', 'default' => '1', 'hint' => '1=مفعل, 0=معطل', 'type' => 'number'],
['key' => 'juice_confetti_count', 'label' => 'عدد جسيمات الاحتفال', 'default' => '30', 'hint' => 'عند الفوز — 0 لإلغاء', 'type' => 'number'],
['key' => 'juice_coin_fly_count', 'label' => 'عدد العملات الطائرة', 'default' => '5', 'hint' => 'عملات تطير للـ HUD', 'type' => 'number'],
['key' => 'juice_bounce_scale', 'label' => 'حجم الارتداد (Bounce)', 'default' => '1.08', 'hint' => '1.0=بلا, 1.05=خفيف, 1.15=مبالغ', 'type' => 'text'],
['key' => 'juice_slam_scale', 'label' => 'حجم الاصطدام (Slam)', 'default' => '1.5', 'hint' => 'عند ظهور النتائج', 'type' => 'text'],
['key' => 'juice_float_amount', 'label' => 'مسافة الطفو (px)', 'default' => '5', 'hint' => 'حركة المربعات في الصفحة الرئيسية', 'type' => 'number'],
['key' => 'juice_particles', 'label' => 'Enable Particles', 'default' => '1', 'hint' => '1=enabled, 0=disabled', 'type' => 'number'],
['key' => 'juice_shake_intensity', 'label' => 'Shake Intensity', 'default' => '4', 'hint' => '0=none, 2=light, 4=normal, 8=strong', 'type' => 'number'],
['key' => 'juice_haptic', 'label' => 'Enable Haptic', 'default' => '1', 'hint' => '1=enabled, 0=disabled', 'type' => 'number'],
['key' => 'juice_confetti_count', 'label' => 'Confetti Particles', 'default' => '30', 'hint' => 'On win — 0 to disable', 'type' => 'number'],
['key' => 'juice_coin_fly_count', 'label' => 'Flying Coins Count', 'default' => '5', 'hint' => 'Coins flying to HUD', 'type' => 'number'],
['key' => 'juice_bounce_scale', 'label' => 'Bounce Scale', 'default' => '1.08', 'hint' => '1.0=none, 1.05=subtle, 1.15=exaggerated', 'type' => 'text'],
['key' => 'juice_slam_scale', 'label' => 'Slam Scale', 'default' => '1.5', 'hint' => 'Result screen impact', 'type' => 'text'],
['key' => 'juice_float_amount', 'label' => 'Float Distance (px)', 'default' => '5', 'hint' => 'Home page tile hover motion', 'type' => 'number'],
];
foreach ($juiceSettings as $j):
$val = $theme[$j['key']] ?? $j['default'];
......@@ -509,16 +510,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- LUDO PAWNS -->
<h2>🎯 بيادق اللودو (Ludo Pawns)</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع صور PNG أو SVG لبيادق اللودو — ستظهر بنفس الحركة والارتداد على اللوحة. الشكل الافتراضي رسم هندسي.</p>
<h2>🎯 Ludo Pawns</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">Upload PNG or SVG for Ludo pawns — they animate with the same bounce on the board. Default is geometric shape.</p>
<div class="section">
<div class="grid">
<?php
$ludoPawns = [
['slot' => 'ludo_pawn_red', 'label' => '🔴 البيدق الأحمر', 'w' => 64, 'h' => 64, 'hint' => 'Player 1 — Red pawn sprite'],
['slot' => 'ludo_pawn_green', 'label' => '🟢 البيدق الأخضر', 'w' => 64, 'h' => 64, 'hint' => 'Player 2 — Green pawn sprite'],
['slot' => 'ludo_pawn_yellow', 'label' => '🟡 البيدق الأصفر', 'w' => 64, 'h' => 64, 'hint' => 'Player 3 — Yellow pawn sprite'],
['slot' => 'ludo_pawn_blue', 'label' => '🔵 البيدق الأزرق', 'w' => 64, 'h' => 64, 'hint' => 'Player 4 — Blue pawn sprite'],
['slot' => 'ludo_pawn_red', 'label' => '🔴 Red Pawn', 'w' => 64, 'h' => 64, 'hint' => 'Player 1 — Red pawn sprite'],
['slot' => 'ludo_pawn_green', 'label' => '🟢 Green Pawn', 'w' => 64, 'h' => 64, 'hint' => 'Player 2 — Green pawn sprite'],
['slot' => 'ludo_pawn_yellow', 'label' => '🟡 Yellow Pawn', 'w' => 64, 'h' => 64, 'hint' => 'Player 3 — Yellow pawn sprite'],
['slot' => 'ludo_pawn_blue', 'label' => '🔵 Blue Pawn', 'w' => 64, 'h' => 64, 'hint' => 'Player 4 — Blue pawn sprite'],
];
foreach ($ludoPawns as $a):
$current = $theme['assets'][$a['slot']] ?? null;
......@@ -533,9 +534,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<input type="file" name="asset" accept=".svg,.png,.webp" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:48px;height:48px;object-fit:contain;background:#2a2a4a;border-radius:8px;padding:4px;"></div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">مرفوع</div>
<div style="font-size:10px;color:#34D399;margin-top:4px;">Uploaded</div>
<?php else: ?>
📤 اضغط للرفع
📤 Click to upload
<?php endif; ?>
<div class="size-hint"><?= $a['w'] ?>×<?= $a['h'] ?>px — <?= $a['hint'] ?></div>
</div>
......@@ -546,39 +547,39 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- SOUND EFFECTS -->
<h2>🔊 المؤثرات الصوتية</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع ملفات MP3/OGG/WAV لاستبدال الأصوات المُولَّدة. الافتراضي أصوات إلكترونية مُصنَّعة.</p>
<h2>🔊 Sound Effects</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">Upload MP3/OGG/WAV to replace generated sounds. Default is synthesized electronic audio.</p>
<div class="section">
<div class="grid">
<?php
$soundSlots = [
['slot' => 'sfx_click', 'label' => '🖱️ نقرة (Click)', 'hint' => 'أزرار UI — قصير جداً'],
['slot' => 'sfx_move', 'label' => '♟ نقلة (Move)', 'hint' => 'تحريك قطعة شطرنج/بيدق'],
['slot' => 'sfx_capture', 'label' => '⚔️ أسر (Capture)', 'hint' => 'أكل قطعة/أسر بيدق'],
['slot' => 'sfx_check', 'label' => '⚠️ كش (Check)', 'hint' => 'تهديد الملك'],
['slot' => 'sfx_castle', 'label' => '🏰 تبييت (Castle)', 'hint' => 'نقلة التبييت في الشطرنج'],
['slot' => 'sfx_gameOver', 'label' => '🏁 انتهاء (Game Over)', 'hint' => 'نهاية المباراة'],
['slot' => 'sfx_win', 'label' => '🏆 فوز (Win)', 'hint' => 'صوت الفوز/النصر'],
['slot' => 'sfx_lose', 'label' => '💀 خسارة (Lose)', 'hint' => 'صوت الخسارة'],
['slot' => 'sfx_coin', 'label' => '🪙 عملة (Coin)', 'hint' => 'كسب عملات — قصير ومعدني'],
['slot' => 'sfx_levelUp', 'label' => '⬆️ ترقية (Level Up)', 'hint' => 'رفع المستوى'],
['slot' => 'sfx_notification', 'label' => '🔔 إشعار (Notification)', 'hint' => 'وصول إشعار/رسالة'],
['slot' => 'sfx_dice', 'label' => '🎲 نرد (Dice)', 'hint' => 'رمي النرد في اللودو'],
['slot' => 'sfx_place', 'label' => '📍 وضع (Place)', 'hint' => 'وضع قطعة دومينو'],
['slot' => 'sfx_reward', 'label' => '🎁 مكافأة (Reward)', 'hint' => 'فتح المكافأة اليومية'],
['slot' => 'sfx_match_found', 'label' => '🎯 وُجِدت مباراة (Match Found)', 'hint' => 'الاقتران مع خصم'],
['slot' => 'sfx_dice_shake', 'label' => '🎲 رجّ النرد (Dice Shake)', 'hint' => 'صوت رجّ النرد قبل الإطلاق — لودو'],
['slot' => 'sfx_boost', 'label' => '🚀 بوست/ستة (Boost/Six)', 'hint' => 'رمي 6 — دور إضافي — لودو'],
['slot' => 'sfx_piece_home', 'label' => '🏠 وصول البيت (Piece Home)', 'hint' => 'قطعة وصلت المركز — لودو'],
['slot' => 'sfx_turn_start', 'label' => '🔔 دورك (Turn Start)', 'hint' => 'تنبيه بدء دورك — لودو/دومينو'],
['slot' => 'sfx_emote', 'label' => '💬 إيموت (Emote)', 'hint' => 'إرسال إيموجي/عبارة اجتماعية'],
['slot' => 'sfx_bg_dice_roll', 'label' => '🎲 نرد طاولة (BG Dice Roll)', 'hint' => 'رمي النرد — طاولة/باكگمون'],
['slot' => 'sfx_bg_piece_move', 'label' => '♟️ حركة قطعة (BG Move)', 'hint' => 'تحريك قطعة على البورد — طاولة'],
['slot' => 'sfx_bg_piece_hit', 'label' => '💥 أكل قطعة (BG Hit)', 'hint' => 'ضرب قطعة الخصم للبار — طاولة'],
['slot' => 'sfx_bg_bear_off', 'label' => '✅ تطليع (BG Bear Off)', 'hint' => 'إخراج قطعة من اللوح — طاولة'],
['slot' => 'sfx_bg_double', 'label' => '⬆️ مضاعفة (BG Double)', 'hint' => 'عرض مكعب المضاعفة — طاولة'],
['slot' => 'sfx_bg_win_game', 'label' => '🎉 فوز جولة (BG Win Game)', 'hint' => 'فوز بجولة واحدة — طاولة'],
['slot' => 'sfx_bg_win_match', 'label' => '🏆 فوز ماتش (BG Win Match)', 'hint' => 'فوز بالماتش كامل — طاولة'],
['slot' => 'sfx_click', 'label' => '🖱️ Click', 'hint' => 'UI buttons — very short'],
['slot' => 'sfx_move', 'label' => '♟ Move', 'hint' => 'Chess/Ludo piece move'],
['slot' => 'sfx_capture', 'label' => '⚔️ Capture', 'hint' => 'Capturing a piece'],
['slot' => 'sfx_check', 'label' => '⚠️ Check', 'hint' => 'King under attack'],
['slot' => 'sfx_castle', 'label' => '🏰 Castle', 'hint' => 'Chess castling move'],
['slot' => 'sfx_gameOver', 'label' => '🏁 Game Over', 'hint' => 'Match end'],
['slot' => 'sfx_win', 'label' => '🏆 Win', 'hint' => 'Victory sound'],
['slot' => 'sfx_lose', 'label' => '💀 Lose', 'hint' => 'Defeat sound'],
['slot' => 'sfx_coin', 'label' => '🪙 Coin', 'hint' => 'Earn coins — short metallic'],
['slot' => 'sfx_levelUp', 'label' => '⬆️ Level Up', 'hint' => 'Level up fanfare'],
['slot' => 'sfx_notification', 'label' => '🔔 Notification', 'hint' => 'Incoming notification'],
['slot' => 'sfx_dice', 'label' => '🎲 Dice Roll', 'hint' => 'Ludo dice roll'],
['slot' => 'sfx_place', 'label' => '📍 Place', 'hint' => 'Domino tile placed'],
['slot' => 'sfx_reward', 'label' => '🎁 Reward', 'hint' => 'Daily reward open'],
['slot' => 'sfx_match_found', 'label' => '🎯 Match Found', 'hint' => 'Opponent paired'],
['slot' => 'sfx_dice_shake', 'label' => '🎲 Dice Shake', 'hint' => 'Shake before throw — Ludo'],
['slot' => 'sfx_boost', 'label' => '🚀 Boost / Six', 'hint' => 'Rolled 6 — extra turn — Ludo'],
['slot' => 'sfx_piece_home', 'label' => '🏠 Piece Home', 'hint' => 'Piece reached center — Ludo'],
['slot' => 'sfx_turn_start', 'label' => '🔔 Turn Start', 'hint' => 'Your turn alert — Ludo/Domino'],
['slot' => 'sfx_emote', 'label' => '💬 Emote', 'hint' => 'Sending emoji/social phrase'],
['slot' => 'sfx_bg_dice_roll', 'label' => '🎲 BG Dice Roll', 'hint' => 'Backgammon dice roll'],
['slot' => 'sfx_bg_piece_move', 'label' => '♟️ BG Piece Move', 'hint' => 'Move piece on board — Backgammon'],
['slot' => 'sfx_bg_piece_hit', 'label' => '💥 BG Hit', 'hint' => 'Send opponent to bar — Backgammon'],
['slot' => 'sfx_bg_bear_off', 'label' => '✅ BG Bear Off', 'hint' => 'Bear off piece — Backgammon'],
['slot' => 'sfx_bg_double', 'label' => '⬆️ BG Double', 'hint' => 'Doubling cube — Backgammon'],
['slot' => 'sfx_bg_win_game', 'label' => '🎉 BG Win Game', 'hint' => 'Win a single game — Backgammon'],
['slot' => 'sfx_bg_win_match', 'label' => '🏆 BG Win Match', 'hint' => 'Win the match — Backgammon'],
];
foreach ($soundSlots as $s):
$current = $theme['assets'][$s['slot']] ?? null;
......@@ -592,10 +593,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<div class="upload-box" onclick="this.querySelector('input[type=file]').click()">
<input type="file" name="asset" accept=".mp3,.ogg,.wav,.m4a,.webm" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div style="font-size:11px;color:#34D399;">مرفوع</div>
<div style="font-size:11px;color:#34D399;">Uploaded</div>
<audio controls src="<?= $current ?>" style="width:100%;height:28px;margin-top:4px;"></audio>
<?php else: ?>
🔊 اضغط لرفع ملف صوتي
🔊 Click to upload audio
<?php endif; ?>
<div class="size-hint"><?= $s['hint'] ?></div>
</div>
......@@ -606,36 +607,36 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- EMOJI REPLACEMENTS -->
<h2>😀 استبدال الرموز التعبيرية</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">استبدل أي رمز تعبيري بصورة SVG أو PNG — تظهر بنفس الحجم بلا تشويه</p>
<h2>😀 Emoji Replacements</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">Replace any emoji with SVG or PNG — shown at the same size without distortion</p>
<div class="section">
<div class="grid">
<?php
$emojiSlots = [
['key' => 'trophy', 'emoji' => '🏆', 'label' => 'كأس الفوز', 'size' => 72, 'hint' => 'شاشة نتيجة الفوز'],
['key' => 'skull', 'emoji' => '💀', 'label' => 'أيقونة الخسارة', 'size' => 72, 'hint' => 'شاشة نتيجة الخسارة'],
['key' => 'handshake', 'emoji' => '🤝', 'label' => 'أيقونة التعادل', 'size' => 72, 'hint' => 'شاشة نتيجة التعادل'],
['key' => 'gift', 'emoji' => '🎁', 'label' => 'صندوق المكافأة', 'size' => 48, 'hint' => 'شاشة المكافأة اليومية'],
['key' => 'robot', 'emoji' => '🤖', 'label' => 'أيقونة البوت', 'size' => 32, 'hint' => 'بجانب اسم الخصم الآلي'],
['key' => 'star', 'emoji' => '⭐', 'label' => 'نجمة التقييم', 'size' => 20, 'hint' => 'شاشة المراجعة'],
['key' => 'dice', 'emoji' => '🎲', 'label' => 'أيقونة النرد', 'size' => 48, 'hint' => 'مربع اللودو + زر الرمي'],
['key' => 'gamepad', 'emoji' => '🎮', 'label' => 'لاعب واحد', 'size' => 24, 'hint' => 'قائمة اللعبة — لاعب واحد'],
['key' => 'swords', 'emoji' => '⚔️', 'label' => 'متعدد اللاعبين', 'size' => 24, 'hint' => 'قائمة اللعبة — أونلاين'],
['key' => 'medal_gold', 'emoji' => '🥇', 'label' => 'ميدالية ذهبية', 'size' => 20, 'hint' => 'المركز الأول في الترتيب'],
['key' => 'medal_silver', 'emoji' => '🥈', 'label' => 'ميدالية فضية', 'size' => 20, 'hint' => 'المركز الثاني'],
['key' => 'medal_bronze', 'emoji' => '🥉', 'label' => 'ميدالية برونزية', 'size' => 20, 'hint' => 'المركز الثالث'],
['key' => 'coin', 'emoji' => '🪙', 'label' => 'عملة', 'size' => 16, 'hint' => 'بجانب أسعار المتجر'],
['key' => 'gem', 'emoji' => '💎', 'label' => 'جوهرة', 'size' => 16, 'hint' => 'بجانب أسعار الجواهر'],
['key' => 'person', 'emoji' => '👤', 'label' => 'صورة لاعب افتراضية', 'size' => 32, 'hint' => 'الأفاتار الافتراضي'],
['key' => 'people', 'emoji' => '👥', 'label' => 'أيقونة الأصدقاء', 'size' => 32, 'hint' => 'شاشة الأصدقاء الفارغة'],
['key' => 'bell', 'emoji' => '🔔', 'label' => 'جرس الإشعارات', 'size' => 18, 'hint' => 'أيقونة الإشعارات في الهيدر'],
['key' => 'speaker_on', 'emoji' => '🔊', 'label' => 'صوت مفعل', 'size' => 20, 'hint' => 'إعدادات الصوت'],
['key' => 'speaker_off', 'emoji' => '🔇', 'label' => 'صوت معطل', 'size' => 20, 'hint' => 'إعدادات الصوت'],
['key' => 'art', 'emoji' => '🎨', 'label' => 'أيقونة المتجر', 'size' => 24, 'hint' => 'بطاقات المتجر'],
['key' => 'puzzle', 'emoji' => '🧩', 'label' => 'أحجيات', 'size' => 16, 'hint' => 'زر الأحجيات في القائمة'],
['key' => 'chart', 'emoji' => '📊', 'label' => 'تحليل', 'size' => 16, 'hint' => 'زر التحليل'],
['key' => 'clipboard', 'emoji' => '📋', 'label' => 'مبارياتي', 'size' => 16, 'hint' => 'زر المباريات'],
['key' => 'share', 'emoji' => '📤', 'label' => 'مشاركة', 'size' => 16, 'hint' => 'زر المشاركة'],
['key' => 'trophy', 'emoji' => '🏆', 'label' => 'Win Trophy', 'size' => 72, 'hint' => 'Win result screen'],
['key' => 'skull', 'emoji' => '💀', 'label' => 'Loss Icon', 'size' => 72, 'hint' => 'Loss result screen'],
['key' => 'handshake', 'emoji' => '🤝', 'label' => 'Draw Icon', 'size' => 72, 'hint' => 'Draw result screen'],
['key' => 'gift', 'emoji' => '🎁', 'label' => 'Reward Box', 'size' => 48, 'hint' => 'Daily reward screen'],
['key' => 'robot', 'emoji' => '🤖', 'label' => 'Bot Icon', 'size' => 32, 'hint' => 'Next to bot opponent name'],
['key' => 'star', 'emoji' => '⭐', 'label' => 'Rating Star', 'size' => 20, 'hint' => 'Review screen'],
['key' => 'dice', 'emoji' => '🎲', 'label' => 'Dice Icon', 'size' => 48, 'hint' => 'Ludo tile + roll button'],
['key' => 'gamepad', 'emoji' => '🎮', 'label' => 'Single Player', 'size' => 24, 'hint' => 'Game menu — vs bot'],
['key' => 'swords', 'emoji' => '⚔️', 'label' => 'Multiplayer', 'size' => 24, 'hint' => 'Game menu — online'],
['key' => 'medal_gold', 'emoji' => '🥇', 'label' => 'Gold Medal', 'size' => 20, 'hint' => '#1 in leaderboard'],
['key' => 'medal_silver', 'emoji' => '🥈', 'label' => 'Silver Medal', 'size' => 20, 'hint' => '#2 in leaderboard'],
['key' => 'medal_bronze', 'emoji' => '🥉', 'label' => 'Bronze Medal', 'size' => 20, 'hint' => '#3 in leaderboard'],
['key' => 'coin', 'emoji' => '🪙', 'label' => 'Coin', 'size' => 16, 'hint' => 'Next to shop prices'],
['key' => 'gem', 'emoji' => '💎', 'label' => 'Gem', 'size' => 16, 'hint' => 'Next to gem prices'],
['key' => 'person', 'emoji' => '👤', 'label' => 'Default Avatar', 'size' => 32, 'hint' => 'Default player avatar'],
['key' => 'people', 'emoji' => '👥', 'label' => 'Friends Icon', 'size' => 32, 'hint' => 'Empty friends screen'],
['key' => 'bell', 'emoji' => '🔔', 'label' => 'Notification Bell', 'size' => 18, 'hint' => 'Header notifications icon'],
['key' => 'speaker_on', 'emoji' => '🔊', 'label' => 'Sound On', 'size' => 20, 'hint' => 'Sound settings'],
['key' => 'speaker_off', 'emoji' => '🔇', 'label' => 'Sound Off', 'size' => 20, 'hint' => 'Sound settings'],
['key' => 'art', 'emoji' => '🎨', 'label' => 'Shop Icon', 'size' => 24, 'hint' => 'Shop cards'],
['key' => 'puzzle', 'emoji' => '🧩', 'label' => 'Puzzles', 'size' => 16, 'hint' => 'Puzzles menu button'],
['key' => 'chart', 'emoji' => '📊', 'label' => 'Analysis', 'size' => 16, 'hint' => 'Analysis button'],
['key' => 'clipboard', 'emoji' => '📋', 'label' => 'My Games', 'size' => 16, 'hint' => 'Match history button'],
['key' => 'share', 'emoji' => '📤', 'label' => 'Share', 'size' => 16, 'hint' => 'Share button'],
['key' => 'checkmark', 'emoji' => '✅', 'label' => 'Success mark', 'size' => 20, 'hint' => 'Operation success feedback'],
['key' => 'cross', 'emoji' => '❌', 'label' => 'Error mark', 'size' => 20, 'hint' => 'Operation failure feedback'],
['key' => 'flag', 'emoji' => '⚐', 'label' => 'Resign flag', 'size' => 16, 'hint' => 'Chess resign button'],
......@@ -716,7 +717,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:<?= min($e['size'], 48) ?>px;height:<?= min($e['size'], 48) ?>px;object-fit:contain;"></div>
<?php else: ?>
<span style="font-size:24px;"><?= $e['emoji'] ?></span>ارفع بديل
<span style="font-size:24px;"><?= $e['emoji'] ?></span>Upload replacement
<?php endif; ?>
<div class="size-hint"><?= $e['size'] ?>×<?= $e['size'] ?>px — <?= $e['hint'] ?></div>
</div>
......@@ -727,38 +728,38 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
</div>
<!-- ASSET UPLOADS -->
<h2>📦 الأصول البصرية</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع صور SVG أو PNG — ستظهر فوراً كافتراضي لكل اللاعبين</p>
<h2>📦 Visual Assets</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">Upload SVG or PNG — appears immediately as default for all players</p>
<div class="section">
<div class="grid">
<?php
$assetSlots = [
['slot' => 'logo', 'label' => 'شعار المنصة', 'w' => 200, 'h' => 60, 'hint' => 'يظهر في الهيدر — يُفضل SVG شفاف'],
['slot' => 'logo_icon', 'label' => 'أيقونة الشعار (مربعة)', 'w' => 64, 'h' => 64, 'hint' => 'الأيقونة المختصرة للشعار'],
['slot' => 'splash_bg', 'label' => 'خلفية شاشة البداية', 'w' => 1080, 'h' => 1920, 'hint' => 'خلفية كاملة عند فتح التطبيق'],
['slot' => 'favicon', 'label' => 'Favicon', 'w' => 32, 'h' => 32, 'hint' => 'أيقونة تبويب المتصفح'],
['slot' => 'coin_icon', 'label' => 'أيقونة العملات (HUD)', 'w' => 24, 'h' => 24, 'hint' => 'تظهر بجانب رقم العملات في الأعلى'],
['slot' => 'gem_icon', 'label' => 'أيقونة الجواهر (HUD)', 'w' => 24, 'h' => 24, 'hint' => 'تظهر بجانب رقم الجواهر في الأعلى'],
['slot' => 'xp_icon', 'label' => 'أيقونة المستوى (HUD)', 'w' => 24, 'h' => 24, 'hint' => 'تظهر بجانب رقم المستوى'],
['slot' => 'chess_icon', 'label' => 'أيقونة الشطرنج', 'w' => 64, 'h' => 64, 'hint' => 'في مربع الشطرنج بالصفحة الرئيسية'],
['slot' => 'domino_icon', 'label' => 'أيقونة الدومينو', 'w' => 64, 'h' => 64, 'hint' => 'في مربع الدومينو بالصفحة الرئيسية'],
['slot' => 'ludo_icon', 'label' => 'أيقونة اللودو', 'w' => 64, 'h' => 64, 'hint' => 'في مربع اللودو بالصفحة الرئيسية'],
['slot' => 'backgammon_icon', 'label' => 'أيقونة الطاولة', 'w' => 64, 'h' => 64, 'hint' => 'في مربع الطاولة بالصفحة الرئيسية'],
['slot' => 'win_trophy', 'label' => 'كأس الفوز', 'w' => 128, 'h' => 128, 'hint' => 'شاشة نتيجة المباراة — فوز'],
['slot' => 'loss_icon', 'label' => 'أيقونة الخسارة', 'w' => 128, 'h' => 128, 'hint' => 'شاشة نتيجة المباراة — خسارة'],
['slot' => 'draw_icon', 'label' => 'أيقونة التعادل', 'w' => 128, 'h' => 128, 'hint' => 'شاشة نتيجة المباراة — تعادل'],
['slot' => 'daily_reward', 'label' => 'صندوق المكافأة اليومية', 'w' => 128, 'h' => 128, 'hint' => 'شاشة المكافأة اليومية'],
['slot' => 'rank_gold', 'label' => 'ميدالية ذهبية (#1)', 'w' => 32, 'h' => 32, 'hint' => 'بجانب المركز الأول في الترتيب'],
['slot' => 'rank_silver', 'label' => 'ميدالية فضية (#2)', 'w' => 32, 'h' => 32, 'hint' => 'بجانب المركز الثاني'],
['slot' => 'rank_bronze', 'label' => 'ميدالية برونزية (#3)', 'w' => 32, 'h' => 32, 'hint' => 'بجانب المركز الثالث'],
['slot' => 'default_avatar', 'label' => 'صورة افتراضية للاعب', 'w' => 128, 'h' => 128, 'hint' => 'تظهر إذا اللاعب ما رفع صورة'],
['slot' => 'notification_bell', 'label' => 'أيقونة الإشعارات', 'w' => 24, 'h' => 24, 'hint' => 'جرس الإشعارات في الهيدر'],
['slot' => 'tab_play', 'label' => 'أيقونة تبويب "العب"', 'w' => 24, 'h' => 24, 'hint' => 'شريط التنقل السفلي'],
['slot' => 'tab_rank', 'label' => 'أيقونة تبويب "الترتيب"', 'w' => 24, 'h' => 24, 'hint' => 'شريط التنقل السفلي'],
['slot' => 'tab_social', 'label' => 'أيقونة تبويب "الأصدقاء"', 'w' => 24, 'h' => 24, 'hint' => 'شريط التنقل السفلي'],
['slot' => 'tab_shop', 'label' => 'أيقونة تبويب "المتجر"', 'w' => 24, 'h' => 24, 'hint' => 'شريط التنقل السفلي'],
['slot' => 'tab_profile', 'label' => 'أيقونة تبويب "حسابي"', 'w' => 24, 'h' => 24, 'hint' => 'شريط التنقل السفلي'],
['slot' => 'logo', 'label' => 'Platform Logo', 'w' => 200, 'h' => 60, 'hint' => 'Header logo — transparent SVG preferred'],
['slot' => 'logo_icon', 'label' => 'Logo Icon (Square)', 'w' => 64, 'h' => 64, 'hint' => 'Compact logo icon'],
['slot' => 'splash_bg', 'label' => 'Splash Background', 'w' => 1080, 'h' => 1920, 'hint' => 'Full background on app launch'],
['slot' => 'favicon', 'label' => 'Favicon', 'w' => 32, 'h' => 32, 'hint' => 'Browser tab icon'],
['slot' => 'coin_icon', 'label' => 'Coin Icon (HUD)', 'w' => 24, 'h' => 24, 'hint' => 'Next to coin count in header'],
['slot' => 'gem_icon', 'label' => 'Gem Icon (HUD)', 'w' => 24, 'h' => 24, 'hint' => 'Next to gem count in header'],
['slot' => 'xp_icon', 'label' => 'Level Icon (HUD)', 'w' => 24, 'h' => 24, 'hint' => 'Next to level number'],
['slot' => 'chess_icon', 'label' => 'Chess Icon', 'w' => 64, 'h' => 64, 'hint' => 'Chess tile on home screen'],
['slot' => 'domino_icon', 'label' => 'Domino Icon', 'w' => 64, 'h' => 64, 'hint' => 'Domino tile on home screen'],
['slot' => 'ludo_icon', 'label' => 'Ludo Icon', 'w' => 64, 'h' => 64, 'hint' => 'Ludo tile on home screen'],
['slot' => 'backgammon_icon', 'label' => 'Backgammon Icon', 'w' => 64, 'h' => 64, 'hint' => 'Backgammon tile on home screen'],
['slot' => 'win_trophy', 'label' => 'Win Trophy', 'w' => 128, 'h' => 128, 'hint' => 'Match result — win'],
['slot' => 'loss_icon', 'label' => 'Loss Icon', 'w' => 128, 'h' => 128, 'hint' => 'Match result — loss'],
['slot' => 'draw_icon', 'label' => 'Draw Icon', 'w' => 128, 'h' => 128, 'hint' => 'Match result — draw'],
['slot' => 'daily_reward', 'label' => 'Daily Reward Box', 'w' => 128, 'h' => 128, 'hint' => 'Daily reward screen'],
['slot' => 'rank_gold', 'label' => 'Gold Medal (#1)', 'w' => 32, 'h' => 32, 'hint' => 'Next to #1 in leaderboard'],
['slot' => 'rank_silver', 'label' => 'Silver Medal (#2)', 'w' => 32, 'h' => 32, 'hint' => 'Next to #2 in leaderboard'],
['slot' => 'rank_bronze', 'label' => 'Bronze Medal (#3)', 'w' => 32, 'h' => 32, 'hint' => 'Next to #3 in leaderboard'],
['slot' => 'default_avatar', 'label' => 'Default Player Avatar', 'w' => 128, 'h' => 128, 'hint' => 'Shown if player has no photo'],
['slot' => 'notification_bell', 'label' => 'Notification Bell', 'w' => 24, 'h' => 24, 'hint' => 'Header notification icon'],
['slot' => 'tab_play', 'label' => 'Tab Icon: Play', 'w' => 24, 'h' => 24, 'hint' => 'Bottom navigation bar'],
['slot' => 'tab_rank', 'label' => 'Tab Icon: Leaderboard', 'w' => 24, 'h' => 24, 'hint' => 'Bottom navigation bar'],
['slot' => 'tab_social', 'label' => 'Tab Icon: Friends', 'w' => 24, 'h' => 24, 'hint' => 'Bottom navigation bar'],
['slot' => 'tab_shop', 'label' => 'Tab Icon: Shop', 'w' => 24, 'h' => 24, 'hint' => 'Bottom navigation bar'],
['slot' => 'tab_profile', 'label' => 'Tab Icon: Profile', 'w' => 24, 'h' => 24, 'hint' => 'Bottom navigation bar'],
];
foreach ($assetSlots as $a):
$current = $theme['assets'][$a['slot']] ?? null;
......@@ -775,9 +776,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<input type="file" name="asset" accept=".svg,.png,.jpg,.jpeg,.webp,.gif,.ico" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:<?= $displayW ?>px;height:<?= $displayH ?>px;object-fit:contain;image-rendering:auto;"></div>
<div style="font-size:10px;color:#64748b;margin-top:4px;">مرفوع</div>
<div style="font-size:10px;color:#64748b;margin-top:4px;">Uploaded</div>
<?php else: ?>
📤 اضغط للرفع
📤 Click to upload
<?php endif; ?>
<div class="size-hint"><?= $a['w'] ?>×<?= $a['h'] ?>px — <?= $a['hint'] ?></div>
</div>
......
......@@ -53,7 +53,6 @@ if ($method === 'POST') {
$values = $input['values'] ?? [];
foreach ($values as $key => $value) {
if (empty($key) || $value === null) continue;
// Upsert: try update first, if no rows affected, insert
$existing = $sdb->get('platform_theme', ['id' => 'eq.' . $key, 'select' => 'id', 'limit' => 1]);
if (!empty($existing) && !isset($existing['error'])) {
$sdb->update('platform_theme', ['value' => $value], ['id' => 'eq.' . $key]);
......@@ -69,6 +68,13 @@ if ($method === 'POST') {
]);
}
}
// Also update local theme.json so admin page stays in sync
$themeFile = __DIR__ . '/../public/assets/brand/theme.json';
$brandDir = dirname($themeFile);
if (!is_dir($brandDir)) mkdir($brandDir, 0777, true);
$local = file_exists($themeFile) ? (json_decode(file_get_contents($themeFile), true) ?: []) : [];
$local = array_merge($local, $values);
file_put_contents($themeFile, json_encode($local, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
jsonResponse(['success' => true]);
}
......
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