Skip to content

matthewevans/benchassist

Repository files navigation

BenchAssist

The sideline rotation manager for youth soccer coaches.
A free, open-source PWA — no backend, no accounts, all data on your device.

Try the Live App · Features · Screenshots · Getting Started


Get BenchAssist — Open the live app on your phone, tap Share → Add to Home Screen. No app store needed.

Screenshots

Live game focus view    Rotation grid    Practice planner

Live game day — track rotations, plan practices, all from the sideline.

Rotation Grid Team Management
Rotation Grid Team Management

Features

Smart Rotation Solver — Two-tier solver runs in a Web Worker: MIP solver (HiGHS WASM) for bench assignment, then exhaustive search for position optimization. Respects constraints: no consecutive bench, minimum play time, goalie rest, and skill balance.

Coach Plan — Manually build rotation plans with a direct-entry matrix. Lock cells as hard (must satisfy) or soft (preferred, relaxable) constraints that the solver honors.

Playtime Optimization — Post-solve analysis detects when adjusting period divisions could improve play-time balance by 5%+ and suggests optimized configurations.

Live Game Mode — Period timers, audio substitution alerts, Now/Next focus view. Add or remove players mid-game with automatic re-solving. Regenerate with lock policy control and preview before applying.

Position-Aware Scheduling — 14 sub-positions across 4 position groups. Players auto-assign to formation slots based on their preferences.

Practice Planner — ~100 curated drills filtered by age bracket (U6–U18). Generate plans by category, duration, and player count. Swap drills and save favorites.

Team & Roster Management — Bulk-import players, track skill rankings, goalie eligibility, and preferred positions.

Bilingual — Full English and Spanish (es-MX) localization.

Data Portability — Selective export/import, schema migrations, and 8-second undo on all destructive actions. All data stays on your device.

Getting Started

Prerequisites

Install & Run

git clone https://github.com/matthewevans/benchassist.git
cd benchassist
pnpm install
pnpm dev

Open localhost:5173 in your browser.

All Commands
Command Description
pnpm dev Start Vite dev server with HMR
pnpm build Type-check + production build
pnpm test Run Vitest in watch mode
pnpm test:run Run tests once (CI mode)
pnpm lint Run ESLint
pnpm format Format all files with Prettier
pnpm typecheck Type-check without emitting

Tech Stack

Layer Technology
Framework React 19, TypeScript 5.9, Vite 7
Styling Tailwind CSS v4, shadcn/ui (New York), Radix UI
State useReducer + Immer (single context, discriminated-union actions)
Routing React Router v7
Solver Web Worker — MIP (HiGHS WASM) + exhaustive search
i18n i18next + react-i18next (en, es-MX)
Testing Vitest, Testing Library, fishery factories
PWA vite-plugin-pwa, auto-update, standalone display
Quality ESLint, Prettier, Husky + lint-staged

Under the Hood

All app state flows through a single useReducer in AppContext with Immer for immutable updates. The reducer uses a discriminated union of action types with an exhaustive never check. State auto-persists to localStorage with a 500ms debounce.

The rotation solver runs in a Web Worker using a two-tier approach: a MIP solver (HiGHS WASM) determines bench assignments, then exhaustive search refines position placement and scores by team-strength variance. After solving, an optimization check evaluates whether adjusting period divisions could improve play-time balance. Mid-game re-solves preserve existing rotations and support constraint relaxation fallback.

The undo system wraps the reducer with Immer's produceWithPatches, capturing inverse patches for destructive actions (delete, import, merge). A ref-based stack holds up to 30 undo entries.

Project Structure
src/
├── components/        # React components
│   ├── ui/            #   shadcn/ui primitives
│   ├── game/          #   Game-specific components
│   └── layout/        #   App shell & navigation
├── context/           # AppContext (single reducer + Immer)
├── data/              # Drill library (~100 entries)
├── hooks/             # Custom hooks (solver, timer, undo, etc.)
├── i18n/              # i18next config + locale JSON files
├── pages/             # Route-level page components (lazy-loaded)
├── storage/           # localStorage persistence & export/import
├── types/             # Domain model, solver messages, drill types
├── utils/             # Stats, validation, positions, optimization, etc.
└── workers/           # Web Worker + solver modules (MIP, exhaustive)

Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/my-change)
  3. Make your changes and add tests
  4. Run pnpm test:run && pnpm lint && pnpm build to verify
  5. Open a Pull Request

License

MIT — Matt Evans

About

BenchAssist is a free, open-source Progressive Web App that helps youth soccer coaches create balanced player rotations.

Resources

License

Stars

Watchers

Forks

Contributors

Languages