Completed one-shot leaderboard backfill migration verified in production. Removed migration scripts, test, unused export, and npm script. Updated documentation and closed UI/UX flow polish plan with session journal.
7.6 KiB
Project Structure
Root Directory
CLAUDE.md- Project instructions and guidelines for Claude Codecomponents.json- shadcn/ui configurationpackage.json- Dependencies and scriptsnext.config.mjs- Next.js configurationeslint.config.mjs- ESLint configurationpostcss.config.mjs- PostCSS configurationjsconfig.json- JavaScript project configurationvitest.config.mjs/vitest.integration.config.mjs- The two vitest lanesplaywright.config.mjs- The browser smoke-test lane (tests/e2e/)docker-compose.yml- Local Redis + SRH for the integration lanedata-build/(gitignored) - Local pipeline artifacts awaitingdata:seedREADME.md- Public-facing project readme
Source Code (src/)
App Router (src/app/)
Next.js 16 App Router structure:
Pages
page.js- Homepage: region picker and leaderboard modallayout.js- Root layout componentglobals.css- Global stylesfavicon.ico- Site favicon
Game Pages
game/page.js- Main game interfacecredits/page.js- Data sources, licenses, and open-source creditsdebug/page.js- Debug hub: lists every debug tool as a peerdebug/layout.js- Shared shell for all debug pages: app bar, DebugNav, themedebug/DebugNav.js- Segmented peer navigation shown on every debug pagedebug/bbox/page.js- Bbox visualization and live Mapillary probingdebug/coverage/page.js- Panorama coverage map, per regiondebug/coverage/CoverageMap.js- Leaflet layer for that page
API Routes (src/app/api/)
new-game/route.js- Creates new game sessions with Redis storageguess/route.js- Processes guess submissions, scores, and fans outleaderboard/route.js- Leaderboard data management with Redisskip/route.js- Skip current round functionalitydebug/mapillary/route.js- Mapillary API debugging and testingdebug/pano/route.js- Resolve one panorama id to an imagedebug/region-coverage/route.js- A region's outline and panorama points
React Components (src/app/components/)
GameClient.js- Main game client componentLeafletMap.js- Interactive map for guess placementPanoramaViewer.js- 360 degree street view displayRegionPicker.js- Homepage province accordion, one row per playable regionRegionSelect.js- Level buttons plus a grouped select, used where a single region has to be chosen from 67LeaderboardList.js- Ranked rows for one boardUsernameModal.js- Set or change the leaderboard name; skip generates a randomPlayer-xxxxxxFirstRoundHint.js- One-time how-to-play banner on the game screenDonateQRModal.js- Donation QR code modalThemeToggle.js- Light/dark switch
Reusable Components (src/components/)
shadcn/ui Components (src/components/ui/)
Only the primitives the app actually renders are vendored in. Add others with the shadcn CLI when a screen needs them, rather than keeping unused ones around.
accordion.jsx- Collapsible sections (province expansion)alert.jsx- Alert notificationsbadge.jsx- Badge componentsbutton.jsx- Button variantscard.jsx- Card layoutsdialog.jsx- Modal dialogsinput.jsx- Input fieldslabel.jsx- Form labelsselect.jsx- Grouped select (region picker)skeleton.jsx- Loading skeletonstabs.jsx- Tab navigation
Generated Data (src/data/)
Both directories are build output. Do not hand-edit; see Rebuilding the generated region data in development.md.
regions/index.js- The 67-node tree: code, name, parent, level, children, center, bbox, and coverage flagsregions/counts.js- Per-region panorama and cell tallies. The one panorama-derived file a client component may import - it carries counts only, never a coordinateboundaries/<province>/*.json- Simplified outlines, one file per region, behind a generatedboundaries/index.jsbarrel
The panorama index itself is not in the repo: the pipeline writes artifacts to
data-build/panos/ (gitignored) and scripts/seed-pano-db.mjs uploads them to
Neon Postgres, which is what the app queries at runtime.
Utility Libraries (src/lib/)
utils.js- Utility functions includingcn()for class name mergingregions.js- Client-safe region tree traversal. Imports nothing frompano-index.jsorpano-db.js;tests/regions.test.jsenforces thatmap-tiles.js- Client-safe tile provider choice: Geoapify whenNEXT_PUBLIC_GEOAPIFY_KEYis set at build time, OSM public server otherwisepano-index.js- Server-side only. Picks a panorama for a region from Postgres and reports which district it landed inpano-db.js- Server-side only. Neon HTTP adapter behind pano-index.jsregion-request.js- Resolves and validates a region code from a requestgame.js- Scoring ladders (base + region-scaled), distance, formattingusername.js- Player name in localStorage, plus the random-name generatorlast-region.js- Last-played region in localStorage (the home page's "Continue in ..." row)leaderboard.js- Leaderboard operations, including the district to province to country fan-outmapillary.js- Mapillary lookup by image idsession.js- Redis-based session management with 30-min expiryupstash.js- Upstash Redis REST client adapter with multi-tenant key prefixtheme.js,use-count-up.js- Theme persistence and a count-up hook
Build Scripts (scripts/)
Each carries a header comment with its flags and its cost.
build-region-boundaries.mjs- OSM/Nominatim to boundaries and the region treebuild-pano-index.mjs- Mapillary z14 tiles to per-province panorama artifactsassign-pano-districts.mjs- Clips and partitions panoramas by districtseed-pano-db.mjs- Validates the artifacts and uploads them to Neonbuild-check.mjs- Production build into.next-checklib/assign-districts.mjs- District assignment shared by the two pano scriptslib/pano-schema.mjs- Panorama table DDL shared by the seed and the tests
Tests (tests/)
Vitest, mostly one file per src/lib/ module, plus a route test for
new-game, guess, and debug/region-coverage. The skip, leaderboard,
debug/mapillary, and debug/pano routes have no dedicated test file; their
underlying src/lib/ logic (leaderboard.js, mapillary.js) is still
covered. fake-upstash-redis.js, mock-upstash.js, redis-harness.js and
wait-for-srh.js are the shared harness that lets the same files run against
either the in-memory fake or a real Redis. fake-neon.js, mock-neon.js and
pano-fixtures.js are the equivalent for the panorama store: PGlite behind the
Neon SDK boundary, loaded with small synthetic rows.
tests/e2e/ holds the Playwright smoke specs (*.spec.js, so vitest never
collects them): the homepage picker, the username modal, and one full round,
all against browser-level stubs in tests/e2e/helpers.js with a fixture
panorama in tests/e2e/fixtures/. Deeper UI behavior (RegionSelect.js, the
coverage page, real panoramas) remains manual testing only.
Documentation (/docs/)
project-overview.md- Project overview, administrative basis, coverage notefeatures.md- Detailed game features documentationtech-stack.md- Technology stack and dependenciesdevelopment.md- Development guidelines, commands, and build sequencegame-flow.md- Complete gameplay flow documentationproject-structure.md- This file - project organization
Planning (/plans/)
- Project planning documents and implementation plans
Public Assets (public/)
zlp.jpg- Donation QR code image- Static assets served directly by Next.js