A platform for curling clubs to run and display live scoreboards, track game history, and manage scoring. It's built as a monorepo with three products sharing a Firebase backend:
| Product | Stack | Purpose |
|---|---|---|
app/ |
Flutter | Scoreboard app (web, mobile, desktop) |
admin/ |
React + Vite | Club and super-admin web portal |
functions/ |
Node.js + Express | REST API and callable Cloud Functions |
Shared Firebase config (firebase.json, firestore.rules, firestore.indexes.json) lives at the repo root.
- Flutter 3.44.0 (stable)
- Node.js 20
- Firebase CLI —
npm install -g firebase-tools - Google Cloud SDK — for local credentials via
gcloud
firebase login
gcloud auth application-default loginfirebase use default # targets curling-scoreboard-devcd app && flutter pub get
cd admin && npm install
cd functions && npm install
cd scripts && npm installOpen the repo in VS Code and use the Launch Web run configuration (.vscode/launch.json). This starts the app in Chrome pointing at the live dev Firebase project (curling-scoreboard-dev).
To run from the terminal:
cd app
flutter run -d chrome --dart-define=FIREBASE_ENV=dev -t lib/main.dartcd admin
npm run devStarts the Vite dev server. The portal uses the dev Firebase project by default.
Functions run on Firebase Cloud Functions — there's no local emulator configured. During development, the app and admin portal connect directly to the live dev project's deployed functions. To deploy your changes to dev:
firebase deploy --only functions --project curling-scoreboard-devA seed script populates the dev Firestore with realistic clubs, sheets, and game history.
cd scripts
npm install
node seed-dev.jsThe script is idempotent for clubs and sheets — re-running it won't create duplicates there. Each run does add a new set of game documents, so run it once unless you want additional game history.
To target a different project:
FIREBASE_PROJECT_ID=my-other-project node seed-dev.jsOpening a PR triggers the validate_pr workflow, which runs formatting, analysis, type checking, tests, and spell checking per product (only for products whose files changed). Once checks pass, a preview build is deployed to a temporary Firebase Hosting channel and linked on the PR.
Every push to main automatically deploys all three products to the live dev environment:
| Workflow | Deploys |
|---|---|
| deploy_web_dev | Flutter web app |
| deploy_admin_dev | Admin portal |
| deploy_functions_dev | Cloud Functions + Firestore rules |
First, generate a version tag using the version_increment workflow. It reads the commits since the last release, writes the changelog and release notes, and commits the tag back to main.
Then trigger each prod workflow manually, entering the version tag (e.g. 0.0.37):
| Workflow | Deploys |
|---|---|
| deploy_web_prod | Flutter web app |
| deploy_admin_prod | Admin portal |
| deploy_functions_prod | Cloud Functions + Firestore rules |
Each workflow checks out the exact tag and deploys it to the live prod environment (curling-scoreboard-prod).