Skip to content

Frontend Structure

Project Organization

Our Next.js application follows an organization that separates concerns based on company or client content and technical functionality. Here's a detailed breakdown of our directory structure:

app/
├── (auth)/               
│   ├── login/          # User authentication flow
│   └── signup/         # New user registration with email verification
├── company/            # Company-specific features and management
│   ├── setup/          # Initial company setup wizard (4 steps)
│   └── dashboard/      # Main company interface
│   │   ├── bookings/   # Appointment management system
│   │   ├── users/      # Customer database and interactions
│   │   └── members/    # Team member management
├── dashboard/          # Client-facing dashboard
│   ├── bookings/       # Client's appointment overview and booking
│   └── settings/       # Account preferences

Each route is carefully structured to handle specific business needs. For example, our company setup wizard guides new businesses through a 4-step process:

  1. Company Name
  2. CEOs Email
  3. CEOs Name
  4. Account Password

Component Organization

components/
├── ui/                         # Core UI building blocks generated by shadcn
│   ├── Button.tsx              # Custom button with different variants
│   ├── Input.tsx               # Form inputs
│   └── Dialog.tsx              # Accessible Dialog system
└── dashboard/                  # Feature-specific components
    ├── scheduler/              # Custom calendar implementation using syncfusion
    └── AppointmentDisplay.tsx  # Appointment card with time and company details

Each component is designed for reusability and follows our design system. For example, our Scheduler and OverviewScheduler components handle:

  • Automatic time display based on the user's booked appointments
  • Week selection
  • Dialogs for appointment management

Company Logic and Data Management

contexts/
├── CompanyContext.tsx    # Manages company-wide state and operations
└── DashboardContext.tsx  # Handles client dashboard state

lib/
├── graphql/              # Data fetching and mutations
│   ├── queries/          # Organized by domain (users, bookings, etc.)
│   └── mutations/        # Write operations with optimistic updates
└── utils.ts              # General utility functions

Core Components

1. Routes and Layouts

The application uses Next.js 13's App Router conventions:

  • Layout files (layout.tsx) provide shared UI for route segments
  • Page files (page.tsx) define the unique content for each route
  • Loading states (loading.tsx) and error handling (error.tsx) are co-located with their routes
  • Route groups (in parentheses) are used to organize routes without affecting the URL structure

2. Component Architecture

Components are divided into two main categories:

  • Server Components: Default in the App Router, they handle data fetching and static content rendering
  • Client Components: Marked with 'use client', they manage interactive features and client-side state

3. State Management

Managing state was one of the biggest challenges initially. We started with a large amount of prop drilling, but quickly realized the restricted scalability associated with it. React Context became our go-to for app-wide data. Apollo Client handles our API communication, and while its learning curve was steep (especially the caching!), however it has managed to streamline data fetching and mutation processes.

4. Type System

Types are centralized in the types directory:

  • Shared interfaces and types
  • Discriminated unions for user roles
  • Form state types
  • GraphQL response types

5. Context System

Contexts provide global state management:

  • Company context for company-specific data
  • Dashboard context for client dashboard data
  • Authentication state management
  • Loading state coordination

6. GraphQL Architecture

We all learned GraphQL as we built this. Initial implementations revealed common challenges - overfetched data through excessive nesting, inadequate error handling, and suboptimal query structures. Through iterative improvements, we've established a robust GraphQL architecture in lib/graphql that has served as a major improvement of our application. We organized queries by domain features, implemented comprehensive error handling, and developed an efficient caching strategy that significantly improved performance. While we continue to fine-tune our real-time update system, the current architecture provides a solid foundation for our application's data management needs.