A volleyball session scheduling and player management platform with intelligent waitlist management.
Sideout is a Phoenix LiveView application designed for volleyball trainers and clubs to manage training sessions, player registrations, and capacity constraints. It solves the common problem of manually managing session capacity, waitlists, and fair player prioritization.
Trainers create sessions with flexible capacity rules, share a public link with players, and Sideout automatically assigns players to confirmed spots or waitlists based on availability and attendance history. The platform supports multiple clubs, co-trainer collaboration, and real-time updates across all connected users.
Built with Elixir and Phoenix LiveView, Sideout provides a seamless, real-time experience without requiring players to create accounts - they simply register via shareable links.
- Flexible Session Scheduling - Create sessions with customizable capacity constraints (max/min players, even numbers, divisible by N, per-field scaling)
- Intelligent Waitlist Management - Automatic priority-based ordering using attendance history and reliability metrics
- Public Registration Links - Share sessions via unique URLs; players register without authentication
- Multi-Club Organization - Support multiple clubs with membership management, co-trainers, and guest club invitations
- Real-Time Updates - LiveView with PubSub for instant capacity updates, waitlist promotions, and notifications
- Attendance Tracking - Check-in system with player statistics, no-show tracking, and attendance history
- Reusable Templates - Create session templates for recurring training schedules
- Priority Calculator - Smart algorithm that ranks waitlist players based on attendance patterns and reliability
- Backend: Elixir 1.14+, Phoenix Framework 1.7+, PostgreSQL 16
- Frontend: Phoenix LiveView, Tailwind CSS 3.4
- Real-time: Phoenix PubSub
- Authentication: Built-in Phoenix authentication (trainers only)
- Rate Limiting: Hammer (ETS-backed)
- QR Codes: eqrcode for shareable session links
- Elixir 1.14 or higher
- PostgreSQL 16 or higher
- Phoenix 1.7+
- Node.js (for asset compilation)
# Clone the repository
git clone <repository-url>
cd sideout
# Install dependencies
mix deps.get
# Setup database (creates, migrates, and seeds)
mix ecto.setup
# Install and setup frontend assets
mix assets.setup
# Start the Phoenix server
mix phx.serverVisit http://localhost:4000 to access the application.
Start PostgreSQL and pgAdmin using Docker Compose:
docker-compose up -dThis starts:
- PostgreSQL on port 5434 (username/password:
postgres) - pgAdmin on port 5050 (admin@sideout.com / admin)
After running mix ecto.setup, log in at http://localhost:4000/users/log_in
| Password | Role | |
|---|---|---|
| coach.mike@sideout.com | password1234 | Trainer |
| coach.sarah@sideout.com | password1234 | Trainer |
| coach.alex@sideout.com | password1234 | Trainer |
The seed file (priv/repo/seeds.exs) creates:
- 3 trainer accounts with different specializations
- 30 players with realistic attendance histories
- 5 session templates (Beginner, Intermediate, Advanced, Open, Beach)
- 12 scheduled sessions (past, current week, next week)
- 100+ registrations including confirmed players and waitlists
- Log in as a trainer
- Navigate to Sessions > New Session
- Choose a template or create from scratch
- Set date, time, fields, and capacity constraints
- Save and share the registration link with players
Each session has a unique shareable link (e.g., /signup/abc123) that players can access without logging in. The link includes:
- Session details (date, time, location, skill level)
- Current capacity status
- Registration form
- QR code for easy mobile access
When players register:
- Confirmed: Automatically assigned if space is available
- Waitlisted: Automatically waitlisted if session is full
- Priority Ranking: Waitlist ordered by attendance history
When a player cancels:
- Next waitlisted player is automatically promoted
- All affected players receive status updates
Define flexible capacity rules using a simple constraint syntax:
"max_18" # Maximum 18 players
"max_18,min_12" # Between 12-18 players
"max_18,min_12,even" # 12-18 players, must be even number
"per_field_9" # 9 players per field (scales with field count)
"divisible_by_6" # Must be divisible by 6 (e.g., for 3v3 teams)Use Cases:
max_20- Simple capacity limit for space constraintsmax_16,min_8- Ensure enough players for effective trainingeven- Pair up players for partner drillsdivisible_by_6- Create balanced 3v3 or 6v6 teamsper_field_9- Allocate 9 players per available court
Constraints are composable and evaluated together using the Specification Pattern.
sideout/
├── lib/
│ ├── sideout/ # Business logic contexts
│ │ ├── accounts/ # Trainer authentication
│ │ ├── clubs/ # Multi-club organization
│ │ ├── scheduling/ # Core domain logic
│ │ │ ├── constraints/ # Capacity constraint implementations
│ │ │ ├── player.ex # Player schema and stats
│ │ │ ├── session.ex # Training session schema
│ │ │ ├── registration.ex # Player registrations
│ │ │ └── priority_calculator.ex # Waitlist algorithm
│ │ └── scheduling.ex # Scheduling context API
│ │
│ └── sideout_web/ # Web interface
│ ├── live/
│ │ ├── session_signup_live.ex # Public registration
│ │ ├── cancellation_live.ex # Registration cancellation
│ │ └── trainer/ # Authenticated views
│ │ ├── dashboard_live.ex # Trainer overview
│ │ ├── session_live/ # Session management
│ │ ├── template_live/ # Template CRUD
│ │ ├── player_live/ # Player database
│ │ └── attendance_live.ex # Check-in system
│ └── components/ # Reusable UI components
│
├── priv/repo/
│ ├── migrations/ # Database migrations
│ └── seeds.exs # Sample data
│
├── assets/ # Frontend assets
│ ├── css/app.css # Custom styles
│ ├── js/app.js # JavaScript hooks
│ └── tailwind.config.js # Custom theme
│
└── test/ # Test suite
Sideout follows Phoenix's Context Pattern for clean separation of concerns:
- Scheduling - Core domain with sessions, players, registrations, and templates
- Accounts - Trainer authentication and user management
- Clubs - Multi-tenant organization with memberships and permissions
- Authorization - Access control for multi-club features
# Run all tests
mix test
# Run tests with coverage
mix test --cover
# Run specific test file
mix test test/sideout/scheduling_test.exsmix ecto.create # Create database
mix ecto.migrate # Run migrations
mix ecto.rollback # Rollback last migration
mix ecto.reset # Drop, create, migrate, and seed
mix run priv/repo/seeds.exs # Seed database only# Format code (auto-fix)
mix format
# Static analysis
mix credo
# Check formatting without changes
mix format --check-formatted# Build assets for development
mix assets.build
# Deploy assets (minified for production)
mix assets.deploySpecification Pattern
Capacity constraints are implemented using the Specification Pattern, allowing flexible composition of rules. Each constraint (max, min, even, etc.) implements the Specification protocol and can be combined with AND/OR logic.
Phoenix Context Pattern Business logic is organized into bounded contexts (Scheduling, Accounts, Clubs), providing clear API boundaries and separation of concerns.
PubSub Pattern Real-time updates are broadcast via Phoenix.PubSub. When capacity changes occur (new registration, cancellation, waitlist promotion), all connected LiveView clients receive instant updates.
Share Token System Public access without authentication is enabled through unique, time-limited share tokens. Each session generates a nanoid-based token for secure, shareable URLs.
Core Entities:
users- Trainer accounts with authenticationclubs- Organization/club recordsclub_memberships- User-club relationships with rolesplayers- Participant records with attendance statisticssession_templates- Reusable session configurationssessions- Training sessions with capacity constraintsregistrations- Player sign-ups with status (confirmed/waitlisted)session_cotrainers- Co-trainer assignments for shared managementsession_guest_clubs- Cross-club session invitations
The waitlist priority system ranks players using a weighted algorithm based on:
- Attendance Rate - Percentage of attended sessions vs. registered
- Recent Activity - More weight for recent session attendance
- Reliability - Lower priority for players with high no-show rates
- Registration Time - Tiebreaker for equal priority scores
This ensures fair distribution of session spots and rewards reliable, active players.
LiveView components subscribe to PubSub topics for:
- Capacity updates when registrations change
- Waitlist promotions when spots open
- Session cancellations and modifications
- Attendance check-in updates
All changes are broadcast instantly to connected trainers and players viewing the session.
- Implementation Plan: See plan.md for comprehensive development roadmap and progress tracking
- Code Documentation: Inline
@moduledocand@docattributes throughout the codebase - Migrations: Database schema history in
priv/repo/migrations/
This is a personal project, but contributions, issues, and feature requests are welcome.
MIT License
Copyright (c) 2026
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.