UI redesign: CS2 gambling aesthetic, glassmorphism, responsive layout, enhanced sounds, admin panel overhaul

This commit is contained in:
root
2026-07-05 14:22:49 +00:00
parent 01c6573f5f
commit 4b0fc594b4
21 changed files with 2750 additions and 9203 deletions
+40 -47
View File
@@ -2,7 +2,7 @@
{% block title %}{{ target_user.username }} — Админ-панель{% endblock %}
{% block nav_users %}active{% endblock %}
{% block page_title %}
👤 {{ target_user.username }}
{{ target_user.username }}
{% if target_user.is_admin %}<span class="a-badge a-badge-admin" style="margin-left:0.5rem">Админ</span>{% endif %}
{% if target_user.is_banned %}<span class="a-badge a-badge-banned" style="margin-left:0.5rem">Забанен</span>{% endif %}
{% endblock %}
@@ -11,62 +11,63 @@
{% endblock %}
{% block content %}
<div class="a-stats" style="grid-template-columns:repeat(4,1fr)">
<div class="a-stats">
<div class="a-stat">
<div class="as-label">Баланс</div>
<div class="as-value">{{ "%.0f"|format(target_user.balance) }} ₽</div>
<div class="as-bar" style="width:60%;background:#f59e0b"></div>
</div>
<div class="a-stat">
<div class="as-label">Предметов</div>
<div class="as-value">{{ inventory|length }}</div>
<div class="as-bar" style="width:45%;background:#60a5fa"></div>
</div>
<div class="a-stat">
<div class="as-label">Открытий кейсов</div>
<div class="as-value">{{ total_openings }}</div>
<div class="as-bar" style="width:30%;background:#a78bfa"></div>
</div>
<div class="a-stat">
<div class="as-label">Контрактов</div>
<div class="as-value">{{ total_contracts }}</div>
<div class="as-bar" style="width:20%;background:#f472b6"></div>
</div>
</div>
<!-- RPU -->
<div class="a-card">
<div class="a-card-header">🎲 РПУ (Режим Персонального Угнетения)</div>
<div class="a-row a-gap-sm a-mb" id="rpuStats" style="justify-content:space-around;text-align:center">
<div><div class="as-value" style="font-size:1.4rem" id="rpuLevel">--</div><div class="as-label" style="font-size:0.7rem">Уровень</div></div>
<div><div class="as-value" style="font-size:1.4rem" id="rpuLuck">--</div><div class="as-label" style="font-size:0.7rem">Множитель</div></div>
<div><div class="as-value" style="font-size:1.4rem" id="rpuSpent">--</div><div class="as-label" style="font-size:0.7rem">Потрачено</div></div>
<div><div class="as-value" style="font-size:1.4rem" id="rpuOpened">--</div><div class="as-label" style="font-size:0.7rem">Открыто</div></div>
<div><div class="as-value" style="font-size:1.4rem" id="rpuAutoStatus">--</div><div class="as-label" style="font-size:0.7rem">Авто</div></div>
<div class="a-card-header">RPU (Режим Персонального Угнетения)</div>
<div class="a-row a-gap-sm a-mb" style="text-align:center">
<div><div class="as-value" style="font-size:1.2rem" id="rpuLevel">--</div><div class="as-label" style="font-size:0.7rem">Уровень</div></div>
<div><div class="as-value" style="font-size:1.2rem" id="rpuLuck">--</div><div class="as-label" style="font-size:0.7rem">Множитель</div></div>
<div><div class="as-value" style="font-size:1.2rem" id="rpuSpent">--</div><div class="as-label" style="font-size:0.7rem">Потрачено</div></div>
<div><div class="as-value" style="font-size:1.2rem" id="rpuOpened">--</div><div class="as-label" style="font-size:0.7rem">Открыто</div></div>
<div><div class="as-value" style="font-size:1.2rem" id="rpuAutoStatus">--</div><div class="as-label" style="font-size:0.7rem">Авто</div></div>
</div>
<div class="a-flex" style="flex-wrap:wrap">
<button class="a-btn a-btn-primary" onclick="openRPUModal()">⚙️ Настроить</button>
<button class="a-btn" style="border-color:#10b981;color:#10b981" onclick="setRPUPreset('lucky')">🍀 Везучий</button>
<button class="a-btn" style="border-color:#f59e0b;color:#f59e0b" onclick="setRPUPreset('normal')">📊 Обычный</button>
<button class="a-btn" style="border-color:#ef4444;color:#ef4444" onclick="setRPUPreset('unlucky')">🌧️ Невезучий</button>
<button class="a-btn a-btn-danger" onclick="setRPUPreset('very_unlucky')">💀 Проклятый</button>
<div class="td-actions" style="flex-wrap:wrap">
<button class="a-btn a-btn-primary" onclick="openRPUModal()">Настроить</button>
<button class="a-btn" style="border-color:#10b981;color:#10b981" onclick="setRPUPreset('lucky')">Везучий</button>
<button class="a-btn" style="border-color:#f59e0b;color:#f59e0b" onclick="setRPUPreset('normal')">Обычный</button>
<button class="a-btn" style="border-color:#ef4444;color:#ef4444" onclick="setRPUPreset('unlucky')">Невезучий</button>
<button class="a-btn a-btn-danger" onclick="setRPUPreset('very_unlucky')">Проклятый</button>
</div>
</div>
<!-- Действия -->
<div class="a-card">
<div class="a-card-header">🔧 Действия</div>
<div class="a-flex" style="flex-wrap:wrap">
<button class="a-btn a-btn-primary" onclick="openBalanceModal()">💰 Баланс</button>
<button class="a-btn" onclick="openGiveItemModal()">🎁 Выдать предмет</button>
<div class="a-card-header">Действия</div>
<div class="td-actions" style="flex-wrap:wrap">
<button class="a-btn a-btn-primary" onclick="openBalanceModal()">Изменить баланс</button>
<button class="a-btn" onclick="openGiveItemModal()">Выдать предмет</button>
{% if target_user.id != user.id %}
<button class="a-btn" onclick="toggleAdmin()">{{ "👑 Снять админа" if target_user.is_admin else "👑 Сделать админом" }}</button>
<button class="a-btn" onclick="toggleAdmin()">{{ "Снять админа" if target_user.is_admin else "Сделать админом" }}</button>
{% endif %}
<button class="a-btn {{ 'a-btn-danger' if not target_user.is_banned else 'a-btn-success' }}" onclick="toggleBan()">
{{ "🔨 Забанить" if not target_user.is_banned else "Разбанить" }}
{{ "Забанить" if not target_user.is_banned else "Разбанить" }}
</button>
</div>
</div>
<!-- Инвентарь -->
<div class="a-card">
<div class="a-card-header">🎒 Инвентарь (последние 50)</div>
<div class="a-card-header">Инвентарь (последние 50)</div>
<div class="a-table-wrap">
<table class="a-table">
<thead><tr><th>ID</th><th>Название</th><th>Редкость</th><th>Float</th><th></th></tr></thead>
@@ -78,7 +79,7 @@
<td>{{ item.rarity }}</td>
<td>{{ "%.4f"|format(item.float_value) }}</td>
<td class="td-actions">
<button class="a-btn a-btn-danger a-btn-sm" onclick="deleteItem({{ item.id }})">🗑️</button>
<button class="a-btn a-btn-danger a-btn-sm" onclick="deleteItem({{ item.id }})">Удалить</button>
</td>
</tr>
{% endfor %}
@@ -89,11 +90,10 @@
{% endblock %}
{% block modals %}
<!-- Баланс -->
<div class="a-overlay" id="balanceModal">
<div class="a-modal">
<div class="a-modal-title">💰 Изменить баланс</div>
<p style="font-size:0.85rem;color:rgba(255,255,255,0.5);margin-bottom:1rem">
<div class="a-modal-title">Изменить баланс</div>
<p class="a-mb" style="font-size:0.85rem;color:rgba(255,255,255,0.5)">
Текущий: <strong style="color:#f59e0b">{{ "%.2f"|format(target_user.balance) }} ₽</strong>
</p>
<div class="a-mb">
@@ -103,9 +103,9 @@
<div class="a-mb">
<label class="a-label">Операция</label>
<select id="balanceOperation" class="a-input">
<option value="add"> Добавить</option>
<option value="remove"> Снять</option>
<option value="set">🎯 Установить</option>
<option value="add">Добавить</option>
<option value="remove">Снять</option>
<option value="set">Установить</option>
</select>
</div>
<div class="a-modal-actions">
@@ -115,10 +115,9 @@
</div>
</div>
<!-- Выдать предмет -->
<div class="a-overlay" id="giveItemModal">
<div class="a-modal" style="max-width:560px">
<div class="a-modal-title">🎁 Выдать предмет</div>
<div class="a-modal-title">Выдать предмет</div>
<div class="a-mb">
<label class="a-label">Поиск</label>
<input type="text" id="itemSearchInput" class="a-input" placeholder="Введите название..." oninput="searchItems()">
@@ -130,14 +129,13 @@
</div>
</div>
<!-- RPU -->
<div class="a-overlay" id="rpuModal">
<div class="a-modal" style="max-width:500px">
<div class="a-modal-title">⚙️ Настройка РПУ</div>
<div class="a-modal-title">Настройка RPU</div>
<div id="rpuSliders" class="a-mb"></div>
<div class="a-mb">
<label class="a-label" style="display:flex;align-items:center;gap:0.5rem;cursor:pointer">
<input type="checkbox" id="rpuAutoAdjust"> 🔄 Автоматическая подкрутка
<input type="checkbox" id="rpuAutoAdjust"> Автоматическая подкрутка
</label>
</div>
<div class="a-modal-actions">
@@ -150,7 +148,7 @@
{% block extra_styles %}
<style>
.slider-group { margin-bottom: 0.75rem; }
.slider-group { margin-bottom:0.75rem; }
.slider-group label { display:flex; justify-content:space-between; font-size:0.78rem; color:rgba(255,255,255,0.6); margin-bottom:0.2rem; }
.slider-group input[type=range] { width:100%; accent-color:#f59e0b; }
#itemSearchResults > div {
@@ -172,7 +170,6 @@ let currentRPU = null;
function closeModal(id) { document.getElementById(id).classList.remove('open'); }
function openModal(id) { document.getElementById(id).classList.add('open'); }
// ====== BALANCE ======
function openBalanceModal() { openModal('balanceModal'); }
async function updateBalance() {
const fd = new FormData();
@@ -184,7 +181,6 @@ async function updateBalance() {
else alert(d.error);
}
// ====== GIVE ITEM ======
function openGiveItemModal() { openModal('giveItemModal'); searchItems(); }
async function searchItems() {
const q = document.getElementById('itemSearchInput').value;
@@ -195,7 +191,7 @@ async function searchItems() {
<img src="${item.image_url || '/static/placeholder.png'}" onerror="this.src='/static/placeholder.png'">
<div class="ir-info">
<div class="ir-name">${item.name}</div>
<div class="ir-meta">${item.rarity} ${item.price_rub || 0} ₽</div>
<div class="ir-meta">${item.rarity} &bull; ${item.price_rub || 0} ₽</div>
</div>
<button class="a-btn a-btn-primary a-btn-sm" onclick="giveItem(${item.id})">Выдать</button>
</div>
@@ -209,7 +205,6 @@ async function giveItem(itemId) {
else alert(d.error);
}
// ====== DELETE ITEM ======
async function deleteItem(invId) {
if (!confirm('Удалить предмет?')) return;
const res = await fetch(`/admin/api/user/${userId}/delete-item/${invId}`, { method:'POST' });
@@ -218,7 +213,6 @@ async function deleteItem(invId) {
else alert(d.error);
}
// ====== TOGGLE ADMIN / BAN ======
async function toggleAdmin() {
if (!confirm('Изменить статус админа?')) return;
const res = await fetch(`/admin/api/user/${userId}/toggle-admin`, { method:'POST' });
@@ -232,7 +226,6 @@ async function toggleBan() {
if (d.success) window.location.reload(); else alert(d.error);
}
// ====== RPU ======
async function loadRPU() {
try {
const res = await fetch(`/admin/api/user/${userId}/rpu`);
@@ -244,7 +237,7 @@ async function loadRPU() {
document.getElementById('rpuLuck').textContent = d.luck_multiplier.toFixed(2) + 'x';
document.getElementById('rpuSpent').textContent = Math.round(d.stats.total_spent).toLocaleString() + ' ₽';
document.getElementById('rpuOpened').textContent = d.stats.total_opened.toLocaleString();
document.getElementById('rpuAutoStatus').textContent = d.auto_adjust ? '' : '';
document.getElementById('rpuAutoStatus').textContent = d.auto_adjust ? 'DA' : 'NET';
} catch(_) {}
}
@@ -263,7 +256,7 @@ function openRPUModal() {
oninput="document.getElementById('rp_${k}_v').textContent=this.value+'x'"></div>`;
});
const lv = currentRPU?.luck_multiplier || 1.0;
html += `<div class="slider-group"><label><span>🍀 Общая удача</span><span id="rp_luck_v">${lv.toFixed(2)}x</span></label>
html += `<div class="slider-group"><label><span>Общая удача</span><span id="rp_luck_v">${lv.toFixed(2)}x</span></label>
<input type="range" id="rp_luck" min="0.1" max="3.0" step="0.05" value="${lv}"
oninput="document.getElementById('rp_luck_v').textContent=this.value+'x'"></div>`;
sliders.innerHTML = html;
@@ -281,7 +274,7 @@ async function saveRPU() {
else alert(d.error);
}
async function setRPUPreset(preset) {
const names = { lucky:'🍀 Везучий', normal:'📊 Обычный', unlucky:'🌧️ Невезучий', very_unlucky:'💀 Проклятый' };
const names = { lucky:'Везучий', normal:'Обычный', unlucky:'Невезучий', very_unlucky:'Проклятый' };
if (!confirm('Применить "'+names[preset]+'"?')) return;
const fd = new FormData(); fd.append('preset', preset);
const res = await fetch(`/admin/api/user/${userId}/rpu/preset`, { method:'POST', body:fd });