Skip to content

Repository files navigation

Field Game Fixtures

A full-stack fixture management system for Eton College Field Game — covering match scheduling, live results, league standings, tournament brackets, and live referee scoring across web and iOS.

Live at field-game.co.uk


Features

Public Web App

  • Fixtures — Browse upcoming matches with filtering by house, school team, umpire, and date range
  • Results — Paginated, filterable match results
  • League Standings — Live league table with W/D/L, points, and goal difference
  • Tournament Brackets — Interactive knockout and round-robin tournament views
  • Field Game Laws — Searchable rules reference
  • Weather — Forecast display for match days (via Open-Meteo at Eton College coordinates)
  • Calendar Export — Download fixtures as .ics or text
  • Offline Support — LocalStorage caching with stale-while-revalidate strategy
  • "My House" Personalisation — Save your house and school team for quick filtering
  • Dark Mode — Full light/dark theme support

Admin & Captain Tools

  • Score Entry — House captains submit match scores; admins can manage all matches
  • Fixture Admin — Edit/delete fixtures with override tracking and audit logs
  • User Management — Role-based access (admin, captain, viewer)
  • Housemaster Admin — Map housemaster initials to formal house names

iOS App

  • Native SwiftUI — All web features in a native iOS experience
  • Referee Module — Full live match scoring with epoch-based timer, event sourcing, golden point, substitution tracking, and crash recovery
  • Offline Caching — Core Data persistence with network fallback
  • Haptics & Audio — Whistle sounds and haptic feedback during refereeing

Automation

  • Email Import — Gmail monitor fetches fixture emails, parses Word document attachments, and imports matches automatically
  • Garmin Watch API — 3-character match codes for Garmin Connect IQ integration

Tech Stack

Layer Technology
Web Frontend Next.js 15, React 19, TypeScript, Tailwind CSS
Database Supabase (PostgreSQL) with Row Level Security
Auth Supabase Auth with role-based access
iOS App SwiftUI, Combine, MVVM, Swift Package Manager
iOS Backend Supabase Swift SDK
Email Parser Python 3, Gmail API, python-docx
Hosting Vercel (web), Xcode Cloud (iOS)
CI/CD GitHub Actions (lint, test, build)

Project Structure

field-game-fixtures/
├── src/                          # Next.js web app
│   ├── app/                      # Pages (fixtures, results, standings, tournaments, admin)
│   │   ├── page.tsx              # Main fixtures page
│   │   ├── results/              # Match results
│   │   ├── standings/            # League table
│   │   ├── tournament/           # Tournament brackets
│   │   ├── laws/                 # Field Game laws
│   │   ├── admin/                # Admin pages (fixtures, users, housemasters)
│   │   ├── resultsentry/         # Captain score entry
│   │   └── api/                  # API routes (Garmin watch, score submission)
│   ├── components/               # React components (MatchCard, FilterBar, etc.)
│   ├── lib/                      # Shared utilities
│   │   ├── houses.ts             # House name ↔ initials mapping
│   │   ├── filters.ts            # Match filtering logic
│   │   ├── competitions.ts       # Competition types and colours
│   │   ├── cache.ts              # LocalStorage caching with TTL
│   │   └── supabase/             # Supabase client configuration
│   └── types/                    # TypeScript type definitions
├── ios-app/                      # Native iOS app (SwiftUI)
│   └── FieldGameFixtures/
│       ├── App/                   # Entry point and tab navigation
│       ├── Models/                # Data models
│       ├── ViewModels/            # MVVM view models
│       │   └── Referee/           # Live match state machine
│       ├── Views/                 # SwiftUI views
│       │   ├── Fixtures/          # Match browsing
│       │   ├── Tournaments/       # Bracket views
│       │   ├── Referee/           # Live scoring UI
│       │   └── Laws/              # Rules reference
│       ├── Services/              # Supabase, caching, crash recovery
│       └── Utilities/             # House mapper, extensions
├── parser/
│   └── parse_fixtures.py         # Word document → structured match data
├── scripts/
│   └── gmail_monitor.py          # Email monitoring and import
├── supabase/
│   └── migrations/               # 22 SQL migration files
├── docs/                         # Documentation and specs
│   ├── DATABASE_SCHEMA.md
│   ├── CODEBASE_DOCUMENTATION.md
│   └── referee/                  # Referee module build plan
└── public/
    └── images/houses/            # House colour images

Quick Start

Prerequisites

  • Node.js 20+
  • Python 3.9+ (for email import)
  • A Supabase project

1. Set Up the Database

Run the migrations in order in the Supabase SQL Editor:

# Or use the Supabase CLI
supabase db push

2. Configure Environment

Create .env.local:

NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

3. Run the Web App

npm install
npm run dev

Visit localhost:3000.

4. Set Up Email Import (Optional)

pip install -r requirements.txt

# First-time Gmail authorisation
cd scripts
python gmail_monitor.py --test

This opens a browser for OAuth consent, then checks for fixture emails without writing to the database.

Automate with cron:

# Check every 15 minutes
*/15 * * * * cd /path/to/field-game-fixtures && python scripts/gmail_monitor.py >> logs/gmail.log 2>&1

5. Build the iOS App

Open ios-app/FieldGameFixtures.xcodeproj in Xcode. The Supabase Swift SDK is fetched via Swift Package Manager. Configure Supabase.xcconfig with your project URL and anon key.


Database Schema

The system uses PostgreSQL via Supabase with these core tables:

Table Purpose
houses House definitions (formal names as primary key)
housemasters Maps housemaster initials → formal house names
matches All fixtures and results (uses initials for team IDs)
user_profiles Users with roles (admin/captain/viewer)
score_audit_log Audit trail for score entries
tournament_definitions Tournament bracket structures (JSONB)
tournament_results Tournament match outcomes

Key views: upcoming_matches, recent_results, league_standings

See docs/DATABASE_SCHEMA.md for full schema documentation.


House Naming Convention

Houses are referenced in two formats — a critical detail for anyone working on the codebase:

Format Example Used In
Formal name "Angelo's", "Baldwin's Bec" houses table, UI display
Housemaster initials "JDM", "SPH" matches table team IDs

All 25 houses have a mapping defined in src/lib/houses.ts. When filtering matches, queries must search for both formats. See CLAUDE.md for the complete mapping and filtering patterns.


Testing

npm run test        # Run tests
npm run test:ci     # Run with coverage
npm run lint        # ESLint

Tests cover house mapping, competition colours, date utilities, and tournament logic.


Deployment

  • Web: Deploy to Vercel with vercel deploy or connect the GitHub repo
  • iOS: Xcode Cloud is configured for automated builds
  • CI: GitHub Actions runs lint, test, and build on every push

Security Notes

  • Never commit credentials.json, token.pickle, or .env.local to git
  • The .gitignore excludes these automatically
  • Admin pages use Supabase Auth with role-based RLS policies
  • Score submission API uses match codes for the Garmin watch integration

License

Private project for Eton College Field Game.

Releases

Packages

Contributors

Languages