8a7bed487f
AdminRouter wraps existing switchTab for deep-linking. - frontend/js/admin/router.js (new, 102L): parse/navigate/current/on/off, recursion guard via _navigating flag - admin.html: +1 <script> before admin.js - admin.js: switchTab(btn, opts) + initAdminRouter IIFE for hashchange dispatch Backward compat: all 21 onclick=switchTab(this) callsites continue working. F5 / back / forward / deep-link verified. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.8 KiB
3.8 KiB
Feature Context: Admin Panel Redesign
Current State
(будет обновляться после каждой фазы)
- ✅ Phase 1 implemented —
window.AdminRouterобёртывает старыйswitchTab(hash ↔ tab двусторонне).switchTabпринимает 2-й аргумент{ fromRouter: true }для предотвращения рекурсии. Default =#stats. Файлы:frontend/js/admin/router.js(новый),frontend/admin.html(+1 строка),frontend/js/admin/admin.js(модификацияswitchTab+ IIFEinitAdminRouter). - ⬜ Phase 2 not started — все 13 секций в admin.js монолите
- ⬜ Phase 3-6 not started
Temporary Workarounds
(пусто — заполняется implementer'ом)
Cross-Phase Dependencies
- Phase 2 depends on Phase 1: sections подписываются на router events, чтобы lazy-init по hashchange
- Phases 3, 4, 5 depend on Phase 2: новые модули будут добавляться в
js/admin/sections/(структура из фазы 2) - Phase 6 depends on Phase 2: deep page для user/session — это новые sections в той же структуре
- Phase 6 removes старую
.user-paneloverlay из admin.html — фазы 1-5 НЕ должны её удалять
Router Contract (Phase 1)
// Subscribe in any future module:
AdminRouter.on('change', ({ route, params, raw }) => { /* ... */ });
// Programmatic deep-link without polluting history:
AdminRouter.navigate('#users/123', { replace: true, silent: true });
- Events emitted:
'change'only (payload: parsed route). - Late subscribers do NOT receive replay — call
AdminRouter.current()on init. silent: truesuppresses the synchronous emit but nativehashchangestill fires; the internal_navigatingflag in router.js prevents the listener from re-firing.switchTab(btn, { fromRouter: true })— call from router handlers to skip the reverse-sync write tolocation.hash(avoids redundantreplaceState).
Implementation Notes
Существующая структура (что менять / что НЕ менять)
Точки входа в admin.js:
LS.initPage()— auth + role checkswitchTab(btn)— текущий tab-роутер; будет обёрнут router'ом, но не удалён до фазы 6- Per-tab
*Initedфлаги (usersInited,sessionsInited, ...) — переедут в section modules
Backward compat обязателен:
goAddQuestion(slug)и подобные cross-tab onclick handlers должны работать- Старые ссылки
<a href="#stats">(если есть) тоже
Конвенции вновь создаваемых модулей
Каждая section (фаза 2):
// js/admin/sections/<name>.js
(function () {
'use strict';
let inited = false;
async function load() { /* ... */ }
window.AdminSections = window.AdminSections || {};
window.AdminSections.<name> = {
init: async () => { if (inited) return; inited = true; await load(); },
reload: load,
};
})();
Router (фаза 1):
// js/admin/router.js
window.AdminRouter = {
navigate(hash) { /* update hash + dispatch */ },
current() { /* parse current hash */ },
on(event, fn) { /* subscribe */ },
};
Какие onclick handlers есть сейчас (выборка)
Из admin.html / admin.js:
onclick="switchTab(this)"— на каждой admin-nav-itemonclick="openUserPanel(event, ${u.id}, '${u.role}')"— на user rowonclick="changeRole(this)"— на role-selectonclick="goAddQuestion('${slug}')"— cross-tab
Эти должны работать без изменений до фазы 6.