6.7 KiB
Session Notes — Read, Edit, Delete
Date: 2026-03-22 Status: Approved
Problem
When an admin/host ends a session, they can write notes via the EndSessionModal. But after that, those notes are effectively invisible — the History page doesn't display them, and there's no way to read, edit, or delete them from the UI. Notes only surface in raw exports.
Solution
Add the ability to view, edit, and delete session notes through two surfaces:
- Notes preview on History page session cards — inline teaser showing the first paragraph
- New Session Detail page (
/history/:id) — full rendered markdown notes with inline edit/delete, plus session management actions
Approach
Minimal extension of existing infrastructure (Approach A). No database schema changes. The sessions.notes TEXT column stays as-is. Frontend gets a new page and a markdown rendering dependency.
Backend API Changes
New Endpoints
PUT /api/sessions/:id/notes
- Auth: Required (admin token)
- Body:
{ "notes": "markdown string" } - Behavior: Overwrites
sessions.notesfor the given session (no COALESCE merge — full replacement) - Response: Updated session object
- Errors: 404 if session not found, 401 if no auth header, 403 if token invalid/expired (consistent with existing
authenticateTokenmiddleware behavior)
DELETE /api/sessions/:id/notes
- Auth: Required (admin token)
- Body: None
- Behavior: Sets
sessions.notes = NULL - Response:
{ success: true } - Errors: 404 if session not found, 401 if no auth header, 403 if token invalid/expired
Modified Endpoints
GET /api/sessions (list)
Add two fields to each session object in the response:
has_notes(boolean) —trueifnotes IS NOT NULL AND notes != ''notes_preview(string | null) — first paragraph of the markdown, truncated to ~150 characters.nullif no notes.- Remove
notesfrom list response — the fullnotesfield must be omitted from list items. Use explicit column selection instead ofSELECT s.*to avoid leaking full notes to unauthenticated clients. The list endpoint only returnshas_notesandnotes_preview.
These are computed server-side from the existing notes column.
GET /api/sessions/:id (single session)
Conditional notes visibility based on auth:
- Authenticated request: Returns full
notesfield (plushas_notesandnotes_preview) - Unauthenticated request: Returns
notes_previewandhas_notesonly.notesfield is omitted or null.
The endpoint currently does not require auth. It will remain publicly accessible but gate the full notes content behind an optional auth check.
Unchanged Endpoints
POST /api/sessions— session creation (unchanged)POST /api/sessions/:id/close— session close with optional notes (unchanged)DELETE /api/sessions/:id— session deletion (unchanged)
Frontend Changes
History Page (/history — History.jsx)
Session Cards (Sidebar)
- Add notes preview teaser below the date/games-count line when
has_notesis true - Visual treatment: indigo left-border accent, subtle background, truncated text with ellipsis
- Clicking a session card navigates to
/history/:id(instead of expanding the inline detail panel)
Action Buttons
- Active sessions: "End Session" button stays on the card (opens
EndSessionModalas before) - Closed sessions: Delete button removed from the card (moved to detail page only)
Removed from History Page
- The inline session detail panel (right side,
md:col-span-2) is replaced by navigation to the detail page ChatImportPanelmoves to the detail page- Export buttons move to the detail page
New Session Detail Page (/history/:id — SessionDetail.jsx)
New route and component.
Layout
- Back link — "← Back to History" navigates to
/history - Session header — Session number, created date/time, games count, active badge if applicable
- Notes section — Primary content area (see Notes Section below)
- Games list — Same as current History detail panel (reuse existing game card markup)
- Action buttons:
- Export as TXT / Export as JSON (same as current)
- Import Chat Log (active sessions only, admin only)
- End Session (active sessions only, admin only — opens
EndSessionModal) - Delete Session (closed sessions only, admin only — confirmation modal)
Notes Section — View Mode
- Renders notes as formatted HTML via
react-markdown - If no notes exist: shows "No notes" placeholder with "Add Notes" button (admin only)
- Admin sees an "Edit" button in the section header
Notes Section — Edit Mode (Admin Only)
- Triggered by clicking "Edit" (or "Add Notes" for empty notes)
- Rendered markdown is replaced in-place by a textarea containing the raw markdown
- "Supports Markdown formatting" hint below the textarea
- Action buttons: Save (green), Cancel (gray), Delete Notes (red, with confirmation)
- Save calls
PUT /api/sessions/:id/notes - Delete Notes calls
DELETE /api/sessions/:id/notesafter a confirmation prompt - Cancel reverts to view mode without saving
Notes Section — Public View (Unauthenticated)
- Shows
notes_previewtext (first paragraph, plain text — not markdown-rendered) - "Log in to view full notes" hint below the preview
- No edit controls
Routing
Add new route in App.jsx:
/history/:id → <SessionDetail />
Existing /history route unchanged.
New Dependency
react-markdown— lightweight markdown-to-React renderer. Used only inSessionDetail.jsxfor rendering notes.
What's NOT Changing
- Database schema — no migration, no new tables, no new columns
EndSessionModal— still works as-is for writing notes at session close timePOST /api/sessions/:id/close— untouched- WebSocket events — no notes-related real-time updates
- Home page — still shows
activeSession.notesas plain text for active sessions (no changes)
Permission Model
| Action | Auth Required |
|---|---|
| View notes preview (list + detail) | No |
| View full notes (detail page) | Yes |
| Edit notes | Yes |
| Delete notes | Yes |
| Delete session | Yes |
| End session | Yes |
This is consistent with the existing pattern where read-only session data is public and mutations require admin auth.
Notes Preview Computation
Server-side logic for notes_preview:
- If
notesis null or empty,notes_preview = null,has_notes = false - Split
noteson the first double-newline (\n\n) to get the first paragraph - Strip markdown formatting (bold, links, etc.) for a clean plain-text preview
- Truncate to 150 characters, append
...if truncated - Return as
notes_previewstring