feat(session): Add session detail page#256
Draft
Amund211 wants to merge 7 commits into
Draft
Conversation
Deploying rainbow with
|
| Latest commit: |
3db89b7
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://1afb7729.rainbow-ctx.pages.dev |
| Branch Preview URL: | https://session-detail-page.rainbow-ctx.pages.dev |
9d7f4e4 to
16b237c
Compare
This was referenced Jun 28, 2026
2262b9f to
35a3e50
Compare
2ed6810 to
86a8c67
Compare
ef62da8 to
640d290
Compare
eba80c6 to
36ee91c
Compare
A per-session permalink view at /session/$uuid/detail?date=<ISO>. Loads a single session from the flashlight POST /v1/session-at endpoint and renders the Current Session layout from the design handoff: player banner with star chip and LIVE/ENDED badge, auto-derived tags, KPI row, game-by-game momentum strip (with clutch/carried/perfect-game badges and a focusable per-game detail), an inline FKDR trajectory chart, totals card, per-mode breakdown, milestones and highlights. Each non-extrapolated row in the existing sessions list now links here via a magnifying-glass icon. The URL date is normalised to the session's start time on first load (navigate replace, with the cache pre-seeded) so the link is canonical and shareable; the Share button copies it and shows a snackbar. session === null renders a "no session yet" empty state. All colours come from the MUI theme rather than a bespoke palette: gamemode identity colours for mode dots/bars, the rainbow accent for the banner ribbon and the heater tag, and semantic tokens (success/error/warning/info/secondary) for everything else — no hand-rolled hex. The trajectory chart reads its strokes/gridlines from the theme like the history charts do. Rebased onto main: the session `ongoing` flag and the `Box` bottom-padding layout change have landed separately, so neither is reintroduced here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "perfect" session badge fired on `losses === 0`, which treats a draw as a win. A session with a draw and no losses would show the badge and its "Won every game this session" tooltip even though not every game was won. Check `wins === games` instead so the badge matches its claim. `flawless` keeps using `losses === 0` — its tooltip only promises "no losses", which a draw doesn't break. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MomentumStrip keeps its place in the component tree when the user navigates to a different session, so its `focused` tile index carried over. With a new, longer segment set that stale index pointed the expanded panel at the wrong game (or a shorter set silently hid it). Track the segment identity and clear `focused` whenever it changes, so the expanded panel always matches the session on screen. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A gap segment can cover a single game (e.g. one Dreams-mode game that can't be split into a core mode), which rendered as "1 games" in the tile label, the tile tooltip and the expanded detail context. Singularize each of these when the count is one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`repeat(${segments.length}, …)` produces the invalid `repeat(0, …)` when a
session has no game segments. The call site currently guards on games, but
clamp the count to at least one so the grid template stays valid regardless.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
f97195a to
fb00a1a
Compare
Assign the mocked `POST /v1/session-at` response to a typed `APISessionAtResponse` variable before returning it, so TypeScript structurally checks the whole literal — including each inline `game` object — against the real wire contract instead of letting the mock drift from the client's `APIGameResult`/`APIGameSegment` shapes. Export `APISessionAtResponse` so the mock can name it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A shareable, read-only session detail page at
/session/$uuid/detail?date=<ISO>, plus the stats-layer groundwork it needs. It loads a single session from the new flashlightPOST /v1/session-atendpoint and renders a full breakdown of that one session, adapted from the design handoff (design/project/Current Session.html) but mapped onto MUI while keeping the design's accent colours / rainbow ribbon.What's on the page
overall) render only when games were played in them.Data model & derivation
src/queries/sessionAt.ts— typed client forPOST /v1/session-at, returning{ session, games }. Each entry ingamesis aGameSegment{ start, end, game }wheregameisnullfor a no-game window or an unsplittable multi-game gap. Unknown gamemodes/outcomes are Sentry-reported and dropped.src/helpers/sessionDetail.ts— pure derivation (all unit-tested): the session aggregate, per-mode breakdown (including the synthetic "Other" bucket =overall − core modes), the FKDR trajectory, milestones, best/fastest game, and the trailing streak. Per-game data is derived on the client from the consecutivehistory[]snapshots — the gamemode whosegamesPlayedmoved names the game and its stat deltas become the per-game stats.Stats-layer groundwork (enables the page)
src/stats/keys.tsintoREAL_GAMEMODE_KEYS(solo/doubles/threes/fours/4v4), withALL_GAMEMODE_KEYS = [...REAL_GAMEMODE_KEYS, "overall"], so the page's mode list has a single source of truth (GAMEMODESreuses it) and adding a mode is a one-line change.nextCloseMilestoneprogression strategy — tighter targets than the regular page (next 10 stars, next 0.5 for ratios, next "half-decade" for counters) so the page's goals feel a session or two out. Milestones reusecomputeStatProgression, so targets and ETAs stay in agreement with the regular session page.computeStatrather than re-implementing ratio math.Backend contract (matches the flashlight PR)
session === nullrenders a "No session yet" panel — the empty / 404-ish state for a player who hasn't played a game with Prism running.URL handling
/session/$uuid/detailwith adatesearch param (Zodz.coerce.date()), following the existing routes'validateSearchpattern so we don't have to URL-encode colons/Zin the path.dateto the session'sstart.queriedAtvianavigate({ replace: true }), pre-seeding the cache for the canonical key so there's no refetch. After that the URL is canonical and safe to share.dateis coerced to a safe default (degrades to the "No session yet" view) rather than tripping the router's error boundary.globalThis.location.hrefand shows a "Link copied!" Snackbar (MUIAlert).Adjacent changes
Layout.tsx— the new/session/$uuid_/detailroute is added touseShownPlayerso the shown-player context resolves on the page.Choices & assumptions
session.start).G{n}, a multi-game gap spans its range (G5-7), and a no-game window is dropped (it moves no finals) rather than drawn as a flat, mislabelled point.Snackbar/Alertinstead of adding a toast library.Tests
pnpm tsc,pnpm oxlint:check,pnpm oxfmt:checkclean.sessionDetail(aggregate, modeBreakdown incl. the "Other" bucket, fkdrTrajectory numbering/labels, computeMilestones incl. blocked/off-pace branches, best/fastest game, trailing streak, inferredGameCount),detailSearch, andnextCloseMilestone(+ strict-monotonic and closer-than-natural properties).POST /v1/session-athandler was added to the shared handlers.Manual verification (staging)
🤖 Generated with Claude Code