Таблица заменена на сетку карточек, сгруппированных по типам
(Рамки/Титулы/Фоны/Эффекты) с заголовками и счётчиками. Каждая
карточка показывает настоящий вид товара:
- frame → кольцо аватара по data.css
- background → .bg-preview.bg-<slug> (тот же CSS, что у клиента)
- title → текст титула в его цвете (data.text/color)
- effect → анимация pulse / иконка-фоллбэк
Фильтр по типу, поиск и счётчик сохранены; неактивные товары
притушены; удаление компактной иконкой.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 4 крупные карточки статистики → компактная строка stat-пиллов
- тулбар: фильтр по типу + поиск по названию + счётчик (N из M)
- таблица: иконка-чип по типу + название с описанием в одной ячейке,
цветные бейджи типов, колонка ID убрана (id ушёл в подпись)
- состояния «Нет товаров» / «Ничего не найдено»
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Инлайн-панель формы внизу страницы заменена на модалку через LS.modal:
- shopAdminCreateItem/EditItem открывают окно openItemModal (create/edit)
- валидация: обязательное название + проверка JSON в поле «Данные»
- блокировка кнопки на время сохранения, ошибки через m.setError
- удалены инлайн-форма из admin.html и неактуальные
shopAdminSaveItem/shopAdminCancelForm/showShopForm + стейт
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Начисление монет осталось в «Пользователях» (быстрое действие quickAwardCoins)
и во вкладке «Геймификация». Из магазина удалены: HTML-блок «Начислить монеты»,
функции shopSearchUser/shopPickUser/shopAdminAwardCoins, их window-экспорты и
неиспользуемые стейт-переменные. Эндпоинт /shop/admin/award-coins не тронут —
им пользуется quickAwardCoins.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- shopAdminCreateItem/EditItem открывали форму под таблицей на 51 строку —
вне экрана, выглядело как «кнопки не работают». Добавлен showShopForm():
scrollIntoView + фокус в поле названия.
- В выпадающем списке типов «Тема» (theme) не поддерживается бэкендом
(валидация POST: frame/title/effect/background) → создание падало с 400.
Заменён на рабочий «Фон» (background); добавлена подпись в typeLabels.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Security review caught: per-row hover actions (users.js) and async
user picker (shop.js, gam.js) interpolated user-controlled name into
JS string literals inside onclick. LS.esc() escapes & < > " but
NOT backslash; the .replace(/'/g, '\'') fallback was broken.
Attack: any authenticated user could set their name to
a\'); alert(1); //
via PATCH /api/auth/profile (stripTags doesn't strip \) — admin
viewing the users/shop/gam picker would execute arbitrary JS.
Fix: switch from JS-string interpolation to data-uid/data-name
attributes, read via dataset in handler. esc() correctly escapes
for HTML-attribute context; dataset returns the raw string with
zero parse re-entry.
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.