Commit Graph
17 Commits
Author SHA1 Message Date
joshuaandClaude Opus 5.5 186a112b95 Phase 11: mark buzzer answers, +1 for a correct one
The host marks the answering team "Got it" or "Wrong" (G / W). Wrong
passes the turn to the next team in the buzz order; Got it closes the
buzzers until "Next question" (B). Undo reverses either mark.

In a direct game a correct answer adds 1 to the team's score in the
game's first criterion, as an ordinary admin score row, so it flows
through the scoring engine (multiplier, placement) and the existing
range/step trigger. The point is written before the mark, so a refused
point never leaves a "correct" with nothing behind it. Judged games
only get the mark: the judges' scores stand.

Needs supabase/migrations/0004_buzz_result.sql applied before deploying.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:55:50 +02:00
joshuaandClaude Opus 5.5 bbcf84bbc3 Phase 10: team buzzers
Each team gets a buzzer link (/b/[token]); a game opts in with a
"Teams buzz in" checkbox. The database's clock orders the buzzes, so
the result doesn't depend on phone clocks. The control room lists the
order and clears it per question (B); the scoreboard shows who buzzed
first with a ding.

Buzzes follow the existing rules: token-gated route handlers with the
service-role client, RLS on the new table, and data-free realtime pings
on a new buzzer:{eventId} channel.

