# Phase 3: Dashboard #overview

**Status:** ✅ Implemented (pending review)
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** fullstack
**Parallelizable with:** Phase 4, Phase 5

## Objective

Сделать `#overview` дефолтным route'ом admin-панели — landing-страница "что требует внимания". Заменяет нынешние обезличенные stat-cards (totalUsers, totalTests, ...) на actionable digest за последние 24-48 часов.

## Tasks

- [x] Backend: новый endpoint `GET /api/admin/overview`:
  ```js
  {
    newUsers24h: number,      // регистрации за 24ч
    newSessions24h: number,    // запущенных тестов
    bannedThisWeek: [{ id, name, banned_at }],
    failedSessions24h: number, // тесты со статусом не completed
    topSessions24h: [{ id, user_name, subject, score, total, finished_at }],  // топ-5 за 24ч
    activeUsers24h: number,    // unique last_login за 24ч
    pendingMigrations: number, // если возможно проверить
    recentErrors: [{ id, type, message, created_at }] // если есть audit log с типом 'error'
  }
  ```
  - Контроллер: `backend/src/controllers/adminController.js` → новая функция `getOverview`
  - Route: добавить в `backend/src/routes/admin.js`
  - Auth: admin или teacher (как остальные admin/* — RBAC same)
  - Performance: один запрос для каждого поля, простые COUNT/SELECT, без JOIN'ов где возможно
- [x] Frontend: новый section `frontend/js/admin/sections/overview.js`:
  - Использует структуру из Phase 2
  - Загружает `/api/admin/overview`
  - Рендерит карточки в `<div id="tab-overview">` секции:
    - **Активность 24ч** — registr, sessions, active users (число + спарклайн если есть данные)
    - **Требует внимания** — banned this week (если >0), failed sessions (если >5%), pending migrations
    - **Топ-сессии** — таблица top-5 за день с click→drilldown
    - **Quick links** — "Все пользователи", "Все сессии", "Создать тест" (deep-link в соответствующие routes)
  - LS.skeleton при загрузке, LS.state.error на fail
- [x] HTML: добавить `<div class="tab-pane" id="tab-overview">` в admin.html (перед остальными tab-pane)
- [x] Nav: добавить admin-nav-item для `overview` (icon: layout-dashboard / activity)
- [x] Регистрация в ROUTE_TO_SECTION (из Phase 2): `overview: 'overview'`
- [x] Сделать `#overview` дефолтным route'ом в router (из Phase 1) — если пустой hash, navigate to `#overview` вместо `#stats`
- [x] Старый `#stats` остаётся как доступный route (legacy backend stats), но не дефолтный

## Files to Modify/Create

- `backend/src/controllers/adminController.js` — добавить `getOverview` функцию (~60-90L)
- `backend/src/routes/admin.js` — добавить `router.get('/overview', requireAdmin, getOverview)`
- `frontend/js/admin/sections/overview.js` — новый, ~250-350L
- `frontend/admin.html` — добавить `<div id="tab-overview">` + nav-item + `<script>` тег
- `frontend/js/admin/admin.js` — изменить default route на `#overview` (если ещё не сделано через router.js config)

## Acceptance Criteria

- `/admin` (без hash) → редирект на `#overview`
- `#overview` показывает реальные данные (новые регистрации видны если кто-то зарегистрировался)
- Карточки кликабельные (click → deep-link в users / sessions с фильтром)
- При отсутствии данных (свежая БД) — empty states, не crash
- Endpoint выполняется <100ms на тестовой БД
- F5 на `#overview` работает
- Auth: только teacher/admin (как остальные /admin/*)

## Notes

### Что считать "требует внимания"

Делаем простые threshold'ы для MVP:
- `bannedThisWeek` > 0 — показать жёлтую карточку с именами
- `failedSessions24h` > 0 — показать список (failed = status != 'completed')
- `pendingMigrations` > 0 — показать красную (но это редкий случай — миграции применяются на старте)

Можно потом расширить до "новых жалоб", "переполненных классов", и т.д. — это уже после merge.

### Дизайн карточек

Bento-grid из 4-6 карточек:
```
+---------+---------+
|  24ч    | Внимание|
| метрики |   !     |
+---------+---------+
|   Топ сессии      |
|   (table)         |
+---------+---------+
| Quick links       |
+-------------------+
```

Использовать существующие стили `.stat-card`, `.section-title` из admin.html — не изобретать.

### Что НЕ делать в этой фазе

- Не делать реалтайм WebSocket-обновления (это уже Phase 7+)
- Не делать графики/чарты (пока числа + sparkline опционально)
- Не делать персонализацию (например "ваши классы")

## Review Checklist

- [ ] Endpoint работает и возвращает корректную форму ответа
- [ ] Frontend handles empty state gracefully
- [ ] Click на quick-link корректно навигирует через AdminRouter
- [ ] Нет hardcoded date math (использовать SQL `datetime('now', '-24 hours')`)
- [ ] Roles correct (admin/teacher only, не у students)
- [ ] No SQL injection — параметры через `?` placeholders
- [ ] Build passes

## Handoff to Next Phase

**Endpoint shape (`GET /api/admin/overview`):**

```json
{
  "newUsers24h": 0,
  "newSessions24h": 0,
  "activeUsers24h": 2,
  "activeClasses": 5,
  "failedSessions24h": 0,
  "bannedThisWeek": [
    { "id": 42, "name": "...", "email": "...", "banned_at": "2026-05-15 12:30:00" }
  ],
  "topSessions24h": [
    { "id": 101, "user_name": "...", "subject_name": "Физика",
      "score": 18, "total": 20, "percent": 90.0, "finished_at": "2026-05-16 09:14:22" }
  ]
}
```

**Performance:** ~0.08ms/call avg (benchmarked 100 iters) — well under 100ms target.

**Auth:** uses `router.use(requireRole('admin'))` block (admin-only, same as `/stats`).
`/features` block (teacher+admin) is above the route. Teacher access NOT granted — matches
sibling `/stats` behavior. If Phase 4-6 wants teacher access, move `/overview` above the
admin-only `router.use(...)` line in `backend/src/routes/admin.js`.

**Quick-link wiring (Phase 4/5 extension point):**
Quick-link buttons live in `overview.js` → render() → `.ov-quick-grid`. They use a
`data-go="#hash"` attribute and a delegated click → `AdminRouter.navigate(...)`. To add
a new quick-link, append a `<button class="ov-quick-btn" data-go="#whatever">` to the grid.

**Deep-link openings for Phase 6:**
- `#users` and `#sessions` are wired (vanilla routes — no filter params yet)
- Future: extend `AdminRouter` to support `#users?filter=banned` / `#sessions?status=failed`
  query-style params. Currently router parses `route + params` (`/users/123` form), no `?`
  parsing — Phase 6 can extend `router.js::parseHash()`.

**Router defaults updated:**
- `admin.js::initAdminRouter::activate()` fallback route changed `'stats'` → `'overview'`
- Initial dispatch (empty hash) navigates to `#overview` instead of `#stats`
- Initial init call: `AdminSections.overview.init()` instead of `AdminSections.stats.init()`

**Files modified/created (line counts):**
- NEW: `frontend/js/admin/sections/overview.js` (~205 lines)
- `backend/src/controllers/adminController.js` (+57 lines: new statements + `getOverview`)
- `backend/src/routes/admin.js` (+1 line)
- `js/api.js` (+1 line helper + 1 export)
- `frontend/admin.html` (nav-item +3 lines, tab-pane +4 lines, script tag +1 line, swap `active`)
- `frontend/js/admin/admin.js` (`ROUTE_TO_SECTION` +1 entry, default route refs swapped from `stats` → `overview`)

**Unrelated to Phase 3 (do not touch):** old `#stats` route remains functional — used as fallback in the past, can be linked from Overview as "детальная статистика" in a future polish pass.
