Admin panel (web)
Overview
Web UI for platform operators to browse all game sessions and inspect detailed per-game logs. Access requires admin authentication via Telegram OTP.
FR: FR-057, FR-058
Routes
| Route | Auth | Description |
|---|---|---|
/admin |
Admin JWT | Redirect to /admin/games |
/admin/login |
Public | Login form (username + OTP code) |
/admin/games |
Admin JWT | Paginated list of all games |
/admin/games/[id] |
Admin JWT | Full timeline log for one game |
Unauthenticated access to protected routes redirects to /admin/login.
Login screen (/admin/login)
┌─────────────────────────────────────┐
│ TableTime Admin │
│ │
│ Username: [Mihaham ] │
│ Code: [______ ] │
│ │
│ [ Войти ] │
│ │
│ Получите код: напишите боту │
│ @YourBot /admin login │
└─────────────────────────────────────┘
- Username prefilled with
Mihaham. - On success: store
tabletime_admin_tokeninlocalStorage, redirect to/admin/games. - Error states: invalid code, expired code, wrong username.
Games list (/admin/games)
Table columns:
| Column | Source |
|---|---|
| Started | game.started_at |
| Plugin | game.plugin_id |
| Session | invite_code (link to session games) |
| Status | playing / finished |
| Winner | participant display name |
| Actions | Link «Лог» → /admin/games/{id} |
Filters (query params):
plugin_id— dice_board, kontakt, shlyapastatus— playing, finishedfrom/to— ISO date rangeinvite_code— session invite codepage,limit— pagination (default limit 50)
Game log (/admin/games/[id])
Header: game metadata (plugin, session code, status, started/finished, winner).
Timeline — chronological list merged from game_actions and game_events:
#1 game.started 2026-06-14 12:00:01
plugin: dice_board
players: Alice, Bob
[▼ initial_state JSON]
#2 roll_dice 2026-06-14 12:00:15 Alice web
[▼ payload] [▼ result_state]
#3 action.rejected 2026-06-14 12:00:20 Bob telegram
NOT_YOUR_TURN
[▼ payload]
#4 choose_cell 2026-06-14 12:00:25 Alice web
row=2, col=3
[▼ payload] [▼ result_state]
#5 game.finished 2026-06-14 12:05:00
winner: Alice
[▼ final_state JSON]
Each entry shows:
- Sequence number
- Kind:
actionorevent - Human-readable label (plugin-aware where possible)
- Timestamp (UTC, local display optional)
- Player name and channel (for actions)
- Expandable JSON blocks for
payload,result_state, event payload
Note for legacy games: lifecycle events (game.started, etc.) exist only from deployment date onward; older games show player actions only.
API client
Admin requests use Authorization: Bearer <admin_token> via adminFetch() in apps/web/lib/api.ts.
Endpoints consumed:
POST /api/v1/auth/admin/otp/verifyGET /api/v1/admin/gamesGET /api/v1/admin/games/{id}GET /api/v1/admin/games/{id}/log
Styling
Same Tailwind patterns as /auth/login. No shadcn dependency required — plain forms and tables.