world-2026-hub/DEVELOPMENT.md

164 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Developer guide — World Cup 2026 Hub
Everything you need to run, understand, deploy and maintain the site. For a non-technical overview of
what the project *is*, see the [README](README.md).
The app is a **static single-page app**: one `index.html`, vanilla ES-module JavaScript, and JSON
files as the only "database". There is **no backend, no build step, no bundler, and no framework**.
You maintain the site by editing JSON — the code never needs to change to update scores or teams.
---
## Run locally
```sh
python -m http.server
# then open http://localhost:8000
```
Any static file server works. A server **is required** — opening `index.html` directly from disk
fails, because browsers block `fetch()` of local JSON files (`file://`).
---
## Project structure
```
worldcup2026/
├── index.html SPA shell (header, tabs, hero, panels, modal root)
├── manifest.json PWA manifest · favicon.ico
├── assets/
│ ├── css/ style.css · bracket.css · stats.css · animations.css
│ ├── js/ app.js (entry) · schedule.js · groups.js · bracket.js
│ │ stats.js · modal.js · stadiums.js · storage.js
│ │ i18n.js · calendar.js
│ ├── images/ flags/*.svg · stadiums/*.svg
│ └── icons/ PWA app icons + favicons
└── data/ ← the only thing you edit to maintain the site
├── teams.json 48 teams: { id, name, flag }
├── groups.json { "A": [4 team ids], … } × 12
├── matches.json 104 matches (UTC times; knockout uses bracketRef)
├── results.json one entry per match: scores + status (+ penalties, + optional stats)
├── stadiums.json name, city, capacity, image, IANA timezone
└── bracket-config.json Round-of-32 slots + best-third assignment
```
---
## Maintaining the data
> The data is **real World Cup 2026 data**, refreshed as the tournament progresses. Everything the
> site shows is derived from the six `data/*.json` files — no code changes required.
>
> The day-to-day refresh routine is documented in
> [`how-refresh-data.md`](how-refresh-data.md); the original mock → real migration is kept as a schema
> reference in [`how-update.md`](how-update.md).
### Updating a result
Edit the match's entry in `data/results.json`:
```json
{ "matchId": 74, "homeScore": 1, "awayScore": 1,
"penalties": { "home": 4, "away": 3 }, "status": "finished" }
```
- `status`: `scheduled``live``finished`. Standings and the bracket only count `finished` matches.
- `penalties` is optional — only for knockout matches decided on penalties.
### Adding / changing matches
`data/matches.json`. **All times are UTC** (the UI converts to local or stadium time). Group matches
carry `homeTeam`/`awayTeam`; knockout matches carry a `bracketRef`
(`R32-1``R32-16`, `R16-1`…, `QF-…`, `SF-…`, `THIRD-PLACE`, `FINAL`) and their teams are resolved
automatically from the standings.
### After the group stage: fill the third-place slots
`data/bracket-config.json` is **the only file to edit** once the 8 best third-placed teams are known.
Map each slot to a group letter (per FIFA's official combination table):
```json
"thirdPlaceAssignment": { "1": "D", "2": "F", "3": "B", "…": "…" }
```
A slot's team becomes `standings[group][3rd]`. Slots left `null` show a "Best 3rd #N" placeholder. The
16 `round32` entries define the bracket order (array position = bracket position) — they normally
never change.
### Teams, stadiums, images
- `teams.json``flag` is a path relative to `assets/images/` (e.g. `flags/bra.svg`).
- `stadiums.json``timezone` must be a valid IANA name (e.g. `America/Mexico_City`); it drives the
"stadium time" display and stays correct across DST.
- Replace the SVGs in `assets/images/` with new artwork keeping the same file names (or update the
JSON paths).
### UI labels (EN / PT)
Every user-facing string goes through `t(key)` — add new labels to **both** dictionaries in
`assets/js/i18n.js`. Data values (team/stadium names, cities) come from JSON and are **not**
translated.
---
## Local storage
The app never modifies the JSON data. User state lives in the browser under `wc2026_*` keys:
| Key | Content |
|---|---|
| `wc2026_simulation` | `{ "R32-6": { "winner": "FRA", "score": "2-1" }, … }` |
| `wc2026_favorites` | `["BRA", "MEX"]` |
| `wc2026_prefs` | `{ "lang": "en"\|"pt", "timeMode": "local"\|"stadium", "lastTab": "bracket", … }` |
Clearing site data resets picks, favourites and preferences.
---
## Deployment
The live site is deployed automatically to **Hostinger over FTP** by GitHub Actions
([`.github/workflows/deploy.yml`](.github/workflows/deploy.yml)) on every push to `master`, and is
served at **[lucaskalil.com/worldcup2026](https://lucaskalil.com/worldcup2026)**.
- The workflow needs three repository secrets: `FTP_SERVER`, `FTP_USERNAME`, `FTP_PASSWORD`.
- Development/documentation files (`README.md`, `DEVELOPMENT.md`, `docs/`, `.agents/`, the specs and
`how-*.md`) are **excluded** from the upload — only `index.html`, `assets/` and `data/` reach the
site.
Because every asset and data path in the code is **relative** (never starting with `/`), the same
folder also works unchanged on **GitHub Pages** or any other static host — just publish the directory.
When editing paths, always keep them relative.
---
## Acceptance criteria (spec §18)
- [x] All matches are loaded via JSON
- [x] All results are loaded via JSON
- [x] The bracket is generated dynamically (config + standings + winner pairing)
- [x] Works on a static host (all paths relative, no server-side code)
- [x] Works on desktop and mobile (≤767 / 7681100 / 1100+ breakpoints)
- [x] Allows knockout-stage simulation (persisted, never mutates JSON)
- [x] Smooth animations (entry, hover, bracket line-draw; reduced-motion safe)
- [x] No backend dependency — fully static
**Performance:** total JS ≈ 74 KB across the ES modules (budget: < 300 KB), no external dependencies,
no blocking third-party requests.
---
## Roadmap (spec §19)
Dark/light theme, real-time statistics via a results API, FIFA ranking integration, World Cup history,
expanded player-level stats, and push notifications. A service-worker offline mode (PWA "Tier 2") is
designed but deliberately deferred see [`.agents/issues.md`](.agents/issues.md).
---
## Internal documentation
Deeper architecture notes, decisions and gotchas live in the [`.agents/`](.agents/) folder
(`project-map.md`, `project-memory.md`, `stats-screen-plan.md`, `issues.md`, `TODO.md`). Read those
before making significant changes.