diff --git a/.agents/TODO.md b/.agents/TODO.md index 51c4749..7cb3b15 100644 --- a/.agents/TODO.md +++ b/.agents/TODO.md @@ -66,6 +66,10 @@ Use checkboxes to track progress. Items marked **🔴 BLOCKER** prevent release; - [ ] Real stadium photos + team flag SVGs in `assets/images/` (10 new-team flags created 2026-06-12 in placeholder style) - [ ] **Pós-Copa: estado final da home.** Quando a Final encerrar, o hero fica vazio (por design atual). Criar um estado pós-torneio (campeão/epílogo) na home — ver entrada "Hero cronômetro inteligente (2026-06-15)" em project-memory; provavelmente converge com a aba Stats (`stats-screen-plan.md`). +### 🟢 OPTIONAL +- [x] ~~PWA Tier 1 — instalável (manifest + ícones + meta tags; 2026-06-16). Atende todos os critérios de aceitação da issue.~~ +- [ ] PWA Tier 2 — service worker + offline (deferido; ver `.agents/issues.md` → "PWA Tier 2". DEVE excluir `data/*.json` do cache p/ não quebrar o live-refresh + `DATA_VERSION`). + --- ## Quick final checklist diff --git a/.agents/issues.md b/.agents/issues.md index f5c1be0..b4401cb 100644 --- a/.agents/issues.md +++ b/.agents/issues.md @@ -60,6 +60,41 @@ Only if: --- +## PWA Tier 2 — Service Worker + Offline (2026-06-16) + +**Status:** Analyzed, deferred (Tier 1 shipped 2026-06-16 — see project-memory "PWA — installable app"). + +### Context +The PWA install issue was delivered as **Tier 1** (manifest + icons + meta tags), which already meets +every acceptance criterion (installable, correct name/icon, standalone launch from the OS shortcut, no +app-pipeline risk). Tier 2 — a service worker for offline launch and the strongest cross-browser +"app feel" — was intentionally left out. It is **not** required for the install prompt in modern +Chrome/Edge. + +### Why deferred (the real risk) +A naïve precaching SW would cache `data/*.json` and **silently defeat the 2026-06-16 live-refresh +system** (the 90s `results.json` poll with `cache:'no-store'` + the `DATA_VERSION` cache-buster) — +open tabs would stop seeing new scores, and `DATA_VERSION` bumps would do nothing. It would also make +the "stale JS module" gotcha (#5) *permanent* (cached assets live until the cache name changes). + +### How to implement (if revisited) — constraints, not optional +1. **Never cache `data/*.json`.** Use network-only, or network-first with the cache only as an + offline fallback (so an offline launch shows the last-seen results). The 90s poll must stay the + owner of freshness. +2. **Version the SW cache** with a constant mirroring/derived from `DATA_VERSION`; clean up old caches + on `activate` — otherwise every code deploy risks serving stale JS forever (gotcha #5). +3. **Register at the subpath** (`worldcup2026/sw.js`) so the SW scope matches the deploy (gotcha #7); + keep `start_url`/`scope` relative as they already are. +4. App-shell strategy: cache-first (versioned) for `index.html` + `assets/css` + `assets/js` + + `assets/icons`; precache on `install`. +5. Verify the poll still updates an open tab **with the SW active** (the easy thing to regress). + +### When to implement +Only if offline launch / a fuller install experience is actually wanted, and only with the data-cache +exclusion + cache-versioning above. Otherwise Tier 1 is sufficient. + +--- + ## Live Data Refresh — Stale Results Until Page Reload (2026-06-15) **Status:** ✅ **Implemented 2026-06-16** (Option A⁺ — "Fixed polling done right"). The analysis below diff --git a/.agents/project-map.md b/.agents/project-map.md index cbd8256..356e102 100644 --- a/.agents/project-map.md +++ b/.agents/project-map.md @@ -24,7 +24,14 @@ worldcup2026/ │ ├── index.html ★ SPA shell — header, nav tabs (Home, Matches, │ Groups, Knockout, Stadiums, Stats), hero, dashboard, -│ modal container; loads app.js as ES module +│ modal container; loads app.js as ES module. +│
has the PWA block (manifest link, theme-color, +│ favicons, apple-mobile-web-app-* meta) +│ +├── manifest.json PWA web app manifest (name/short_name, standalone, +│ theme/background #081421, icons[]) — relative paths +│ (start_url ".", scope "./") for the subpath deploy +│ favicon.ico Root favicon (16+32, from the trophy logo) │ ├── assets/ │ ├── css/ @@ -59,7 +66,11 @@ worldcup2026/ │ │ │ matches only), hero pulse + overview + goals-by-stage. │ │ │ PARTIAL (during-cup) — grows into the post-cup plan. │ │ └── calendar.js .ics export (RFC 5545, CRLF, Blob download) -│ └── images/ Team flag SVGs, stadium placeholders +│ ├── images/ Team flag SVGs, stadium placeholders +│ └── icons/ PWA app icons (from the header trophy logo): icon.svg +│ (master + manifest SVG), icon-192/512.png (purpose any), +│ icon-maskable-192/512.png (safe-zone padded), +│ apple-touch-icon.png (180), favicon-16/32.png, favicon.ico │ ├── data/ All content — REAL WC2026 data since 2026-06-12 │ ├── teams.json 48 real qualifiers: { id, name, flag } (FIFA codes) @@ -149,6 +160,7 @@ matches.json time (UTC) ── formatMatchTime(match, stadium, mode) | Where is simulation state stored/cleared? | `localStorage` key `wc2026_simulation`, via `assets/js/storage.js` | | Where do I change colors/theme? | CSS variables at the top of `assets/css/style.css` | | Where do I add a stadium? | `data/stadiums.json` + image in `assets/images/` | +| Where do I change the app name / install icon / theme color? | `manifest.json` (name/short_name/theme) + `assets/icons/` (regenerate PNGs from `icon.svg`) + PWA `` in `index.html` `` | | How do I replace mock data with real WC2026 data? | `how-update.md` (root) — done 2026-06-12; kept as schema reference | | How do I update scores during the tournament? | `how-refresh-data.md` (root) — daily results.json routine + thirdPlaceAssignment how-to | diff --git a/.agents/project-memory.md b/.agents/project-memory.md index 34edd75..5729ed6 100644 --- a/.agents/project-memory.md +++ b/.agents/project-memory.md @@ -231,6 +231,15 @@ Static web app showing the FIFA World Cup 2026 (Mexico/USA/Canada, 48 teams) — - **Não tratado (aceito, baixo risco — mudanças raras, poucas/dia):** modal aberto não auto-atualiza (relê no próximo open); re-render durante interação (drag do bracket / digitação no filtro) — filtros sobrevivem (state módulo-level), scroll pode pular. - **Verificado (preview, sem tocar no disco — `window.fetch` interceptado pra simular jogo 16 IRN×NZL finished 3–0, `visibilitychange` disparando `pollResults`):** dashboard Encerradas 15→16 / Próximas 89→88; hero trocou IRN×NZL→FRA×SEN (jogo 16 virou `over`); Group G recomputou (Irã `1 1 0 0 3 0 +3 3`); bracket(32)/stats(4 tiles)/matches(104) re-renderizaram; **console limpo**. Restaurado o `fetch` real → poll seguinte **auto-revertou** pra 15/89 (prova a assinatura nos dois sentidos). `DATA_VERSION` **não** bumpado (nenhum dado mudou no disco — só código). +### PWA — installable app (Tier 1, 2026-06-16) +- **O site virou um PWA instalável** (issue "Adicionar suporte a instalação como aplicativo"). Escopo entregue = **Tier 1** (manifest + ícones + meta tags) — atende a TODOS os critérios de aceitação (instalável, nome/ícone corretos, abre standalone pelo atalho do SO). **Service worker / offline ficou de fora de propósito** (Tier 2, registrado em `.agents/issues.md`). +- **Decisão-chave (por quê só Tier 1):** um SW que cacheasse `data/*.json` **quebraria** o poll de 90s + `DATA_VERSION` (live-refresh de 2026-06-16) — abas abertas parariam de ver placares novos. Tier 1 não toca em nada do pipeline de dados/JS → risco zero pro live-refresh. Se Tier 2 entrar, o SW **precisa excluir `data/*.json`** do cache (network-only/network-first) e versionar junto com `DATA_VERSION` (senão piora a gotcha #5 de módulo velho). +- **Arquivos novos:** `manifest.json` (raiz), `favicon.ico` (raiz), `assets/icons/` (icon.svg master + icon-192/512.png `purpose:any` + icon-maskable-192/512.png + apple-touch-icon.png 180 + favicon-16/32.png + favicon.ico). **Nenhum JS mudou.** `index.html` ganhou um bloco PWA no `` (link manifest, ``, favicons, `apple-mobile-web-app-capable/-status-bar-style black-translucent/-title "WC 2026 Hub"`). +- **Ícones derivados do logo do header** (o troféu SVG inline) sobre o gradiente escuro `#10243b→#081421` com o dourado `#d4af37`. Fontes em `assets/icons/icon.svg` (any, troféu a ~60%) e `icon-maskable.svg` (troféu a ~46%, dentro da safe-zone do maskable). **Rasterizados com ImageMagick** (`magick -background none icon.svg -resize NxN ...`); `favicon.ico` = 16+32 combinados. **Para trocar o ícone:** editar o(s) SVG e re-rodar os mesmos comandos `magick` (não há build step; é geração de asset 1x). +- **Manifest:** `name "World Cup 2026 Hub"` / `short_name "WC 2026 Hub"` (nome estático, EN — manifest não faz i18n runtime); `display:standalone`; `background_color`+`theme_color` = `#081421` (`--bg-primary`, evita flash branco no splash); `start_url:"."` + `scope:"./"` **relativos** (gotcha #7 — site mora em `…/worldcup2026/`; absoluto quebraria). Nomeado `manifest.json` (não `.webmanifest`) p/ MIME seguro na Hostinger. +- **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. + ### 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). diff --git a/assets/icons/apple-touch-icon.png b/assets/icons/apple-touch-icon.png new file mode 100644 index 0000000..1b33973 Binary files /dev/null and b/assets/icons/apple-touch-icon.png differ diff --git a/assets/icons/favicon-16.png b/assets/icons/favicon-16.png new file mode 100644 index 0000000..0dd1b29 Binary files /dev/null and b/assets/icons/favicon-16.png differ diff --git a/assets/icons/favicon-32.png b/assets/icons/favicon-32.png new file mode 100644 index 0000000..73dad03 Binary files /dev/null and b/assets/icons/favicon-32.png differ diff --git a/assets/icons/favicon.ico b/assets/icons/favicon.ico new file mode 100644 index 0000000..bfb28be Binary files /dev/null and b/assets/icons/favicon.ico differ diff --git a/assets/icons/icon-192.png b/assets/icons/icon-192.png new file mode 100644 index 0000000..c5fa252 Binary files /dev/null and b/assets/icons/icon-192.png differ diff --git a/assets/icons/icon-512.png b/assets/icons/icon-512.png new file mode 100644 index 0000000..8953497 Binary files /dev/null and b/assets/icons/icon-512.png differ diff --git a/assets/icons/icon-maskable-192.png b/assets/icons/icon-maskable-192.png new file mode 100644 index 0000000..dcf488c Binary files /dev/null and b/assets/icons/icon-maskable-192.png differ diff --git a/assets/icons/icon-maskable-512.png b/assets/icons/icon-maskable-512.png new file mode 100644 index 0000000..781d37a Binary files /dev/null and b/assets/icons/icon-maskable-512.png differ diff --git a/assets/icons/icon-maskable.svg b/assets/icons/icon-maskable.svg new file mode 100644 index 0000000..05978e4 --- /dev/null +++ b/assets/icons/icon-maskable.svg @@ -0,0 +1,18 @@ + diff --git a/assets/icons/icon.svg b/assets/icons/icon.svg new file mode 100644 index 0000000..7c5698f --- /dev/null +++ b/assets/icons/icon.svg @@ -0,0 +1,18 @@ + diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..bfb28be Binary files /dev/null and b/favicon.ico differ diff --git a/index.html b/index.html index 7412ae4..510acb4 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,18 @@