Product Requirements Document

PRD: Academy & Coaches

Enable clubs to manage their coaching staff via an academy entity, offer bookable private/group lessons, sell lesson packages (simple, mixed, and recurring), and track commissions. The academy is an independent entity that can span multiple facilities.

Completev0.12.0Updated 2026-03-04

Enable clubs to manage their coaching staff via an academy entity, offer bookable private/group lessons, sell lesson packages (simple, mixed, and recurring), and track commissions. The academy is an independent entity that can span multiple facilities.

Problem Statement

For Clubs

Clubs currently manage coaches informally — spreadsheets, WhatsApp groups, manual scheduling. There's no way to track lesson revenue, coach utilization, or commissions. Coaches can't see their own schedules without asking the front desk.

For Players

Players wanting a lesson have to call or message the club, ask who's available, negotiate a time, and pay in person. There's no self-service way to browse coaches, see availability, and book a lesson online.

For Coaches

Coaches have no visibility into their schedule, earnings, or upcoming lessons. They rely on the club to relay booking information.

Epics & User Stories

Must Should Could|AdminCoachPlayer
E1

Academy & Coach Profiles (Admin)

Admin can create an academy entity that spans one or more facilities, then manage coach profiles within it. Coaches belong to the academy and inherit access to all linked facilities. Supports freelance (commission-only) and in-house (salary + commission) employment types. Commission can be a flat amount or percentage.

E1-1must

As admin, I want to Create an academy for my club — set name, description, and link one or more facilities

E1-2must

As admin, I want to Manage academy focus areas (e.g., 'Beginners', 'Competitive', 'Kids') to categorize lessons

E1-3must

As admin, I want to Create a coach profile within the academy — set title, bio, photo, specialties, lesson rate, employment type (freelance/in-house), salary (for in-house), and commission (flat amount or percentage)

E1-4must

As admin, I want to Edit or deactivate a coach profile without removing the team member

E1-5must

As admin, I want to See a list of all coaches with their status, lesson rate, employment type, and active lesson count

E1-6should

As admin, I want to Set each coach's available hours per facility (weekly schedule, similar to court schedules)

E2

Lesson Booking (Admin)

Admin can create, edit, and cancel lessons on behalf of players. Lessons reserve a court and a coach for a time slot. Supports private (1-on-1) and semi-private (small group) lessons. Lessons can have a focus area from the academy's defined list.

E2-1must

As admin, I want to Create a lesson by selecting a coach, court, date/time, duration, focus area, and player — price auto-calculated from coach's lesson rate

E2-2must

As admin, I want to System prevents double-booking a coach or court (conflict detection for both)

E2-3must

As admin, I want to Edit a lesson — change court, time, or player. Cancel with optional refund

E2-4should

As admin, I want to Create a semi-private lesson with multiple players (2-4), price split or per-player

E2-5must

As admin, I want to Lessons appear on the main calendar alongside regular bookings, visually distinct (different color/badge)

E3

Lesson Packages

Academy can offer lesson packages — session banks (buy N sessions at a discount) and monthly recurring packages with auto-generated lessons. Packages support mixed session types (e.g., 4 private + 2 semi-private) and can be academy-wide or coach-specific.

E3-1should

As admin, I want to Create a session-bank package — name, items (mixed lesson types with session count + duration), total price, validity period (e.g. '10 sessions for $450, valid 90 days')

E3-2should

As admin, I want to Create a recurring monthly package with a schedule — system auto-generates lessons each billing period

E3-3should

As admin, I want to Assign a purchased package to a player and track remaining sessions per item type

E3-4should

As admin, I want to When creating a lesson, optionally deduct from a player's active package instead of charging individually

E3-5could

As admin, I want to See all active packages, their utilization per session type, and approaching expirations

E4

Coach Calendar

A dedicated calendar view filtered to a single coach, showing their lessons, availability, and off-hours. Accessible by admins (and optionally by the coach themselves).

E4-1must

As admin, I want to View a coach's calendar showing all their lessons for a day/week, with open slots visible

E4-2should

As admin, I want to Create a lesson directly from the coach calendar by clicking an open slot

E4-3could

As coach, I want to Log in and see my own lesson schedule for the day/week (read-only or limited actions)

E5

Lesson Reports & Commissions

Track lesson revenue, coach utilization, and commission payouts. Reports differentiate in-house coaches (salary + commission) from freelance (commission only).

E5-1should

As admin, I want to See lesson revenue broken down by coach, time period, and lesson type (private vs semi-private)

E5-2should

As admin, I want to See each coach's total hours, lesson count, earned commission, employment type, and salary for a period

E5-3could

As admin, I want to Export commission report for payroll processing

E6

Public Booking — Book with Coaches (Future)

Player-facing page where users browse coaches, view profiles, and book a lesson directly. Follows the same frictionless flow as court booking (no login required until payment).

E6-1could

As player, I want to Browse a list of coaches on the facility page — see photo, name, title, specialties, and lesson rate

