# RFC - Coach Availability + Date-First Discovery v1 ## Status - Implemented ## Summary - Coaches define weekly availability windows (day-of-week + time ranges). - Duration is fixed at 90 minutes for now. - Clubs provide real court availability via ATC public-read. - Player picks a 90m slot from a calendar view; coaches and clubs are filtered to match. - Public reservation discovery now asks for a zone first, using the club's public location label when ATC exposes one and falling back to the club name otherwise. - `/coaches` now forces the first account differentiation between `Coach` and `Club`. - `/coaches/settings` now lands on a coach dashboard first, with a day-strip agenda for upcoming classes, while the scheduling/commercial tools live in `Mi Perfil`. - `/coaches/settings` now also lets coaches assign existing Ballbox players into concrete booked slots and manage them from a dedicated `Alumnos` section with WhatsApp / Telegram contact fields. - `/clubs/coaches` now lands on a club dashboard first, with the existing invite, roster, and quick venue settings moved into a separate configuration section. - `/clubs/venues/[venueId]` now gives each venue a dedicated setup screen with opening/closing time, weekly coach hours, external-coach availability, reservation settings including a club-only discount, courts, and venue-specific coach assignment. - Authenticated coach/club screens now use a shared navbar with section tabs and a profile-initial menu for sign out instead of top-corner text links or standalone explainer hero panels. This RFC is intentionally verbose because it will be used as the only context by the implementers. It reflects the current demo phase: coaches and offers are still seeded, ATC only provides court availability (not coach availability), and the product should avoid inventing court availability while still showing meaningful coach intent. ## Why This Change - Current model binds an offer to a single court, which is too rigid for coach availability. - We need a demo-friendly flow that shows real court availability without pretending ATC provides coach availability. Additional context from the current demo: - Coaches are fictional/demo entries, seeded into the DB. - The platform is not yet a booking engine; it is a discovery + request flow. - ATC is only used for public-read availability of courts, not for coaches. - We still want the calendar UX to feel real, even when availability is partial or missing. ## Goals - Separate coach availability from court availability. - Show meaningful real availability in date-first UI. - Keep auth simple: per-account email + password access for both coaches and clubs. - Preserve the demo-oriented data seeding approach. User-facing intent: - A coach wants to fill more class hours and is flexible across clubs. - A club wants more courts booked. - A player wants to pick a time that works for them, not necessarily a specific coach up front. ## Non-goals - Real coach identity or secure auth. - Player availability matchmaking. - Booking writes or payment flow. - Capacity-aware bookings (e.g., 2 of 4 seats) in v1. ## Product Model ### Coach availability - Weekly availability ranges per coach. - Example: Monday 10:00-13:00, Wednesday 18:00-21:00. - Duration is fixed at 90 minutes. Notes: - Availability is a coach intent, not a guaranteed class schedule. - A coach can declare availability across multiple clubs. - We should allow overlapping ranges; the UI should prevent accidental duplicates when possible. - Ranges are weekly recurring; no date exceptions are planned in v1. ### Clubs - Coach can select eligible clubs. - Default selection is all clubs (opt-out to a manual list). Notes: - A coach is not tied to a single court. The club provides the set of courts. - If a coach selects no clubs, treat it as "none" (the calendar should show that no courts match). ### ATC integration - ATC provides court availability by club. - We do not bind to a specific court. Notes: - We should not invent availability. If ATC returns empty, we show the coach range with a "no courts available" indicator. - ATC data may be stale or empty for far-future dates; we should be resilient in the UI. ### Player flow - Player picks a 90m slot from a calendar view. - Modal shows coaches that are available at that slot. - After coach selection, show clubs where that coach can teach and ATC has availability. Notes: - Players can optionally filter by coach or club before selecting a time. - If the player does not filter, the calendar should show all available ranges. - The selected slot is always a fixed 90 minutes. - We do not capture player "availability ranges" in v1. ## Authentication - New account hub at `/coaches` (demo-grade, not secure). - `Sign in` is the default entry state; `Sign up` is the fallback for people without an account yet. - `Sign up` first asks whether the account is `Coach` or `Club`. - Coach credential: coach email + password. - Club credential: club contact email + password. - Cookies: - `ballbox_coach_session` - `ballbox_club_session` Notes: - This is intentionally minimal. It is not meant for production. - Coaches and clubs can now create their own accounts from the same hub. - The bound email must exist in DB for login; otherwise access should fail with a clear error. ## Data Model Changes ### New - `CoachAvailability` - `id` - `coachId` - `dayOfWeek` (0-6) - `startTime` (HH:MM) - `endTime` (HH:MM) - `timezone` - `createdAt`, `updatedAt` - `CoachClub` (many-to-many) - `coachId` - `clubId` - `CoachSalesPackage` - `id` - `coachId` - `title` - `price` - `durationMinutes` - `maxStudents` - `format` - `active` - `sortOrder` - `createdAt`, `updatedAt` - `CoachStudentBooking` - `coachId` - `playerId` - `venueId` - `startsAt` - `endsAt` - `status` - `note` - `createdAt`, `updatedAt` - `Player` - optional `whatsappPhone` - optional `telegramHandle` ### Modified - `Club` - optional `contactEmail` plus `passwordHash` - `Coach` - primary affiliation is derived from `clubId` (`independent` vs `club`) - now also stores `passwordHash` and `mercadoPagoHandle` - now may also be assigned to one `Venue` inside that club - now also keeps reusable sales-package rows for its commercial setup - `CoachInvite` - tokenized invite link from club to coach - invite may be generic (no email pre-bound) - `Venue` - now also stores one opening time and one closing time - now also stores weekly coach-hour ranges - now also stores whether external coaches are allowed in that venue - now also stores a reservation price for coach classes - now also stores a discount over that reservation price for coaches assigned to the club - now also stores a late-cancellation window in hours - `VenueCourt` - belongs to one venue - stores whether it is covered - stores whether coaches are allowed on that court - `ClassOffer` - Keep as Ballbox-local offer data. - Remove `atcCourtId` coupling in lookup logic. - Duration fixed to 90m (enforced in UI/validation). Notes: - We still need offers for the discovery surface, but they should not be treated as schedules. - If we keep `atcSportclubId` on offers, it should be treated as a default club, not a restriction. ## UI / UX ### Account hub - Route: `/coaches` - Default step: `Sign in` - If the user does not have an account, they move to `Sign up` - During `Sign up`, the first decision is `Coach` vs `Club` Interaction details: - Sign-in asks only for email + password. - Sign-up asks only for name, email, password, and repeated password. - If a coach arrives through an invite link, Ballbox preloads the invite and links that coach to the club after sign-in or sign-up. ### Coach settings - Route: `/coaches/settings` (protected) - Current UI opens on a dashboard with per-club metrics plus a strip of days with booked classes, then shows affiliation, assigned sede when present, editable weekly availability inside allowed venue/club hours, reusable sales packages, and a Mercado Pago placeholder field inside `Mi Perfil`. A third `Alumnos` section now handles the student roster, contacts, and manually occupied slots. - Coaches can now also load existing Ballbox players into concrete booked slots, as long as those slots fall inside the coach's active availability and the selected venue's enabled coach hours. - Coaches can now also save WhatsApp / Telegram contact data on tracked players from that student roster. Interaction details: - If the coach belongs to a club and already has a sede, Ballbox shows the venue fee and cancellation window that get added on top of the coach's own package price. - If that venue has a club-coach discount, Ballbox shows the discounted fee for club coaches while leaving the external-coach fee unchanged. - If the coach belongs to a club but still has no sede, Ballbox blocks availability editing until the club assigns one. - If the coach is independent, Ballbox lets that coach choose only clubs with active venues that allow external coaches, and clamps availability to those enabled venue hours. - Coaches can define reusable packages with title, price, duration, max students, and individual/group mode. - Coaches can also save a Mercado Pago alias/email placeholder for future payout wiring. ### Club roster - Route: `/clubs/coaches` (protected) - Club first sees a dashboard with venue-level and coach-level metrics plus estimated reservation-fee contribution, and can then move into a separate configuration section for invite links, venue creation, and roster management. Navigation between those sections now lives in the shared top navbar. ### Venue detail - Route: `/clubs/venues/[venueId]` (protected) - Club configures one venue at a time, including opening/closing time, coach hours, whether external coaches are allowed, reservation pricing/cancellation rules, court-by-court coach enablement, and which coaches belong to that venue. ### Player discovery - Zone-first calendar (read-only) - Filters: zone first, then coach / club (optional) - Selecting a 90m slot opens a modal: - Step 1: list coaches available for that slot - Step 2: list clubs where that coach can teach and ATC has availability Interaction details: - The calendar should be scannable (like GCal week view), but can be a simplified grid. - When filtered by coach or club, the calendar only shows ranges compatible with that filter. - Selecting a slot is the primary action; filters are secondary. ### Empty availability state - If coach is available but ATC has no matching courts: - Show the slot with a clear indicator ("Coach disponible, pero sin canchas en ese horario"). Edge-case note: - A coach can have a long availability range where only a subset of the 90m slots have courts available. The UI should show the unavailable segments explicitly so the player understands why some slots are not selectable. ## API Changes ### New routes - `POST /api/account/session` - `POST /api/coaches/accounts` - `GET /api/coaches/invites/[token]` - `POST /api/coaches/session` (login) - `POST /api/coaches/logout` - `GET /api/coaches/me` - `PUT /api/coaches/me` - `GET /api/coaches/availability` - `PUT /api/coaches/availability` - `GET /api/coaches/clubs` - `PUT /api/coaches/clubs` - `POST /api/clubs/accounts` - `POST /api/clubs/invites` - `POST /api/clubs/session` - `POST /api/clubs/logout` - `GET /api/clubs/me` - `GET /api/clubs/coaches` - `PATCH /api/clubs/coaches/[coachId]` - `POST /api/clubs/venues` - `GET /api/clubs/venues/[venueId]` - `PATCH /api/clubs/venues/[venueId]` ### Updated routes - `GET /api/classes/offers` - `GET /api/classes/offers/[offerId]` - ATC availability should be queried by club(s), not by specific court. ## Matching Logic - For each coach: - Expand weekly availability to concrete 90m slots in the visible range. - Filter to clubs selected by coach (or all). - Intersect with ATC availability per club. - Render the slot if either: - there is at least one ATC club/court available, or - we want to show "coach available, no courts" fallback. Notes: - The visible range should be bounded (e.g., 2 weeks) to avoid expensive expansion. - ATC results should be cached; we can reuse existing caching helpers. - The matching algorithm must be deterministic so repeated renders show the same slots. ## Migration / Seed - Seed coaches as today (demo data). - Seed clubs with demo contact emails for the club-side account flow. - Seed availability ranges per coach. - Seed coach-club relationships (default all). Notes: - Seed data should include at least one coach with multiple ranges and multiple clubs. - Seed data should include at least one coach with availability but no clubs selected (to exercise the empty state). ## Risks / Gotchas - ATC availability can be empty for far-future dates. - Weekly availability requires timezone handling; store timezone per coach. - Calendar UI is complex; implement drag/resize carefully. Additional risks: - Availability ranges can overlap; we should normalize or deduplicate on save. - Slot expansion can be expensive if the visible range is too large. - The demo auth gate should not be accidentally reused for real production auth. ## Future Work - Player availability input and group capacity matching. - Multi-student capacity per slot and re-matching logic. - Real coach auth and per-coach passwords. More detailed future work notes (do not implement in v1): - Player availability ranges (similar UI to coach), enabling matchmaking when capacity is more than one. - Partial capacity reservations (e.g., 2 of 4 seats) where another player can fill remaining capacity. - Tentative holds with expirations when multiple players express interest. - When matching shifts from single-player to multi-player, prioritize matches that maximize fill rate.