feat: [T06] create leaderboard UI#15
Merged
agnt-platform[bot] merged 4 commits intoMay 14, 2026
Merged
Conversation
Adds the PostgreSQL schema for the snake leaderboard: - users: one row per player handle, with bearer token for X-Player-Token auth - sessions: one row per game played (start/end + final score + meta JSONB) - scores: append-only score feed indexed for top-N reads Indexes: - users_player_lower_uniq case-insensitive unique handle - users_api_token_uniq token lookup for write auth - sessions_user_started_idx recent sessions per user - scores_score_created_idx global top-N (ties resolved by earliest submit) - scores_user_score_idx best-score-per-user Migrations live as numbered .sql files under backend/migrations/ and are idempotent. backend/src/db.ts exposes a lazy pool plus a runMigrations() helper for tests/bootstrap so typecheck does not require a running DB. Adds pg + @types/pg to backend.
REST endpoints under /api: POST /api/users/register create user, returns X-Player-Token POST /api/scores submit a score (auth required) GET /api/leaderboard?limit=N top-N (limit clamped 1..100, default 10) GET /api/users/:id/best all-time best for one user Layered as: routes/leaderboard.ts -> repo.ts -> db.ts. SQL lives only in repo.ts; routes do validation + auth. Validation uses zod (handle regex, score >= 0 and bounded, limit clamp). Auth model: a single bearer token per user, sent as X-Player-Token. That's sufficient for a hobby leaderboard and avoids dragging in JWT/bcrypt for T05's scope. Tests: 9 integration tests under src/__tests__/leaderboard.test.ts using pg-mem to provide an in-memory PostgreSQL adapter, so CI does not need a real database. Migrations are applied through the same runMigrations() helper used in production. Test runner: node --test (--import tsx). Shared types extended additively: - SubmitScoreRequest gains optional `meta` - ScoreEntry gains optional `rank` - new SubmitScoreResponse / LeaderboardResponse / RegisterUserResponse Adds zod (runtime), pg-mem + supertest (tests).
…d-ui # Conflicts: # backend/package.json
Adds frontend/src/leaderboard/:
- api.ts tiny fetch wrapper with typed response + LeaderboardError
- Leaderboard.tsx responsive React component (table of top-N)
Behaviour:
- Polls GET /api/leaderboard every 5s by default (pollMs prop)
- Pauses polling when document.hidden so background tabs don't burn cycles
- Cancels in-flight requests on unmount / next refresh via AbortController
- Loading, error, and empty states; transient failures keep prior data
visible with a warning banner (no UI blanking on a network blip)
- Manual Refresh button
- Relative timestamps (Ns/Nm/Nh/Nd ago)
Styling:
- Themed to match T01's dark palette (#0a0f1c base)
- Gold/silver/bronze player colour for top-3
- Mobile breakpoint at 480px hides the When column for narrow screens
Wired into App.tsx alongside the Board from T02 (additive — game UI is
unchanged). Uses LeaderboardResponse / ScoreEntry from @snake/shared.
3 tasks
Contributor
|
💸 Reward sent: 540 SNAKE
|
Contributor
|
💸 Reward sent: 5.998 TON
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements T06 — Create Leaderboard UI.
Component
frontend/src/leaderboard/:Leaderboard.tsx— responsive React component, table of top-N entriesapi.ts— typedfetchwrapper around/api/leaderboardwithLeaderboardErrorBehaviour
GET /api/leaderboardevery 5 s (configurable viapollMsprop; pass0to disable).AbortController— no setState-after-unmount warnings, no race-y stale renders.Refreshbutton.Ns/Nm/Nh/Nd ago).Styling
#0a0f1cbase,#7ad7ffaccent).Whencolumn for narrow viewports.Integration
Wired into
App.tsxbelow T02's<Board>— the game UI is otherwise unchanged. UsesLeaderboardResponse/ScoreEntryfrom@snake/shared(therankand response types added in T05).Verification
npm run typecheck— passesnpm run build— passes (CSS 2.85 kB / JS 148.81 kB)npm test --workspace @snake/backend— 9/9 still pass after the merge