mnswpr/AYO.md
Ayo f019afd7c8 feat: reusable, time-windowed leaderboard (@cozy-games/leaderboard)
Extract the leaderboard into a generic, backend-agnostic package and add
rolling Today/Week/Month/All-Time windows, a web component, and local-first
development against the Firestore emulator.

Package (leaderboard/):
- LeaderBoardService: backend-agnostic core via a storage-adapter seam, with
  Firebase and Supabase adapters (Supabase client injected, no added dep)
- Rolling time windows (last 24h / 7d / 30d) with hover tooltips; top-N by score
- <cozy-leaderboard> web component built on web-component-base: compose the UI
  in HTML, configure the backend once in JS
- Every user-facing string is configurable (labels, tooltips, empty/loading/
  error messages, anonymous name) so i18n lives in the app; README + CONFIGURATION

App (mnswpr.com):
- Compose the board declaratively in index.html via <cozy-leaderboard>
- Nickname + randomized greeting bar; score submission through the element
- Legends: the current all-time leaders frozen into a static /legends page
- Firebase config and leaderboard namespace via Vite env vars; emulator-first
  local dev (VITE_FIRESTORE_EMULATOR)

Firebase schema-as-code:
- firebase.json, .firebaserc, firestore.rules (public reads, create-only scores,
  no client updates/deletes, namespace-generalized), empty indexes (rolling
  windows need none)
- prod (mw-*) vs dev/test (mw-test-*) separation by collection namespace
- emulator config, seed script, and docs (firebase-leaderboards.md,
  leaderboard-env-migration.md, AYO.md)

Utils/tests: UTC date-bucket helper (retained as metadata) with Vitest coverage.
2026-07-03 13:39:44 +02:00

92 lines
4 KiB
Markdown

# 🧹 AYO — Leaderboard Migration Checklist
Manual steps to finish the leaderboard rollout. **All code changes are already
applied** in the working tree — these are the external actions on Firebase and
Netlify that have to be done by hand.
> One project (`secure-moment-188701`), one difference between environments: the
> **collection namespace**. Production uses `mw-*`, dev/test uses `mw-test-*`.
> Full rationale: [`docs/leaderboard-env-migration.md`](docs/leaderboard-env-migration.md).
---
## ✅ Step 1 — Deploy Firestore rules + indexes
From the repo root:
```bash
npx firebase login
npx firebase deploy --only firestore:rules,firestore:indexes --project prod
```
- Uses committed [`firestore.rules`](firestore.rules) + [`firestore.indexes.json`](firestore.indexes.json).
- `prod``secure-moment-188701` (via [`.firebaserc`](.firebaserc)).
- ⚠️ Deploying **replaces** the console rules. The committed rules cover every
collection (`mw-*` and `mw-test-*`), so it's safe — but review first.
- No composite indexes to build — rolling windows (`time_stamp >=`) and all-time
(`orderBy score`) use Firestore's automatic single-field indexes.
## ✅ Step 2 — Set Netlify environment variables
In the Netlify site settings, add:
| Variable | Value |
| --- | --- |
| `VITE_FIREBASE_API_KEY` … (all 8) | **same as [`app/.env.development`](app/.env.development)** (same project) |
| `VITE_LB_NAMESPACE` | **`mw`** ← makes production use the `mw-*` collections |
> Local dev already uses `mw-test` via the committed `.env.development` — nothing
> to do there.
## ✅ Step 3 — (Optional) Seed the test config doc
Create `mw-test-config/configuration` in Firestore with the same `passingStatus`
and `message` as the prod `mw-config/configuration`.
Skip it and the test board still works — the default qualifier just accepts all
wins in test.
## ✅ Step 4 — Seed the dev database with sample scores
> ⚠️ **Must run _after_ Step 1.** The seed writes to `mw-test-scores`, which is
> only allowed once the generalized rules are deployed — otherwise every write
> returns `permission-denied`. (No indexes to wait on — the windows use
> automatic single-field indexes.)
Populate the dev boards so they aren't empty while developing:
```bash
(cd app && node ../scripts/seed-dev-scores.js)
```
- Uses [`scripts/seed-dev-scores.js`](scripts/seed-dev-scores.js) — ~12 sample
scores per level, timestamps spread across today / this week / this month /
older so **all four tabs** populate.
- Dev-only and idempotent-ish (re-running just adds more rows); it never touches
the production `mw-*` collections.
> 💡 **Local dev uses the emulator by default — this cloud seed is optional.**
> `pnpm dev` points at the local **Firestore emulator** (needs a JDK): run
> `pnpm emulators` + `pnpm seed:emulator` and you're set — no deploy, no cloud.
> The cloud seed above is only needed for a hosted/preview environment. To opt
> out of the emulator, set `VITE_FIRESTORE_EMULATOR=` empty in `app/.env.local`.
> See [`docs/firebase-leaderboards.md`](docs/firebase-leaderboards.md#local-firestore-emulator-default-for-local-dev).
## ✅ Step 5 — Verify
| Environment | How | Expected |
| --- | --- | --- |
| **Production** (`mw`) | Win a game on the live site | Score shows; doc in `mw-scores/{level}/games` |
| **Local dev** (`mw-test`) | `pnpm dev` (after Step 4) | Board shows sample scores; winning adds to `mw-test-scores/{level}/games`; prod `mw-scores` untouched |
| **Rules** | Read both boards | Reads succeed; a malformed write is rejected |
---
## 📌 Still open (not blocking)
- **Nothing is committed yet** — all changes are in the working tree.
- **`scripts/export-legends.js`** still hard-codes the (dev = prod) Firebase keys
from the one-off Legends export. It's identical to `app/.env.development`; can
be de-duped to read from the env file on request.
- **Legends** is already frozen into static HTML ([`app/legends.html`](app/legends.html))
— no action needed.