Skip to content

feat(bracket): save on page exit + human-friendly cascade warnings#287

Merged
0800tim merged 1 commit into
mainfrom
feat/bracket-save-on-page-exit
Jun 4, 2026
Merged

feat(bracket): save on page exit + human-friendly cascade warnings#287
0800tim merged 1 commit into
mainfrom
feat/bracket-save-on-page-exit

Conversation

@0800tim

@0800tim 0800tim commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Two related UX safety nets on the bracket page.

Save on page exit

beforeunload + unmount listener that flushes the bracket via fetch(..., {keepalive: true}) to /v1/bracket/submit when the user navigates away with unsaved changes. Covers tab close, reload, external link, back/forward AND internal Next.js route changes (Pools, Profile, the drawer). Gated on authenticated + dirty + no in-flight save. Payload is ~5-10KB JSON, well under keepalive's 64KB cap.

Human-friendly cascade warnings

Previous render leaked engine codes at the user (annex_c_third_pool_incomplete FIFA Annex C routing requires exactly 8 best-third picks (got 6). × 8). New CascadeWarnings component:

  1. Contextual banner at the top of late tabs (R32 onward) when warnings originate upstream. Gold CTA jumps to the prior stage via setTab.
  2. Plain-English single-line summary per distinct warning code; the 8 duplicate codes collapse to one sentence.
  3. <details> with friendly list for curious users.

All 10 engine codes mapped, unknown codes fall back to a generic line so future codes never leak raw.

🤖 Generated with Claude Code

Two related UX safety nets on the bracket page.

Save on page exit
-----------------

Adds a beforeunload + unmount listener that flushes the bracket to
/v1/bracket/submit via fetch keepalive when the user navigates away
with unsaved changes. Covers tab close, reload, external link,
back/forward AND internal Next.js route changes (Pools, Profile,
the drawer, etc.). Gated on authenticated + dirty + no in-flight
save, so anonymous users and clean brackets don't generate
spurious traffic. Payload is the same ~5-10KB JSON the 30s
autosave already POSTs; well under keepalive's 64KB cap.

Refs are used to bridge the latest bracket/isDirty/auth values into
the listener so the empty-deps useEffect can register once and
still see current state.

Human-friendly cascade warnings
-------------------------------

The previous render dumped engine codes at the user:

  'annex_c_third_pool_incomplete FIFA Annex C routing requires
   exactly 8 best-third picks (got 6).'

Tim spotted this and asked for plain English plus, when the user
sits on a later tab (R32 onward) with empty slots because an
earlier stage is incomplete, a banner pointing them back.

New CascadeWarnings component renders three things:

  1. Contextual banner at the top of late tabs with a 'Go to
     <prior stage>' gold-pill CTA when the cascade warnings'
     origin is upstream of the current tab. Click jumps to that
     tab via setTab.
  2. Single plain-English summary line per distinct warning code
     (collapses the 8 duplicate annex_c_third_pool_incomplete
     lines to one).
  3. A <details> with the friendly list so a curious user can
     expand without us writing copy for every combo.

All engine codes mapped:

  missing_group_prediction / incomplete_group_order
    -> 'A group still needs every match predicted...'

  missing_wildcard_pick / annex_c_third_pool_incomplete
    -> 'The Top 8 3rd-placed teams stage needs all 8 picks before
        the Round of 32 can fill in.'

  annex_c_lookup_missing / annex_c_no_third_for_group_winner
    -> 'Your Top 8 3rds combination is rare enough that FIFA's
        Annex C lookup table doesn't cover it. Try swapping one of
        the picks.'

  team_not_in_group / duplicate_team_in_group
    -> 'A group has a duplicated team. Re-pick that group's
        matches to fix the ordering.'

  winner_not_in_match
    -> 'A knockout pick references a team that isn't in the
        matchup any more. Re-pick the winner.'

  withdrawn_team_advancing
    -> 'A team in this matchup has withdrawn from the tournament.'

Unknown codes fall back to a generic 'Something upstream needs
picking before this stage can finish resolving.' line so a new
engine code never leaks raw at the user.

The old <details className="bracket-warnings"> markup is gone;
the CSS class is left in place in bracket.css for any legacy
selectors (no styled rules referenced it directly).

Tim 2026-06-05.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Signed-off-by: Tim Thomas <0800tim@gmail.com>
@0800tim 0800tim merged commit 5375015 into main Jun 4, 2026
11 of 12 checks passed
0800tim added a commit that referenced this pull request Jun 4, 2026
Brings exit-save + human-friendly cascade warnings.
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

DRY-RUN — this verdict is informational; CI is not blocked.

Auto-triage: GREEN — auto-triage clear

Risk score: 0/100

Metric Value
Files changed 3
Lines added 397
Lines removed 13
Apps touched apps/web
New dependencies 0
New 3rd-party hosts 0

No flags raised by the automated scanners. A human reviewer will still take a look.

Labels applied: area:web, auto-triage:green

Posted by @vtorn/pr-triage-bot. How this works: docs/security/01-pr-triage-process.md. Disagree with the verdict? Comment /triage override <reason> and a maintainer will re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant