# Phase 2: Split admin.html → per-section modules **Status:** ⬜ Not Started **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** frontend ## Objective Разделить монолит `admin.js` (3500L) на per-section модули в `frontend/js/admin/sections/*.js`. После фазы `admin.js` становится оркестратором (~500-800L): он только подключает router, инициализирует общие виджеты (notif, sidebar) и делегирует загрузку section-данных в соответствующий модуль. ## Tasks - [ ] Создать `frontend/js/admin/sections/` директорию - [ ] Определить единый паттерн модуля: ```js // js/admin/sections/.js (function () { 'use strict'; let inited = false; const ctx = { user: null, isAdmin: false }; // прокидываем из admin.js async function load() { /* существующий loadX код */ } window.AdminSections = window.AdminSections || {}; window.AdminSections. = { init: async (sharedCtx) => { Object.assign(ctx, sharedCtx); if (inited) return; inited = true; await load(); }, reload: load, }; })(); ``` - [ ] Извлечь 13 секций (в порядке риска — от меньшего к большему): - [ ] `stats.js` — `loadStats` + связанные функции (small, ~50L) - [ ] `sublog.js` — submission log (medium) - [ ] `sims.js`, `games.js`, `tpl.js` — admin-only (small каждая) - [ ] `subjects.js` — настройка доступных тестов - [ ] `permissions.js` - [ ] `shop.js` — items + purchases + award coins - [ ] `gam.js` — gamification stats + award xp - [ ] `assignments.js` - [ ] `tests.js` - [ ] `questions.js` — самая большая, ~800L (включая Q-modal) - [ ] `users.js` — users-table + pagination + user-panel (overlay остаётся!) - [ ] `sessions.js` — sessions-table + session detail - [ ] Модифицировать `admin.js`: - Удалить функции, перенесённые в sections - Заменить inline вызовы (`loadUsers()` → `AdminSections.users.init(ctx)`) - Добавить генератор route→section маппинга: ```js const ROUTE_TO_SECTION = { stats: 'stats', users: 'users', sessions: 'sessions', questions: 'questions', tests: 'tests', assignments: 'assignments', subjects: 'subjects', permissions: 'permissions', shop: 'shop', gam: 'gam', tpl: 'tpl', sims: 'sims', games: 'games', sublog: 'sublog', }; AdminRouter.on('change', ({ route }) => { const sec = ROUTE_TO_SECTION[route]; if (sec && AdminSections[sec]) AdminSections[sec].init(sharedCtx); }); ``` - [ ] Все 13 `