Needs supabase/migrations/0003_buzzer.sql applied before deploying.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:33:44 +02:00
joshuaandClaude Opus 5.5 48cecfff7f Fix magic link redirect to localhost behind a reverse proxy
After verifying the link, /auth/confirm built its redirect from
request.nextUrl. Behind a proxy or tunnel a route handler sees the
address the server is bound to (localhost:4322), so the browser was
sent off the public domain and away from the session cookie it had
just been given. Build the redirect from NEXT_PUBLIC_SITE_URL instead,
the same value the magic link email already uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:56:53 +02:00
joshuaandClaude Opus 5.5 95ccf21d12 Release 1.0.0
Sets the app version to 1.0.0. Also includes the .gitignore entry for
/docs added in the working copy (existing docs stay tracked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v1.0.0
2026-10-03 22:20:29 +02:00
joshuaandClaude Opus 5.5 bf5b6194f6 Add scoreboard countdown timer and sound effects
- Timer: one per event, set from the control room (duration, presets,
  start/pause/resume/reset, show/hide, T shortcut). Rules are pure
  functions in src/lib/timer.ts; the server's clock sets the end time and
  each screen counts down locally, corrected for its own clock.
  Needs supabase/migrations/0002_timer.sql.
- Sound effects on the scoreboard, synthesised with Web Audio (no files):
  drum-roll + crash per revealed team, fanfare for first place, a chime
  on game change, ticks for the last 10 seconds and a buzzer at zero.
  Triggers are pure functions comparing consecutive payloads.
- 'Turn sound on' button on the scoreboard, since browsers block audio
  until a click; the choice is remembered across reloads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 22:14:10 +02:00
joshuaandClaude Opus 5.5 f1b3a862f8 Phase 8: share panel, polish, accessibility and deploy config
- Share links section: scoreboard link, every judged game's judge links
  and open link with QR codes, and 'Copy all judge links' as plain text
  for WhatsApp. Warns when links point at localhost.
- Error and not-found pages with plain-language copy; a real start page.
- Accessibility: darker 'alert strong' red for text and white-on-red
  (Alert was 4.0:1 on white), no text below 70% ink, 44px block buttons
  and link targets, scoreboard status stays readable when idle.
- vercel.json pins functions to Dublin next to the West EU database;
  README documents deployment and pre-game-night checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 16:02:29 +02:00
joshuaandClaude Opus 5.5 2702491096 Phase 7: judges — links, phone page, open link, judged control room
- Setup: nominate judges, copy personal links, QR codes (generated on
  demand in the browser), revoke/restore, and an open-link toggle.
- GET /api/judge/[token] returns only that judge's own scores; PUT
  .../scores runs every check from ARCHITECTURE.md in checkJudgeWrite
  (pure, unit-tested) after one parallel lookup, plus a rate limit.
- /j/[token]: mobile cards per team, spotlight pinned first, autosave
  with per-card status, read-only when locked/done, link-off when revoked.
- /jo/[token]: name form -> personal link, remembered in localStorage so
  a re-scan doesn't create a second judge.
- Control room: averages with on-request overrides, judge progress
  matrix, per-judge breakdown, spotlight select and lock toggle.
- usePendingEdits shares the optimistic autosave logic between the admin
  grid and the judge page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:29:33 +02:00
joshuaandClaude Opus 5.5 1410c0fa13 Move scoreboard visibility controls to a full-width bar
The toggle lived in the right-hand column, which drops below the scoring
grid on windows narrower than 1280px, so it was off screen. It now sits
directly under the title at every width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:08:47 +02:00
joshuaandClaude Opus 5.5 da09f59b2a Phase 6: scoreboard visibility toggle and reveal mode
- setVisibility: visible / frozen / hidden. Freezing snapshots the scores
  on the server with the same engine the scoreboard uses; freezing twice
  keeps the original snapshot.
- Reveal mode: start, reveal next (last place first, one click per team),
  finish (sets visible) and cancel. The host sees who is next; the
  scoreboard plays a visual drum-roll for teams revealed while watching.
- H cycles visibility in the control room.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:58:32 +02:00
joshuaandClaude Opus 5.5 f7122b193c Phase 5: control room with direct scoring, bonuses and shortcuts
- /admin/events/[eventId]/live: game list with status, previous/next,
  mark done/reopen, scoring grid, standings, and bonus/penalty with undo.
- Scores autosave per cell with optimistic UI; the validate_score trigger
  and RLS do the checking so a save costs one DB round trip.
- BlockInput and NumberStepper are shared UI, ready for the judge page.
- buildLiveView shapes all data on the server from the scoring engine, so
  the client components contain no score math.
- N / P switch games, ? shows shortcut help.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:48:41 +02:00
joshuaandClaude Opus 5.5 17eb76d036 Phase 4: scoreboard page, public API and realtime refresh
- buildScoreboardPayload is the single place deciding what the public may
  see: visible, frozen (snapshot scores, live current game), hidden (no
  scores, alphabetical) and reveal (unrevealed teams carry no score/order).
- GET /api/public/scoreboard/[token] validates the token, then loads the
  event with the service client in one parallel round trip.
- notify() pings sb/admin/game channels over Realtime's REST endpoint with
  no data; useRefreshChannel refetches immediately on a ping, throttles
  bursts, and falls back to a 10s poll, focus and reconnect.
- /s/[token]: FLIP rank animation, count-up totals, reduced motion,
  fullscreen, cursor hide and a connection indicator.
- Admin setup actions now notify, so renames and reorders show up live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:19:03 +02:00
joshuaandClaude Opus 5.5 b60c9db4f4 Phase 3: admin magic-link auth and event setup screens
- /login sends a Supabase magic link (admin emails only); /auth/confirm
  completes sign-in; proxy.ts guards /admin and every server action
  re-checks with requireAdmin(), since actions are plain POST endpoints.
- /admin lists, creates, duplicates and deletes events.
- /admin/events/[eventId] edits teams (colour, order) and games with
  criteria, presets and notes. All input goes through zod schemas that
  mirror the database constraints so errors read as plain sentences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:47:36 +02:00
joshuaandClaude Opus 5.5 0c66e9b80e Ignore the coverage report in ESLint
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:33:42 +02:00
joshuaandClaude Opus 5.5 84dfe6a54b Phase 2: scoring engine with full branch coverage
Pure functions in src/lib/scoring implementing docs/SCORING.md: cell values
(override > judge average), weighted raw totals, raw and placement points
with tie averaging, and competition-ranked standings. Coverage thresholds
are enforced at 100% for the module. Also silences the hydration warning
caused by browser extensions modifying <html>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:33:21 +02:00
joshuaandClaude Opus 5.5 bc8e4f3f5a Phase 1: generate database types and type the Supabase clients
Types are generated from the live database's API schema by
scripts/gen-types.mjs (npm run gen:types), in the same shape as
supabase gen types, so no Supabase CLI login is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:20:31 +02:00
joshuaandClaude Opus 5.5 b134f87326 Phase 1: token schemas and anon RLS check script
tokens.ts validates link tokens (32 hex chars) before any DB query.
check:rls proves the anon key cannot read or insert on any table, and
fails loudly if the migration has not been applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:49:53 +02:00
joshuaandClaude Opus 5.5 c89a82295b Phase 0: scaffold Next.js, Tailwind tokens, Supabase clients, env validation
Next.js 16 App Router with strict TypeScript, Tailwind 4 theme tokens from
docs/UI.md, Vitest, and the three Supabase clients. Env vars are validated
with zod in next.config.ts so dev and build fail fast with a readable error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:27:11 +02:00