# 🥏 Disc Agenda — Frisbee Tournament Platform A self-hosted tournament management web app for ultimate frisbee, featuring live multiplayer scoring via WebSockets, a mobile-friendly questionnaire with QR codes, and a clean athletic visual design. ## Architecture ``` ┌──────────────────────────────────────────┐ │ Go 1.26 Backend (single binary) │ │ ├─ REST API (gorilla/mux) │ │ ├─ WebSocket hub (gorilla/websocket) │ │ ├─ SPA file server │ │ └─ File-based JSON storage │ ├──────────────────────────────────────────┤ │ React 19 + Vite 8 Frontend │ │ ├─ react-router-dom v7 (SPA) │ │ ├─ qrcode.react (QR generation) │ │ └─ Custom CSS (Bebas Neue + Barlow) │ ├──────────────────────────────────────────┤ │ Data: flat JSON files + JSONL audit logs │ │ (no database required) │ └──────────────────────────────────────────┘ ``` ## Features - **Tournament hub** — home page with location, dates, teams, rules - **Schedule** — round-grouped game schedule with status badges - **Live scoring** — WebSocket-powered multiplayer scoreboard with +/- and SET controls - **Audit log** — every score change persisted as JSONL per game - **Questionnaire** — mobile-friendly survey with QR code, team selectors, spirit voting, custom questions - **Results** — final standings table with spirit award highlight - **Past tournaments** — archive of completed events ## Quick Start ### Docker (recommended) ```bash docker compose up --build # → http://localhost:8080 ``` Data persists in a Docker volume. Seed data auto-copies on first run. ### Local Development **Prerequisites:** Go 1.26+, Node 22+ Terminal 1 — backend: ```bash cd backend go mod tidy go run ./cmd/server -data ../data -static ../frontend/dist -port 8080 ``` Terminal 2 — frontend (with hot reload + API proxy): ```bash cd frontend npm install npm run dev # → http://localhost:5173 (proxies /api and /ws to :8080) ``` ## Project Structure ``` ├── backend/ │ ├── cmd/server/main.go # Entry point, router, SPA handler │ └── internal/ │ ├── handlers/handlers.go # REST + WS HTTP handlers │ ├── models/models.go # Domain types │ ├── storage/storage.go # File-based persistence │ └── websocket/hub.go # Per-game WS hub + broadcast ├── frontend/ │ ├── src/ │ │ ├── api.js # API client + WS factory │ │ ├── App.jsx # Router │ │ ├── main.jsx # Entry │ │ ├── components/ # Header, Footer, Icons │ │ ├── pages/ # All page components │ │ └── styles/global.css # Full stylesheet │ ├── vite.config.js # Dev proxy config │ └── index.html ├── data/ # Seed data (JSON files) │ ├── tournaments.json │ └── tournaments/{id}/ │ ├── schedule.json │ ├── questionnaire_config.json │ ├── results.json │ └── games/ # Score state + audit logs ├── Dockerfile # Multi-stage build ├── docker-compose.yml └── Makefile ``` ## API Reference | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/tournaments` | List all tournaments | | GET | `/api/tournaments/{id}` | Tournament details | | GET | `/api/tournaments/{id}/schedule` | Game schedule | | GET | `/api/tournaments/{id}/games/{gid}/score` | Current score | | POST | `/api/tournaments/{id}/games/{gid}/score` | Update score (REST) | | GET | `/api/tournaments/{id}/games/{gid}/audit` | Audit log | | WS | `/ws/game/{id}/{gid}?user_id=x` | Live score WebSocket | | GET | `/api/tournaments/{id}/questionnaire` | Questionnaire config + teams | | POST | `/api/tournaments/{id}/questionnaire` | Submit response | | GET | `/api/tournaments/{id}/questionnaire/results` | All responses (admin) | | GET | `/api/tournaments/{id}/results` | Final standings | ### WebSocket Protocol Connect: `ws://host/ws/game/{tourneyId}/{gameId}?user_id=alice` **Send** (client → server): ```json {"action": "increment", "team": "home"} {"action": "decrement", "team": "away"} {"action": "set", "team": "home", "value": 12} ``` **Receive** (server → all clients): ```json { "type": "score_update", "state": {"game_id": "g01", "home_score": 8, "away_score": 6, ...}, "audit": {"action": "increment", "team": "home", "old_home": 7, ...} } ``` ## Configuration All via flags or env vars: | Flag | Env | Default | Description | |------|-----|---------|-------------| | `-port` | `PORT` | `8080` | Listen port | | `-data` | `DATA_DIR` | `./data` | Data directory | | `-static` | — | `./static` | Frontend files | ## Adding Tournament Data Edit JSON files directly in `data/`: - `tournaments.json` — add/edit tournament objects - `tournaments/{id}/schedule.json` — game schedule - `tournaments/{id}/questionnaire_config.json` — custom survey questions - `tournaments/{id}/results.json` — final standings No admin UI yet — data is managed via files. Future: add admin panel. ## Tech Stack - **Backend:** Go 1.26, gorilla/mux 1.8.1, gorilla/websocket 1.5.3, rs/cors - **Frontend:** React 19, Vite 8, react-router-dom 7, qrcode.react - **Storage:** Flat JSON files + JSONL audit logs - **Container:** Alpine 3.21, multi-stage Docker build ## Documentation - **[Technical Documentation](docs/DOCUMENTATION.md)** — detailed API reference, data models, WebSocket protocol, deployment guides, troubleshooting - **[Project Description](docs/PROJECT.md)** — project overview, motivation, features, design philosophy, roadmap