E6-2could

As player, I want to View a coach's profile with bio, available times, and a 'Book Lesson' button

E6-3could

As player, I want to Select a time slot from the coach's availability and complete the booking with payment (same frictionless flow as court booking)

Data Model

user_facility_rolesmodifyExisting — role 'pro' renamed to 'coach'. Team members with role 'coach' are coach candidates
iduser_idfacility_idrole (owner/admin/manager/coach/member/guest)
academiesnewAcademy entity — independent org that can span multiple facilities
id (UUID PK)name (text)description (text)logo_url (text)owner_facility_id (FK → facilities) — the facility that created the academycreated_at (timestamptz)
academy_facilitiesnewMany-to-many link between academies and facilities
id (UUID PK)academy_id (FK → academies)facility_id (FK → facilities)UNIQUE(academy_id, facility_id)created_at (timestamptz)
academy_focus_areasnewFocus areas for the academy (e.g., Beginners, Competitive, Kids)
id (UUID PK)academy_id (FK → academies)name (text)is_active (bool)sort_order (int)created_at (timestamptz)
coachesnewCoach profiles — belongs to an academy, inherits linked facilities. Supports freelance (commission-only) and in-house (salary + commission) employment types
id (UUID PK)user_id (FK → auth.users)academy_id (FK → academies, NOT NULL)employment_type (text) — 'freelance' | 'in_house'salary_amount (int, nullable) — base monthly salary for in-house coachestitle (text) — e.g. 'Head Coach', 'Padel Pro'bio (text)photo_url (text)specialties (jsonb) — e.g. ['padel', 'beginners', 'competitive']lesson_rate_amount (int) — default per-hour ratecommission_type (text) — 'flat' | 'percentage'commission_value (numeric) — flat amount in cents or percentageis_active (bool)created_at (timestamptz)
coach_schedulesnewWeekly availability for each coach per facility (same pattern as court_schedules)
id (UUID PK)coach_id (FK → coaches)facility_id (FK → facilities)day_of_week (int 0-6)start_time (time)end_time (time)is_available (bool)UNIQUE(coach_id, facility_id, day_of_week)
lessonsnewIndividual lesson bookings — links a coach, court, player(s), and time slot. Supports focus areas.
id (UUID PK)coach_id (FK → coaches)facility_id (FK → facilities)court_id (FK → courts)booking_id (FK → bookings, nullable) — links to a court bookinglesson_type (text) — 'private' | 'semi_private'max_players (int)description (text)focus_area_id (FK → academy_focus_areas, nullable)custom_focus_area (text, nullable)start_time (timestamptz)end_time (timestamptz)duration_minutes (int)price_amount (int)commission_amount (int) — calculated at creationstatus (text) — 'scheduled' | 'completed' | 'cancelled' | 'no_show'notes (text)player_package_id (FK → player_packages, nullable)created_at (timestamptz)
lesson_playersnewPlayers in a lesson (for semi-private lessons with multiple players)
id (UUID PK)lesson_id (FK → lessons)user_id (FK → auth.users, nullable)name (text)email (text)is_primary (bool)
lesson_packagesnewLesson packages — session banks or recurring monthly. Supports mixed session types via package_items.
id (UUID PK)academy_id (FK → academies)facility_id (FK → facilities, nullable) — null = academy-widecoach_id (FK → coaches, nullable) — null = any coachname (text) — e.g. '10-Session Pack'description (text)package_type (text) — 'session_bank' | 'recurring'price_amount (int) — total package pricebilling_period (text, nullable) — 'monthly' | 'quarterly' (for recurring)valid_days (int, nullable) — expiry window from purchase (for session_bank)schedule (jsonb, nullable) — recurring schedule definitionis_active (bool)created_at (timestamptz)
package_itemsnewItems within a package — supports mixed session types (e.g., 4 private + 2 semi-private)
id (UUID PK)package_id (FK → lesson_packages)lesson_type (text) — 'private' | 'semi_private'session_count (int)duration_minutes (int)
player_packagesnewPurchased packages — tracks a player's package status and Stripe references
id (UUID PK)package_id (FK → lesson_packages)user_id (FK → auth.users)facility_id (FK → facilities)purchased_at (timestamptz)expires_at (timestamptz, nullable)status (text) — 'active' | 'exhausted' | 'expired' | 'cancelled'stripe_subscription_id (text, nullable)stripe_payment_intent_id (text, nullable)created_at (timestamptz)
player_package_sessionsnewPer-item session tracking within a purchased package
id (UUID PK)player_package_id (FK → player_packages)package_item_id (FK → package_items)sessions_remaining (int)sessions_used (int)

API Endpoints

