# 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 `