mirror of
https://github.com/LucasKalil-Programador/world-2026-hub.git
synced 2026-07-04 17:41:28 -03:00
164 lines
6.6 KiB
Markdown
164 lines
6.6 KiB
Markdown
# 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 / 768–1100 / 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.
|