76e376ee04
PLAN.md + 6 subplans + CONTEXT.md Strategy: Incremental | Mode: Automated | Execution: Orchestrator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
152 lines
8.4 KiB
Markdown
152 lines
8.4 KiB
Markdown
# 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/<name>.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.<name> = {
|
||
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 `<script>` тегов добавить в `admin.html` (после router.js, перед admin.js)
|
||
- [ ] Глобальные функции, которые используются из onclick HTML, остаются доступными через `window.X`:
|
||
- `changeRole`, `openUserPanel`, `goAddQuestion`, `confirmDeleteUser`, etc.
|
||
- Каждый section module экспортирует свои onclick-handler'ы через `window.X = X` или через делегацию из admin.js
|
||
- [ ] Удалить per-tab `*Inited` флаги из admin.js — они переехали внутрь section modules
|
||
|
||
## Files to Modify/Create
|
||
|
||
- `frontend/js/admin/sections/stats.js` — новый
|
||
- `frontend/js/admin/sections/users.js` — новый, ~400-500L
|
||
- `frontend/js/admin/sections/sessions.js` — новый
|
||
- `frontend/js/admin/sections/questions.js` — новый, ~800L
|
||
- `frontend/js/admin/sections/tests.js` — новый
|
||
- `frontend/js/admin/sections/assignments.js` — новый
|
||
- `frontend/js/admin/sections/subjects.js` — новый
|
||
- `frontend/js/admin/sections/permissions.js` — новый
|
||
- `frontend/js/admin/sections/shop.js` — новый
|
||
- `frontend/js/admin/sections/gam.js` — новый
|
||
- `frontend/js/admin/sections/tpl.js` — новый
|
||
- `frontend/js/admin/sections/sims.js` — новый
|
||
- `frontend/js/admin/sections/games.js` — новый
|
||
- `frontend/js/admin/sections/sublog.js` — новый
|
||
- `frontend/js/admin/admin.js` — сильно ужать (с 3500L до ~500-800L)
|
||
- `frontend/admin.html` — добавить 13 `<script>` тегов
|
||
|
||
## Acceptance Criteria
|
||
|
||
- `admin.js` ≤ 800L (от 3500L)
|
||
- Каждый section-файл ≤ 900L (questions.js самый большой)
|
||
- Все 13 табов работают идентично текущему поведению (no regressions)
|
||
- Cross-tab handlers (`goAddQuestion`, `confirmDelete*`) работают
|
||
- Lazy-load работает: при первом открытии tab делается fetch, при повторном — нет
|
||
- F5 на любом `#X` корректно ленево-грузит секцию (через router из Phase 1)
|
||
- Browser back/forward работают
|
||
- Никаких console errors в Devtools
|
||
|
||
## Notes
|
||
|
||
### Стратегия извлечения
|
||
|
||
Один section за раз, мелкими безопасными шагами:
|
||
1. Скопировать функции `loadX, openXModal, deleteX, ...` в новый файл sections/<name>.js, обернуть в IIFE
|
||
2. Экспортировать через `window.AdminSections.X`
|
||
3. Подключить `<script>` в admin.html
|
||
4. В admin.js заменить вызовы (`loadX()` → `AdminSections.X.init(ctx)`)
|
||
5. Удалить дубликаты в admin.js
|
||
6. Тест: открыть tab — работает?
|
||
7. Перейти к следующей секции
|
||
|
||
### Что НЕ переезжает в sections
|
||
|
||
- `LS.initPage()` + auth check — остаётся в admin.js
|
||
- `switchTab` (helper) — остаётся
|
||
- `pctClass`, `fmtDate`, `fmtTime` — общие утилиты, остаются (или переезжают в `admin/_shared.js`)
|
||
- Sidebar / notif init — остаётся
|
||
- Router setup — остаётся
|
||
|
||
### Глобальные функции из onclick
|
||
|
||
Сейчас многие функции вызываются из HTML onclick (`onclick="openUserPanel(...)"`). Чтобы не переписывать HTML на этой фазе, в каждом section module экспортируем нужные функции через `window.X = X` внутри IIFE. Phase 5/6 могут заменить onclick на event delegation, но Phase 2 этого не делает (incremental).
|
||
|
||
### Тестирование каждой секции
|
||
|
||
После каждой выделенной секции:
|
||
- Открыть `/admin` → переключиться на этот tab → данные загрузились
|
||
- Все кнопки/модалки секции работают
|
||
- Cross-tab navigation (если есть) работает
|
||
- F5 на `#<route>` корректно открывает tab
|
||
|
||
Если регрессия — откатить эту итерацию, разобраться, починить.
|
||
|
||
### Совет implementer'у
|
||
|
||
Если фаза становится огромной — можно сделать несколько коммитов внутри phase branch. Это inscope. Не нужно делать один гигантский коммит на 14 файлов.
|
||
|
||
## Review Checklist
|
||
|
||
- [ ] Все 13 секций имеют одинаковую структуру (init/reload)
|
||
- [ ] admin.js ≤ 800L, в нём нет дублирования с sections
|
||
- [ ] Все window.X экспорты есть для onclick handlers
|
||
- [ ] Lazy-init работает (профилировка: при открытии tab → fetch, при повторе → нет)
|
||
- [ ] F5 на каждом из 13 routes восстанавливает секцию
|
||
- [ ] Build passes: server starts, no errors
|
||
|
||
## Handoff to Next Phase
|
||
|
||
<!-- Implementer должен зафиксировать:
|
||
- Структуру window.AdminSections.X.init/reload (точное API)
|
||
- Какие функции стали глобальными через window.X (список)
|
||
- Как Phase 3 (dashboard) должна добавиться: новый sections/overview.js + new route в ROUTE_TO_SECTION
|
||
- Где живут shared утилиты (pctClass, fmtDate, esc) — admin.js или вынесены в _shared.js -->
|