ux(admin): sticky table headers + collapsible nav + унификация лейблов
3 победы из аудита админ-панели за один заход:
1) STICKY TABLE HEADERS
admin.html:142 — добавлен position:sticky; top:0; z-index:5; на <th>
Заголовки колонок теперь остаются видны при scroll длинных таблиц
(Users, Sessions, Shop, Gam — 100+ строк). Background фон поменян
на opaque #E5EAF7 чтобы строки скроллились чисто за header'ом.
Стоимость: 1 CSS-правило. Эффект: пользователи не теряют контекст
столбцов при просмотре длинного списка.
2) COLLAPSIBLE NAV GROUPS
admin.html:875+ — 4 группы (Аналитика, Контент, Пользователи,
Система) вместо плоского списка 21 кнопки с просто визуальными
сепараторами. Каждая группа сворачивается кликом по заголовку.
Состояние per-группа в localStorage (ls_adm_g_<slug>).
Группа «Система» (только админ) теперь объединяет shop, gam, sims,
games, audit, errors, health — раньше они шли вперемешку с
teacher-видимыми табами (sublog, topics, broadcast). Переместил
sublog/broadcast в группу «Пользователи», topics в «Контент» —
логичнее по смыслу.
Паттерн один-в-один как у sidebar.js (где мы это сделали ранее).
3) УНИФИКАЦИЯ ЛЕЙБЛОВ
Правило: «+ Добавить» для атомов (вопрос, тема, опция, товар),
«+ Создать» для составных объектов (тест, задание, курс).
Изменения:
- admin.html:1431 — «Создать» → «Добавить» (форма темы — атом)
- admin.html:1195 — «Новый товар» → «Добавить товар»
- admin.js:415 — q-modal title «Новый вопрос» → «Добавить вопрос»
- admin.js:2239 — shop-form-title «Новый товар» → «Добавить товар»
Теперь кнопка в toolbar и заголовок модалки/формы согласованы.
Остались крупные пункты из аудита (на отдельный заход):
- Q-modal wizard (split на 2 шага) — 🔴 высокий приоритет
- Pagination в больших таблицах — 🟡
- Standardized error/loading states — 🔵
This commit is contained in:
+116
-74
@@ -139,7 +139,8 @@
|
||||
/* tables */
|
||||
.table-wrap { background: var(--surface); backdrop-filter: var(--blur); border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; margin-bottom: 48px; box-shadow: var(--shadow); }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th { padding: 14px 20px; text-align: left; font-size: 0.82rem; font-weight: 700; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; border-bottom: 1px solid var(--border); background: rgba(238,242,255,0.5); }
|
||||
/* Sticky header: stays visible while body scrolls (in .sb-content). Background opaque (not transparent) so rows scroll cleanly behind. */
|
||||
th { padding: 14px 20px; text-align: left; font-size: 0.82rem; font-weight: 700; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; border-bottom: 1px solid var(--border); background: #E5EAF7; position: sticky; top: 0; z-index: 5; }
|
||||
td { padding: 15px 20px; font-size: 0.94rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
|
||||
tr:last-child td { border-bottom: none; }
|
||||
tr.clickable { cursor: pointer; transition: background var(--tr); }
|
||||
@@ -196,6 +197,22 @@
|
||||
}
|
||||
.admin-nav-label:first-child { margin-top: 0; }
|
||||
.admin-nav-sep { height: 1px; background: var(--border); margin: 8px 6px; }
|
||||
/* Collapsible nav groups */
|
||||
.admin-nav-group { margin-bottom: 6px; }
|
||||
.admin-nav-ghdr {
|
||||
width: 100%; padding: 6px 10px 4px; border: none; background: none;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
font-family: 'Manrope', system-ui, sans-serif;
|
||||
font-size: 0.68rem; font-weight: 800; letter-spacing: 0.08em;
|
||||
color: var(--text-3); text-transform: uppercase;
|
||||
cursor: pointer; transition: color .12s, opacity .12s; opacity: .72;
|
||||
}
|
||||
.admin-nav-ghdr:hover { color: var(--violet); opacity: 1; }
|
||||
.admin-nav-ghdr .adm-chev { width: 12px; height: 12px; transition: transform .18s; }
|
||||
.admin-nav-group.collapsed .adm-chev { transform: rotate(-90deg); }
|
||||
.admin-nav-body { display: flex; flex-direction: column; max-height: 1500px; overflow: hidden; transition: max-height .25s ease, opacity .18s; opacity: 1; }
|
||||
.admin-nav-group.collapsed .admin-nav-body { max-height: 0; opacity: 0; pointer-events: none; }
|
||||
.admin-nav-group .admin-nav-item { padding-left: 10px; }
|
||||
.admin-nav-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 12px; border-radius: 10px; border: none;
|
||||
@@ -874,80 +891,105 @@
|
||||
<div class="admin-layout">
|
||||
<nav class="admin-nav" id="admin-nav">
|
||||
|
||||
<div class="admin-nav-label">Аналитика</div>
|
||||
<button class="admin-nav-item active" data-tab="stats" onclick="switchTab(this)">
|
||||
<i data-lucide="bar-chart-2" style="width:15px;height:15px"></i> Статистика
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="sessions" onclick="switchTab(this)">
|
||||
<i data-lucide="clock" style="width:15px;height:15px"></i> История сессий
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="classroom" onclick="switchTab(this)">
|
||||
<i data-lucide="video" style="width:15px;height:15px"></i> Онлайн-уроки
|
||||
</button>
|
||||
<div class="admin-nav-group" data-ng="analytics">
|
||||
<button class="admin-nav-ghdr" onclick="toggleAdminGroup('analytics')">
|
||||
<span>Аналитика</span>
|
||||
<svg class="adm-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="admin-nav-body">
|
||||
<button class="admin-nav-item active" data-tab="stats" onclick="switchTab(this)">
|
||||
<i data-lucide="bar-chart-2" style="width:15px;height:15px"></i> Статистика
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="sessions" onclick="switchTab(this)">
|
||||
<i data-lucide="clock" style="width:15px;height:15px"></i> История сессий
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="classroom" onclick="switchTab(this)">
|
||||
<i data-lucide="video" style="width:15px;height:15px"></i> Онлайн-уроки
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav-sep"></div>
|
||||
<div class="admin-nav-label">Контент</div>
|
||||
<button class="admin-nav-item" data-tab="questions" onclick="switchTab(this)">
|
||||
<i data-lucide="help-circle" style="width:15px;height:15px"></i> Вопросы
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="tests" onclick="switchTab(this)">
|
||||
<i data-lucide="clipboard-list" style="width:15px;height:15px"></i> Тесты
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="assignments" onclick="switchTab(this)">
|
||||
<i data-lucide="file-check" style="width:15px;height:15px"></i> Задания
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="subjects" onclick="switchTab(this)" id="btn-tab-subjects" style="display:none">
|
||||
<i data-lucide="book-marked" style="width:15px;height:15px"></i> Доступные тесты
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="tpl" onclick="switchTab(this)" id="btn-tab-tpl" style="display:none">
|
||||
<i data-lucide="copy" style="width:15px;height:15px"></i> Шаблоны
|
||||
</button>
|
||||
<div class="admin-nav-group" data-ng="content">
|
||||
<button class="admin-nav-ghdr" onclick="toggleAdminGroup('content')">
|
||||
<span>Контент</span>
|
||||
<svg class="adm-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="admin-nav-body">
|
||||
<button class="admin-nav-item" data-tab="questions" onclick="switchTab(this)">
|
||||
<i data-lucide="help-circle" style="width:15px;height:15px"></i> Вопросы
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="tests" onclick="switchTab(this)">
|
||||
<i data-lucide="clipboard-list" style="width:15px;height:15px"></i> Тесты
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="assignments" onclick="switchTab(this)">
|
||||
<i data-lucide="file-check" style="width:15px;height:15px"></i> Задания
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="subjects" onclick="switchTab(this)" id="btn-tab-subjects" style="display:none">
|
||||
<i data-lucide="book-marked" style="width:15px;height:15px"></i> Доступные тесты
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="tpl" onclick="switchTab(this)" id="btn-tab-tpl" style="display:none">
|
||||
<i data-lucide="copy" style="width:15px;height:15px"></i> Шаблоны
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="topics" onclick="switchTab(this)">
|
||||
<i data-lucide="list-tree" style="width:15px;height:15px"></i> Темы
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav-sep"></div>
|
||||
<div class="admin-nav-label">Пользователи</div>
|
||||
<button class="admin-nav-item" data-tab="users" onclick="switchTab(this)">
|
||||
<i data-lucide="users" style="width:15px;height:15px"></i> Пользователи
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="permissions" onclick="switchTab(this)" id="btn-tab-permissions" style="display:none">
|
||||
<i data-lucide="shield" style="width:15px;height:15px"></i> Права доступа
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="avatars" onclick="switchTab(this);loadAvatarRequests()">
|
||||
<i data-lucide="image" style="width:15px;height:15px"></i> Аватары
|
||||
<span class="admin-badge" id="av-badge" style="display:none"></span>
|
||||
</button>
|
||||
<div class="admin-nav-group" data-ng="users">
|
||||
<button class="admin-nav-ghdr" onclick="toggleAdminGroup('users')">
|
||||
<span>Пользователи</span>
|
||||
<svg class="adm-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="admin-nav-body">
|
||||
<button class="admin-nav-item" data-tab="users" onclick="switchTab(this)">
|
||||
<i data-lucide="users" style="width:15px;height:15px"></i> Пользователи
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="permissions" onclick="switchTab(this)" id="btn-tab-permissions" style="display:none">
|
||||
<i data-lucide="shield" style="width:15px;height:15px"></i> Права доступа
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="avatars" onclick="switchTab(this);loadAvatarRequests()">
|
||||
<i data-lucide="image" style="width:15px;height:15px"></i> Аватары
|
||||
<span class="admin-badge" id="av-badge" style="display:none"></span>
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="sublog" onclick="switchTab(this)">
|
||||
<i data-lucide="file-x" style="width:15px;height:15px"></i> Журнал работ
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="broadcast" onclick="switchTab(this)">
|
||||
<i data-lucide="megaphone" style="width:15px;height:15px"></i> Рассылка
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-nav-sep" id="admin-nav-system-sep" style="display:none"></div>
|
||||
<div class="admin-nav-label" id="admin-nav-system-label" style="display:none">Система</div>
|
||||
<button class="admin-nav-item" data-tab="shop" onclick="switchTab(this)" id="btn-tab-shop" style="display:none">
|
||||
<i data-lucide="shopping-bag" style="width:15px;height:15px"></i> Магазин
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="gam" onclick="switchTab(this)" id="btn-tab-gam" style="display:none">
|
||||
<i data-lucide="trophy" style="width:15px;height:15px"></i> Геймификация
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="sims" onclick="switchTab(this)" id="btn-tab-sims" style="display:none">
|
||||
<i data-lucide="atom" style="width:15px;height:15px"></i> Симуляции
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="games" onclick="switchTab(this)" id="btn-tab-games" style="display:none">
|
||||
<i data-lucide="gamepad-2" style="width:15px;height:15px"></i> Игры
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="sublog" onclick="switchTab(this)">
|
||||
<i data-lucide="file-x" style="width:15px;height:15px"></i> Журнал работ
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="topics" onclick="switchTab(this)">
|
||||
<i data-lucide="list-tree" style="width:15px;height:15px"></i> Темы
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="broadcast" onclick="switchTab(this)">
|
||||
<i data-lucide="megaphone" style="width:15px;height:15px"></i> Рассылка
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="audit" onclick="switchTab(this)">
|
||||
<i data-lucide="scroll-text" style="width:15px;height:15px"></i> Аудит-лог
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="errors" onclick="switchTab(this)">
|
||||
<i data-lucide="bug" style="width:15px;height:15px"></i> Ошибки
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="health" onclick="switchTab(this)">
|
||||
<i data-lucide="activity" style="width:15px;height:15px"></i> Здоровье
|
||||
</button>
|
||||
<div class="admin-nav-group" data-ng="system" id="admin-nav-system-group" style="display:none">
|
||||
<button class="admin-nav-ghdr" onclick="toggleAdminGroup('system')">
|
||||
<span>Система</span>
|
||||
<svg class="adm-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="admin-nav-body">
|
||||
<button class="admin-nav-item" data-tab="shop" onclick="switchTab(this)" id="btn-tab-shop" style="display:none">
|
||||
<i data-lucide="shopping-bag" style="width:15px;height:15px"></i> Магазин
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="gam" onclick="switchTab(this)" id="btn-tab-gam" style="display:none">
|
||||
<i data-lucide="trophy" style="width:15px;height:15px"></i> Геймификация
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="sims" onclick="switchTab(this)" id="btn-tab-sims" style="display:none">
|
||||
<i data-lucide="atom" style="width:15px;height:15px"></i> Симуляции
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="games" onclick="switchTab(this)" id="btn-tab-games" style="display:none">
|
||||
<i data-lucide="gamepad-2" style="width:15px;height:15px"></i> Игры
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="audit" onclick="switchTab(this)">
|
||||
<i data-lucide="scroll-text" style="width:15px;height:15px"></i> Аудит-лог
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="errors" onclick="switchTab(this)">
|
||||
<i data-lucide="bug" style="width:15px;height:15px"></i> Ошибки
|
||||
</button>
|
||||
<button class="admin-nav-item" data-tab="health" onclick="switchTab(this)">
|
||||
<i data-lucide="activity" style="width:15px;height:15px"></i> Здоровье
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<div class="admin-main">
|
||||
@@ -1192,7 +1234,7 @@
|
||||
</div>
|
||||
|
||||
<div class="adm-panel" id="shop-item-form" style="display:none">
|
||||
<div class="adm-panel-title" id="shop-form-title">Новый товар</div>
|
||||
<div class="adm-panel-title" id="shop-form-title">Добавить товар</div>
|
||||
<div class="adm-form-row">
|
||||
<div class="adm-form-group" style="flex:1">
|
||||
<label>Название</label>
|
||||
@@ -1428,7 +1470,7 @@
|
||||
<div class="adm-panel" style="padding:16px 20px">
|
||||
<div class="adm-form-row" style="margin:0">
|
||||
<div class="adm-form-group" style="flex:1"><label>Название</label><input type="text" id="topics-new-name" placeholder="Название темы" /></div>
|
||||
<button class="adm-btn adm-btn-primary adm-btn-small" onclick="createTopic()" style="align-self:flex-end">Создать</button>
|
||||
<button class="adm-btn adm-btn-primary adm-btn-small" onclick="createTopic()" style="align-self:flex-end">Добавить</button>
|
||||
<button class="adm-btn adm-btn-small" style="background:var(--border-h);color:var(--text-3);align-self:flex-end" onclick="document.getElementById('topics-add-row').style.display='none'">Отмена</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user