Skip to main content

Overview

The Hub frontend is built with Next.js 16 using the App Router, React 19, and TypeScript. The application follows a modular, role-based architecture with separate sections for players, venue owners, and administrators.

Technology Stack

Framework

Next.js 16.1.6 with React 19.2.3

Language

TypeScript 5.9.3

Styling

Tailwind CSS 4.2 + shadcn/ui

UI Components

Radix UI primitives

Directory Structure

src/
├── app/                    # Next.js App Router pages
│   ├── admin/             # Admin dashboard routes
│   ├── dashboard/         # Player dashboard routes
│   ├── owner/             # Venue owner routes
│   ├── match/             # Match management routes
│   ├── venue/             # Public venue pages
│   ├── onboarding/        # User onboarding flow
│   ├── api/               # API routes
│   │   └── proxy/         # Backend API proxy
│   ├── layout.tsx         # Root layout
│   ├── page.tsx           # Homepage
│   └── globals.css        # Global styles
├── components/            # React components
│   ├── ui/               # shadcn/ui components
│   ├── admin/            # Admin-specific components
│   ├── dashboard/        # Dashboard components
│   ├── owner/            # Owner-specific components
│   ├── match/            # Match-related components
│   ├── venue/            # Venue components
│   └── forms/            # Form components
├── lib/                  # Utility libraries
│   ├── auth0.ts         # Auth0 configuration
│   ├── api.ts           # API client utilities
│   └── utils.ts         # Helper functions
├── hooks/                # Custom React hooks
└── types/                # TypeScript type definitions

App Router Structure

Role-Based Routing

The application implements three primary user roles with dedicated route sections:
/dashboard              # Player home
/dashboard/profile      # User profile
/dashboard/settings     # Account settings
/dashboard/bookings     # Booking history
/match/search          # Find matches
/match/create          # Create new match
/match/my              # User's matches
/match/invitations     # Match invitations
/match/join/[token]    # Join match via link
/match/[id]            # Match details
/venue/[id]            # Venue details
/venue/[id]/resource/[resourceId]  # Court booking

Layout Hierarchy

import {ThemeProvider} from '@/components/theme-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="es" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="dark"
          themes={["dark", "light"]}
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}
Layout components are server components by default and handle authentication checks before rendering child routes.

Server vs Client Components

Server Components (Default)

Pages are server components that:
  • Fetch data directly from the backend API
  • Check authentication status
  • Pass data to client components
app/match/search/page.tsx
import {auth0} from "@/lib/auth0"
import {apiFetch} from "@/lib/api"
import {MatchSearchClient} from "@/components/match/match-search-client"

export default async function MatchSearchPage() {
  const session = await auth0.getSession()
  if (!session) redirect("/")
  
  const user = await apiFetch<UserProfile>("/api/me")
  return <MatchSearchClient user={user} />
}

Client Components

Interactive components marked with 'use client':
  • Handle user interactions
  • Manage local state
  • Use React hooks
  • Make client-side API calls
components/match/match-search-client.tsx
'use client'

import {useState} from 'react'
import {apiFetchClient} from '@/lib/api'

export function MatchSearchClient({ user }) {
  const [matches, setMatches] = useState([])
  // Interactive logic here...
}

API Integration

Proxy Route Pattern

The application uses an API proxy route to securely forward authenticated requests:
app/api/proxy/[...path]/route.ts
// Catches all routes: /api/proxy/*
// Forwards to backend with Auth0 token
// Located at: src/app/api/proxy/[...path]/route.ts
The proxy route automatically adds the Auth0 access token to all backend requests and handles CORS and authentication transparently.

API Client Functions

lib/api.ts
import {auth0} from '@/lib/auth0'

// Use in Server Components
export async function apiFetch<T>(
  endpoint: string,
  options?: RequestInit
): Promise<T> {
  const session = await auth0.getSession()
  const headers = new Headers(options?.headers)
  headers.set('Authorization', `Bearer ${session.tokenSet.accessToken}`)
  
  const response = await fetch(`${API_URL}${endpoint}`, {
    ...options,
    headers
  })
  
  if (!response.ok) throw new ApiError(response.status, message)
  return response.json()
}

Path Aliases

Configured in components.json and tsconfig.json:
{
  "@/components": "src/components",
  "@/lib": "src/lib",
  "@/hooks": "src/hooks",
  "@/ui": "src/components/ui",
  "@/utils": "src/lib/utils"
}
Usage:
import {Button} from '@/components/ui/button'
import {auth0} from '@/lib/auth0'
import {useToast} from '@/hooks/use-toast'

Environment Configuration

Required environment variables are defined in .env.local
# Auth0
AUTH0_DOMAIN=your-domain.auth0.com
AUTH0_CLIENT_ID=...
AUTH0_CLIENT_SECRET=...
AUTH0_BASE_URL=http://localhost:3000
AUTH0_SECRET=...
AUTH0_AUDIENCE=...

# Backend API
API_URL=http://localhost:8000

Static Assets

Public assets are served from the /public directory:
public/
├── icon.svg              # App icon (SVG)
├── icon-light-32x32.png  # Light theme favicon
├── icon-dark-32x32.png   # Dark theme favicon
└── apple-icon.png        # Apple touch icon
Reference in code:
<Image src="/icon.svg" alt="Logo" />

Next Steps

Component Organization

Learn about shadcn/ui components and custom component structure

Routing & Auth

Deep dive into routing patterns and authentication flow

Build docs developers (and LLMs) love