Новый админ-раздел: Account ID / токен (маскируется) / модель Cloudflare,
лимиты (пауза, дневной лимит) из БД, статистика, кнопка теста генерации.
imggenController: лимиты и модель теперь из конфига, поддержка JSON и
бинарного ответа CF, переиспользуемые generateImage() и stats().
Бэкенд GET/PUT /api/admin/imggen + POST /api/admin/imggen/test (admin-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Баг «не помнит»: на самом деле free-лимит Gemini (429). callLLM теперь
возвращает ошибку; при 429 показываем «много запросов, подожди минутку —
память не потеряется» и НЕ ломаем историю (убираем неудачный вопрос); при
сбое — «не получилось, попробуй позже». Раньше показывалось «не нашёл ответ».
- В окне «Спроси» — пояснение, сколько помнит Квантик (≈6 реплик, рабочая память).
- Окна красивее: шире, аватар Квантика в шапке, мягкая анимация.
- Управление помощником вынесено в отдельный раздел админки «Помощник Квантик»
(системный вкл/выкл + модель/ключ/тест/RAG/кнопки экзамена/статистика/качество);
из раздела «Игры» конфиг убран.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Обзор теперь показывает и итоги за всё время (Пользователей, Тестов пройдено, Средний
результат) и средний результат по предметам за всё время — данные грузятся из
adminGetStats параллельно с обзором. Дублей нет: Обзор был про 24ч и контент.
Убрано полностью: nav-кнопка «Статистика», панель #tab-stats, маршрут stats в
ROUTE_TO_SECTION, подключение и файл sections/stats.js. #stats-хэш падает на #overview.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
adminController.getHealth: активные health-проверки — отклик БД (ping, мс) и
тест записи на диск рядом с БД; вердикт уходит в critical при недоступной БД
или диске, warning при медленном отклике БД (>100мс). Плюс recentErrorList —
последние 8 записей error_log (level/route/method/message/время).
admin.js: панель «Диагностика» — индикаторы БД/диска (зелёный/красный) +
лента последних ошибок с цветом по уровню.
Проверено: checks {dbOk,dbPingMs,diskWritable}, список ошибок отдаётся.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
metrics.js: сэмплинг раз в минуту в кольцевой буфер (cap 24ч, unref) —
ts/rss/heapUsed/reqPerMin/reqDelta/err5xx/p95; history() + поле history в
snapshot (последние 180 точек).
admin.js: секция «Тренды» с 4 мини-графиками (canvas): Память RSS, Запросы/мин,
Ошибки 5xx, Латентность p95 — линия + заливка + подписи макс/последнее.
Обновляются вместе с live-рефрешем.
Проверено: сэмплер пишет, история в snapshot, графики рисуются (на старте —
«накопление данных…», далее наполняются).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
backend/src/utils/metrics.js: лёгкие in-memory метрики (сброс при рестарте) —
всего запросов, req/min (скользящее окно), латентность avg/p50/p95/p99,
разбивка по статусам 2xx/3xx/4xx/5xx, топ маршрутов по частоте/латентности/
ошибкам (группировка по шаблону route.path, не по URL).
server.js: middleware (на /api, по res 'finish') пишет латентность и статус.
adminController.getMetrics + GET /api/admin/metrics (под admin-auth).
admin.js: health-страница переведена на refreshHealth/renderHealth (Level 1)
+ секция «Метрики запросов»: карточки req/min/всего/avg/p95/p99/5xx, цветная
полоса статусов, топ медленных/частых/ошибочных маршрутов.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add user-detail.js (~370L) and session-detail.js (~180L) section
modules that render full pages for #users/:id and #sessions/:id, plus
admin.js dispatch and HTML tab-panes. The legacy .user-panel overlay
is intentionally still in place — sub-commit 2 will remove it once the
deep pages are verified.
* admin.js: DEEP_ROUTES map + activateDeepPane(); activate(route, params)
signature; initial dispatch respects hash params (so F5 on #users/123
goes straight to the deep page).
* admin.html: new tab-panes #tab-user-detail / #tab-session-detail and
two script tags. Old #user-panel overlay untouched.
* user-detail.js: header (avatar/role/email/meta) + sub-tabs
(Обзор/Сессии/Классы/Audit) with URL-synced sub-tab routing
(#users/N/sessions etc). Overview: 4 stat cards + per-subject SVG
bar chart. Sessions: clickable rows that navigate to #sessions/N.
Classes: placeholder empty-state (no per-user classes endpoint).
Audit: client-side filter of /admin/audit-log by uid match. Header
action buttons (Изменить/Права/История/Бан/Удалить) call existing
overlay handlers; window.activeUid is set before opening any modal.
* session-detail.js: full header (user/subject/score/stats) + per-
question correctness layout reusing the drawer renderer. Delete
button uses LS.adminDeleteSession then navigates to #sessions.
Clicking the user name opens the user deep page.
* users.js: quickOpenUserSessions now navigates to
#users/<uid>/sessions instead of the bare #sessions list.
Verified node --check on all new/modified JS. baseline npm test still
shows pre-existing 3 auth failures unrelated to this change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Полный 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
по-прежнему доступен как опция, если понадобится дальнейшее снижение
когнитивной нагрузки.
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 — для
постепенной миграции (паттерн установлен).
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 — 🔵
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 на эстетику ниже, чем у других задач.