Commit graph

18 commits

Author SHA1 Message Date
lila
ea33b7fcc8 feat(web): add minimal playable quiz at /play
- Add Vite proxy for /api → localhost:3000 (no CORS needed in dev)
- Create /play route with hardcoded game settings (en→it, nouns, easy)
- Three-phase state machine: loading → playing → finished
- Show prompt, optional gloss, and 4 answer buttons per question
- Submit answers to /api/v1/game/answer, show correct/wrong feedback
- Manual Next button to advance after answering
- Score screen on completion
- Add selectedOptionId to AnswerResult schema (discovered during
  frontend work that the result needs to be self-contained for
  rendering feedback without separate client state)

Intentionally unstyled — component extraction and polish come next.
2026-04-11 12:56:03 +02:00
lila
075a691849 feat(api): add answer evaluation endpoint
Complete the game answer flow:

- Add evaluateAnswer service function: looks up the session in the
  GameSessionStore, compares the submitted optionId against the stored
  correct answer, returns an AnswerResult.
- Add submitAnswer controller with safeParse validation and error
  handling (session/question not found → 404).
- Add POST /api/v1/game/answer route.
- Fix createGameSession: was missing the answerKey tracking and the
  gameSessionStore.create() call, so sessions were never persisted.

The full singleplayer game loop now works end-to-end:
POST /game/start → GameSession, POST /game/answer → AnswerResult.
2026-04-11 12:12:54 +02:00
lila
0755c57439 feat(api): wire GameSessionStore into createGameSession
The service now tracks the correct optionId for each question and
stores the answer key in the GameSessionStore after building the
session. The client response is unchanged — the store is invisible
to the outside.

- Build answerKey (questionId → correctOptionId) during question
  assembly by finding the correct answer's position after shuffle
- Store the answer key via gameSessionStore.create() before returning
- Add excludeText parameter to getDistractors to prevent a distractor
  from having identical text to the correct answer (different term,
  same translation). Solved at the query level, not with post-filtering.
- Module-level InMemoryGameSessionStore singleton in the service
2026-04-11 11:52:38 +02:00
lila
1940ff3965 feat(api): add in-memory GameSessionStore
Add the session storage infrastructure for tracking correct answers
during a game. Designed for easy swap to Valkey in Phase 4.

- GameSessionStore interface with create/get/delete methods, all async
  to match the eventual Valkey implementation
- InMemoryGameSessionStore backed by a Map
- GameSessionData holds only the answer key (questionId → correctOptionId)
- Also fix root build script to build packages in dependency order
2026-04-11 11:42:13 +02:00
lila
f53ac618bb feat(api): assemble full GameSession with shuffled answer options
Extend the game flow from raw term rows to a complete GameSession
matching the shared schema:

- Add getDistractors model query: fetches N same-POS, same-difficulty,
  same-target-language terms excluding a given termId. Returns bare
  strings since distractors only need their display text.
- Fix getGameTerms select clause to use the neutral field names
  (sourceText, targetText, sourceGloss) that the type already declared.
- Rename gameService entry point to createGameSession; signature now
  takes a GameRequest and returns a GameSession.
- Per question: fetch 3 distractors, combine with the correct answer,
  shuffle (Fisher-Yates), assign optionIds 0-3 by post-shuffle index,
  and assemble into a GameQuestion with a fresh UUID.
- Wrap the questions in a GameSession with its own UUID.
- Run per-question distractor fetches in parallel via Promise.all.

Known gap: the correct option is not yet remembered server-side, so
/game/answer cannot evaluate submissions. SessionStore lands next.
2026-04-10 21:44:36 +02:00
lila
b3b32167c9 formatting 2026-04-10 20:09:46 +02:00
lila
9fc3ba375a feat: scaffold quiz API vertical slice
- Add GameRequestSchema and derived types to packages/shared
- Add SupportedLanguageCode, SupportedPos, DifficultyLevel type exports
- Add getGameTerms() model to packages/db with pos/language/difficulty/limit filters
- Add prepareGameQuestions() service skeleton in apps/api
- Add createGame controller with Zod safeParse validation
- Wire POST /api/v1/game/start route
- Add scripts/gametest/test-game.ts for manual end-to-end testing
2026-04-09 13:47:01 +02:00
lila
7d80b20390 wip version of the api 2026-04-05 00:33:34 +02:00
lila
2ebf0d0a83 infra: add Docker Compose setup for local development
- Configure PostgreSQL 18 and Valkey 9.1 services
- Create multi-stage Dockerfiles for API and Web apps
- Set up pnpm workspace support in container builds
- Configure hot reload via volume mounts for both services
- Add healthchecks for service orchestration
- Support dev/production stage targets (tsx watch vs compiled)
2026-03-25 18:56:04 +01:00
lila
681c6d2b4f installing and configuring tailwind 2026-03-21 20:59:26 +01:00
lila
9ebbf83f93 formatting 2026-03-21 19:33:07 +01:00
lila
2025cc7298 chore: configure root eslint with react and tanstack router rules 2026-03-21 19:32:38 +01:00
lila
1765923cb6 feat: scaffold vite react app and configure web package 2026-03-21 11:59:52 +01:00
lila
a0f008be74 feat(api): scaffold express server with /api/health endpoint 2026-03-21 10:17:53 +01:00
lila
04acd4b580 chore: configure vitest with project-based setup and coverage 2026-03-20 19:25:00 +01:00
lila
ce42eb1811 chore: configure prettier with ignore rules and format scripts + running format 2026-03-20 18:37:38 +01:00
lila
3dfb75ea83 chore: configure typescript project references and shared compiler options 2026-03-20 14:01:48 +01:00
lila
66848f282f chore: initialise pnpm workspace monorepo 2026-03-20 10:00:21 +01:00