Back to .md Directory

Implementation Plan: Otterly Fun Swim School

Maps the full architecture, database schema, page routes, and phased implementation for a swim school booking platform built on Next.js and self-hosted Supabase.

May 2, 2026
0 downloads
0 views
ai rag
View source

What this file does

Maps the full architecture, database schema, page routes, and phased implementation for a swim school booking platform built on Next.js and self-hosted Supabase.

When to use it

  • Starting a new Next.js + Supabase project with a booking system
  • Planning a self-hosted Supabase deployment for a small business app
  • Designing a multi-client system (web, mobile, WhatsApp) sharing one API layer
  • Documenting a project's tech stack, schema, and deployment plan for a team

Assumes this stack

Next.js 14 (App Router)Supabase (self-hosted)PostgreSQLTailwind CSSTypeScriptDocker Compose

Implementation Plan: Otterly Fun Swim School

1. Tech Stack Overview

LayerTechnologyNotes
FrontendNext.js 14 (App Router)React-based, SSR/SSG, TypeScript
StylingTailwind CSSUtility-first, responsive design
Backend / APINext.js Route Handlers + SupabaseREST API via route handlers; Supabase for auth, DB, storage
DatabasePostgreSQL (via self-hosted Supabase)Relational, Row Level Security (RLS)
AuthenticationSupabase AuthEmail/password for trainers; magic-link or OAuth for parents (future)
EmailSupabase Edge Functions + Resend (or SMTP)Transactional emails for booking confirmations
HostingDocker Compose (self-hosted Supabase) + Vercel or VPS for Next.jsSupabase stack on VPS; Next.js deployed separately
Future: WhatsApp BotSupabase Edge Functions + WhatsApp Business APIStateless functions hitting shared DB
Future: Mobile AppReact Native (Expo)Shares API layer with web

2. Architecture Diagrams

2.1 High-Level System Architecture

graph TB
    subgraph "Clients"
        WEB["🌐 Next.js Web App<br/>(Parents & Trainers)"]
        MOBILE["šŸ“± Mobile App<br/>(React Native — Future)"]
        WHATSAPP["šŸ’¬ WhatsApp Bot<br/>(Future)"]
    end

    subgraph "Self-Hosted Supabase (Docker)"
        AUTH["šŸ” Supabase Auth"]
        API["šŸ”Œ PostgREST API"]
        DB[("🐘 PostgreSQL")]
        STORAGE["šŸ“ Supabase Storage"]
        EDGE["⚔ Edge Functions"]
        REALTIME["šŸ”„ Realtime"]
    end

    subgraph "External Services"
        EMAIL["šŸ“§ Email Service<br/>(Resend / SMTP)"]
        WA_API["WhatsApp<br/>Business API"]
    end

    WEB --> AUTH
    WEB --> API
    MOBILE --> AUTH
    MOBILE --> API
    WHATSAPP --> EDGE
    EDGE --> WA_API
    EDGE --> DB
    EDGE --> EMAIL
    API --> DB
    AUTH --> DB
    API --> STORAGE

2.2 Database Schema (ERD)

