docs: add session notes read/edit/delete design spec
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# 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:
|
||||
|
||||
1. **Notes preview on History page session cards** — inline teaser showing the first paragraph
|
||||
2. **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.notes` for the given session (no COALESCE merge — full replacement)
|
||||
- **Response:** Updated session object
|
||||
- **Errors:** 404 if session not found, 401 if unauthenticated
|
||||
|
||||
#### `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 unauthenticated
|
||||
|
||||
### Modified Endpoints
|
||||
|
||||
#### `GET /api/sessions` (list)
|
||||
|
||||
Add two fields to each session object in the response:
|
||||
|
||||
- `has_notes` (boolean) — `true` if `notes IS NOT NULL AND notes != ''`
|
||||
- `notes_preview` (string | null) — first paragraph of the markdown, truncated to ~150 characters. `null` if no notes.
|
||||
|
||||
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 `notes` field (plus `has_notes` and `notes_preview`)
|
||||
- **Unauthenticated request:** Returns `notes_preview` and `has_notes` only. `notes` field 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_notes` is 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 `EndSessionModal` as 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
|
||||
- `ChatImportPanel` moves 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/notes` after a confirmation prompt
|
||||
- Cancel reverts to view mode without saving
|
||||
|
||||
#### Notes Section — Public View (Unauthenticated)
|
||||
|
||||
- Shows `notes_preview` text (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 in `SessionDetail.jsx` for 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 time
|
||||
- **`POST /api/sessions/:id/close`** — untouched
|
||||
- **WebSocket events** — no notes-related real-time updates
|
||||
- **Home page** — still shows `activeSession.notes` as 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`:
|
||||
|
||||
1. If `notes` is null or empty, `notes_preview = null`, `has_notes = false`
|
||||
2. Split `notes` on the first double-newline (`\n\n`) to get the first paragraph
|
||||
3. Strip markdown formatting (bold, links, etc.) for a clean plain-text preview
|
||||
4. Truncate to 150 characters, append `...` if truncated
|
||||
5. Return as `notes_preview` string
|
||||
Reference in New Issue
Block a user