Skip to content

Bookings Hub

The bookings hub is the host-facing management surface at /p/[slug]/bookings: a kanban board and a month calendar over every booking for the podcast, plus the Guest Network invites that have not converted yet. It sits ON TOP of the core booking flow; it introduces no new state machine of its own.

How the layers fit

  • State transitions are RPC-atomic. Every mutating route loads context, calls a SECURITY DEFINER RPC for the DB transition, then runs side effects (Google Calendar, emails, automation events, AI research) outside the transaction, each in its own try/catch. Side-effect failures never fail the response. See the route header of src/api/routes/bookings/index.ts.
  • The hub reads two endpoints. GET /api/bookings?podcastId= (bookings with joined link/session/episode/attendees) and GET /api/guest-network/invitations/podcast?podcastId= (the Sent lane). Fetch helpers live in src/lib/components/bookings/booking-api.ts and follow the client-auth rule (browser Supabase session token, Bearer header, { ok, ... } results that never throw).
  • Board position is never stored. Lanes are derived from booking_status, the episode's publish state, and the clock. See Hub Architecture.

Code map

PiecePath
Page (tabs, filters, action dispatch)src/routes/(app)/p/[slug]/bookings/+page.svelte
Server load (role gating)src/routes/(app)/p/[slug]/bookings/+page.server.ts
Lane derivation + drag legality (pure)src/lib/components/bookings/board-utils.ts
Board / lanes / cardsBookingsBoard.svelte, BoardLane.svelte, BookingCard.svelte, InviteCard.svelte
Calendar viewBookingsCalendarView.svelte
Detail sheetBookingDetailSheet.svelte
Confirm/decline/cancel/delete dialogsBookingActionDialogs.svelte
Reschedule dialogRescheduleDialog.svelte
Status presentation (one palette everywhere)src/lib/components/bookings/status-meta.ts
Row shapes (BoardBooking, PodcastInvite, BookingAction)src/lib/components/bookings/types.ts
Bookings API (mutations + confirm side effects)src/api/routes/bookings/index.ts
Booking-sessions API (calendar recovery)src/api/routes/booking-sessions/index.ts

Role gating

The server load (+page.server.ts) resolves the podcast via requirePodcastAccess and computes hasAdminAccess = role in (owner, admin). Booking mutations (confirm, decline, cancel, reschedule, outcomes, delete) are admin-level; members get a read-only board and calendar. Independently, isMutable derives from the podcast lifecycle status: a paused or pending-deletion podcast renders the whole hub read-only. The API enforces the same boundary server-side (requirePodcastRoleByResolver('admin', ...) on every mutation; member on reads).

Pages in this section

Internal documentation - Not for public distribution