erDiagram
    TRAINERS {
        uuid id PK
        text email
        text full_name
        text phone
        timestamptz created_at
    }

    COURSES {
        uuid id PK
        text title
        text description
        text level "beginner | intermediate | advanced"
        text prerequisites
        text goals
        int price_cents
        text currency
        int max_participants
        text location
        boolean is_active
        timestamptz created_at
    }

    COURSE_SESSIONS {
        uuid id PK
        uuid course_id FK
        uuid trainer_id FK
        date start_date
        date end_date
        text day_of_week
        time start_time
        time end_time
        int spots_available
        text status "open | full | cancelled"
    }

    PARENTS {
        uuid id PK
        uuid auth_user_id FK "nullable — future auth"
        text email
        text full_name
        text phone
        timestamptz created_at
    }

    CHILDREN {
        uuid id PK
        uuid parent_id FK
        text full_name
        date date_of_birth
        text swimming_level
        text notes
    }

    BOOKINGS {
        uuid id PK
        uuid session_id FK
        uuid child_id FK
        uuid parent_id FK
        text status "pending | confirmed | cancelled"
        timestamptz booked_at
        text payment_status "unpaid | paid | refunded"
        text payment_reference
    }

    SESSION_LOGS {
        uuid id PK
        uuid session_id FK
        uuid trainer_id FK
        date log_date
        text notes
        text attendance "jsonb — child_id + present boolean"
        timestamptz created_at
    }

    TRAINERS ||--o{ COURSE_SESSIONS : leads
    COURSES ||--o{ COURSE_SESSIONS : "has many"
    PARENTS ||--o{ CHILDREN : "has many"
    PARENTS ||--o{ BOOKINGS : places
    CHILDREN ||--o{ BOOKINGS : "is booked in"
    COURSE_SESSIONS ||--o{ BOOKINGS : contains
    COURSE_SESSIONS ||--o{ SESSION_LOGS : "has logs"
    TRAINERS ||--o{ SESSION_LOGS : writes

2.3 Next.js Page / Route Structure

graph LR
    subgraph "Public Pages (Parents)"
        HOME["/"]
        ABOUT["/about"]
        COURSES_PAGE["/courses"]
        COURSE_DETAIL["/courses/[id]"]
        SCHEDULE["/schedule"]
        BOOKING["/courses/[id]/book"]
        PREP["/preparation"]
        CONFIRM["/booking/confirmation"]
    end

    subgraph "Trainer Dashboard (Auth Required)"
        DASH["/dashboard"]
        SESSIONS["/dashboard/sessions"]
        SESSION_LOG["/dashboard/sessions/[id]/log"]
        MANAGE_COURSES["/dashboard/courses"]
    end

    subgraph "API Routes (/api)"
        API_BOOK["/api/bookings"]
        API_COURSES["/api/courses"]
        API_SESSIONS["/api/sessions"]
        API_LOGS["/api/session-logs"]
        API_EMAIL["/api/send-email"]
        API_WA["/api/whatsapp (future)"]
    end

    HOME --> COURSES_PAGE --> COURSE_DETAIL --> BOOKING --> CONFIRM
    DASH --> SESSIONS --> SESSION_LOG
    DASH --> MANAGE_COURSES

2.4 Booking Flow (Sequence Diagram)

sequenceDiagram
    actor Parent
    participant Web as Next.js Frontend
    participant API as Next.js API Route
    participant DB as Supabase PostgreSQL
    participant Email as Email Service

    Parent->>Web: Browse courses & schedule
    Web->>API: GET /api/courses
    API->>DB: SELECT courses + sessions
    DB-->>API: Course data
    API-->>Web: JSON response
    Web-->>Parent: Display courses with availability

    Parent->>Web: Select course session & fill form
    Web->>API: POST /api/bookings {session_id, parent info, child info}
    API->>DB: BEGIN TRANSACTION
    API->>DB: Check spots_available > 0
    DB-->>API: OK
    API->>DB: INSERT parent (if new)
    API->>DB: INSERT child (if new)
    API->>DB: INSERT booking (status: confirmed)
    API->>DB: UPDATE session spots_available -= 1
    API->>DB: COMMIT
    DB-->>API: Booking created
    API->>Email: Send confirmation email with payment info
    Email-->>Parent: šŸ“§ Booking confirmation
    API-->>Web: 201 Created
    Web-->>Parent: Show confirmation page

2.5 Future: WhatsApp Bot Architecture

graph TB
    PARENT_WA["šŸ“± Parent on WhatsApp"] -->|message| WA_CLOUD["WhatsApp Cloud API"]
    WA_CLOUD -->|webhook| EDGE_FN["⚔ Supabase Edge Function<br/>/whatsapp-webhook"]
    EDGE_FN -->|query| DB[("🐘 PostgreSQL")]
    EDGE_FN -->|call| AI["šŸ¤– AI/LLM API<br/>(OpenAI / Anthropic)"]
    AI -->|response| EDGE_FN
    EDGE_FN -->|reply| WA_CLOUD
    WA_CLOUD -->|message| PARENT_WA

    EDGE_FN -->|"book course"| DB
    EDGE_FN -->|"send confirmation"| EMAIL["šŸ“§ Email Service"]

2.6 Future: Mobile App Architecture

graph TB
    subgraph "Mobile App (React Native / Expo)"
        SCREENS["Screens:<br/>Home, Courses, My Bookings,<br/>Payments, Profile"]
        PUSH["Push Notifications"]
    end

    subgraph "Shared Backend (Self-Hosted Supabase)"
        AUTH["Supabase Auth"]
        API["PostgREST API"]
        RT["Realtime Subscriptions"]
        DB[("PostgreSQL")]
    end

    SCREENS --> AUTH
    SCREENS --> API
    API --> DB
    RT --> SCREENS
    PUSH -.->|"via Edge Function"| SCREENS

3. Implementation Phases

Phase 1 — Foundation & Information Pages

Goal: Get the website live with static content and course information.

TaskDetails
Set up Next.js projectApp Router, TypeScript, Tailwind CSS, ESLint
Self-host SupabaseDocker Compose on VPS, configure PostgreSQL, Auth, Storage
Create database schemaCourses, course_sessions, trainers tables + seed data
Build public pagesHome, About/Location, Courses listing, Course detail, Preparation tips
Responsive designMobile-first layout with Tailwind
SEO & metadataOpen Graph tags, structured data for local business

Phase 2 — Booking System & Email

Goal: Parents can book courses and receive confirmation emails.

TaskDetails
Extend schemaAdd parents, children, bookings tables
Build booking flowBooking form on course page → API route → DB insert
Availability logicReal-time spot tracking, disable booking when full
Email integrationSet up Resend or SMTP; send confirmation with payment info
Booking confirmation pageThank-you page with summary and next steps
Input validationZod schemas for all form inputs

Phase 3 — Trainer Dashboard

Goal: Trainers can log in, view sessions, and keep training logs.

TaskDetails
Supabase Auth setupEmail/password login for trainers
Trainer dashboard layoutProtected routes with middleware
Session managementView upcoming & past sessions
Session logsCreate/edit logs per session (notes, attendance tracking)
Course managementCRUD operations for courses and sessions
Row Level SecurityRLS policies so trainers only access their own data

Phase 4 — AI-Powered WhatsApp Bot (Future)

Goal: Parents can interact with the swim school via WhatsApp.

TaskDetails
WhatsApp Business API setupRegister number, configure webhook
Supabase Edge FunctionWebhook handler for incoming messages
AI integrationConnect to LLM for natural language understanding
Intent handlingAnswer FAQs, check availability, make bookings
Conversation stateTrack multi-turn conversations in DB
Testing & moderationSafety guardrails, fallback to human support

Phase 5 — Mobile App (Future)

Goal: Parents manage bookings and interact with the school on mobile.

TaskDetails
Expo / React Native setupShared API layer with web
AuthenticationSupabase Auth with secure token storage
Core screensHome, Courses, My Bookings, Payments, Profile
Push notificationsCourse reminders, booking confirmations
Realtime updatesLive availability via Supabase Realtime

4. Project Structure

otterly-fun/
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ app/                        # Next.js App Router
│   │   ā”œā”€ā”€ (public)/               # Public route group
│   │   │   ā”œā”€ā”€ page.tsx            # Home
│   │   │   ā”œā”€ā”€ about/page.tsx
│   │   │   ā”œā”€ā”€ courses/
│   │   │   │   ā”œā”€ā”€ page.tsx        # Course listing
│   │   │   │   └── [id]/
│   │   │   │       ā”œā”€ā”€ page.tsx    # Course detail
│   │   │   │       └── book/page.tsx
│   │   │   ā”œā”€ā”€ schedule/page.tsx
│   │   │   ā”œā”€ā”€ preparation/page.tsx
│   │   │   └── booking/
│   │   │       └── confirmation/page.tsx
│   │   ā”œā”€ā”€ dashboard/              # Trainer (protected)
│   │   │   ā”œā”€ā”€ page.tsx
│   │   │   ā”œā”€ā”€ sessions/
│   │   │   │   ā”œā”€ā”€ page.tsx
│   │   │   │   └── [id]/log/page.tsx
│   │   │   └── courses/page.tsx
│   │   ā”œā”€ā”€ api/
│   │   │   ā”œā”€ā”€ bookings/route.ts
│   │   │   ā”œā”€ā”€ courses/route.ts
│   │   │   ā”œā”€ā”€ sessions/route.ts
│   │   │   ā”œā”€ā”€ session-logs/route.ts
│   │   │   └── send-email/route.ts
│   │   └── layout.tsx
│   ā”œā”€ā”€ components/
│   │   ā”œā”€ā”€ ui/                     # Reusable UI components
│   │   ā”œā”€ā”€ courses/                # Course-specific components
│   │   ā”œā”€ā”€ booking/                # Booking form components
│   │   └── dashboard/              # Trainer dashboard components
│   ā”œā”€ā”€ lib/
│   │   ā”œā”€ā”€ supabase/
│   │   │   ā”œā”€ā”€ client.ts           # Browser Supabase client
│   │   │   ā”œā”€ā”€ server.ts           # Server Supabase client
│   │   │   └── admin.ts            # Service-role client
│   │   ā”œā”€ā”€ email.ts                # Email sending utility
│   │   └── validations.ts          # Zod schemas
│   ā”œā”€ā”€ types/
│   │   └── database.ts             # Generated Supabase types
│   └── styles/
│       └── globals.css
ā”œā”€ā”€ supabase/
│   ā”œā”€ā”€ docker-compose.yml          # Self-hosted Supabase
│   ā”œā”€ā”€ migrations/                 # SQL migration files
│   │   ā”œā”€ā”€ 001_create_trainers.sql
│   │   ā”œā”€ā”€ 002_create_courses.sql
│   │   ā”œā”€ā”€ 003_create_sessions.sql
│   │   ā”œā”€ā”€ 004_create_parents_children.sql
│   │   ā”œā”€ā”€ 005_create_bookings.sql
│   │   └── 006_create_session_logs.sql
│   ā”œā”€ā”€ seed.sql                    # Initial seed data
│   └── config.toml
ā”œā”€ā”€ public/
│   └── images/
ā”œā”€ā”€ .env.local
ā”œā”€ā”€ next.config.ts
ā”œā”€ā”€ tailwind.config.ts
ā”œā”€ā”€ tsconfig.json
ā”œā”€ā”€ package.json
└── README.md

5. Key Technical Decisions

Why Self-Hosted Supabase?

  • Full control over data and infrastructure (important for handling children's data).
  • No vendor lock-in; can scale independently.
  • Cost-effective at scale compared to Supabase Cloud.
  • Edge Functions run locally, ideal for future WhatsApp bot webhook handling.

Why Next.js App Router?

  • Server components reduce client-side JavaScript for fast page loads.
  • Built-in API routes keep the backend co-located with the frontend.
  • SSR for SEO-critical pages (courses, schedules).
  • Easy to add authentication middleware for the trainer dashboard.

Future-Proof API Design

  • All data access goes through Supabase PostgREST or Next.js API routes.
  • The same API layer will serve the mobile app and WhatsApp bot — no duplication.
  • Row Level Security ensures consistent authorization regardless of client.

Database Design for Extensibility

  • parents table has an optional auth_user_id — no auth required now, ready for login later.
  • session_logs with JSON attendance enables flexible tracking without schema changes.
  • All tables use UUIDs as primary keys for easy cross-system integration.

6. Environment & Deployment

# .env.local (example)
NEXT_PUBLIC_SUPABASE_URL=https://supabase.yourdomain.com
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
RESEND_API_KEY=your-resend-api-key
ComponentDeployment
Supabase (Postgres, Auth, PostgREST, Edge Functions)Docker Compose on VPS (e.g., Hetzner, DigitalOcean)
Next.jsVercel (easiest) or self-hosted on same/different VPS
Domain & SSLCaddy or Nginx reverse proxy with Let's Encrypt
CI/CDGitHub Actions — lint, test, deploy on push to main

7. Non-Functional Requirements

  • Performance: Lighthouse score > 90 on all public pages.
  • Accessibility: WCAG 2.1 AA compliance.
  • Security: RLS on all tables, input validation with Zod, CSRF protection.
  • Privacy: GDPR-aware — minimal data collection, parental consent flows.
  • Monitoring: Basic uptime monitoring and error tracking (e.g., Sentry).
  • Backups: Automated PostgreSQL backups via cron on VPS.

What's inside

7 sections: tech stack table, 6 Mermaid diagrams, 5 implementation phases, project tree, key decisions, environment config, non-functional requirements

Change this for your project

  • Replace otterly-fun with your project name in the directory tree and README
  • Replace https://supabase.yourdomain.com with your actual Supabase URL
  • Replace your-anon-key, your-service-role-key, and your-resend-api-key with your own credentials
  • Replace josefinaalgotsson/otterly-fun with your repository URL

Where it goes

Save in docs/ or the repository root. Gives agents and new contributors a map of the codebase.

Worth borrowing

  • Separate parent-facing public routes from trainer dashboard routes using Next.js route groups
  • Use optional auth_user_id on the parents table to defer authentication until needed
  • Store attendance as JSONB in session_logs to avoid schema changes for flexible tracking

Related Documents