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:
- Company Name
- CEOs Email
- CEOs Name
- 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.