mnswpr/AYO.md
Ayo 9e1114be78 feat: reusable, time-windowed leaderboard (@cozy-games/leaderboard) (#1)
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.

Reviewed-on: git.ayo.run:3000/
Co-authored-by: Ayo <ayo@ayco.io>
Co-committed-by: Ayo <ayo@ayco.io>
2026-07-03 11:41:14 +00:00

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 uses mw-*, dev/test uses mw-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.
  • prodsecure-moment-188701 (via .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 (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:

(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 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.

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) — no action needed.