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.
4 KiB
🧹 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 usesmw-*, dev/test usesmw-test-*. Full rationale:docs/leaderboard-env-migration.md.
✅ Step 1 — Deploy Firestore rules + indexes
From the repo root:
npx firebase login
npx firebase deploy --only firestore:rules,firestore:indexes --project prod
- Uses committed
firestore.rules+firestore.indexes.json. prod→secure-moment-188701(via.firebaserc).- ⚠️ Deploying replaces the console rules. The committed rules cover every
collection (
mw-*andmw-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 (same project) |
VITE_LB_NAMESPACE |
mw ← makes production use the mw-* collections |
Local dev already uses
mw-testvia 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 returnspermission-denied. (No indexes to wait on — the windows use automatic single-field indexes.)
Populate the dev boards so they aren't empty while developing:
(cd app && node ../scripts/seed-dev-scores.js)
- Uses
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 devpoints at the local Firestore emulator (needs a JDK): runpnpm emulators+pnpm seed:emulatorand 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, setVITE_FIRESTORE_EMULATOR=empty inapp/.env.local. Seedocs/firebase-leaderboards.md.
✅ 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.jsstill hard-codes the (dev = prod) Firebase keys from the one-off Legends export. It's identical toapp/.env.development; can be de-duped to read from the env file on request.- Legends is already frozen into static HTML (
app/legends.html) — no action needed.