fix(#73): consume canonical Weekend Context contract and repair navigation

Address the independent review blockers on PR #80 after rebasing onto the
authoritative #72 Weekend Context API.

- Replace the invented frontend WeekendContext with the exact backend contract
  (temporal_state, previous/focus/next meetings, previous_completed/active/next/
  default_analysis sessions with availability). Every valid canonical payload now
  maps to a designed state via a total resolveViewState; a well-formed response
  can never fall through to the limited-data placeholder.
- Make the canonical read the single source of truth: useWeekendContext no longer
  fans out to season/meetings/per-weekend/OpenF1/live queries. Only supplementary
  championship + news reads run, and only once the canonical context resolves.
- Fix the Prepare/analysis flow: /preview is a stable alias that renders the
  preparation surface (PreSessionView) instead of redirecting back to the same
  between-races screen.
- One primary navigation system per breakpoint: the mobile top-bar links are
  hidden so the bottom bar is the sole primary nav, and Admin is moved out of
  every Primary landmark into an operator-utilities toolbar.
- Add Vitest coverage for the contract mapping, every temporal state, loading/
  error/limited surfaces, the no-fanout guarantee, the /preview CTA, and the nav
  hierarchy; add hermetic Playwright journeys (seeded + injected canonical
  payloads), 390/768/1440 overflow checks, and Weekend visual snapshots. Retire
  the stale Command Center specs/snapshots.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-12 18:38:08 -04:00
parent 06223d992f
commit 07e5857760
30 changed files with 1468 additions and 811 deletions

View File

@@ -172,7 +172,14 @@ a { color: inherit; text-decoration: none; }
.bottom-nav-link:hover { color: var(--text-2); }
@media (max-width: 640px) {
/* On mobile the bottom bar is the single primary navigation system. The top
bar's Primary links are hidden so the two are never both shown; the top bar
keeps only the logo and the Admin operator utility. */
.app-bottom-nav { display: flex; }
.app-nav .nav-links { display: none; }
.app-nav { justify-content: space-between; }
/* Keep page content clear of the fixed bottom bar. */
.wk-page { padding-bottom: calc(60px + env(safe-area-inset-bottom, 0) + var(--s4)); }
}
/* ── Focus visibility (accessibility) ── */

View File

@@ -189,45 +189,24 @@
.wk-champ-gap { color: var(--text-3); font-size: 11px; min-width: 34px; text-align: right; }
.wk-champ-note { margin-top: var(--s4); font-size: 11px; color: var(--text-3); }
/* ── Season nav strip ── */
.wk-season-strip {
list-style: none;
display: flex;
gap: var(--s3);
overflow-x: auto;
padding-bottom: var(--s3);
scrollbar-width: thin;
/* ── Season progress strip ── */
.wk-countdown-tbc { font-size: 14px; color: var(--text-3); }
.wk-season-progress { display: flex; flex-direction: column; gap: var(--s3); }
.wk-season-progress-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.wk-season-bar {
position: relative;
height: 8px;
border-radius: 999px;
background: var(--surface-3);
overflow: hidden;
}
.wk-season-strip::-webkit-scrollbar { height: 4px; }
.wk-round-item { flex: 0 0 auto; }
.wk-round {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--s2);
width: 54px;
padding: var(--s3) 0;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-2);
.wk-season-bar-fill {
position: absolute;
inset: 0 auto 0 0;
background: var(--red);
border-radius: 999px;
}
.wk-round-status-next { border-color: var(--red); color: var(--text); }
.wk-round-status-completed { border-color: var(--border-2); }
.wk-round-num { font-size: 10px; font-weight: 700; }
.wk-round-flag { font-size: 16px; }
.wk-round-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.wk-round-completed { background: var(--green); }
.wk-round-next { background: var(--red); }
.wk-round-upcoming { background: var(--surface-3); border: 1px solid var(--border-2); }
.wk-season-legend {
display: flex;
gap: var(--s5);
margin-top: var(--s4);
font-size: 10px;
color: var(--text-3);
}
.wk-season-legend span { display: inline-flex; align-items: center; gap: 5px; }
.wk-season-legend i { width: 7px; height: 7px; }
.wk-season-progress-label { font-size: 11px; color: var(--text-3); }
/* ── Briefing ── */
.wk-briefing-list { list-style: none; display: flex; flex-direction: column; }