feat(admin): Phase 6 sub-commit 2 — remove .user-panel overlay
Now that the deep pages (sub-commit 1) work, retire the legacy
.user-panel inline overlay entirely.
* admin.html: removed <div class="user-panel" id="user-panel"> block
inside #tab-users, removed dead .user-panel* CSS (kept .btn-close
for any external use).
* users.js: removed openUserPanel / closeUserPanel / reloadUserPanel
and their closure state (activeTr, activeUserRole). User row onclick
switched from openUserPanel(...) → AdminRouter.navigate('#users/N').
clearUserHistory / toggleBanUser / confirmDeleteUser / openEditUserModal
/ openUserPermsModal / doSet/doReset* all refactored to use the
getActiveUid() helper (reads window.activeUid, set by user-detail.init)
+ reloadDetailAndList() helper (refreshes deep page + list together).
* sessions.js: row click + eye-button switched from toggleDrawer(id)
→ gotoSession(id) → AdminRouter.navigate('#sessions/N'). Removed
toggleDrawer + renderDrawer functions (~60L) and openDrawerId state.
Inline drawer markup removed from the row template.
Verified node --check on all touched JS. ast-index confirms zero
remaining usages of openUserPanel / closeUserPanel / reloadUserPanel /
toggleDrawer across the repo.
This completes Phase 6 and the admin-redesign feature.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,18 @@
|
||||
- ✅ Phase 3 implemented — `#overview` стал дефолтным route'ом admin-панели. Backend: `GET /api/admin/overview` (admin-only, ~0.08ms/call) возвращает digest за 24ч: новые регистрации, запущенные сессии, активные юзеры, активные классы, failed-сессии, забаненные за неделю (из audit log), топ-5 завершённых сессий. Frontend: `frontend/js/admin/sections/overview.js` (~205L) рендерит bento-grid карточки + alerts + топ-таблицу + quick-links (deep-link через `AdminRouter.navigate`). `admin.js`: дефолт `'stats'` → `'overview'` в `activate()`, initial nav, и initial init. Old `#stats` остался работающим (доступен через nav-item). Файлы: `frontend/js/admin/sections/overview.js` (NEW), `backend/src/controllers/adminController.js` (+57L: `overviewStmts` + `getOverview`), `backend/src/routes/admin.js` (+1L), `js/api.js` (+1 helper), `frontend/admin.html` (nav-item + tab-pane + script tag), `frontend/js/admin/admin.js` (ROUTE_TO_SECTION + default route refs).
|
||||
- ✅ Phase 4 implemented — Cmd+K (Ctrl+K) global command palette. Backend: `GET /api/admin/search?q=X` (admin-only) returns `{users[5], tests[3], classes[3]}` via 3 prepared LIKE queries (`title AS name` for tests, `invite_code AS code` for classes). Frontend: `frontend/js/admin/palette.js` (~320L) — custom modal (NOT LS.modal) with capture-phase Ctrl+K listener that `stopImmediatePropagation`'s to override `/js/search.js`. Debounced 150ms, ↑↓ Enter Esc keyboard nav, click-outside close. Action registry (8 entries) is hardcoded — extend by appending to `ACTIONS` const. Result interactions: user → `AdminRouter.navigate('#users/' + id)` (Phase 6 deep page hook), test → `#tests`, class → `/classes#id`. Exposed: `window.AdminPalette = { open, close, isOpen }`, `LS.adminGlobalSearch(q)`. Files: `frontend/js/admin/palette.js` (NEW), `backend/src/controllers/adminController.js` (+50L: `searchStmts` + `globalSearch`), `backend/src/routes/admin.js` (+1L), `js/api.js` (+4L helper + export), `frontend/admin.html` (+1 script tag).
|
||||
- ✅ Phase 5 implemented — per-row hover quick actions для users + sessions tables. Users row (admin && uid !== self): 4 кнопки (Ban/Unban toggle, Award coins via LS.modal с amount+reason, Sessions → AdminRouter.navigate('#sessions'), Delete). Sessions row: 2 кнопки (View → toggleDrawer, Delete). Все `event.stopPropagation()` чтобы не триггерить row-click overlay/drawer. CSS injected ONCE через `ensureRowActionsStyles()` (de-dup по `#row-actions-style` id, обе секции проверяют existence). Mobile ≤768px: actions hidden (row-click overlay остаётся fallback'ом). Backend: NEW `DELETE /api/admin/sessions/:id` (admin-only) → `_deleteSessionTx` транзакция: nullify `assignment_sessions.session_id`, delete `user_answers` + `session_questions` (FK CASCADE но делаем explicit для visibility), delete `test_sessions`. Audit log: `'session.delete'`. Файлы: `frontend/js/admin/sections/users.js` (343→469L, +126), `frontend/js/admin/sections/sessions.js` (159→210L, +51), `backend/src/controllers/adminController.js` (+27L: `_deleteSessionTx` + `deleteSession`), `backend/src/routes/admin.js` (+1L), `js/api.js` (+1 helper + export). NO эмоджи, inline SVG (Lucide outline-style 24x24 viewBox), Lucide уже доступен через CDN. User-panel overlay НЕ удалена — оставлена для Phase 6.
|
||||
- ⬜ Phase 6 not started
|
||||
- ✅ Phase 6 implemented (sub-commits bd30200 + new) — deep entity pages replace legacy `.user-panel` overlay. NEW: `frontend/js/admin/sections/user-detail.js` (~370L) and `frontend/js/admin/sections/session-detail.js` (~180L), both IIFE pattern. `admin.js` has `DEEP_ROUTES = { users:'user-detail', sessions:'session-detail' }` + `activateDeepPane()`; `activate(route, params)` checks for first-param to dispatch deep page (parent nav-item stays highlighted). Sub-tabs (overview/sessions/classes/audit) with URL sync via `udSwitchTab()` → `AdminRouter.navigate('#users/N/<sub>', { replace: true, silent: true })`. Backend endpoints reused: `GET /api/admin/users/:id/sessions` (user history), `GET /api/admin/sessions/:id` (session detail), `GET /api/admin/audit-log?limit=500` (client-side filtered by uid for Audit tab). Removed: `<div class="user-panel" id="user-panel">` overlay HTML, `.user-panel*` CSS, `openUserPanel`/`closeUserPanel`/`reloadUserPanel` JS, `toggleDrawer`/`renderDrawer` in sessions.js. Row onclick: `openUserPanel(...)` → `AdminRouter.navigate('#users/N')`; sessions row → `gotoSession(id)` → `AdminRouter.navigate('#sessions/N')`. `clearUserHistory`/`toggleBanUser`/`confirmDeleteUser` now use `getActiveUid()` helper (reads `window.activeUid` set by user-detail.init) instead of overlay closure. `quickOpenUserSessions(uid)` → `#users/<uid>/sessions` (deep page, Sessions sub-tab). Classes sub-tab is placeholder (no per-user classes endpoint exists). Charts: simple inline SVG bar chart for per-subject avg %.
|
||||
|
||||
## Phase 6 Routes Glossary
|
||||
|
||||
- `#users` — list (Phase 2 section)
|
||||
- `#users/123` — deep page, default Overview sub-tab
|
||||
- `#users/123/sessions` — deep page, Sessions sub-tab
|
||||
- `#users/123/classes` — deep page, Classes sub-tab (placeholder)
|
||||
- `#users/123/audit` — deep page, Audit sub-tab (admin only)
|
||||
- `#sessions` — list (Phase 2 section)
|
||||
- `#sessions/456` — deep page
|
||||
- Cmd+K palette user pick → `#users/N` (opens deep page)
|
||||
|
||||
## Temporary Workarounds
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
- [x] Phase 3: Dashboard #overview [domain: fullstack] → [subplan](./phase-3-dashboard.md) (parallelizable with 4, 5)
|
||||
- [x] Phase 4: Cmd+K command palette [domain: fullstack] → [subplan](./phase-4-palette.md) (parallelizable with 3, 5)
|
||||
- [x] Phase 5: Per-row quick actions [domain: frontend] → [subplan](./phase-5-quick-actions.md) (parallelizable with 3, 4)
|
||||
- [ ] Phase 6: Deep entity pages [domain: frontend] → [subplan](./phase-6-deep-pages.md)
|
||||
- [x] Phase 6: Deep entity pages [domain: frontend] → [subplan](./phase-6-deep-pages.md)
|
||||
|
||||
**Параллелизация:** фазы 3, 4, 5 независимы (touch different files, no shared state) — выполняются параллельно после завершения фазы 2.
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
| Phase 3: Dashboard | fullstack | ✅ Done | ✅ PASS w/ 3 SQL warnings (post-merge polish) | ✅ | ✅ 41acbdd |
|
||||
| Phase 4: Palette | fullstack | ✅ Done | ✅ PASS w/ notes (limit param cleanup applied) | ✅ | ✅ f562fe4 |
|
||||
| Phase 5: Quick actions | frontend | ✅ Done | ✅ PASS w/ notes | ✅ | ✅ 69113ab |
|
||||
| Phase 6: Deep pages | frontend | 🟡 In Progress | ⬜ | ✅ node --check | ⬜ |
|
||||
| Phase 6: Deep pages | frontend | ✅ Done | ⬜ | ✅ node --check | ⬜ (2 sub-commits: bd30200 + new) |
|
||||
|
||||
## Final Review
|
||||
- [ ] Comprehensive code review (final-reviewer agent)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Phase 6: Deep entity pages
|
||||
|
||||
**Status:** 🟡 In Progress (sub-commit 1 of 2 done)
|
||||
**Status:** ✅ Done (sub-commits: bd30200 + final remove-overlay commit)
|
||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||
**Domain:** frontend
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] **User detail page** (`frontend/js/admin/sections/user-detail.js`):
|
||||
- [x] **User detail page** (`frontend/js/admin/sections/user-detail.js`):
|
||||
- Реагирует на route `#users/:id`
|
||||
- Layout:
|
||||
- **Header**: avatar, name, role badge, email, action buttons (ban/edit/perms/delete), back-link to `#users`
|
||||
@@ -22,23 +22,19 @@
|
||||
- **Graphs** (опционально, можно отдельным таб'ом):
|
||||
- Простой SVG-чарт: успеваемость по неделям
|
||||
- Mini-bar chart: avg % по предметам
|
||||
- [ ] **Session detail page** (`frontend/js/admin/sections/session-detail.js`):
|
||||
- [x] **Session detail page** (`frontend/js/admin/sections/session-detail.js`):
|
||||
- Реагирует на route `#sessions/:id`
|
||||
- Layout: header (user, subject, score, дата) + список вопросов/ответов (правильно/нет, текст), back-link
|
||||
- [ ] **Router updates** (`frontend/js/admin/router.js` если ещё не поддерживает):
|
||||
- `#users/123` → emit { route: 'users', params: ['123'] }
|
||||
- `#sessions/456` → emit { route: 'sessions', params: ['456'] }
|
||||
- [ ] **Admin.js dispatch**:
|
||||
- При route с params → init detail-section вместо list-section
|
||||
- При route без params → init list-section (как раньше)
|
||||
- [ ] **Удалить overlay-код:**
|
||||
- В `frontend/admin.html` удалить `<div class="user-panel" id="user-panel">` блок
|
||||
- В `sections/users.js` удалить `openUserPanel`, `closeUserPanel`, `reloadUserPanel`
|
||||
- В `sections/users.js` поменять onclick: `onclick="openUserPanel(event,${u.id},'${u.role}')"` → `onclick="AdminRouter.navigate('#users/${u.id}')"`
|
||||
- [ ] **Replace** в Phase 5 quick action "Sessions" — теперь `AdminRouter.navigate('#users/${uid}/sessions')`:
|
||||
- Парсить sub-tab из route
|
||||
- [x] **Router updates** (`frontend/js/admin/router.js` если ещё не поддерживает): router из Phase 1 уже парсит params — обновлять не пришлось
|
||||
- [x] **Admin.js dispatch**: добавлена `DEEP_ROUTES` map + `activateDeepPane()` + `activate(route, params)`
|
||||
- [x] **Удалить overlay-код:**
|
||||
- [x] В `frontend/admin.html` удалён `<div class="user-panel" id="user-panel">` блок + `.user-panel*` CSS
|
||||
- [x] В `sections/users.js` удалены `openUserPanel`, `closeUserPanel`, `reloadUserPanel`
|
||||
- [x] В `sections/users.js` onclick переключён на `AdminRouter.navigate('#users/${u.id}')`
|
||||
- [x] **Replace** в Phase 5 quick action "Sessions": `quickOpenUserSessions(uid)` → `AdminRouter.navigate('#users/' + uid + '/sessions')`
|
||||
- Парсить sub-tab из route (выполнено через `params[1]` в `activate()`)
|
||||
- Открывать user-detail page с активным Sessions tab
|
||||
- [ ] **Глоссарий routes после фазы:**
|
||||
- [x] **Глоссарий routes после фазы:**
|
||||
- `#overview` — dashboard (Phase 3)
|
||||
- `#users` — list
|
||||
- `#users/123` — user detail (overview tab default)
|
||||
@@ -112,6 +108,38 @@
|
||||
|
||||
## Handoff to Next Phase
|
||||
|
||||
<!-- Это финальная фаза. Implementer записывает: что ещё не сделано,
|
||||
какие follow-up задачи стоит зафиксировать (графики, realtime, мобильная версия).
|
||||
Эти заметки помогут final-reviewer и при подготовке merge-summary. -->
|
||||
Это финальная фаза. Что реализовано в этой фазе:
|
||||
|
||||
### Done
|
||||
|
||||
- Deep page `#users/:id` с sub-tabs (overview/sessions/classes/audit) и URL-sync sub-routing
|
||||
- Deep page `#sessions/:id` с полным разбором ответов
|
||||
- F5 / browser-back / закладки работают на любом deep-URL
|
||||
- Overlay `.user-panel` полностью удалён (HTML + CSS + JS)
|
||||
- Sessions row-click переключён с inline drawer на deep page navigation (`gotoSession(id)`)
|
||||
- Audit sub-tab фильтрует system-wide audit-log по uid client-side
|
||||
- Inline SVG bar chart для per-subject avg % на Overview sub-tab (no chart.js dep)
|
||||
- Cmd+K palette user-pick (Phase 4) теперь открывает deep page (ранее был fallback на `#users`)
|
||||
- 2 sub-commit разбивка для безопасности: `bd30200` (add deep pages, overlay still works) → finale (remove overlay)
|
||||
|
||||
### Post-merge follow-ups (NOT блокирующие для merge)
|
||||
|
||||
1. **Classes sub-tab — placeholder.** Нет backend endpoint `GET /admin/users/:id/classes`. Текущий UI показывает empty-state со ссылкой на `/classes`. После merge добавить endpoint (выбрать из `class_members` по `user_id`).
|
||||
2. **Audit sub-tab — client-side filter.** Фильтрация делается на 500 строк из общего лога — для бóльших инсталляций нужен `GET /admin/audit-log?user_id=N` (server-side). Сейчас работает корректно для типичной нагрузки LearnSpace (<10k записей).
|
||||
3. **Charts — single bar chart only.** План включал опциональные графики "успеваемость по неделям" — оставил на post-merge. Использовать тот же inline SVG паттерн (`.ud-bars` + `.ud-bar-fill`).
|
||||
4. **Mobile.** Header кнопки сжимаются на ≤640px (см. CSS `.ud-header` block в user-detail.js), но при большом количестве действий могут перекрыть. Можно добавить overflow menu (`<details>`) post-merge если жалобы.
|
||||
5. **`.user-panel` CSS не полностью удалён** — оставлен только `.btn-close` (используется ещё где-то?). Если нет — можно удалить тоже.
|
||||
6. **`window.activeUid` — глобальное состояние.** Сейчас и user-detail.js, и users.js пишут/читают `window.activeUid`. Это работает, но в идеале нужно перенести user-only modals (eu-modal, up-modal) в user-detail.js целиком. Не критично, но улучшит изоляцию.
|
||||
|
||||
### Final smoke checklist (для final-reviewer)
|
||||
|
||||
- [ ] Открыть `/admin#overview` — dashboard
|
||||
- [ ] Cmd+K → найти юзера → пик из списка → открыть deep page
|
||||
- [ ] На deep page переключить sub-tabs (URL обновляется)
|
||||
- [ ] F5 на `#users/N/sessions` → page восстановлен
|
||||
- [ ] Browser back → возврат на `#users` list
|
||||
- [ ] Header action: Изменить → modal → save → header обновлён
|
||||
- [ ] Header action: Бан → toast → header обновлён (метка "Разблокировать")
|
||||
- [ ] Click on session row in Sessions sub-tab → `#sessions/M` (deep session page)
|
||||
- [ ] Session page: Delete → toast → navigate back to `#sessions` list
|
||||
- [ ] No console errors
|
||||
|
||||
Reference in New Issue
Block a user