From f3751f0042f7c42a565b47fd665daaaeb9914a3a Mon Sep 17 00:00:00 2001 From: Lucas Kalil Date: Tue, 16 Jun 2026 19:36:39 -0300 Subject: [PATCH] docs: log stats screen Stage A (feature/stats-final-screen) Append the Stage A entry to project-memory.md, refresh project-map.md (app.js/stats.js descriptions + optional data layers + branch note), and add the A-J stage tracker to TODO.md. Co-Authored-By: Claude Opus 4.8 --- .agents/TODO.md | 21 +++++++++++++++++++++ .agents/project-map.md | 25 +++++++++++++++++++------ .agents/project-memory.md | 13 +++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/.agents/TODO.md b/.agents/TODO.md index 9c2cea3..64eab52 100644 --- a/.agents/TODO.md +++ b/.agents/TODO.md @@ -72,6 +72,27 @@ Use checkboxes to track progress. Items marked **πŸ”΄ BLOCKER** prevent release; --- +## 7. Stats final screen β€” `feature/stats-final-screen` (post-Cup release) + +Full build of `.agents/stats-screen-plan.md` on a feature branch, merged to `master` at the end of +the Cup. One approval gate before each stage. A–F are buildable from today's data; G/H/I are +data-blocked (graceful-degradation shell lets the UI land dark until JSON arrives). + +### 🟒 OPTIONAL (release feature) +- [x] ~~Stage 0 β€” branch `feature/stats-final-screen` off `master`~~ +- [x] ~~Stage A β€” degradation engine + fault-tolerant `loadData` + sticky scrollspy sub-nav + media fallback~~ (2026-06-16) +- [ ] Stage B β€” verdict hero (gated on FINAL finished; aggregate-hero fallback) + goals-by-round chart +- [ ] Stage C β€” final ranking 1–48 (phase-reached chain), favorite-row highlight, team record cards +- [ ] Stage D β€” auto record-cards + "format-48 debuts" band +- [ ] Stage E β€” 104-match results archive (accordion by phase, row β†’ modal) +- [ ] Stage F β€” team comparator (diverging bars) +- [ ] Stage G β€” Layer 2 cheap data (attendance, `cards`β†’{y,r} migration, decidedIn, coords) β€” **SCHEDULE LATE** +- [ ] Stage H β€” Layer 3 players (`players.json` + `player-events.json` + `awards.json`) +- [ ] Stage I β€” Layer 4 editorial (`curiosities.json` + `all-time-baselines.json`) +- [ ] Stage J β€” polish (responsive/a11y audit, perf, Lighthouse, `DATA_VERSION` bump, README/i18n) + +--- + ## Quick final checklist ``` diff --git a/.agents/project-map.md b/.agents/project-map.md index 356e102..a4f087c 100644 --- a/.agents/project-map.md +++ b/.agents/project-map.md @@ -3,6 +3,8 @@ Navigation map of the codebase. Use this to find which file owns a concern before reading code. > **Status 2026-06-12 (all 12 steps + real-data migration done):** everything works with **real World Cup 2026 data** β€” all views, bracket interactions, simulation, responsive/a11y pass, favorites, time toggle, challenge, share link, `.ics` export. Remaining: keep `results.json` current, fill `thirdPlaceAssignment` after the group stage (~Jun 27), Lighthouse run + GitHub Pages deploy. Spec source of truth: `world-cup-2026-hub-spec-en.md` + `complement-spec-worldcup2026-en.md` (complement **wins on conflict**). +> +> **Branch note (2026-06-16):** the full post-Cup Stats screen (`.agents/stats-screen-plan.md`, stages A–J) is being built on **`feature/stats-final-screen`** (merges to `master` at the end of the Cup). **Stage A done** on that branch (degradation engine + fault-tolerant `loadData` + sticky scrollspy sub-nav + flag monogram fallback). `master` keeps the partial Stats tab + daily refreshes. Descriptions below reflect the branch. --- @@ -51,7 +53,10 @@ worldcup2026/ β”‚ β”‚ β”‚ live data refresh (startResultsPolling: 90s poll of β”‚ β”‚ β”‚ results.json, no-store + ?t, content signature, pauses β”‚ β”‚ β”‚ when tab hidden, stops at FINAL; on change also refetches -β”‚ β”‚ β”‚ bracket-config.json; fires `datachange`) +β”‚ β”‚ β”‚ bracket-config.json; fires `datachange`); +β”‚ β”‚ β”‚ loadOptional() = fault-tolerant fetch of the stats screen's +β”‚ β”‚ β”‚ optional data layers (absent β†’ silent empty default); +β”‚ β”‚ β”‚ trackHeaderHeight() keeps the --header-h CSS var live β”‚ β”‚ β”œβ”€β”€ schedule.js Match list, filters (incl. occurrence toggle β”‚ β”‚ β”‚ Played/Upcoming via hybrid matchState), search, β”‚ β”‚ β”‚ sort, "My Matches"; 60s clock-tick re-render @@ -63,8 +68,12 @@ worldcup2026/ β”‚ β”‚ β”œβ”€β”€ storage.js localStorage wrapper β€” wc2026_* keys, auto-JSON β”‚ β”‚ β”œβ”€β”€ i18n.js EN/PT-BR dicts + t(key), lang toggle β”‚ β”‚ β”œβ”€β”€ stats.js β˜… Stats tab: tournament-to-date aggregates (finished -β”‚ β”‚ β”‚ matches only), hero pulse + overview + goals-by-stage. -β”‚ β”‚ β”‚ PARTIAL (during-cup) β€” grows into the post-cup plan. +β”‚ β”‚ β”‚ matches only), hero pulse + overview + goals-by-stage + +β”‚ β”‚ β”‚ 48-team sortable table. SECTIONS registry (graceful- +β”‚ β”‚ β”‚ degradation gate: section + chip render only if available, +β”‚ β”‚ β”‚ else removed from DOM) + sticky scrollspy sub-nav (anchor +β”‚ β”‚ β”‚ chips, hash-safe) + flagImg monogram fallback. Grows into +β”‚ β”‚ β”‚ the post-cup plan (.agents/stats-screen-plan.md, A–J). β”‚ β”‚ └── calendar.js .ics export (RFC 5545, CRLF, Blob download) β”‚ β”œβ”€β”€ images/ Team flag SVGs, stadium placeholders β”‚ └── icons/ PWA app icons (from the header trophy logo): icon.svg @@ -80,9 +89,13 @@ worldcup2026/ β”‚ β”œβ”€β”€ results.json { matchId, homeScore, awayScore, penalties?, status } β€” β”‚ β”‚ update as the tournament progresses β”‚ β”œβ”€β”€ stadiums.json 16 real venues: { id, name, city, capacity, image, timezone } -β”‚ └── bracket-config.json β˜… official R32 structure + thirdPlaceAssignment (all null) β€” -β”‚ the ONLY file to edit once real 3rd places are known -β”‚ (slot β†’ allowed-groups table in project-memory.md) +β”‚ β”œβ”€β”€ bracket-config.json β˜… official R32 structure + thirdPlaceAssignment (all null) β€” +β”‚ β”‚ the ONLY file to edit once real 3rd places are known +β”‚ β”‚ (slot β†’ allowed-groups table in project-memory.md) +β”‚ └── (optional, NOT yet created) stats-screen data layers loaded fault-tolerantly by +β”‚ loadOptional(): players.json, player-events.json, +β”‚ awards.json, keeper-stats.json, curiosities.json, +β”‚ all-time-baselines.json β€” absent = silent empty default β”‚ β”œβ”€β”€ README.md Setup, GitHub Pages deploy, JSON maintenance guide β”œβ”€β”€ how-update.md Real-data migration runbook (mock β†’ real β€” DONE 2026-06-12) diff --git a/.agents/project-memory.md b/.agents/project-memory.md index 22e6bae..44f6277 100644 --- a/.agents/project-memory.md +++ b/.agents/project-memory.md @@ -240,6 +240,19 @@ Static web app showing the FIFA World Cup 2026 (Mexico/USA/Canada, 48 teams) β€” - **Deploy:** os arquivos novos (manifest.json, favicon.ico, assets/icons/) **nΓ£o** estΓ£o no `exclude` do `deploy.yml` β†’ sobem normalmente. HTTPS da Hostinger jΓ‘ satisfaz o requisito de PWA. - **Verificado (preview localhost:8126, contexto seguro):** manifest 200 e parseado (name/short/start `.`/scope `./`/display standalone/theme `#081421`); todos os Γ­cones 200 `image/png`; `favicon.ico` 200; `` + apple tags presentes; **console limpo**; app intacto (hero FRAΓ—SEN, 4 cards do dashboard, 16 encerradas/88 prΓ³ximas β€” sem regressΓ£o visual). **NΓ£o testΓ‘vel pelo preview:** o prompt de instalaΓ§Γ£o real / "Add to Home Screen" + Γ­cone no SO β€” confirmar no Chrome/Edge devtools (aba Application) ou num celular apΓ³s o deploy. +### Stats final screen β€” branch + Stage A (2026-06-16) +- **New feature branch `feature/stats-final-screen`** (off `master`) to build the full post-Cup stats screen from `.agents/stats-screen-plan.md` (stages A–J). `master` stays live with the partial Stats tab + daily refreshes; merge `master`β†’branch periodically; the branch merges back to `master` at the **end of the Cup** (= release). Pure-UI stages **A–F first**; schema-changing data stages (esp. **G**, which migrates `results.json` `cards`β†’`{y,r}`) deferred to late to avoid conflicting with master's daily `results.json` edits. **Approval gate before each stage.** +- **Stage A β€” degradation engine + scaffolding (DONE, branch only):** + - **Fault-tolerant `loadData()` (`app.js`):** the 6 core files still **throw** on failure (fatal β†’ `showError`); 6 NEW optional layers (`players`, `player-events`, `awards`, `keeper-stats`, `curiosities`, `all-time-baselines`) load via `loadOptional(name, fallback)` β†’ an **absent/404 file returns the empty default SILENTLY** (graceful degradation Β§0.2; absence is the normal "layer not arrived yet" state and keeps the console clean for verification), and it **warns only on a present-but-malformed file**. Exposed on `data` as `players/playerEvents/awards/keeperStats/curiosities/allTimeBaselines`. Core + optional fetch concurrently (two `Promise.all`, core awaited first). + - **`DATA_VERSION` deliberately NOT bumped in Stage A** β€” no deployed *data* changed (the optional files don't exist yet), and bumping it on the branch would conflict with master's daily bump on every merge. Bump only when real data files land (Stage G/H). (Supersedes the plan's literal "bump when wiring loadData".) + - **Section-gating contract β€” `SECTIONS` registry in `stats.js`:** each section `{ id, navKey, available(model), body(model) }` renders β€” and shows its sub-nav chip β€” **only when `available` holds**; otherwise it is **omitted from the DOM entirely** (no placeholder, no `β€”`, no "coming soon"), and the nav never points at emptiness (Β§0.1). Overview/Teams are `available:()=>true`; players/records/comparator/archive are `available:()=>false` (chips absent until later stages flip them + supply `body`). + - **Sticky scrollspy sub-nav:** `render()` now emits hero + `` (anchor chips) + one `` per available section + footer. Chips are `` but their click is **`preventDefault` + `scrollIntoView`** β€” they **NEVER set `location.hash`** (the tab router listens on `hashchange`; a real `#stats-teams` fragment would route to an unknown tab β†’ bounce to Home β€” the key gotcha here). Scrollspy is **position-based** (rAF-throttled `scroll` listener reading `getBoundingClientRect`), **not** an IntersectionObserver band: the band left a short final section unlit at the page bottom, so the position approach + an explicit "at page bottom β†’ last section" rule is used (robust on short pages). `setActiveChip` keeps the active chip visible via the nav's own `scrollLeft` (no page jump). + - **`--header-h` CSS var** kept live by `trackHeaderHeight()` (`app.js`, `ResizeObserver` on the variable-height sticky header β€” one row β‰₯1100px, two bands below). The sub-nav sticks at `top: var(--header-h)` and sections use `scroll-margin-top: calc(var(--header-h) + 3.75rem)`, correct at every breakpoint (verified 137px wide / 98px at 375px). + - **Media fallback (Β§0.3):** `flagImg(team,w,h)` emits the flag `` with `data-monogram=""`; a one-time capture-phase `error` listener (`installImageFallback`) replaces a broken flag with a `` of the 3-letter code β€” never a broken-image icon. Existing stats flag usages (leaders, team table) converted to `flagImg`. + - **New i18n keys** (EN+PT): `stats.sectionsNav` + `stats.navOverview/navTeams/navPlayers/navRecords/navComparator/navArchive`. **New CSS** in `stats.css`: `.stats-subnav`/`.stats-subnav-chip`, `.stats-section` (scroll-margin + inter-section spacing + first-heading margin reset), `.flag-fallback`. **No `index.html` change** β€” the sub-nav is JS-rendered into the existing `#stats-root`. + - **Verified (preview 8126, branch):** console clean (no errors, no 404 warnings); 6 optional layers = empty defaults, core 48/104/104; sub-nav sticky at `--header-h`, only Overview+Teams chips; scrollspy topβ†’Overview / bottomβ†’Teams; chip click leaves hash `#stats`; EN↔PT relabels chips + sections/table survive; flag fallback β†’ "XYZ" span at 22Γ—15; mobile 375px (2-band header, 2Γ—2 tiles, nav follows 98px header); Home hero+dashboard regression clean. + - **Next:** Stage B (verdict hero gated on FINAL finished, falling back to the current aggregate hero; + goals-by-round chart) β€” awaiting approval. Will consume `getBracketTree().champion` + `resolveBracketTeams('FINAL'|'THIRD-PLACE')` from `bracket.js`. + ### How to update real-world data (scores, schedule) Follow `how-refresh-data.md` (project root). In short: 1. Edit `data/results.json` (scores/status) or `data/matches.json` (schedule, rare).