Pages and Routes
This app uses the Next.js Pages Router. Client-side navigation is handled with next/router and most pages gate access with authClient.useSession().
App shell
_app — src/pages/_app.tsx
Responsibilities
- Registers Mantine theme + global styles and mounts
Notifications. - Boots PostHog analytics once on mount.
- Composes global context providers in this order:
SocketProvider->PartyProvider->MatchmakingProvider->FriendshipProvider. - Restores the last active game by reading
localStorage.stored_gameand redirecting to/game/[gameID]if present. - Hides the navbar on
/,/login, and/signup.
_document — src/pages/_document.tsx
Responsibilities
- Sets
<Html lang="en">and Mantine color-scheme script. - Defines the favicon for the base document.
Public routes
Home — / (src/pages/index.tsx)
- Uses dynamic imports for the hero, how-it-works, live demo, and CTA sections.
- If a session exists, shows an avatar button that opens the side panel.
- Sends the
homepage_viewedPostHog event.
Tutorial — /tutorial (src/pages/tutorial.tsx)
- Scroll-driven tutorial page that tracks visibility with
IntersectionObserver. - Sends the
tutorial_viewedPostHog event.
Login — /login (src/pages/login.tsx)
- Uses Mantine
useFormfor email + password. - Calls
authClient.signIn.email, then redirects to/matchmakingon success. - Emits PostHog success/failure events and uses
showErrorNotificationon error.
Signup — /signup (src/pages/signup.tsx)
- Uses Mantine
useFormfor name + email + password. - Calls
authClient.signUp.email, then redirects to/matchmakingon success. - Emits PostHog success/failure events and uses
showErrorNotificationon error.
Authenticated routes
Matchmaking — /matchmaking (src/pages/matchmaking.tsx)
- Requires an active session; redirects to
/loginif unauthenticated. - Uses
MatchmakingContext+PartyContextto track queue state and party membership. - Emits:
joinQueue/leaveQueuefor matchmaking.partySearchso party guests can mirror queue state.
- Renders two flows:
- Create Game (
DifficultySection) uses/api/rooms/createand optionally emitssendGameWithParty. - Matchmaking (
FindLobbySection) updates queue selection and status.
- Create Game (
- Optional join-by-id flow via
JoinGameSection.
Game room — /game/[gameID] (src/pages/game/[gameID].tsx)
Auth and bootstrapping
- Redirects to
/loginif unauthenticated. - Wraps the room in
GameStateProvider+GameTestCasesProvider. - Fetches
/api/rooms/{gameId}/{userId}to load the active problem, game type, and team/role info. - Auto-joins the first team for 2-player games if no team is assigned.
- Saves the game ID to
localStorage.stored_gameso refreshes can resume.
Socket flow (high level)
joinGamejoins both the game room and team room.codeChange,updateTestCases, and chat events broadcast to the team room.gameStarting,gameStarted,roleSwapWarning,roleSwapping,roleSwap, andgameEndeddrive the timer + role UI.submitCodefinalizes a team submission;submitTestCasesexecutes tester runs.
UI behavior
- Team selection UI (
TeamSelect) gates entry when a player has no team. - Spectators can toggle between team views (coder/tester POV) and have read-only editors.
- The Monaco editor is editable only for coders and only when not spectating.
- Test cases are limited to 5 total and can be added/removed by testers.
Known gap
- The page listens for an
invalidGamesocket event, but the server does not emit it.
Results — /results/[gameID] (src/pages/results/[gameID].tsx)
- Requires an active session; redirects to
/loginif unauthenticated. - Fetches results via
useGameResults(/api/results/{gameId}). - Computes scores using
calculateScorePairand renders:AnalysisBoxfor final code + runtime metrics.TestCaseResultsBoxfor scoring tests and game-made tests.
- Clears
localStorage.stored_gameonce results load.
Development-only route
Infra/Auth test page — /test (src/pages/test.tsx)
- Manual smoke tests for Postgres/Redis + auth flows.
- Not linked from any UI surface. Treat as internal tooling.