DB:
- quiz_question gains `lang` ('fr' default) and `active` (true default); migration 0002
- only active questions are served to games (pool filter)
History:
- engine writes a game_history row at game end (modes + ranked results), fire-and-forget
- public GET /api/history/:code lists a room's recent games
- lobby shows a "Parties précédentes" collapsible (date, modes, winner/scores)
Back-office:
- create form has a FR/EN language select; lang stored
- per-question active toggle (PATCH /questions/:id) — disable without deleting;
inactive rows dimmed and labelled
- seeds set lang (opentdb=en, manual/pokemon=fr)
Verified e2e: migration applied; game played → /api/history returns it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- settings carry `categories` (empty = all); quiz/image pool filters by category
(DB + in-code bank), threaded through prepareQuizForRoom/loadGroup/loadQuizPool
- public GET /api/categories lists available categories
- lobby: category chips (Toutes + each), shown when quiz/images is active
- shared: RoomSettings.categories + UpdateSettingsPayload
Verified e2e: /api/categories returns the list; filtering by ["Pokémon"] serves
only Pokémon questions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- image_reveal becomes its own game mode: GameType gains "image"; the quiz
pool is filtered by allowed formats derived from gameType (image →
image_reveal only, quiz → mcq/truefalse/free, mixed → all). loadQuizPool
takes a formats list; pool fallback respects it.
- lobby settings reworked into a refined "Réglages" card: 4 mode tiles
(icon + label, locked <3 players for blindtest/mixed), question count as a
stepper, blindtest config grouped; dynamic label for image mode + hint.
- admin: real upload button (hidden input + styled Button with Upload icon)
instead of the bare file input.
- app title set to "NerdWare …" in index.html (+ lang fr, description).
Verified e2e: image mode serves only image_reveal questions (with imageUrl).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Server:
- quiz-round handles image_reveal like a text-answer format (tolerant match
on acceptedAnswers), exposing imageUrl in the round payload; degressive
scoring comes for free from the existing speed bonus (earlier = blurrier =
more points)
- repo includes image_reveal in the pool (requires imageUrl + acceptedAnswers)
- back-office: image upload (@fastify/multipart) saved to UPLOADS_DIR and
served statically on /uploads (@fastify/static); create supports image_reveal
- env: UPLOADS_DIR; gitignore uploads/
Client:
- admin form: "Image à deviner" — upload + preview + accepted answers
- in-game: ImageReveal shows the image with a time-cadenced decreasing blur
(synced via server timestamps) + text input; reveal shows the clear image
and the answer
shared: QuizQuestionPayload.imageUrl
Verified against real server+DB: upload (url), static serve (200), create
(201), validation (400).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mixed mode (default):
- gameType is now mixed | quiz | blindtest, default "mixed"; the lobby
interleaves quiz + blindtest rounds so modes alternate
- lobby reworked: 3-way game-type switch, quiz count and/or blindtest config
+ track submission shown per selected type
Free-text quiz questions:
- quiz supports the `free` format (e.g. maths) alongside mcq/truefalse:
payload without choices, {text} vote, tolerant matching on acceptedAnswers
- shared QuizQuestionPayload.choices optional; reveal truth carries answer
- match util moved to game/match.ts (shared by quiz + blindtest)
- repo/seed include free + acceptedAnswers; in-code bank gains free questions
- client quiz view renders a text input for free questions
Mode-aware transitions:
- RoundTransition themed per mode (colors/label/emoji); announces the mode
on change (quiz <-> blindtest), lighter teaser within the same mode
- custom media buckets per mode: assets/transitions/{quiz,blindtest}/ + shared
- store tracks roundModeChanged
Verified e2e: mixed game alternates quiz/blindtest to completion; free-text
scoring covered by tests (22 pass).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drizzle (postgres-js) setup: schema (quiz_category, quiz_question,
quiz_played, game_history), client (nullable — no DATABASE_URL → null),
drizzle.config, initial migration, migrate script
- Open Trivia DB seed: curated geek categories, mcq + truefalse, base64
decode (incl. type/difficulty), 1 req/5s rate limit, idempotent
(prompt unique); also seeds the FR in-code bank as source 'manual'
- quiz wired to the DB: per-room pool preloaded at game start (loadQuizPool
excludes already-played), records quiz_played for cross-session
anti-repeat; falls back to the in-code bank when no DB
- docker-compose for local Postgres; db:* scripts; DATABASE_URL in env
Roadmap V1 step 5. Verified against a real Postgres: migrate + seed (270
OpenTDB + 10 manual), DB-backed game serves OpenTDB questions and fills
quiz_played; no-DB fallback still serves the in-code bank.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>