Commit Graph

10 Commits

Author SHA1 Message Date
Maxim Dolgolyov f562fe4a71 feat(admin): phase 4 — Cmd+K command palette
Global search modal: actions + users + tests + classes.

- GET /api/admin/search?q=X (~50L controller): 3 parameterized LIKE queries, admin-only

- frontend/js/admin/palette.js (~366L): custom lightweight modal (not LS.modal — footer-button oriented), Ctrl+K/Cmd+K capture-phase override of generic /js/search.js, debounce 150ms, race-guard via _reqSeq, min-query 2 chars, 8 hardcoded actions, ↑↓ wrap + Enter, click-outside close

- adminGlobalSearch helper: drop ignored 'limit' param (server hardcodes 5/3/3)

window.AdminPalette = { open, close, isOpen } exposed for Phase 5/6 use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:39:59 +03:00
Maxim Dolgolyov 41acbdd0d0 feat(admin): phase 3 — dashboard #overview landing
GET /api/admin/overview returns 24h digest (~0.08ms/call).

- adminController.getOverview: 7 prepared statements (users 24h, sessions 24h, active users, classes count, failed sessions, banned this week, top-5 sessions)

- new section frontend/js/admin/sections/overview.js (~205L): bento-grid cards, alerts (only when >0), top-5 table, quick-links

- nav-item + tab-pane reordered: #overview is now default; #stats remains routable

Auth: admin-only (inside requireRole('admin') block, sibling of /stats).

Backward compat: all 13 existing routes unchanged.

Known follow-ups (post-merge polish):

- activeClasses counts all (label could be 'Всего классов')

- failedSessions24h includes in_progress (could tighten to abandoned only)

- topSessions24h drops NULL-score completed rows

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:26:59 +03:00
Maxim Dolgolyov fa67ad1294 fix(admin): expose updateCharCounter for Q-modal oninput handler
Phase 2 review caught this: updateCharCounter was defined inside

questions.js IIFE but never exposed via window.X; admin.html:1672

calls it via oninput, would throw ReferenceError on every keypress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:58:59 +03:00
Maxim Dolgolyov 92030b462c feat(admin): phase 2 — split admin.js into 13 section modules
Replace ~3500L admin.js monolith with thin orchestrator (~700L) +

14 IIFE-wrapped per-section modules under /js/admin/sections/.

Section modules expose AdminSections.<name>.init/reload (lazy init via

switchTab/router) and re-expose onclick handlers via window.X for

backward compat. Shared helpers (MODES/DIFFS, fmtDate, pctClass,

renderMath, qTypeBadge, pagination) live in /js/admin/_shared.js

exposed on window.AdminCtx.

switchTab now dispatches to AdminSections via ROUTE_TO_SECTION map;

non-extracted system tabs (topics/audit/errors/health/classroom/avatars)

remain inline in admin.js. user-panel overlay markup untouched — Phase 6

will remove it.
2026-05-16 22:50:14 +03:00
Maxim Dolgolyov 8a7bed487f feat(admin): phase 1 — hash-router
AdminRouter wraps existing switchTab for deep-linking.

- frontend/js/admin/router.js (new, 102L): parse/navigate/current/on/off, recursion guard via _navigating flag

- admin.html: +1 <script> before admin.js

- admin.js: switchTab(btn, opts) + initAdminRouter IIFE for hashchange dispatch

Backward compat: all 21 onclick=switchTab(this) callsites continue working.

F5 / back / forward / deep-link verified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:22:20 +03:00
Maxim Dolgolyov bd7a9dbee2 feat(admin): pagination для users-таблицы (50/стр)
- adminGetUsers возвращает { users, total, page, limit }

- pagination-controls (← 1 … N →) с ellipsis для длинных списков

- shop/gam search callers адаптированы под новый формат ответа

- helper _renderPgnControls переиспользуем для sessions/shop

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:24:18 +03:00
Maxim Dolgolyov bcee5a57e3 ux(admin): Q-modal ergonomic improvements — формулы скрыты, preview по требованию
Полный wizard-refactor Q-modal был отложен как высокорискованный
(сложная форма с многими типами вопросов, риск регрессии). Вместо
этого — безопасные ergonomic-улучшения:

