Skip to content

tinkertanker/codeexp_bingo

Repository files navigation

BrainHack Bingo

A companion web app for the DSTA CODE_EXP / BrainHack 2026 hackathon "Secret Challenge — Bingo". Replaces the original sticker/paper bingo card with a digital experience: teams play a 4×4 bingo card across the event, complete squares by scanning each other's QR codes / uploading photos / asking mentors for stamps, and earn lucky-draw entries for completed lines. A live scoreboard + photo wall runs on the venue TV; an in-app spin animation handles the draw.

Quick start

npm install
cp .env.example .env

# First-time only: bootstrap a Convex deployment. Opens a browser for login, then
# generates convex/_generated/, deploys schema + functions, and prints the URL.
npx convex dev

# In another terminal:
npm run dev

Open http://localhost:5173. The splash page redirects to a team's bingo card if a magic-link token is in localStorage; otherwise it shows a "got a magic link?" message. To get a token, sign in to the admin panel (/admin) and create teams.

npx convex dev writes VITE_CONVEX_URL into .env.local automatically. Edit .env to set VITE_ADMIN_PASSCODE and VITE_ORGANISER_NAMES. Mirror those server-side too:

npx convex env set ADMIN_PASSCODE 'change-me'
npx convex env set ORGANISER_NAMES 'YJ,Marcus'

# Seed the 16 bingo squares + create the singleton gameState row (idempotent).
npx convex run seed:seedAll '{ "passcode": "change-me" }'

See docs/CONVEX_BOOTSTRAP.md for fuller setup notes.

Required environment variables

Var Purpose
VITE_CONVEX_URL Convex deployment URL (printed by npx convex dev)
VITE_ADMIN_PASSCODE Shared mentor passcode (also ships in client bundle — fine for the trust model)
VITE_ORGANISER_NAMES Comma-separated names allowed to flip game state and run the lucky draw (e.g. YJ,Marcus)

Server-side equivalents ADMIN_PASSCODE and ORGANISER_NAMES must match — set them with npx convex env set.

What's where

src/
├── App.tsx                         router with code-split routes
├── main.tsx                        Vite entry — wires <ConvexProvider>
├── index.css                       Tailwind directives + theme utilities
├── lib/                            pure logic + small client helpers
│   ├── types.ts                    re-exports Convex Doc<> types under app-friendly names
│   ├── token.ts                    localStorage helpers (team token, admin creds)
│   ├── lines.ts                    bingo line counting (rows/cols/diagonals)
│   ├── qr.ts                       QR magic-link encode/parse
│   ├── storage.ts                  uploadToConvex helper (POST to upload URL)
│   ├── standings.ts                compute lines + entries per team
│   ├── admin.ts                    client-side passcode/organiser checks
│   └── project.ts                  ZIP inspection (server runs the GitHub check)
├── hooks/
│   └── useTeam.ts                  stacks Convex useQuery() calls for a team's view
├── components/                     reusable UI
│   ├── BingoGrid.tsx, SquareCell.tsx, TeamHeader.tsx
│   ├── QRScanner.tsx, PhotoCapture.tsx
│   ├── AdminLayout.tsx
│   └── Leaderboard.tsx, PhotoWall.tsx
└── pages/                          one component per route
    ├── Splash.tsx, TeamHome.tsx, SquareDetail.tsx, TeamQR.tsx
    ├── ProjectSubmit.tsx, BoothDeepfake.tsx, Scoreboard.tsx
    └── admin/
        ├── AdminLogin.tsx, ApprovalQueue.tsx
        ├── TeamsManage.tsx, GameControls.tsx, DrawSpin.tsx

convex/                             Convex backend
├── schema.ts                       table definitions + shared validators
├── teams.ts                        list, getByToken, create, regenerateToken
├── squares.ts                      list (16 squares, sorted by position)
├── completions.ts                  submit* mutations + listForTeam + listPending (hydrated)
├── photos.ts                       recent (with hydrated public URLs)
├── codeSubmissions.ts              getForTeam, listAll, save (upsert)
├── gameState.ts                    get, setOpen
├── draw.ts                         run (weighted random server-side), clearWinners
├── scoreboard.ts                   bundle (one query for the whole TV view), stats
├── upload.ts                       generateUploadUrl mutation for storage
├── githubCheck.ts                  action (server-side fetch to GitHub REST)
├── admin.ts                        assertAdmin, assertOrganiser, approve/reject mutations
├── mentorActions.ts                logMentorAction helper used across mutations
├── seed.ts                         idempotent seedAll mutation for the 16 squares
└── _generated/                     auto-generated by `npx convex dev` — never edit by hand

docs/
├── CONVEX_BOOTSTRAP.md             first-time setup walkthrough
├── TESTING.md                      manual test checklist
└── HANDOVER.md                     intern follow-ups + open questions

Routes

Public

  • / — splash, redirects to team home if a token is stored.
  • /t/:token — team home (the bingo card).
  • /t/:token/square/:position — square detail with the right verification UI.
  • /t/:token/qr — big QR for other teams to scan.
  • /t/:token/project — GitHub URL + ZIP submission.
  • /booth/deepfake — auto-completes the booth square (scan-target for the printed booth poster).
  • /scoreboard — public TV view, designed for 1920×1080.

Admin (passcode + name)

  • /admin — sign-in.
  • /admin/queue — pending photo / IG approvals.
  • /admin/teams — manage 40 teams + magic links.
  • /admin/game — open/close the game + live stats.
  • /admin/draw — organiser-only lucky draw with spin animation.

Trust model (no real auth)

  • Each team has an opaque token. The "magic link" is /t/<token>. Anyone with that URL acts as that team.
  • Mentors share a single passcode. The frontend gates the /admin UI; every Convex mutation also re-checks the passcode server-side via assertAdmin (and assertOrganiser for game-open/close + draw). They identify themselves with a free-text name on every admin login; that name is stamped on every approve/reject action (mentorActions table = audit trail).
  • Organisers are the subset of mentor names listed in VITE_ORGANISER_NAMES (client) and ORGANISER_NAMES (Convex env). Both must match.
  • The blue colour-distinct rule and game-open lock are enforced server-side in convex/completions.ts — bypassing the UI gets you a server error.

Developer scripts

npm run dev        # Vite dev server with HMR (run `npx convex dev` separately)
npm run build      # tsc -b && vite build → dist/
npm run lint       # ESLint
npm run preview    # serve dist/ for local production smoke test

Status & next steps

See docs/HANDOVER.md for known follow-ups and open questions, and docs/TESTING.md for the manual test checklist.

Manual test plan

See docs/TESTING.md for an end-to-end test checklist. Run through it on two browser profiles (or two phones on the same Wi-Fi) to verify all the team-to-team interactions work.

About

Companion app for the DSTA CODE_EXP / BrainHack 2026 hackathon: a digital 4x4 bingo card with QR scan, photo upload, mentor approvals, live scoreboard, and lucky draw.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors