@@ -66,116 +117,123 @@
// Top-10
const topBody = document.getElementById('gam-top-body');
- if (stats.topByXP?.length) {
- topBody.innerHTML = stats.topByXP.slice(0, 10).map((u, i) => `
- | ${i + 1} |
- ${esc(u.name || u.email || 'ID:' + (u.id || u.user_id))} |
- ${u.xp} |
- ${u.level} |
- ${u.coins} |
-
`).join('');
- } else {
- topBody.innerHTML = '
| Нет данных |
';
- }
+ topBody.innerHTML = stats.topByXP?.length
+ ? stats.topByXP.slice(0, 10).map((u, i) => `
+ | ${i + 1} |
+ ${esc(u.name || u.email || 'ID:' + (u.id || u.user_id))} |
+ ${u.xp} |
+ ${u.level} |
+ ${u.coins} |
+
`).join('')
+ : '
| Нет данных |
';
- // Recent XP
+ // Recent XP log
const logBody = document.getElementById('gam-log-body');
- if (stats.recentXP?.length) {
- logBody.innerHTML = stats.recentXP.slice(0, 20).map(e => `
- | ${fmtDate(e.created_at || e.date)} |
- ${esc(e.name || e.user_name || '—')} |
- +${e.amount} |
- ${fmtXPReason(e.reason)} |
-
`).join('');
- } else {
- logBody.innerHTML = '
| Нет данных |
';
- }
+ logBody.innerHTML = stats.recentXP?.length
+ ? stats.recentXP.slice(0, 20).map(e => `
+ | ${fmtDate(e.created_at || e.date)} |
+ ${esc(e.name || e.user_name || '—')} |
+ +${e.amount} |
+ ${fmtXPReason(e.reason)} |
+
`).join('')
+ : '
| Нет данных |
';
// Purchases
const purchBody = document.getElementById('gam-purchases-body');
- if (stats.recentPurchases?.length) {
- purchBody.innerHTML = stats.recentPurchases.slice(0, 20).map(p => `
- | ${fmtDate(p.purchased_at)} |
- ${esc(p.user_name || '—')} |
- ${esc(p.item_name || '—')} |
- ${esc(p.type || '—')} |
- ${p.price} |
-
`).join('');
- } else {
- purchBody.innerHTML = '
| Нет покупок |
';
- }
+ purchBody.innerHTML = stats.recentPurchases?.length
+ ? stats.recentPurchases.slice(0, 20).map(p => `
+ | ${fmtDate(p.purchased_at)} |
+ ${esc(p.user_name || '—')} |
+ ${esc(p.item_name || '—')} |
+ ${esc(p.type || '—')} |
+ ${p.price} |
+
`).join('')
+ : '
| Нет покупок |
';
+ populateSelects();
if (window.lucide) lucide.createIcons();
- } catch(e) {
+ } catch (e) {
document.getElementById('gam-stats-grid').innerHTML = `
Ошибка: ${esc(e.message)}
`;
}
}
- async function gamSearchUser(q, prefix) {
- clearTimeout(_gamSearchTimer);
- const box = document.getElementById(prefix + '-results');
- if (q.length < 2) { box.classList.remove('open'); return; }
- _gamSearchTimer = setTimeout(async () => {
- try {
- const r = await LS.adminGetUsers({ q, limit: 8 });
- const label = u => u.name || u.email;
- box.innerHTML = (r.users || []).map(u => `
- ${esc(label(u))}${esc(u.role)}
-
`).join('') || '
Не найдено
';
- box.classList.add('open');
- } catch(e) { box.classList.remove('open'); }
- }, 300);
+ /* ── Пресеты XP ──────────────────────────────────────────────────────── */
+ function gamSetXP(val) {
+ const inp = document.getElementById('gam-award-xp');
+ if (!inp) return;
+ inp.value = val;
+ inp.readOnly = false;
+ document.querySelectorAll('.gam-xp-preset').forEach(b => b.classList.toggle('active', b.dataset.xp == val));
}
- function gamPickUser(el) {
- const prefix = el.dataset.prefix;
- document.getElementById(prefix + '-uid').value = el.dataset.uid;
- document.getElementById(prefix + '-user').value = el.dataset.name || '';
- document.getElementById(prefix + '-results').classList.remove('open');
+ function gamSetCoins(val) {
+ const inp = document.getElementById('gam-award-coins');
+ if (!inp) return;
+ inp.value = val;
+ document.querySelectorAll('.gam-coins-preset').forEach(b => b.classList.toggle('active', b.dataset.coins == val));
}
+ function gamSetReason(val) {
+ const inp = document.getElementById('gam-award-reason');
+ if (inp) inp.value = val;
+ }
+
+ /* ── Начислить XP/Монеты ─────────────────────────────────────────────── */
async function gamAdminAward() {
if (_gamAwarding) return;
- const userId = parseInt(document.getElementById('gam-award-uid').value);
- const xp = parseInt(document.getElementById('gam-award-xp').value) || 0;
- const coins = parseInt(document.getElementById('gam-award-coins').value) || 0;
- const reason = document.getElementById('gam-award-reason').value.trim();
- if (!userId) { LS.toast('Выберите пользователя', 'error'); return; }
- if (!xp && !coins) { LS.toast('Введите XP или монеты', 'error'); return; }
+ const userId = parseInt(document.getElementById('gam-award-uid').value, 10);
+ const xpRaw = document.getElementById('gam-award-xp').value;
+ const coinsRaw = document.getElementById('gam-award-coins').value;
+ const xp = xpRaw === '' ? 0 : Number(xpRaw);
+ const coins = coinsRaw === '' ? 0 : Number(coinsRaw);
+ const reason = document.getElementById('gam-award-reason').value.trim() || 'Admin award';
+
+ if (!userId) { LS.toast('Выберите пользователя', 'error'); return; }
+ if (xp < 0 || coins < 0) { LS.toast('Значения не могут быть отрицательными', 'error'); return; }
+ if (!xp && !coins) { LS.toast('Введите XP или монеты (больше 0)', 'error'); return; }
+
_gamAwarding = true;
try {
const r = await LS.adminGamAward({ userId, xp, coins, reason });
- LS.toast(`Начислено! XP: ${r.xp}, Уровень: ${r.level}, Монеты: ${r.coins}`, 'success');
+ const userName = document.getElementById('gam-award-uid').selectedOptions?.[0]?.textContent || 'пользователю';
+ LS.toast(`Начислено ${userName}: XP +${xp} Монеты +${coins} → Уровень ${r.level}`, 'success');
+ // Reset form
document.getElementById('gam-award-uid').value = '';
- document.getElementById('gam-award-user').value = '';
+ document.getElementById('gam-award-xp').value = '0';
+ document.getElementById('gam-award-coins').value = '0';
document.getElementById('gam-award-reason').value = '';
+ document.querySelectorAll('.gam-xp-preset,.gam-coins-preset').forEach(b => b.classList.remove('active'));
inited = false;
await load();
inited = true;
- } catch(e) { LS.toast('Ошибка: ' + e.message, 'error'); }
+ } catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); }
finally { _gamAwarding = false; }
}
+ /* ── Сбросить прогресс ───────────────────────────────────────────────── */
async function gamAdminReset() {
- const userId = parseInt(document.getElementById('gam-reset-uid').value);
- const userName = document.getElementById('gam-reset-user').value;
+ const sel = document.getElementById('gam-reset-uid');
+ const userId = parseInt(sel?.value, 10);
+ const userName = sel?.selectedOptions?.[0]?.textContent || '';
if (!userId) { LS.toast('Выберите пользователя', 'error'); return; }
- if (!await LS.confirm(`ВСЕ XP, монеты и достижения «${userName}» будут удалены безвозвратно.`, { title: 'Сбросить прогресс?', confirmText: 'Сбросить', danger: true })) return;
+ if (!await LS.confirm(
+ `ВСЕ XP, монеты и достижения пользователя «${userName}» будут удалены безвозвратно.`,
+ { title: 'Сбросить прогресс?', confirmText: 'Сбросить', danger: true }
+ )) return;
try {
await LS.adminGamReset({ userId });
- LS.toast('Прогресс сброшен', 'success');
- document.getElementById('gam-reset-uid').value = '';
- document.getElementById('gam-reset-user').value = '';
- inited = false;
- await load();
- inited = true;
- } catch(e) { LS.toast('Ошибка: ' + e.message, 'error'); }
+ LS.toast('Прогресс сброшен: ' + userName, 'success');
+ sel.value = '';
+ inited = false; await load(); inited = true;
+ } catch (e) { LS.toast('Ошибка: ' + e.message, 'error'); }
}
- window.gamSearchUser = gamSearchUser;
- window.gamPickUser = gamPickUser;
- window.gamAdminAward = gamAdminAward;
- window.gamAdminReset = gamAdminReset;
+ window.gamAdminAward = gamAdminAward;
+ window.gamAdminReset = gamAdminReset;
+ window.gamSetXP = gamSetXP;
+ window.gamSetCoins = gamSetCoins;
+ window.gamSetReason = gamSetReason;
+ window.gamFilterUsers = filterUserSelect;
window.AdminSections = window.AdminSections || {};
window.AdminSections.gam = {