MethodPathDescription
GET/api/admin/academiesList academies for the facility
POST/api/admin/academiesCreate an academy
PATCH/api/admin/academies/[id]Update academy details
GET/api/admin/academies/[id]/facilitiesList linked facilities
POST/api/admin/academies/[id]/facilitiesLink a facility
DELETE/api/admin/academies/[id]/facilitiesUnlink a facility
GET/api/admin/academies/[id]/focus-areasList focus areas
POST/api/admin/academies/[id]/focus-areasCreate a focus area
PATCH/api/admin/academies/[id]/focus-areas/[faId]Update a focus area
DELETE/api/admin/academies/[id]/focus-areas/[faId]Delete a focus area
GET/api/admin/coachesList all coaches for a facility
POST/api/admin/coachesCreate a coach profile
GET/api/admin/coaches/[id]Get coach detail
PATCH/api/admin/coaches/[id]Update coach profile
GET/api/admin/coaches/[id]/scheduleGet coach weekly schedule
PUT/api/admin/coaches/[id]/scheduleUpdate coach weekly schedule
GET/api/admin/lessonsList lessons (filters: coach, date, status)
POST/api/admin/lessonsCreate a lesson (+ linked booking)
PATCH/api/admin/lessons/[id]Update / cancel a lesson
POST/api/admin/lessons/[id]/playersAdd player to lesson
DELETE/api/admin/lessons/[id]/playersRemove player from lesson
GET/api/admin/lesson-packagesList packages
POST/api/admin/lesson-packagesCreate a lesson package
PATCH/api/admin/lesson-packages/[id]Update a lesson package
GET/api/admin/player-packagesList player packages
POST/api/admin/player-packagesAssign/purchase a package
PATCH/api/admin/player-packages/[id]Update player package status
POST/api/admin/player-packages/[id]/generateGenerate lessons for recurring package
GET/api/admin/player-subscriptionsList a facility's academy subscriptions (replaces the removed /api/admin/academy-reports)
GET/api/v1/facilities/[slug]/coachesPublic: list active coaches
GET/api/v1/facilities/[slug]/coaches/[id]Public: coach detail + availability
GET/api/v1/facilities/[slug]/lesson-packagesPublic: list active packages

Acceptance Criteria

AC-E1

Academy & Coach Profiles

  • academies table exists, linked to facilities via academy_facilities (many-to-many)
  • Academy focus areas can be created, edited, and deactivated
  • coaches table exists with RLS policies — coaches belong to an academy, not a single facility
  • Admin can create a coach profile with employment type (freelance/in-house) and commission type (flat/percentage)
  • In-house coaches have a salary_amount field; freelance coaches do not
  • Coach list page shows name, title, rate, employment type, status, active lesson count
  • Coach profile can be deactivated without deleting the team member
  • Coach photo uploads to Supabase Storage 'facility-assets' bucket
AC-E2

Lesson Booking

  • lessons table exists with RLS, linked to coaches and bookings
  • Lessons support focus_area_id (from academy focus areas) and custom_focus_area
  • Creating a lesson also creates a booking on the same court/time (reserves the court)
  • Conflict detection checks both coach availability AND court availability
  • Lessons appear on the main calendar with a distinct visual style (badge or color)
  • Admin can cancel a lesson, which also cancels the linked booking
  • Price auto-calculates from coach lesson_rate_amount x duration
AC-E3

Lesson Packages

  • lesson_packages and player_packages tables exist with RLS
  • Packages support mixed session types via package_items table
  • Session-bank packages: admin can create with items (lesson_type + count + duration), price, validity
  • Recurring packages: monthly billing with auto-generated lessons from schedule
  • When a player has an active package, lesson creation can deduct from matching session type
  • Package status auto-transitions to 'exhausted' when all sessions_remaining = 0
  • Package status auto-transitions to 'expired' when expires_at is past
  • player_package_sessions tracks remaining sessions per item type independently
AC-E4

Coach Calendar

  • Coach calendar view shows a single coach's lessons for day/week
  • Open slots are visible based on coach_schedules
  • Clicking an open slot opens the lesson creation dialog pre-filled with the coach
  • Coach calendar is accessible from the Coaches list page
AC-E5

Reports & Commissions

  • Lessons report shows revenue, hours, and lesson count by coach
  • Commission calculation: flat (commission_value per lesson) or percentage (lesson price x commission_value / 100)
  • Report differentiates in-house (salary + commission) vs freelance (commission only) coaches
  • Report can be filtered by date range and coach

Dependencies (Already Built)

  • Role 'coach' in user_facility_roles (renamed from 'pro' in v0.12.0)
  • Court availability and booking conflict detection (built in v0.6.0)
  • Supabase Storage for coach photos (built in v0.5.0)
  • Admin calendar grid — lessons will overlay (built in v0.4.0)
  • Pricing engine — lessons use coach rates, not court pricing rules (independent)

Out of Scope

  • Player self-service lesson booking via public app (E6 — marked 'could', deferred to later version)
  • Coach self-service portal (coach logs in and manages own schedule)
  • Automated commission payouts via Stripe
  • Video/recording integration for lessons
  • Coach ratings or player reviews