Files
game-scoreboard/docs/DATA_MODEL.md
T
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

2.4 KiB

Data model

Full SQL in supabase/migrations/ (0001_init.sql, then 0002_timer.sql, 0003_buzzer.sql, 0004_buzz_result.sql).

Table Purpose Key columns
events A game night owner_id, scoreboard_token, scoreboard_visibility, frozen_snapshot, current_game_id, reveal_active, reveal_count, timer_visible, timer_duration_ms, timer_remaining_ms, timer_ends_at
teams Teams in an event name, color (#RRGGBB), sort_order, buzzer_token
games Activities scoring_mode, points_mode, multiplier, placement_points int[], status, admin_notes, judge_notes, open_token, open_link_enabled, judging_locked, spotlight_team_id, buzzer_enabled
criteria Scoring blocks per game label, description, input_type, min_value, max_value, step, weight
judges Judges per game name, token, revoked, source (nominated/open_link)
scores One value per (team, criterion, judge) judge_id NULL = admin score (direct mode or override)
adjustments Bonus/penalty points (+/-), reason, optional game_id
buzzes Who buzzed for the current question game_id, team_id, buzzed_at, result (correct/wrong/NULL)

Notes

  • scores unique on (team_id, criterion_id, judge_id) with NULLS NOT DISTINCT, so there's exactly one admin row per team+criterion → clean upserts.
  • In judged games, an admin row (judge_id IS NULL) is an override for that cell.
  • Deleting a criterion/team/judge cascades its scores.
  • Timer (0002_timer.sql): running = timer_ends_at set; paused = timer_remaining_ms set; reset = both null (full timer_duration_ms).
  • Buzzers (0003_buzzer.sql): buzzes is unique on (game_id, team_id) — one buzz per team per question; buzzed_at defaults to clock_timestamp() and decides the order. Clearing the buzzers deletes the game's rows. A trigger checks the team is in the game's event. teams.buzzer_token is a 128-bit token like the others; duplicating an event gives the copied teams new ones.
  • Buzzer answers (0004_buzz_result.sql): buzzes.result is the host's mark. The team answering is the earliest buzz with result IS NULL, unless some buzz is correct (question decided). The point for a correct answer is not stored on the buzz: it is +1 on the admin's scores row for the game's first criterion.
  • frozen_snapshot holds the ScoreboardPayload captured at freeze time.