Commit 72ffeaf7 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: Phase 7.2 — replace hardcoded emojis with emoji() theming calls

27 files converted to use the emoji(slot, fallback, size) function from
core/theme.js, enabling full emoji customization via the admin branding
panel. Covers core modules, all 4 game scenes, play flow, social,
rewards, org, and tournaments.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent c54762c0
......@@ -5,6 +5,7 @@ import * as audio from './audio.js';
import * as juice from './juice.js';
import * as bus from './bus.js';
import * as scene from './scene.js';
import { emoji } from './theme.js';
import { t } from './i18n.js';
let overlayEl = null;
......@@ -101,7 +102,7 @@ function showAutoWin() {
show(`
<div style="text-align:center;padding:32px;max-width:300px;">
<div style="font-size:56px;margin-bottom:12px;animation:float 2s ease-in-out infinite;">🏆</div>
<div style="font-size:56px;margin-bottom:12px;animation:float 2s ease-in-out infinite;">${emoji('trophy', '🏆', 56)}</div>
<div style="font-size:22px;font-weight:800;color:#34D399;margin-bottom:8px;">${t('match.you_won')}</div>
<div style="font-size:14px;color:#94a3b8;margin-bottom:20px;">${t('match.opponent_left')}</div>
<button id="abandon-back-btn" style="padding:12px 32px;background:linear-gradient(135deg,#E4AC38,#FFCC66);border:none;border-radius:10px;color:#1a1a1a;font-weight:700;font-size:15px;cursor:pointer;">${t('match.return')}</button>
......@@ -124,7 +125,7 @@ export function showConnectionLost() {
show(`
<div style="text-align:center;padding:32px;max-width:280px;">
<div style="width:48px;height:48px;margin:0 auto 16px;border-radius:50%;background:#EF4444;display:flex;align-items:center;justify-content:center;">
<span style="font-size:24px;">⚠️</span>
<span style="font-size:24px;">${emoji('warning', '⚠️', 24)}</span>
</div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;margin-bottom:6px;">${t('match.connection_lost')}</div>
<div style="font-size:13px;color:#94a3b8;">${t('match.reconnecting')}</div>
......
......@@ -60,10 +60,10 @@ function showOpponentActions(container, opponent) {
menu.style.cssText = 'position:absolute;top:50px;right:12px;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);border-radius:12px;padding:8px;z-index:100;box-shadow:0 8px 24px rgba(0,0,0,0.5);display:flex;flex-direction:column;gap:4px;min-width:140px;';
menu.innerHTML = `
<button class="mp-action" data-action="profile">${emoji('person', '👤', 12)} ${t('mp.profile')}</button>
<button class="mp-action" data-action="friend"> ${t('mp.add_friend')}</button>
<button class="mp-action" data-action="friend">${emoji('add_friend', '➕', 12)} ${t('mp.add_friend')}</button>
<button class="mp-action" data-action="mute">${emoji('mute', '🔇', 12)} ${t('block.mute')}</button>
<button class="mp-action" data-action="block" style="color:#EF4444;">${emoji('block', '🚫', 12)} ${t('block.block')}</button>
<button class="mp-action" data-action="report" style="color:#EF4444;">⚠️ ${t('mp.report')}</button>
<button class="mp-action" data-action="report" style="color:#EF4444;">${emoji('warning', '⚠️', 12)} ${t('mp.report')}</button>
`;
const style = document.createElement('style');
......
......@@ -2,6 +2,7 @@ import * as net from './net.js';
import * as bus from './bus.js';
import * as store from './store.js';
import * as realtime from './realtime.js';
import { emoji } from './theme.js';
import { t } from './i18n.js';
let pendingMatches = [];
......@@ -24,7 +25,7 @@ function showPairingToast(data) {
toast.id = 'tournament-toast';
toast.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:9999;padding:12px 16px;background:linear-gradient(135deg,#1a1a2e,#0f0f1e);border-bottom:2px solid #E4AC38;display:flex;align-items:center;gap:12px;animation:slideDown .3s ease;';
toast.innerHTML = `
<span style="font-size:20px;">🏆</span>
<span style="font-size:20px;">${emoji('trophy', '🏆', 20)}</span>
<div style="flex:1;">
<div style="font-size:13px;font-weight:700;color:#f8fafc;">${t('tournament.round_ready', { n: data.roundNumber })}</div>
<div style="font-size:11px;color:#94a3b8;">${t('tournament.tap_to_play')}</div>
......
......@@ -52,7 +52,7 @@ export function mountGame(el, p) {
const oppAvatarUrl = params.opponentAvatar ? String(params.opponentAvatar).replace(/[<>"]/g, '') : '';
const oppAvatar = oppAvatarUrl
? `<img src="${oppAvatarUrl}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">`
: (mode === 'bot' ? '🤖' : emoji('person', '👤', 18));
: (mode === 'bot' ? emoji('robot', '🤖', 18) : emoji('person', '👤', 18));
const oppLevel = params.opponentLevel || (mode === 'bot' ? '—' : 1);
const cubeVal = match.cube ? match.cube.value : 1;
......
......@@ -126,15 +126,15 @@ function renderSetup(el, params) {
<div class="bg-section-title">${t('ludo.bot_level')}</div>
<div class="bg-grid" id="difficulty-grid">
<button class="bg-chip" data-diff="easy">
<span>😊</span>
<span>${emoji('bot_easy', '😊', 18)}</span>
<span class="bg-chip-label">${t('ludo.easy')}</span>
</button>
<button class="bg-chip bg-chip-active" data-diff="medium">
<span>🧐</span>
<span>${emoji('bot_medium', '🧐', 18)}</span>
<span class="bg-chip-label">${t('ludo.medium')}</span>
</button>
<button class="bg-chip" data-diff="hard">
<span>🧠</span>
<span>${emoji('bot_hard', '🧠', 18)}</span>
<span class="bg-chip-label">${t('ludo.hard')}</span>
</button>
</div>
......
......@@ -106,7 +106,7 @@ export function mountGame(el, params) {
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:8px 14px;background:var(--bg-surface);">
<div style="display:flex;align-items:center;gap:10px;">
<div id="opponent-avatar" style="width:36px;height:36px;border-radius:50%;background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;overflow:hidden;border:2px solid ${mode === 'bot' ? 'var(--text-muted)' : 'var(--blue)'};">
${mode === 'bot' ? `<img src="https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amina'}.png" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.textContent='🤖'">` : '<span style="font-size:16px;">👤</span>'}
${mode === 'bot' ? `<img src="https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amina'}.png" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.innerHTML='${emoji('robot', '🤖', 16)}'">` : `<span style="font-size:16px;">${emoji('person', '👤', 16)}</span>`}
</div>
<div>
<div style="font-size:13px;font-weight:600;color:var(--text-primary);" id="opponent-name">${mode === 'bot' ? (botId || t('game.bot')) : t('game.loading_opponent')}</div>
......@@ -131,7 +131,7 @@ export function mountGame(el, params) {
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:8px 14px;background:#0f0f1e;">
<div style="display:flex;align-items:center;gap:10px;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;border:2px solid var(--gold, #E4AC38);">
${store.get('player.avatar_url') ? `<img src="${store.get('player.avatar_url')}" style="width:100%;height:100%;object-fit:cover;">` : `<span style="font-size:16px;">👤</span>`}
${store.get('player.avatar_url') ? `<img src="${store.get('player.avatar_url')}" style="width:100%;height:100%;object-fit:cover;">` : `<span style="font-size:16px;">${emoji('person', '👤', 16)}</span>`}
</div>
<div>
<div style="font-size:13px;font-weight:600;color:var(--text-primary);">${store.get('player.display_name') || store.get('player.username') || 'You'}</div>
......@@ -140,7 +140,7 @@ export function mountGame(el, params) {
<div id="player-captured" style="font-size:11px;color:#94a3b8;letter-spacing:1px;"></div>
</div>
</div>
<button id="emote-inline-toggle" style="width:32px;height:32px;border-radius:50%;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);color:#f8fafc;font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;margin-inline-start:4px;">💬</button>
<button id="emote-inline-toggle" style="width:32px;height:32px;border-radius:50%;background:#1a1a2e;border:1px solid rgba(255,255,255,0.08);color:#f8fafc;font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;margin-inline-start:4px;">${emoji('speech_bubble', '💬', 14)}</button>
</div>
<div id="clock-player" class="chess-clock active">${clock.format(tc.time)}</div>
</div>
......
......@@ -109,7 +109,7 @@ function buildLayout(mode) {
<!-- Opponent zone (top) -->
<div id="domino-opp-bar" class="dg-opp-bar">
<div class="dg-opp-left">
<div id="opp-avatar" class="dg-avatar">${isLive ? '👤' : '🤖'}</div>
<div id="opp-avatar" class="dg-avatar">${isLive ? emoji('person', '👤', 17) : emoji('robot', '🤖', 17)}</div>
<div class="dg-opp-info">
<div id="opp-name" class="dg-opp-name">${isLive ? t('common.opponent') : t('game.bot')}</div>
<div class="dg-opp-meta">
......@@ -121,7 +121,7 @@ function buildLayout(mode) {
<div class="dg-opp-right">
${isLive ? '<div id="conn-dot" class="dg-conn-dot"></div>' : ''}
<div id="boneyard-count" class="dg-boneyard-badge">
<span class="dg-boneyard-icon">📦</span>
<span class="dg-boneyard-icon">${emoji('boneyard', '📦', 13)}</span>
<span id="boneyard-num">14</span>
</div>
</div>
......@@ -162,7 +162,7 @@ function buildLayout(mode) {
<!-- Controls -->
<div id="domino-controls" class="dg-controls">
<button class="dg-ctrl-btn dg-btn-resign" id="btn-resign">${t('game.resign')}</button>
<button class="dg-ctrl-btn dg-btn-emote" id="btn-emote">😄</button>
<button class="dg-ctrl-btn dg-btn-emote" id="btn-emote">${emoji('smiley', '😄', 18)}</button>
<button class="dg-ctrl-btn dg-btn-draw" id="btn-draw">${t('domino.draw_boneyard')}</button>
<button class="dg-ctrl-btn dg-btn-pass" id="btn-pass" style="display:none;">${t('domino.pass')}</button>
</div>
......
......@@ -98,9 +98,9 @@ function renderMenu(el) {
function renderBotPicker(el) {
const levels = [
{ key: 'beginner', label: t('domino.beginner'), desc: t('domino.beginner_desc'), icon: '😊', color: '#4ade80', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' },
{ key: 'intermediate', label: t('domino.intermediate'), desc: t('domino.intermediate_desc'), icon: '🧐', color: '#fbbf24', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' },
{ key: 'expert', label: t('domino.expert'), desc: t('domino.expert_desc'), icon: '🧠', color: '#f87171', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' }
{ key: 'beginner', label: t('domino.beginner'), desc: t('domino.beginner_desc'), icon: emoji('bot_easy', '😊', 30), color: '#4ade80', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' },
{ key: 'intermediate', label: t('domino.intermediate'), desc: t('domino.intermediate_desc'), icon: emoji('bot_medium', '🧐', 30), color: '#fbbf24', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' },
{ key: 'expert', label: t('domino.expert'), desc: t('domino.expert_desc'), icon: emoji('bot_hard', '🧠', 30), color: '#f87171', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' }
];
el.innerHTML = `
......@@ -150,9 +150,9 @@ function renderBotPicker(el) {
function renderTargetPicker(el, botLevel) {
const targets = [
{ value: 50, label: t('domino.50_points'), desc: t('domino.50_desc'), icon: '⚡', color: '#4ade80', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' },
{ value: 100, label: t('domino.100_points'), desc: t('domino.100_desc'), icon: '🎯', color: '#fbbf24', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' },
{ value: 150, label: t('domino.150_points'), desc: t('domino.150_desc'), icon: '🔥', color: '#f87171', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' }
{ value: 50, label: t('domino.50_points'), desc: t('domino.50_desc'), icon: emoji('lightning', '⚡', 30), color: '#4ade80', bg: 'rgba(74,222,128,0.08)', border: 'rgba(74,222,128,0.2)' },
{ value: 100, label: t('domino.100_points'), desc: t('domino.100_desc'), icon: emoji('target', '🎯', 30), color: '#fbbf24', bg: 'rgba(251,191,36,0.08)', border: 'rgba(251,191,36,0.2)' },
{ value: 150, label: t('domino.150_points'), desc: t('domino.150_desc'), icon: emoji('fire', '🔥', 30), color: '#f87171', bg: 'rgba(248,113,113,0.08)', border: 'rgba(248,113,113,0.2)' }
];
el.innerHTML = `
......
......@@ -112,7 +112,7 @@ export function mountGame(el, params) {
const panels = Array.from({ length: numPlayers }, (_, i) => {
const isMe = i === myPlayerIndex;
const isBot = PLAYER_NAMES[i]?.startsWith('Bot');
const avatar = isMe && player.avatar_url ? `<img src="${player.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : isBot ? '🤖' : '👤';
const avatar = isMe && player.avatar_url ? `<img src="${player.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : isBot ? emoji('robot', '🤖', 14) : emoji('person', '👤', 14);
const name = isMe ? (player.display_name || player.username || t('common.you')) : (PLAYER_NAMES[i] || 'Bot');
const boardSlot = activeSeats[i] ?? i;
const level = isMe ? `Lv.${player.level || 1}` : (isBot ? '' : '');
......@@ -134,7 +134,7 @@ export function mountGame(el, params) {
<div id="dice-area" style="display:flex;align-items:center;gap:12px;padding:14px 16px;background:linear-gradient(180deg,#12122a,#0a0a1a);border-top:1px solid rgba(228,172,56,0.15);justify-content:center;padding-bottom:max(14px, env(safe-area-inset-bottom, 0px));position:relative;overflow:hidden;transition:background 0.4s ease;">
<div style="position:absolute;inset:0;background:radial-gradient(ellipse at 50% 0%,rgba(228,172,56,0.06) 0%,transparent 70%);pointer-events:none;"></div>
<button class="btn btn-secondary" id="exit-btn" style="min-height:44px;min-width:44px;padding:0;font-size:13px;color:#EF4444;border-radius:50%;background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);">✕</button>
<button class="btn btn-secondary" id="emote-btn" style="min-height:44px;min-width:44px;padding:0;font-size:18px;border-radius:50%;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);">😄</button>
<button class="btn btn-secondary" id="emote-btn" style="min-height:44px;min-width:44px;padding:0;font-size:18px;border-radius:50%;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);">${emoji('smiley', '😄', 18)}</button>
<div id="dice-box" style="width:56px;height:56px;background:linear-gradient(145deg,#ffffff,#f0ede8);border-radius:12px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:7px;box-shadow:0 4px 12px rgba(0,0,0,0.4),inset 0 2px 0 rgba(255,255,255,0.9),0 0 0 2px rgba(228,172,56,0.15);transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1),opacity 0.3s ease,filter 0.3s ease;">
</div>
<button class="btn btn-primary" id="roll-btn" style="font-size:15px;padding:14px 32px;min-height:52px;border-radius:14px;background:linear-gradient(135deg,#E4AC38,#F59E0B);box-shadow:0 4px 14px rgba(228,172,56,0.3);font-weight:800;transition:background 0.3s ease,color 0.3s ease,opacity 0.3s ease;" disabled>${t('game.roll_dice')}</button>
......@@ -665,7 +665,7 @@ function showOpponentPopup(el, profile) {
<div style="font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:4px;">${profile.display_name || profile.username}</div>
<div style="font-size:11px;color:#64748b;margin-bottom:14px;">Level ${profile.level || 1}</div>
<div style="display:flex;gap:8px;justify-content:center;">
<button id="opp-add-friend" style="padding:8px 16px;background:#2563EB;border:none;border-radius:8px;color:#fff;font-size:12px;font-weight:600;cursor:pointer;"> ${t('mp.add_friend')}</button>
<button id="opp-add-friend" style="padding:8px 16px;background:#2563EB;border:none;border-radius:8px;color:#fff;font-size:12px;font-weight:600;cursor:pointer;">${emoji('plus', '➕', 12)} ${t('mp.add_friend')}</button>
<button id="opp-close" style="padding:8px 16px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.08);border-radius:8px;color:#94a3b8;font-size:12px;cursor:pointer;">${t('common.close')}</button>
</div>
`;
......
......@@ -5,7 +5,7 @@ import * as juice from '../../../core/juice.js';
import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
const PLACE_EMOJI = ['🥇', '🥈', '🥉', '4️⃣'];
const PLACE_EMOJI = [emoji('medal_1', '🥇', 18), emoji('medal_2', '🥈', 18), emoji('medal_3', '🥉', 18), '4️⃣'];
const PLACE_LABEL = [t('ludo.place_first'), t('ludo.place_second'), t('ludo.place_third'), t('ludo.place_fourth')];
const PLACE_COLOR = ['#FFD700', '#C0C0C0', '#CD7F32', '#64748b'];
......@@ -98,7 +98,7 @@ export function mountResult(el, params) {
</div>
${ratingChange ? `
<div class="lr-reward-pill" style="color:${ratingChange > 0 ? '#4ade80' : '#fca5a5'};border-color:${ratingChange > 0 ? 'rgba(74,222,128,0.2)' : 'rgba(252,165,165,0.2)'};">
<span>📈</span>
<span>${emoji('chart_up', '📈', 16)}</span>
<span>${ratingChange > 0 ? '+' : ''}${ratingChange}</span>
</div>` : ''}
</div>
......
......@@ -122,15 +122,15 @@ function renderSetup(el, params) {
<div class="lr-section-title">${t('ludo.bot_level')}</div>
<div class="lr-grid" id="difficulty-grid">
<button class="lr-chip" data-diff="easy">
<span>😊</span>
<span>${emoji('bot_easy', '😊', 18)}</span>
<span class="lr-chip-label">${t('ludo.easy')}</span>
</button>
<button class="lr-chip lr-chip-active" data-diff="medium">
<span>🧐</span>
<span>${emoji('bot_medium', '🧐', 18)}</span>
<span class="lr-chip-label">${t('ludo.medium')}</span>
</button>
<button class="lr-chip" data-diff="hard">
<span>🧠</span>
<span>${emoji('bot_hard', '🧠', 18)}</span>
<span class="lr-chip-label">${t('ludo.hard')}</span>
</button>
</div>
......@@ -236,12 +236,12 @@ function renderSetup(el, params) {
const isBot = active && !isHuman;
return `
<div class="lr-seat lr-seat-${positions[i]} ${active ? 'lr-seat-active' : 'lr-seat-empty'}" style="--seat-color:${colors[i]};">
<div class="lr-seat-dot">${isHuman ? '👤' : isBot ? '🤖' : ''}</div>
<div class="lr-seat-dot">${isHuman ? emoji('person', '👤', 14) : isBot ? emoji('robot', '🤖', 14) : ''}</div>
<div class="lr-seat-label">${active ? labels[i] : '—'}</div>
</div>
`;
}).join('')}
<div class="lr-board-center">🎲</div>
<div class="lr-board-center">${emoji('dice', '🎲', 24)}</div>
</div>
`;
}
......
......@@ -72,7 +72,7 @@ function renderOrg(el, org) {
try {
await net.post('orgs.php', { action: 'join', org_id: org.id });
audio.play('coin', 'reward');
el.querySelector('#join-btn').textContent = `✅ ${t('org.joined')}`;
el.querySelector('#join-btn').innerHTML = `${emoji('checkmark', '✅', 14)} ${t('org.joined')}`;
el.querySelector('#join-btn').disabled = true;
} catch (e) {
audio.play('click');
......
......@@ -9,10 +9,10 @@ export async function mountBotSelect(el, params) {
el.innerHTML = `
<div style="padding:var(--s-4);display:flex;flex-direction:column;gap:var(--s-4);">
<div style="display:flex;align-items:center;gap:var(--s-3);">
<button class="btn btn-secondary" id="back-btn" style="width:44px;height:44px;padding:0;font-size:18px;">←</button>
<h2 style="font-size:18px;font-weight:700;">${t('play.select_bot')}</h2>
<button class="btn btn-secondary" id="back-btn" style="width:var(--bot-back-btn-size);height:var(--bot-back-btn-size);padding:0;font-size:var(--bot-back-btn-font);">←</button>
<h2 style="font-size:var(--bot-title-font);font-weight:700;">${t('play.select_bot')}</h2>
</div>
<div id="bots-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3);">
<div id="bots-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:var(--bot-grid-gap);">
<div class="skeleton" style="height:140px;border-radius:var(--r-xl);"></div>
<div class="skeleton" style="height:140px;border-radius:var(--r-xl);"></div>
<div class="skeleton" style="height:140px;border-radius:var(--r-xl);"></div>
......@@ -42,14 +42,14 @@ function renderBots(el, bots, params) {
const initial = (bot.name_ar || bot.name || '?')[0];
return `
<div class="card bot-card" data-id="${bot.id}" style="cursor:pointer;padding:var(--s-3);position:relative;overflow:hidden;">
<div style="position:absolute;top:0;left:0;right:0;height:3px;background:${diffColor};"></div>
<div style="width:56px;height:56px;border-radius:var(--r-full);background:var(--bg-elevated);margin:var(--s-2) auto var(--s-2);display:flex;align-items:center;justify-content:center;font-size:20px;overflow:hidden;border:2px solid ${diffColor};">
<div style="position:absolute;top:0;left:0;right:0;height:var(--bot-card-accent-height);background:${diffColor};"></div>
<div style="width:var(--bot-card-avatar-size);height:var(--bot-card-avatar-size);border-radius:var(--r-full);background:var(--bg-elevated);margin:var(--s-2) auto var(--s-2);display:flex;align-items:center;justify-content:center;font-size:20px;overflow:hidden;border:var(--bot-card-avatar-border) solid ${diffColor};">
${bot.portrait_url ? `<img src="https://stockfishapi.caprover.al-arcade.com${bot.portrait_url}" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.querySelector('.bot-fallback').style.display='flex'"><span class="bot-fallback" style="display:none;font-size:22px;font-weight:800;color:${diffColor};">${initial}</span>` : `<span style="font-size:22px;font-weight:800;color:${diffColor};">${initial}</span>`}
</div>
<div style="text-align:center;">
<div style="font-size:14px;font-weight:700;">${bot.name_ar || bot.name}</div>
<div style="font-size:11px;color:var(--text-secondary);margin-top:2px;">${bot.style_ar || bot.style}</div>
<div style="font-size:11px;color:${diffColor};font-weight:600;margin-top:4px;font-family:var(--font-lat);">${bot.elo_min}-${bot.elo_max}</div>
<div style="font-size:var(--bot-card-name-font);font-weight:700;">${bot.name_ar || bot.name}</div>
<div style="font-size:var(--bot-card-sub-font);color:var(--text-secondary);margin-top:2px;">${bot.style_ar || bot.style}</div>
<div style="font-size:var(--bot-card-sub-font);color:${diffColor};font-weight:600;margin-top:4px;font-family:var(--font-lat);">${bot.elo_min}-${bot.elo_max}</div>
</div>
</div>
`;}).join('');
......
......@@ -146,20 +146,20 @@ function showChallengeOptions(el, targetId, targetName, friendProfile) {
<!-- Game selection -->
<div style="display:flex;gap:8px;margin-bottom:14px;">
<button class="cfo-game active" data-game="chess" style="flex:1;padding:12px;border-radius:12px;background:#2563EB;border:2px solid #2563EB;color:#fff;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.15s;"> ${t('game.chess')}</button>
<button class="cfo-game" data-game="ludo" style="flex:1;padding:12px;border-radius:12px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.15s;">🎲 ${t('game.ludo')}</button>
<button class="cfo-game" data-game="domino" style="flex:1;padding:12px;border-radius:12px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.15s;">🁣 ${t('game.domino')}</button>
<button class="cfo-game active" data-game="chess" style="flex:1;padding:12px;border-radius:12px;background:#2563EB;border:2px solid #2563EB;color:#fff;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.15s;">${emoji('chess_pawn', '♟', 14)} ${t('game.chess')}</button>
<button class="cfo-game" data-game="ludo" style="flex:1;padding:12px;border-radius:12px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.15s;">${emoji('dice', '🎲', 14)} ${t('game.ludo')}</button>
<button class="cfo-game" data-game="domino" style="flex:1;padding:12px;border-radius:12px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;transition:all 0.15s;">${emoji('domino_tile', '🁣', 14)} ${t('game.domino')}</button>
</div>
<!-- Time control -->
<div id="cfo-time" style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:20px;">
<button class="cfo-tc" data-tc="bullet_1_0"> ${t('time.1min')}</button>
<button class="cfo-tc active" data-tc="blitz_3_0">🔥 ${t('time.3min')}</button>
<button class="cfo-tc" data-tc="blitz_5_0">💨 ${t('time.5min')}</button>
<button class="cfo-tc" data-tc="rapid_10_0">🕐 ${t('time.10min')}</button>
<button class="cfo-tc" data-tc="bullet_1_0">${emoji('lightning', '⚡', 12)} ${t('time.1min')}</button>
<button class="cfo-tc active" data-tc="blitz_3_0">${emoji('fire', '🔥', 12)} ${t('time.3min')}</button>
<button class="cfo-tc" data-tc="blitz_5_0">${emoji('wind', '💨', 12)} ${t('time.5min')}</button>
<button class="cfo-tc" data-tc="rapid_10_0">${emoji('clock', '🕐', 12)} ${t('time.10min')}</button>
</div>
<button class="btn btn-primary" id="cfo-send" style="width:100%;min-height:50px;font-size:15px;font-weight:700;border-radius:14px;">⚔️ ${t('challenge.send')}</button>
<button class="btn btn-primary" id="cfo-send" style="width:100%;min-height:50px;font-size:15px;font-weight:700;border-radius:14px;">${emoji('challenge_swords', '⚔️', 15)} ${t('challenge.send')}</button>
<button id="cfo-cancel" style="width:100%;margin-top:10px;background:none;border:none;color:#64748b;font-size:13px;cursor:pointer;font-family:inherit;padding:10px;">${t('common.cancel')}</button>
</div>
<style>
......
......@@ -30,7 +30,7 @@ export function mountLobby(el, params = {}) {
const tcLabel = formatTimeControl(timeControl);
const gameLabel = gameKey === 'ludo' ? t('game.ludo') : gameKey === 'domino' ? t('game.domino') : t('game.chess');
const gameIcon = gameKey === 'ludo' ? '🎲' : gameKey === 'domino' ? '🁣' : '♟';
const gameIcon = gameKey === 'ludo' ? emoji('dice', '🎲', 20) : gameKey === 'domino' ? emoji('domino_tile', '🁣', 20) : emoji('chess_pawn', '♟', 20);
el.innerHTML = `
<div class="lobby-layout">
......
......@@ -33,7 +33,7 @@ export function mountTable(el) {
<div class="play-home">
<!-- Player greeting -->
<div style="width:100%;max-width:var(--home-max-width);margin-bottom:var(--home-greeting-margin);display:flex;align-items:center;justify-content:space-between;">
<div style="font-size:var(--home-greeting-font);font-weight:700;color:var(--text-primary);">${username} 👋</div>
<div style="font-size:var(--home-greeting-font);font-weight:700;color:var(--text-primary);">${username} ${emoji('wave', '👋', 18)}</div>
<div style="font-size:var(--gm-btn-sub-font);color:var(--text-muted);">Lv. ${player?.level || 1}</div>
</div>
......
import * as scene from '../../../core/scene.js';
import * as bus from '../../../core/bus.js';
import * as audio from '../../../core/audio.js';
import { emoji } from '../../../core/theme.js';
import { t } from '../../../core/i18n.js';
const categories = [
{
name: 'Bullet', nameKey: 'time.bullet', icon: '⚡', color: '#FBBF24',
name: 'Bullet', nameKey: 'time.bullet', iconSlot: 'lightning', iconFallback: '⚡', color: '#FBBF24',
controls: [
{ key: 'bullet_1_0', labelKey: 'time.1min', sub: '1+0' },
{ key: 'bullet_1_1', label: '1 | 1', sub: '1+1' },
......@@ -13,7 +14,7 @@ const categories = [
]
},
{
name: 'Blitz', nameKey: 'time.blitz', icon: '🔥', color: '#F97316',
name: 'Blitz', nameKey: 'time.blitz', iconSlot: 'fire', iconFallback: '🔥', color: '#F97316',
controls: [
{ key: 'blitz_3_0', labelKey: 'time.3min', sub: '3+0' },
{ key: 'blitz_3_2', label: '3 | 2', sub: '3+2' },
......@@ -23,7 +24,7 @@ const categories = [
]
},
{
name: 'Rapid', nameKey: 'time.rapid', icon: '🏃', color: '#22C55E',
name: 'Rapid', nameKey: 'time.rapid', iconSlot: 'runner', iconFallback: '🏃', color: '#22C55E',
controls: [
{ key: 'rapid_10_0', labelKey: 'time.10min', sub: '10+0' },
{ key: 'rapid_10_5', label: '10 | 5', sub: '10+5' },
......@@ -33,7 +34,7 @@ const categories = [
]
},
{
name: 'Classical', nameKey: 'time.classical', icon: '♔', color: '#8B5CF6',
name: 'Classical', nameKey: 'time.classical', iconSlot: 'crown', iconFallback: '♔', color: '#8B5CF6',
controls: [
{ key: 'classical_45_45', label: '45 | 45', sub: '45+45' },
{ key: 'classical_60_0', labelKey: 'time.60min', sub: '60+0' },
......@@ -52,7 +53,7 @@ export function mountTimeSelect(el, params) {
${categories.map(cat => `
<div class="tc-category">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<span style="font-size:18px;">${cat.icon}</span>
<span style="font-size:18px;">${emoji(cat.iconSlot, cat.iconFallback, 18)}</span>
<span style="font-size:14px;font-weight:700;color:${cat.color};">${t(cat.nameKey)}</span>
<span style="font-size:11px;color:#64748b;font-family:var(--font-lat);">${cat.name}</span>
</div>
......
......@@ -145,6 +145,6 @@ function renderList(achievements, category) {
}
function tierIcon(tier) {
const icons = { 1: '⭐', 2: '🌟', 3: '💫', 4: '👑' };
return icons[tier] || '⭐';
const icons = { 1: emoji('star', '⭐', 16), 2: emoji('star_glow', '🌟', 16), 3: emoji('sparkles', '💫', 16), 4: emoji('crown', '👑', 16) };
return icons[tier] || emoji('star', '⭐', 16);
}
......@@ -36,7 +36,7 @@ export async function mountChallenges(el) {
function renderChallenges(el, data) {
const { challenges, streak, streak_bonus } = data;
el.querySelector('#streak-badge').textContent = `🔥 ${t('challenges.streak', { n: streak || 0 })}`;
el.querySelector('#streak-badge').innerHTML = `${emoji('fire', '🔥', 12)} ${t('challenges.streak', { n: streak || 0 })}`;
const list = el.querySelector('#challenges-list');
list.innerHTML = challenges.map((c, i) => `
......
......@@ -51,7 +51,7 @@ function render(el, streak, alreadyClaimed, dayIndex, todayReward) {
<div style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;align-items:center;gap:16px;">
<!-- Streak badge -->
<div style="background:linear-gradient(135deg,#92400e,#E4AC38);padding:8px 20px;border-radius:99px;display:flex;align-items:center;gap:6px;">
<span style="font-size:18px;">🔥</span>
${emoji('fire', '🔥', 18)}
<span style="font-size:14px;font-weight:800;color:#1a1a1a;">${t('daily.streak_days', { n: streak })}</span>
</div>
......
......@@ -7,9 +7,9 @@ export const TIERS = [
{ get name() { return t('rank.silver'); }, nameEn: 'Silver', min: 1000, max: 1199, color: '#C0C0C0', get icon() { return emoji('medal_silver', '🥈', 16); } },
{ get name() { return t('rank.gold'); }, nameEn: 'Gold', min: 1200, max: 1399, color: '#FFD700', get icon() { return emoji('medal_gold', '🥇', 16); } },
{ get name() { return t('rank.platinum'); }, nameEn: 'Platinum', min: 1400, max: 1599, color: '#00CED1', get icon() { return emoji('gem', '💎', 16); } },
{ get name() { return t('rank.diamond'); }, nameEn: 'Diamond', min: 1600, max: 1799, color: '#B9F2FF', icon: '💠' },
{ get name() { return t('rank.master'); }, nameEn: 'Master', min: 1800, max: 2000, color: '#FF4500', icon: '👑' },
{ get name() { return t('rank.grandmaster'); }, nameEn: 'Grandmaster', min: 2000, max: 9999, color: '#8B008B', icon: '🏅' },
{ get name() { return t('rank.diamond'); }, nameEn: 'Diamond', min: 1600, max: 1799, color: '#B9F2FF', get icon() { return emoji('diamond', '💠', 16); } },
{ get name() { return t('rank.master'); }, nameEn: 'Master', min: 1800, max: 2000, color: '#FF4500', get icon() { return emoji('crown', '👑', 16); } },
{ get name() { return t('rank.grandmaster'); }, nameEn: 'Grandmaster', min: 2000, max: 9999, color: '#8B008B', get icon() { return emoji('medal_gold', '🏅', 16); } },
];
export function getTier(rating) {
......
......@@ -5,7 +5,7 @@ import { emoji } from '../../../core/theme.js';
export async function mountActivity(el) {
el.innerHTML = `
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;">📰 ${t('social.activity_tab')}</h2>
<h2 style="font-size:18px;font-weight:700;color:#f8fafc;">${emoji('news', '📰', 18)} ${t('social.activity_tab')}</h2>
<div id="activity-list"></div>
</div>
`;
......
......@@ -224,7 +224,7 @@ function renderMessages(el, scrollToBottom = false) {
html += `<div class="chat-bubble system">${escapeHtml(msg.content)}</div>`;
} else if (msg.message_type === 'invite') {
const meta = typeof msg.metadata === 'string' ? JSON.parse(msg.metadata || '{}') : (msg.metadata || {});
const gameLabel = meta.game_key === 'ludo' ? '🎲 ' + t('game.ludo') : '♟ ' + t('game.chess');
const gameLabel = meta.game_key === 'ludo' ? emoji('dice', '🎲', 13) + ' ' + t('game.ludo') : emoji('chess_pawn', '♟', 13) + ' ' + t('game.chess');
html += `
<div class="chat-bubble ${isMine ? 'mine' : 'theirs'}" style="background:${isMine ? 'rgba(37,99,235,0.3)' : 'rgba(228,172,56,0.15)'};border:1px solid ${isMine ? 'rgba(37,99,235,0.3)' : 'rgba(228,172,56,0.3)'};">
<div style="font-size:12px;font-weight:600;margin-bottom:4px;">${emoji('challenge_swords', '⚔️', 13)} ${t('chat.challenge_title', { name: gameLabel })}</div>
......@@ -265,9 +265,9 @@ function showInviteFromChat(el) {
<div style="font-size:12px;color:#64748b;margin-bottom:16px;">${t('chat.choose_game')}</div>
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:12px;">
<button class="cig active" data-game="chess" style="flex:1;padding:10px;border-radius:10px;background:#2563EB;border:2px solid #2563EB;color:#fff;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;"> ${t('game.chess')}</button>
<button class="cig" data-game="domino" style="flex:1;padding:10px;border-radius:10px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">🁣 ${t('game.domino')}</button>
<button class="cig" data-game="ludo" style="flex:1;padding:10px;border-radius:10px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">🎲 ${t('game.ludo')}</button>
<button class="cig active" data-game="chess" style="flex:1;padding:10px;border-radius:10px;background:#2563EB;border:2px solid #2563EB;color:#fff;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">${emoji('chess_pawn', '♟', 13)} ${t('game.chess')}</button>
<button class="cig" data-game="domino" style="flex:1;padding:10px;border-radius:10px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">${emoji('domino_tile', '🁣', 13)} ${t('game.domino')}</button>
<button class="cig" data-game="ludo" style="flex:1;padding:10px;border-radius:10px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">${emoji('dice', '🎲', 13)} ${t('game.ludo')}</button>
</div>
<div id="cig-time" style="display:flex;gap:6px;justify-content:center;margin-bottom:16px;flex-wrap:wrap;">
......
......@@ -106,7 +106,7 @@ async function checkInvites(el) {
const gameLabel = inv.game_key === 'ludo' ? t('game.ludo') : inv.game_key === 'domino' ? t('game.domino') : t('game.chess');
return `
<div style="display:flex;align-items:center;gap:10px;padding:10px 16px;background:linear-gradient(135deg,#1a2a1a,#0f1f0f);border-bottom:1px solid rgba(52,211,153,0.2);animation:slideDown 0.3s;">
<span style="font-size:20px;">⚔️</span>
${emoji('challenge_swords', '⚔️', 20)}
<div style="flex:1;">
<div style="font-size:13px;font-weight:600;color:#34D399;">${t('social.challenges_you', { name })}</div>
<div style="font-size:11px;color:#64748b;">${gameLabel}</div>
......@@ -348,7 +348,7 @@ function renderFriendCard(f) {
<div style="font-size:11px;color:${f.is_online ? '#34D399' : '#64748b'};">${f.is_online ? t('common.online') : t('common.offline')}${f.level ? ` — ${t('common.level', { n: f.level })}` : ''}</div>
</div>
<div class="friend-actions">
<div class="friend-action" data-chat="${f.id}" title="${t('social.chat')}" style="background:rgba(37,99,235,0.15);border-color:rgba(37,99,235,0.3);color:#3B82F6;">💬</div>
<div class="friend-action" data-chat="${f.id}" title="${t('social.chat')}" style="background:rgba(37,99,235,0.15);border-color:rgba(37,99,235,0.3);color:#3B82F6;">${emoji('chat_bubble', '💬', 14)}</div>
${f.is_online ? `<div class="friend-action" data-invite="${f.id}" title="${t('challenge.send')}" style="background:rgba(228,172,56,0.15);border-color:rgba(228,172,56,0.3);color:#E4AC38;">${emoji('challenge_swords', '⚔️', 14)}</div>` : ''}
<div class="friend-action" data-remove="${f.id}" title="${t('social.remove_friend')}" style="font-size:11px;color:#64748b;">✕</div>
</div>
......@@ -413,13 +413,13 @@ function showInviteDialog(content, targetId, targetName) {
dialog.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:999;display:flex;align-items:center;justify-content:center;padding:24px;';
dialog.innerHTML = `
<div style="background:#1a1a2e;border-radius:16px;padding:24px;width:100%;max-width:320px;text-align:center;">
<div style="font-size:24px;margin-bottom:8px;">⚔️</div>
<div style="font-size:24px;margin-bottom:8px;">${emoji('challenge_swords', '⚔️', 24)}</div>
<div style="font-size:16px;font-weight:700;color:#f8fafc;margin-bottom:4px;">${t('challenge.send')}${targetName}</div>
<div style="font-size:12px;color:#64748b;margin-bottom:16px;">${t('challenge.select_game')}</div>
<div style="display:flex;gap:8px;justify-content:center;margin-bottom:12px;">
<button class="inv-game active" data-game="chess" style="flex:1;padding:10px;border-radius:10px;background:#2563EB;border:2px solid #2563EB;color:#fff;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;"> ${t('game.chess')}</button>
<button class="inv-game" data-game="ludo" style="flex:1;padding:10px;border-radius:10px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">🎲 ${t('game.ludo')}</button>
<button class="inv-game active" data-game="chess" style="flex:1;padding:10px;border-radius:10px;background:#2563EB;border:2px solid #2563EB;color:#fff;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">${emoji('chess_pawn', '♟', 13)} ${t('game.chess')}</button>
<button class="inv-game" data-game="ludo" style="flex:1;padding:10px;border-radius:10px;background:#1a1a2e;border:2px solid rgba(255,255,255,0.08);color:#94a3b8;font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;">${emoji('dice', '🎲', 13)} ${t('game.ludo')}</button>
</div>
<div id="time-options" style="display:flex;gap:6px;justify-content:center;margin-bottom:16px;flex-wrap:wrap;">
......@@ -633,14 +633,14 @@ async function loadActivity(content) {
friend_add: t('social.friend_add')
};
const icons = {
game_win: '🏆', game_loss: '💔', game_draw: '🤝',
achievement_unlock: '🎖️', level_up: '⬆️',
tournament_join: '🏅', tournament_win: '🥇', friend_add: '👋'
game_win: emoji('trophy', '🏆', 14), game_loss: emoji('broken_heart', '💔', 14), game_draw: emoji('handshake', '🤝', 14),
achievement_unlock: emoji('medal', '🎖️', 14), level_up: emoji('level_up', '⬆️', 14),
tournament_join: emoji('medal_gold', '🏅', 14), tournament_win: emoji('medal_gold', '🥇', 14), friend_add: emoji('wave', '👋', 14)
};
content.innerHTML = activities.map(a => {
const actor = a.actor || {};
const label = labels[a.action] || a.action;
const icon = icons[a.action] || '📌';
const icon = icons[a.action] || emoji('pin', '📌', 14);
return `<div style="display:flex;gap:10px;padding:10px;background:#1a1a2e;border-radius:10px;margin-bottom:6px;">
<div style="width:36px;height:36px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;">
${actor.avatar_url ? `<img src="${actor.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : icon}
......
......@@ -193,7 +193,7 @@ async function checkGroupInvites(el, groupId, myId) {
const isInviter = accepted[0] === myId;
return `
<div style="display:flex;align-items:center;gap:8px;padding:8px 16px;background:linear-gradient(135deg,#1a2a1a,#0f1f0f);border-bottom:1px solid rgba(52,211,153,0.2);">
<span style="font-size:16px;">⚔️</span>
${emoji('challenge_swords', '⚔️', 16)}
<div style="flex:1;">
<div style="font-size:12px;font-weight:600;color:#34D399;">${gameLabel}${accepted.length}/${inv.required_players}</div>
</div>
......@@ -243,21 +243,21 @@ function showGamePicker(el, groupId) {
<div style="background:#1a1a2e;border-radius:20px 20px 0 0;padding:24px;width:100%;max-width:400px;display:flex;flex-direction:column;gap:12px;">
<div style="font-size:16px;font-weight:700;color:#f8fafc;text-align:center;">${t('group.play_invite')}</div>
<button class="game-pick" data-game="chess" data-players="2" style="display:flex;align-items:center;gap:12px;padding:14px;background:#2a2a4a;border-radius:12px;border:none;cursor:pointer;width:100%;">
<span style="font-size:24px;">♟️</span>
${emoji('chess_pawn', '♟️', 24)}
<div style="text-align:right;flex:1;">
<div style="font-size:14px;font-weight:600;color:#f8fafc;">${t('game.chess')}</div>
<div style="font-size:11px;color:#64748b;">${t('group.players')}</div>
</div>
</button>
<button class="game-pick" data-game="ludo" data-players="4" style="display:flex;align-items:center;gap:12px;padding:14px;background:#2a2a4a;border-radius:12px;border:none;cursor:pointer;width:100%;">
<span style="font-size:24px;">🎲</span>
${emoji('dice', '🎲', 24)}
<div style="text-align:right;flex:1;">
<div style="font-size:14px;font-weight:600;color:#f8fafc;">${t('game.ludo')}</div>
<div style="font-size:11px;color:#64748b;">2-4 ${t('group.players')}</div>
</div>
</button>
<button class="game-pick" data-game="domino" data-players="2" style="display:flex;align-items:center;gap:12px;padding:14px;background:#2a2a4a;border-radius:12px;border:none;cursor:pointer;width:100%;">
<span style="font-size:24px;">🁣</span>
${emoji('domino_tile', '🁣', 24)}
<div style="text-align:right;flex:1;">
<div style="font-size:14px;font-weight:600;color:#f8fafc;">${t('game.domino')}</div>
<div style="font-size:11px;color:#64748b;">${t('group.players')}</div>
......
......@@ -40,7 +40,7 @@ export async function mountGroupMembers(el, params = {}) {
container.innerHTML = members.map(m => {
const p = m.profiles || {};
const roleLabel = m.role === 'owner' ? `👑 ${t('group.owner')}` : m.role === 'admin' ? `⭐ ${t('group.admin')}` : '';
const roleLabel = m.role === 'owner' ? `${emoji('crown', '👑', 12)} ${t('group.owner')}` : m.role === 'admin' ? `${emoji('star', '⭐', 12)} ${t('group.admin')}` : '';
const isMe = m.user_id === myId;
const showRemove = canManage && !isMe && m.role !== 'owner';
......
......@@ -177,7 +177,7 @@ async function loadTournaments(el) {
btn.textContent = t('tournament.registering');
try {
await net.post('tournaments.php', { action: 'register', tournament_id: btn.dataset.tid });
btn.textContent = '✓ ' + t('tournament.registered');
btn.innerHTML = emoji('checkmark', '✓', 12) + ' ' + t('tournament.registered');
btn.style.background = '#34D399';
btn.closest('.tour-hub-card')?.classList.add('registered');
} catch (err) {
......
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