Some checks failed
Build and Push / build (push) Failing after 8s
Full-stack tournament management app with real-time scoring: - Go 1.26 backend with REST API and WebSocket live scoring - React 19 + Vite 8 frontend with mobile-first design - File-based JSON storage with JSONL audit logs - Multi-stage Docker build with Gitea CI/CD pipeline - Post-tournament questionnaire with spirit voting - Technical documentation and project description Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
221 lines
11 KiB
Markdown
221 lines
11 KiB
Markdown
# Disc Agenda -- Project Description
|
|
|
|
## 1. Overview
|
|
|
|
Disc Agenda is a self-hosted web application for managing ultimate frisbee tournaments. It provides a single URL where players and spectators can view the tournament schedule, follow live scores in real-time, fill out post-tournament questionnaires, and browse final standings -- all from their phones, with no app install required.
|
|
|
|
Built for the Czech ultimate frisbee community, the platform is currently deployed for the **Fujarna** tournament series in Prague.
|
|
|
|
---
|
|
|
|
## 2. Problem Statement
|
|
|
|
Small and casual frisbee tournaments typically lack proper digital infrastructure. Organizers rely on a patchwork of tools:
|
|
|
|
- **Google Sheets** for schedules (not real-time, clunky on mobile)
|
|
- **WhatsApp/Telegram groups** for score announcements (noisy, easy to miss)
|
|
- **Paper scorecards** at the field (single point of truth, hard to read from a distance)
|
|
- **Google Forms** for post-tournament feedback (disconnected from the event)
|
|
|
|
This creates friction for everyone: players don't know when their next game starts or what the current score is in a parallel game, spectators can't follow along remotely, and organizers spend time relaying information manually.
|
|
|
|
**Disc Agenda solves this** by providing a single, mobile-friendly web app where:
|
|
- The schedule is always visible and up-to-date
|
|
- Any participant can update scores in real-time
|
|
- Scores appear live on everyone's screen via WebSocket
|
|
- Post-tournament feedback is collected in context
|
|
|
|
---
|
|
|
|
## 3. Key Features
|
|
|
|
### 3.1 Tournament Hub
|
|
|
|
The landing page for each tournament displays all essential information at a glance: event name, date, location, venue, participating teams, and rules. Clear navigation links guide users to the schedule, questionnaire, and results.
|
|
|
|
### 3.2 Live Scoreboard
|
|
|
|
The core feature. Each game has its own scoring page with large, touch-friendly controls:
|
|
|
|
- **+/- buttons** for quick score increments
|
|
- **SET button** for direct value entry (correcting mistakes)
|
|
- **Real-time sync** -- multiple people can score simultaneously, and all changes appear instantly on every connected device via WebSocket
|
|
- **Audit trail** -- every score change is logged with timestamp and user identity, viewable on the game page
|
|
- **QR code** -- each game page includes a QR code linking to the post-tournament questionnaire, making it easy to share
|
|
|
|
Game states transition automatically: `Scheduled` -> `Live` (when the first point is scored) -> `Final` (set manually). Once a game is marked as final, the score is locked.
|
|
|
|
### 3.3 Live Schedule
|
|
|
|
The schedule page groups all games by round (Pool A, Pool B, placement bracket) with start times and field assignments. Scores update in real-time across all games -- viewers don't need to open individual game pages to see current scores.
|
|
|
|
### 3.4 Post-Tournament Questionnaire
|
|
|
|
A mobile-friendly survey form with:
|
|
|
|
- **Spirit of the Game voting** -- select which team showed the best sportsmanship
|
|
- **Team selector** -- identify your own team
|
|
- **Custom questions** -- organizer-configurable (food rating, field conditions, free-text feedback)
|
|
- **Attend next tournament** -- gauge interest for future events
|
|
|
|
Responses are stored per tournament and can be reviewed by organizers.
|
|
|
|
### 3.5 Results & Standings
|
|
|
|
A final standings table showing each team's position, win/loss/draw record, points scored and conceded, point differential, and spirit score. The team with the highest spirit score receives a highlighted award.
|
|
|
|
### 3.6 Past Tournaments Archive
|
|
|
|
An archive page listing completed tournaments, allowing the community to look back at previous events and their results.
|
|
|
|
---
|
|
|
|
## 4. How It Works
|
|
|
|
### For the Organizer
|
|
|
|
1. **Prepare data** -- create JSON files defining the tournament (teams, schedule, questionnaire)
|
|
2. **Deploy** -- `docker compose up` on any server (a Raspberry Pi works fine)
|
|
3. **Share the URL** -- send it to teams before or on tournament day
|
|
|
|
### On Tournament Day
|
|
|
|
1. Players open the URL on their phones
|
|
2. Designated scorers (or anyone) navigate to a game page and tap +/- to update scores
|
|
3. All connected viewers see score changes instantly
|
|
4. The schedule page reflects live scores across all games in real-time
|
|
5. Between games, players can check upcoming matches and current standings
|
|
|
|
### After the Tournament
|
|
|
|
1. Organizer creates `results.json` with final standings
|
|
2. Players fill out the questionnaire (linked via QR codes on game pages)
|
|
3. Organizer reviews feedback via the questionnaire results API
|
|
4. Tournament status is set to `completed` and appears in the archive
|
|
|
|
---
|
|
|
|
## 5. Technology Stack & Rationale
|
|
|
|
### Go Backend
|
|
|
|
**Why Go:** A single statically-compiled binary with excellent concurrency primitives. Perfect for WebSocket-heavy applications where multiple clients need real-time updates. Minimal runtime dependencies -- the compiled binary runs on bare Alpine Linux.
|
|
|
|
**Libraries:** Only three external dependencies:
|
|
- `gorilla/mux` -- HTTP router
|
|
- `gorilla/websocket` -- WebSocket protocol
|
|
- `rs/cors` -- Cross-origin request handling
|
|
|
|
### React + Vite Frontend
|
|
|
|
**Why React:** The component model maps naturally to the interactive scoreboard UI -- each game card, score counter, and status badge is a composable piece. React's state management handles the bidirectional WebSocket data flow cleanly.
|
|
|
|
**Why Vite:** Modern build tooling with fast hot-reload during development. Produces optimized static assets for production.
|
|
|
|
**Minimal dependencies:** No state management library (React's built-in state is sufficient), no UI component framework (custom CSS provides an athletic visual identity), just `react-router-dom` for routing and `qrcode.react` for QR generation.
|
|
|
|
### File-Based Storage
|
|
|
|
**Why no database:** The application serves small tournaments (10-20 teams, 20-30 games). JSON files provide:
|
|
- Zero operational overhead (no database to install, configure, or maintain)
|
|
- Human-readable data (edit tournament data with any text editor)
|
|
- Easy backup (copy a directory)
|
|
- Portability (works anywhere a filesystem exists)
|
|
|
|
Audit logs use JSONL (JSON Lines) format for append-only writes that survive crashes without corruption.
|
|
|
|
**Trade-off:** Not suitable for high-concurrency write scenarios or multi-server deployments. Single-server, single-process is the intended deployment model.
|
|
|
|
### Docker
|
|
|
|
**Why Docker:** Reproducible deployment in a single command. The multi-stage build produces a minimal image (~15 MB) containing just the Go binary, frontend assets, and Alpine Linux. A named volume persists tournament data across container updates.
|
|
|
|
---
|
|
|
|
## 6. Architecture Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Go Backend (single binary) │
|
|
│ ├── REST API → JSON file storage │
|
|
│ ├── WebSocket hubs → real-time broadcasting │
|
|
│ └── SPA handler → serves React frontend │
|
|
├─────────────────────────────────────────────────┤
|
|
│ React Frontend (compiled static assets) │
|
|
│ ├── Schedule view ← tournament WebSocket │
|
|
│ ├── Scoreboard ← game WebSocket │
|
|
│ └── Forms → REST API │
|
|
├─────────────────────────────────────────────────┤
|
|
│ Data (flat JSON files) │
|
|
│ ├── Tournament config, schedule, results │
|
|
│ ├── Per-game score state │
|
|
│ └── JSONL audit logs │
|
|
└─────────────────────────────────────────────────┘
|
|
```
|
|
|
|
The entire application runs as a single process. The Go server handles HTTP requests, WebSocket connections, and file I/O. The React frontend is compiled to static files and served by the same Go process. No separate web server, no reverse proxy (though one is recommended for TLS), no database.
|
|
|
|
---
|
|
|
|
## 7. Current State
|
|
|
|
### First Tournament: Fujarna - 14.3.2026
|
|
|
|
- **Location:** Prague, Czech Republic
|
|
- **Venue:** Kotlarka multi-purpose sports facility
|
|
- **Teams:** 10 teams in 2 pools of 5
|
|
- Pool A: FUJ 1, Kocicaci, Spitalska, Sunset, Hoko-Coko Diskyto
|
|
- Pool B: FUJ 2, Bjorn, GyBot, Poletime, Kachny
|
|
- **Format:** Full round-robin within pools (10 games per pool), followed by crossover placement bracket (5th place through Grand Final)
|
|
- **Schedule:** 25 games total, 08:30 - 17:10, 20-minute games
|
|
- **Rules:** 20 min per game, no breaks, no timeouts, no draws allowed
|
|
|
|
### Application Status
|
|
|
|
- All core features are implemented and functional
|
|
- UI is in Czech language throughout
|
|
- Ready for production deployment via Docker
|
|
- CI/CD pipeline configured for Gitea container registry
|
|
|
|
---
|
|
|
|
## 8. Design Philosophy
|
|
|
|
**Simple over complex.** No admin UI, no user accounts, no database. Tournament data is managed by editing JSON files. This keeps the codebase small, the deployment trivial, and the maintenance burden near zero.
|
|
|
|
**Mobile-first.** Every page is designed for phone screens -- the primary use case is field-side scoring and schedule checking. Large touch targets, readable typography, responsive layout.
|
|
|
|
**Real-time everywhere.** WebSocket connections power both the scoreboard and the schedule. If a score changes, everyone sees it within milliseconds. No polling, no manual refresh.
|
|
|
|
**Self-hosted.** Full control over data and infrastructure. No external service dependencies, no API keys, no subscriptions. Runs on any machine that can run Docker -- a home server, a VPS, or a Raspberry Pi.
|
|
|
|
**Trust-based.** In a community of ~50 frisbee players, authentication is overhead without benefit. Anyone can update scores, and the audit log provides accountability.
|
|
|
|
---
|
|
|
|
## 9. Future Plans
|
|
|
|
Potential directions for the platform:
|
|
|
|
- **Admin panel** -- web UI for managing tournaments, teams, and schedules (replacing manual JSON editing)
|
|
- **Authentication** -- optional login for score-keeping to prevent accidental edits
|
|
- **Automatic standings** -- calculate standings from game results instead of manual `results.json`
|
|
- **Photo gallery** -- tournament photos integrated into the event page
|
|
- **Push notifications** -- alerts for game start, game end, or close scores
|
|
- **Player registration** -- team sign-up and roster management
|
|
- **Statistics** -- game history, scoring trends, spirit score analytics
|
|
- **Multi-language support** -- currently Czech-only
|
|
- **Multiple fields** -- field assignment management for larger tournaments
|
|
|
|
---
|
|
|
|
## 10. Contributing
|
|
|
|
The project is organized for easy contribution:
|
|
|
|
- **Backend** (`backend/`): Standard Go project structure with `cmd/` and `internal/` packages
|
|
- **Frontend** (`frontend/`): React SPA with page-based component organization
|
|
- **Data** (`data/`): JSON seed data that can be modified for testing
|
|
|
|
For detailed technical reference, API documentation, and development setup instructions, see the [Technical Documentation](DOCUMENTATION.md). For quick start and project structure, see the [README](../README.md).
|