CI surfaced react-hooks/set-state-in-effect on the synchronous setState(computeState(token)) inside the useEffect body. The earlier shape mirrored token -> state via an effect, which is exactly the "you might not need an effect" pattern React 19's eslint rule now flags. Switch to derived state: compute during render, use a useReducer tick to force re-render on the 30s cadence (so relative timestamps stay current even when token props don't change). Same observable behavior, no cascading renders. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
61 lines
2.4 KiB
Markdown
61 lines
2.4 KiB
Markdown
# Session Closure from History Page — Design
|
|
|
|
> **Date:** 2026-03-11
|
|
|
|
## Problem
|
|
|
|
Active sessions on the Session History page only have "View Details" and "Resume" buttons. Engineers have no way to close out sessions that were abandoned, resolved externally, or otherwise no longer needed — without resuming the entire flow.
|
|
|
|
## Design Decisions
|
|
|
|
- **Outcome model:** Hybrid — reuse existing 4 outcomes (resolved, escalated, workaround, unresolved) + add 2 early-closure outcomes (cancelled, resolved_externally)
|
|
- **UX:** Inline popover anchored to a "Close" button on the session card — no modal, no slide panel
|
|
- **Scope:** Active sessions only (started but not completed). No bulk close. No AI summary generation.
|
|
- **Backend:** No new endpoints or migrations. Expand `SessionOutcome` literal type; existing `POST /sessions/{id}/complete` handles everything.
|
|
|
|
## Data Model
|
|
|
|
No new columns. Expand `SessionOutcome` in `backend/app/schemas/session.py`:
|
|
|
|
```python
|
|
SessionOutcome = Literal["resolved", "escalated", "workaround", "unresolved", "cancelled", "resolved_externally"]
|
|
```
|
|
|
|
`VARCHAR(20)` on `session.outcome` fits both new values (max 19 chars for `resolved_externally`).
|
|
|
|
## UI
|
|
|
|
### Close Button
|
|
|
|
Appears on active session cards (`started_at` is set, `completed_at` is null), between "View Details" and "Resume":
|
|
|
|
```
|
|
[View Details] [Close] [Resume]
|
|
```
|
|
|
|
Secondary button styling (border, muted text). Not shown on prepared or completed sessions.
|
|
|
|
### Close Popover
|
|
|
|
Anchored below the "Close" button:
|
|
|
|
- **Outcome selector:** `<select>` with 6 options — Resolved, Escalated, Workaround, Unresolved, Cancelled, Resolved Externally
|
|
- **Notes:** Optional textarea (2 rows)
|
|
- **Confirm:** `bg-gradient-brand`, disabled until outcome selected
|
|
- **Cancel / click outside:** Closes popover
|
|
- Glass card styling (`glass-card-static` pattern)
|
|
|
|
On confirm: calls `POST /sessions/{id}/complete` with `{ outcome, outcome_notes }`, updates local state, shows toast.
|
|
|
|
## Implementation Scope
|
|
|
|
### Backend (2 files)
|
|
1. `backend/app/schemas/session.py` — add new outcome values to `SessionOutcome`
|
|
2. Update frontend outcome type to match
|
|
|
|
### Frontend (2-3 files)
|
|
1. `frontend/src/types/` — update `SessionOutcome` TypeScript type
|
|
2. `frontend/src/pages/SessionHistoryPage.tsx` — add Close button, popover, outcome label formatting for new values
|
|
|
|
No new components, endpoints, or migrations.
|