1) FORMULA BAR — collapsed by default
   Раньше: 18 кнопок формул всегда занимали ~50px вертикали в модалке,
   но нужны только при создании math-вопросов.
   Теперь: маленькая кнопка «Вставить формулу» с chevron. Click → bar
   разворачивается. Состояние сохраняется в пределах сессии (DOM-стейт).

2) PREVIEW — показывается только когда есть текст
   Раньше: пустой preview-блок с placeholder «Введите текст вопроса…»
   занимал ~80px независимо от состояния.
   Теперь: .q-preview-wrap.hidden скрывается полностью пока textarea
   пуста. Появляется по input с debounce 150ms (уже было).

Эффект: модал стал ~130px ниже в типичном кейсе (создание non-math
вопроса). На 1080p теперь умещается без скролла для single/multi
с 4 опциями.

Без wizard'а, без риска регрессии — но visible UX-win. Wizard-refactor
по-прежнему доступен как опция, если понадобится дальнейшее снижение
когнитивной нагрузки.
2026-05-16 20:04:08 +03:00
Maxim Dolgolyov 6b7d0355b6 ux(admin): lock-icons на admin-only табах + LS.state helpers
1) LOCK-ICONS на admin-only табах
   Раньше: 7 табов (Магазин, Геймификация, Шаблоны, Симуляции, Игры,
   Доступные тесты, Права доступа) скрывались от учителей через
   display:none. Учитель не знал что они существуют — discoverability 0.

   Теперь:
   - Все табы видны всем, но для не-админа добавлен .locked класс
   - .locked: opacity .42, cursor not-allowed, lock-icon справа
   - title=\"Только для администраторов\" — нативный tooltip
   - switchTab() при клике на .locked показывает toast вместо
     переключения

   Эффект: учитель видит границы своих прав; знает что есть в системе,
   но не доступно ему — может попросить админа дать доступ.

2) LS.state — общий helper для loading/empty/error состояний
   api.js:381 — добавлен LS.state с тремя методами:

   LS.state.loading(el, msg?)           — спиннер + опц. текст
   LS.state.empty(el, msg, icon='inbox') — пустое состояние с иконкой
   LS.state.error(el, err, retryFn?)    — красная иконка + текст
                                          + опц. кнопка «Повторить»

   Все три используют один CSS (.ls-state*) с одним визуальным языком.
   inject стилей лениво (id=ls-state-style).

   Демо-миграция: 3 error-handler'а в admin.js (Stats / Users /
   Sessions) переписаны на LS.state.error с retry-функцией. Юзер
   теперь может нажать «Повторить» вместо перезагрузки страницы.

   Остальные 20+ inline error/empty/spinner'ов в admin.js — для
   постепенной миграции (паттерн установлен).
2026-05-16 19:56:58 +03:00
Maxim Dolgolyov ffd7bac0ac 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 — 🔵
2026-05-16 19:48:31 +03:00
Maxim Dolgolyov f7b6785050 refactor: admin.html — извлечён inline <script> в /js/admin/admin.js
admin.html: 5368 → 1922 строк (−64%, −3446 строк)
frontend/js/admin/admin.js: новый файл 3449 строк

Inline <script> блок (1915-5361) был полностью внутри HTML и не
кешировался отдельно — любое изменение HTML инвалидировало
огромный JS, и наоборот. Теперь:

  - HTML загружается быстро (122 КБ vs 270 КБ)
  - JS кешируется независимо (190 КБ; 7d max-age в prod)
  - Любой ctrl+F по JS в редакторе теперь не требует пробираться
    через тысячи строк HTML

Порядок выполнения сохранён байт-в-байт:
  <script src="/js/api.js"></script>
  <script src="/js/sidebar.js"></script>
  <script src="/js/admin/admin.js"></script>      ← было inline
  ... (далее остаётся как было)
  <script src="/js/notifications.js"></script>
  <script src="/js/search.js"></script>
  <script src="/js/mobile.js"></script>

Никаких изменений в логике, scope, DOM-ready timing — чистая
эстетическая операция. Все 22 вкладки + все модалки и обработчики
продолжают работать ровно как раньше.

Это фундамент для дальнейшего сплита (если понадобится): можно
будет в /js/admin/ разнести по табам (sessions.js, classroom.js,
gamification.js и т.д.) с lazy-load по клику. Сейчас не сделано,
т.к. ROI на эстетику ниже, чем у других задач.
2026-05-16 18:59:38 +03:00