UI redesign: CS2 gambling aesthetic, glassmorphism, responsive layout, enhanced sounds, admin panel overhaul
This commit is contained in:
@@ -1,203 +1,86 @@
|
||||
<style>
|
||||
.activity-sidebar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 260px;
|
||||
background: var(--bg-card);
|
||||
border-right: 1px solid var(--border-color);
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding-top: 60px;
|
||||
transform: translateX(0);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
}
|
||||
.activity-sidebar.closed {
|
||||
transform: translateX(-100%);
|
||||
position: fixed; left: 0; top: 0; bottom: 0; width: 240px;
|
||||
background: var(--bg-card); border-right: 1px solid var(--border);
|
||||
z-index: 40; display: flex; flex-direction: column; overflow: hidden;
|
||||
padding-top: 56px; transform: translateX(0);
|
||||
transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
|
||||
}
|
||||
.activity-sidebar.closed { transform: translateX(-100%); }
|
||||
.activity-sidebar-header {
|
||||
padding: 0.6rem 0.8rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.activity-sidebar-header h3 {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.live-dot {
|
||||
width: 5px; height: 5px;
|
||||
background: #22c55e;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
animation: pulse 1.5s ease infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%,100%{opacity:1}50%{opacity:0.3}
|
||||
}
|
||||
.activity-sidebar-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0.4rem;
|
||||
padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--border);
|
||||
display: flex; justify-content: space-between; align-items: center; flex-shrink: 0;
|
||||
}
|
||||
.activity-sidebar-list { flex: 1; overflow-y: auto; padding: 0.35rem; }
|
||||
.activity-sidebar-list::-webkit-scrollbar { width: 3px; }
|
||||
.activity-sidebar-list::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 2px; }
|
||||
.activity-sidebar-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||
.activity-sidebar-item {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 0.5rem;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 0.15rem;
|
||||
transition: background 0.15s;
|
||||
display: flex; gap: 0.4rem; padding: 0.35rem 0.5rem; border-radius: 6px;
|
||||
margin-bottom: 0.1rem; cursor: pointer; transition: background 0.15s;
|
||||
animation: fadeInItem 0.3s ease;
|
||||
}
|
||||
.activity-sidebar-item:hover { background: var(--bg-dark); }
|
||||
.activity-sidebar-item .asi-icon {
|
||||
font-size: 0.85rem;
|
||||
width: 24px; height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-dark);
|
||||
border-radius: 5px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.activity-sidebar-item .asi-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.activity-sidebar-item .asi-content .asi-username {
|
||||
color: var(--primary-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
.activity-sidebar-item .asi-content .asi-time {
|
||||
font-size: 0.6rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
.activity-sidebar-item .asi-item-preview {
|
||||
width: 28px; height: 21px;
|
||||
object-fit: contain;
|
||||
border-radius: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.activity-sidebar-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
padding: 0.2rem;
|
||||
line-height: 1;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.activity-sidebar-close:hover { opacity: 1; }
|
||||
|
||||
/* Кнопка открытия ленты (слева, по центру) */
|
||||
.activity-sidebar-item:hover { background: var(--bg-hover); }
|
||||
.activity-sidebar-item .asi-icon { font-size: 0.8rem; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; background: var(--bg-dark); border-radius: 4px; flex-shrink: 0; }
|
||||
.activity-sidebar-item .asi-content { flex: 1; min-width: 0; font-size: 0.68rem; line-height: 1.25; }
|
||||
.activity-sidebar-item .asi-content .asi-username { color: var(--primary); font-weight: 500; }
|
||||
.activity-sidebar-item .asi-content .asi-time { font-size: 0.55rem; color: var(--text-dim); margin-top: 0.05rem; }
|
||||
.activity-sidebar-item .asi-item-preview { width: 26px; height: 20px; object-fit: contain; border-radius: 3px; flex-shrink: 0; }
|
||||
.activity-toggle-btn {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-left: none;
|
||||
border-radius: 0 8px 8px 0;
|
||||
padding: 0.6rem 0.3rem;
|
||||
cursor: pointer;
|
||||
z-index: 49;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.2s;
|
||||
writing-mode: vertical-rl;
|
||||
letter-spacing: 0.05em;
|
||||
display: none;
|
||||
user-select: none;
|
||||
position: fixed; left: 0; top: 50%; transform: translateY(-50%);
|
||||
background: var(--bg-card); border: 1px solid var(--border); border-left: none;
|
||||
border-radius: 0 8px 8px 0; padding: 0.5rem 0.2rem; cursor: pointer;
|
||||
z-index: 39; font-size: 0.75rem; color: var(--text-dim);
|
||||
writing-mode: vertical-rl; letter-spacing: 0.04em;
|
||||
display: none; user-select: none;
|
||||
}
|
||||
.activity-toggle-btn:hover {
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
.activity-toggle-btn.visible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
@keyframes fadeInItem {
|
||||
from { opacity: 0; transform: translateY(-5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.has-activity-sidebar {
|
||||
padding-left: 260px;
|
||||
transition: padding-left 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
}
|
||||
.has-activity-sidebar.sidebar-closed {
|
||||
padding-left: 0;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.activity-sidebar { display: none; }
|
||||
.activity-toggle-btn { display: none !important; }
|
||||
.has-activity-sidebar { padding-left: 0; }
|
||||
.activity-toggle-btn:hover { color: var(--primary); border-color: var(--primary); }
|
||||
.activity-toggle-btn.visible { display: flex; align-items: center; gap: 0.2rem; }
|
||||
@keyframes fadeInItem { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
|
||||
.has-activity-sidebar { padding-left: 240px; transition: padding-left 0.3s cubic-bezier(0.4,0,0.2,1); }
|
||||
.has-activity-sidebar.sidebar-closed { padding-left: 0; }
|
||||
@media (max-width:900px) {
|
||||
.activity-sidebar { display: none; } .activity-toggle-btn { display: none !important; } .has-activity-sidebar { padding-left: 0; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="activity-sidebar" id="activitySidebar">
|
||||
<div class="activity-sidebar-header">
|
||||
<h3><span class="live-dot"></span> Лента</h3>
|
||||
<span style="font-size:0.7rem;color:var(--text-secondary);display:flex;align-items:center;gap:0.25rem;">
|
||||
<span class="flex gap-1 items-center" style="font-size:0.75rem;font-weight:600;font-family:var(--font-display);">
|
||||
<span style="width:5px;height:5px;border-radius:50%;background:#22c55e;display:inline-block;animation:pulse 1.5s ease infinite;"></span>
|
||||
Лента
|
||||
</span>
|
||||
<span class="flex gap-1 items-center text-xs text-dim">
|
||||
<span id="onlineCount">0</span> 👤
|
||||
</span>
|
||||
<button class="activity-sidebar-close" onclick="toggleActivitySidebar()" title="Скрыть">✕</button>
|
||||
<button class="activity-sidebar-close" onclick="toggleActivitySidebar()" style="background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:0.85rem;padding:0.1rem;line-height:1;">✕</button>
|
||||
</div>
|
||||
<div class="activity-sidebar-list" id="activitySidebarList">
|
||||
<div style="text-align:center;padding:2rem;color:var(--text-secondary);font-size:0.85rem;">Загрузка...</div>
|
||||
<div style="text-align:center;padding:2rem;color:var(--text-dim);font-size:0.78rem;">Загрузка...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="activity-toggle-btn" id="activityToggleBtn" onclick="toggleActivitySidebar()" title="Показать ленту">
|
||||
📰 Лента
|
||||
</button>
|
||||
<button class="activity-toggle-btn" id="activityToggleBtn" onclick="toggleActivitySidebar()" title="Показать ленту">📰 Лента</button>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const list = document.getElementById('activitySidebarList');
|
||||
const sidebar = document.getElementById('activitySidebar');
|
||||
document.body.classList.add('has-activity-sidebar');
|
||||
|
||||
// Восстанавливаем состояние из localStorage
|
||||
if (localStorage.getItem('sidebar_closed') === 'true') {
|
||||
sidebar.classList.add('closed');
|
||||
document.body.classList.add('sidebar-closed');
|
||||
document.getElementById('activityToggleBtn').classList.add('visible');
|
||||
}
|
||||
|
||||
WS.on('online_count', (data) => {
|
||||
document.getElementById('onlineCount').textContent = data.online || 0;
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await fetch('/web/api/activity?limit=10');
|
||||
const data = await res.json();
|
||||
if (data.success && data.activities) {
|
||||
renderActivities(data.activities);
|
||||
}
|
||||
if (data.success && data.activities) renderActivities(data.activities);
|
||||
} catch (e) {
|
||||
if (list) list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-secondary);">Не удалось загрузить</div>';
|
||||
if (list) list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-dim);font-size:0.78rem;">Не удалось загрузить</div>';
|
||||
}
|
||||
|
||||
WS.on('activity', (data) => {
|
||||
const act = data.activity;
|
||||
if (!act || !list) return;
|
||||
@@ -206,9 +89,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
const item = createActivityItem(act);
|
||||
list.insertBefore(item, list.firstChild);
|
||||
if (act.activity_type === 'achievement') SoundManager.achievement();
|
||||
while (list.children.length > 15) {
|
||||
list.removeChild(list.lastChild);
|
||||
}
|
||||
while (list.children.length > 15) list.removeChild(list.lastChild);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -217,58 +98,28 @@ const RARITY_COLORS = {
|
||||
'mil-spec': '#4b69ff', 'mil-spec-grade': '#4b69ff',
|
||||
'restricted': '#8847ff', 'classified': '#d32ce6',
|
||||
'covert': '#eb4b4b', 'rare-special-item': '#ffd700',
|
||||
'rare-special': '#ffd700', 'extraordinary': '#ffd700',
|
||||
'contraband': '#ffaa00'
|
||||
'rare-special': '#ffd700', 'extraordinary': '#ffd700', 'contraband': '#ffaa00'
|
||||
};
|
||||
|
||||
function createActivityItem(act) {
|
||||
const iconMap = {
|
||||
'case_open': '📦', 'contract': '🔄', 'upgrade': '🆙',
|
||||
'achievement': '🏆', 'crash': '💥'
|
||||
};
|
||||
const iconMap = { 'case_open': '📦', 'contract': '🔄', 'upgrade': '🆙', 'achievement': '🏆', 'crash': '💥' };
|
||||
const item = document.createElement('div');
|
||||
item.className = 'activity-sidebar-item';
|
||||
item.style.cursor = 'pointer';
|
||||
|
||||
const raritySlug = (act.data && act.data.rarity || '').toLowerCase().replace(/ /g, '-').replace(/_/g, '-').replace(/™/g, '');
|
||||
const rarityColor = RARITY_COLORS[raritySlug] || '';
|
||||
if (rarityColor) {
|
||||
item.style.background = `rgba(${hexToRgb(rarityColor)}, 0.08)`;
|
||||
}
|
||||
|
||||
if (rarityColor) { const v = parseInt(rarityColor.replace('#',''),16); item.style.background = `rgba(${(v>>16)},${(v>>8)&255},${v&255},0.08)`; }
|
||||
item.addEventListener('click', () => window.location.href = `/profiles/${act.user_id}`);
|
||||
|
||||
const icon = iconMap[act.activity_type] || '📌';
|
||||
const imgHtml = (act.data && act.data.item_image)
|
||||
? `<img class="asi-item-preview" src="${act.data.item_image}" onerror="this.style.display='none'">`
|
||||
: '';
|
||||
const timeStr = act.created_at
|
||||
? new Date(act.created_at).toLocaleTimeString('ru-RU', {hour:'2-digit',minute:'2-digit'})
|
||||
: '';
|
||||
item.innerHTML = `
|
||||
<div class="asi-icon">${icon}</div>
|
||||
<div class="asi-content">
|
||||
<span class="asi-username">${escapeHtml(act.username)}</span>
|
||||
<span>${escapeHtml(act.message)}</span>
|
||||
<div class="asi-time">${timeStr}</div>
|
||||
</div>
|
||||
${imgHtml}
|
||||
`;
|
||||
const imgHtml = (act.data && act.data.item_image) ? `<img class="asi-item-preview" src="${act.data.item_image}" onerror="this.style.display='none'">` : '';
|
||||
const timeStr = act.created_at ? new Date(act.created_at).toLocaleTimeString('ru-RU',{hour:'2-digit',minute:'2-digit'}) : '';
|
||||
item.innerHTML = `<div class="asi-icon">${icon}</div><div class="asi-content"><span class="asi-username">${escapeHtml(act.username)}</span> ${escapeHtml(act.message)}<div class="asi-time">${timeStr}</div></div>${imgHtml}`;
|
||||
return item;
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const v = parseInt(hex.replace('#', ''), 16);
|
||||
return (v >> 16) + ',' + ((v >> 8) & 255) + ',' + (v & 255);
|
||||
}
|
||||
|
||||
function renderActivities(activities) {
|
||||
const list = document.getElementById('activitySidebarList');
|
||||
if (!list) return;
|
||||
if (!activities || activities.length === 0) {
|
||||
list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-secondary);font-size:0.85rem;">Пока нет активностей</div>';
|
||||
return;
|
||||
}
|
||||
if (!activities || activities.length === 0) { list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-dim);font-size:0.78rem;">Пока нет активностей</div>'; return; }
|
||||
list.innerHTML = '';
|
||||
activities.forEach(a => list.appendChild(createActivityItem(a)));
|
||||
}
|
||||
@@ -283,10 +134,4 @@ function toggleActivitySidebar() {
|
||||
btn.classList.toggle('visible', isOpen);
|
||||
localStorage.setItem('sidebar_closed', isOpen ? 'true' : 'false');
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const d = document.createElement('div');
|
||||
d.textContent = text || '';
|
||||
return d.innerHTML;
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user