Skip to content

App Structure Guide

This document describes the structure and conventions for the app application located in packages/app/src/. This guide explains what each folder contains conceptually and the naming patterns used throughout the codebase.

Folder Structure Overview

src/
├── app/                    # Next.js App Router pages and layouts
├── components/             # Reusable UI components
├── constants/              # Application constants and configuration
├── hooks/                  # Custom React hooks
├── lib/                    # Utility libraries and helpers
├── providers/              # React context providers
├── schemas/                # Zod validation schemas
├── sana/                   # WhatsApp bot integration module
├── services/               # Data access layer services
├── styles/                 # Global styles and CSS files
├── types/                  # TypeScript type definitions
└── utils/                  # Utility functions and helpers

Detailed Folder Descriptions

/app - Next.js App Router Pages

Purpose: Contains all pages, layouts, and route-specific components using Next.js 14 App Router.

Structure:

  • (auth)/ - Authentication pages (login, register) - route group
  • (authenticated)/ - Protected pages requiring authentication - route group
  • api/ - API routes and server-side functions
  • Route files: page.tsx, layout.tsx, not-found.tsx, error.tsx

Naming Conventions:

  • Folders: kebab-case for routes (e.g., projects/, user-profile/)
  • Files: camelCase for components (e.g., page.tsx, layout.tsx)
  • Route groups: parentheses (groupName)/
  • Dynamic routes: brackets [paramName]/
  • Catch-all routes: [...slug]/

Key Patterns:

  • Each route can have its own layout.tsx for nested layouts
  • Client components should be in separate -client.tsx files when needed
  • Every component should have a corresponding .test.tsx file

/components - Reusable UI Components

Purpose: Houses all reusable React components organized by domain and type.

Structure:

  • auth/ - Authentication-related components
  • layout/ - Application layout components (header, sidebar, footer)
  • projects/ - Project-specific components
  • ui/ - Base UI components (buttons, inputs, cards, etc.)
  • __mocks__/ - Mock components for testing

Naming Conventions:

  • Folders: PascalCase matching component name
  • Components: PascalCase (e.g., Button.tsx, CreateProjectForm.tsx)
  • Each component folder contains: ComponentName.tsx, ComponentName.test.tsx, index.ts
  • UI components additionally have: ComponentName.stories.tsx
  • CSS modules: ComponentName.module.css

Key Patterns:

  • Components in ui/ folder must have Storybook stories
  • Use ShadCN UI components via npx shadcn@latest add <component>
  • Replace data-slot with data-testid after adding ShadCN components
  • Import components from specific files, not barrel exports
  • Always include WAI-ARIA attributes for accessibility

/constants - Application Constants

Purpose: Centralized location for application-wide constants and configuration values.

Structure:

  • database.ts - Database-related constants
  • urls.ts - URL constants and routes
  • index.ts - Barrel export for constants

Naming Conventions:

  • Files: camelCase with descriptive names
  • Constants: UPPER_SNAKE_CASE for immutable values
  • Objects: camelCase for configuration objects

/hooks - Custom React Hooks

Purpose: Custom React hooks for reusable stateful logic.

Structure:

  • Hook files: useHookName.ts
  • Test files: useHookName.test.ts
  • Mock files: __mocks__/useHookName.ts
  • Barrel export: index.ts

Naming Conventions:

  • Files: camelCase starting with use prefix
  • Hook functions: camelCase with use prefix
  • Export the hook as default from individual files

Key Patterns:

  • Server state managed via React Query with Supabase
  • Use helper functions from lib/ for data fetching
  • Always include comprehensive tests
  • Create mock implementations in __mocks__/ folder

/lib - Utility Libraries and Helpers

Purpose: Contains utility functions, helper modules, and third-party integrations.

Structure:

  • supabase/ - Supabase client configurations
  • authHelper.ts - Authentication utility functions
  • databaseHelper.ts - Database query helpers
  • storageHelper.ts - File storage utilities
  • clientLogger.ts - Client-side logging utility
  • environment.ts - Environment variable helpers
  • __mocks__/ - Mock implementations for testing

Naming Conventions:

  • Files: camelCase with descriptive names ending in Helper.ts or purpose
  • Functions: camelCase with descriptive names
  • Always include corresponding .test.ts files

Key Patterns:

  • Always use helper functions instead of Supabase directly
  • Use clientLogger for all logging (error, warn, info, debug)
  • Environment variables should be accessed through environment.ts
  • Export types from types/ folder, not from implementation files

/providers - React Context Providers

Purpose: React context providers for global state management.

Structure:

  • Individual providers: ProviderName.tsx
  • Test files: ProviderName.test.tsx
  • Root provider: RootProviders.tsx
  • Mock providers: __mocks__/ProviderName.tsx

Naming Conventions:

  • Files: PascalCase ending with Provider.tsx
  • Context names: PascalCase ending with Context
  • Hook names: camelCase starting with use prefix

Key Patterns:

  • Use TypeScript interfaces for context values
  • Always provide default values for contexts
  • Include error boundaries where appropriate
  • Combine all providers in RootProviders.tsx

/schemas - Zod Validation Schemas

Purpose: Zod validation schemas for data validation and type inference.

Structure:

  • Schema files: feature.schema.ts
  • Test files: feature.schema.test.ts
  • One schema file per domain/feature

Naming Conventions:

  • Files: camelCase ending with .schema.ts
  • Schema variables: camelCase ending with Schema
  • Type exports: PascalCase matching the schema purpose

Key Patterns:

  • Export schema definitions, inferred types, and transform functions
  • Use z.infer<typeof SchemaName> for type inference
  • Create transform functions for API data conversion
  • Always include comprehensive tests

Database Integration:

  • When adding operations for a new database table:
  • Create the validation schema in this folder
  • Create corresponding types in /types folder if needed
  • Create a service in /services folder for data operations

/services - Data Access Layer

Purpose: Service layer for all data operations, API calls, and external integrations.

Structure:

  • Service files: featureService.ts
  • Test files: featureService.test.ts
  • Mock files: __mocks__/featureService.ts

Naming Conventions:

  • Files: camelCase ending with Service.ts
  • Functions: camelCase with descriptive action names
  • Follow the ServiceResponse pattern from SERVICE_PATTERNS.md

Key Patterns:

  • Use helper functions from lib/ instead of Supabase directly
  • Always return ServiceResponse<T> with { data, error } structure
  • Include comprehensive error handling and logging
  • Use clientLogger.error() for error logging
  • Never throw errors - always return them in response object

Database Integration:

  • For new database tables, create corresponding service files
  • Include CRUD operations as needed
  • Follow authentication patterns from SERVICE_PATTERNS.md
  • Use cleanup patterns for multi-step operations

/styles - Global Styles

Purpose: Global CSS files and style configurations.

Structure:

  • globals.css - Global styles and CSS variables

Key Patterns:

  • Use TailwindCSS for utility classes
  • Component-specific styles should use CSS modules
  • Design system colors defined in tailwind.config.js
  • Use cn() utility from @/lib/utils for conditional classes

/sana - WhatsApp Bot Integration

Purpose: WhatsApp bot functionality integrated within the app.

Structure:

  • src/ - Source code for the WhatsApp bot
  • index.ts - Main entry point
  • lib/ - Library utilities (logger, helpers)
  • services/ - WhatsApp-specific services
  • API Route: /app/api/sana/webhook/route.ts

Naming Conventions:

  • Same as the rest of the app
  • Services: camelCase (e.g., messageService.ts)
  • Functions: camelCase for functionality

Key Patterns:

  • Deployed as Vercel API Function
  • Direct imports from app services and types
  • Uses its own logger for WhatsApp-specific logging

/types - TypeScript Type Definitions

Purpose: TypeScript type definitions and interfaces.

Structure:

  • Feature types: feature.ts
  • Testing types: testing/ subfolder
  • Barrel export: index.ts

Naming Conventions:

  • Files: camelCase with descriptive names
  • Interfaces: PascalCase with descriptive names
  • Types: PascalCase with descriptive names
  • Enums: PascalCase with descriptive names

Key Patterns:

  • Always export types from this folder, not from implementation files
  • Group related types in single files
  • Use interfaces for object shapes
  • Use types for unions, primitives, and computed types
  • Include testing-specific types in testing/ subfolder

/utils - Utility Functions

Purpose: Pure utility functions and helper methods.

Structure:

  • Utility files: purpose.ts
  • Test files: purpose.test.ts
  • Mock files: __mocks__/purpose.ts

Naming Conventions:

  • Files: camelCase with descriptive names
  • Functions: camelCase with descriptive names

Key Patterns:

  • Functions should be pure when possible
  • Include comprehensive tests
  • Use TypeScript strictly - avoid any types
  • Export individual functions, not default exports

File Naming and Development Conventions

General File Naming

  • Components: PascalCase (e.g., CreateProjectForm.tsx)
  • Pages/Layouts: kebab-case for routes, camelCase for files
  • Hooks: camelCase starting with use
  • Services: camelCase ending with Service
  • Utilities: camelCase with descriptive names
  • Types: camelCase for files, PascalCase for definitions
  • Constants: camelCase for files, UPPER_SNAKE_CASE for values

Development Process

Every time you create or modify a file, follow this process:

  1. Main File:
  2. Run prettier: npx prettier --write path/to/file.ts
  3. Run lint: npx eslint path/to/file.ts --fix
  4. Run typecheck: yarn typecheck
  5. Run prettier again if changes were made
  6. Save file (auto-sorts imports)

  7. Tests:

  8. Create/update corresponding test file
  9. Run prettier on test file
  10. Run lint on test file: npx eslint path/to/file.test.ts --fix
  11. Run typecheck
  12. Run the specific test: yarn test path/to/file.test.ts
  13. Save file

  14. UI Components:

  15. Create Storybook story for UI components
  16. Run prettier, lint, typecheck on story file
  17. Save file

  18. E2E Tests:

  19. Consider if E2E tests needed for user flows
  20. Create/update E2E tests in e2e/tests/
  21. Run specific E2E test to verify

Import Conventions

  • Use @/ path alias for src directory
  • Import from specific component files, not barrel exports
  • Use @mocks/ for root-level mocks
  • Avoid dependency cycles between modules

Code Quality Standards

  • TypeScript: Never use any type - create proper interfaces
  • Testing: 80% statement/function coverage, 75% branch coverage
  • Accessibility: Include WAI-ARIA attributes
  • Logging: Use clientLogger for all app logging
  • Error Handling: Use ServiceResponse pattern in services
  • Linting: Run npx eslint file --fix to sort imports and fix formatting
  • Formatting: Do not manually add extra lines - let prettier/eslint handle it

Helper Usage Guidelines

Database Operations

Always use helper functions instead of Supabase directly:

// ✅ Correct - Use helper
import { databaseHelper } from '@/lib/databaseHelper';
const result = await databaseHelper.query(/* ... */);

// ❌ Wrong - Direct Supabase usage
import { supabaseClient } from '@/lib/database/client/browser';
const result = await supabaseClient.from('table').select();

Storage Operations

Use storage helper for file operations:

// ✅ Correct - Use helper
import { storageHelper } from '@/lib/storageHelper';
const url = await storageHelper.upload(file, path);

// ❌ Wrong - Direct Supabase storage
import { supabaseClient } from '@/lib/database/client/browser';
const result = await supabaseClient.storage.from('bucket').upload();

Authentication

Use auth helper for authentication operations:

// ✅ Correct - Use helper
import { authHelper } from '@/lib/authHelper';
const user = await authHelper.getCurrentUser();

// ❌ Wrong - Direct Supabase auth
import { supabaseClient } from '@/lib/database/client/browser';
const {
  data: { user },
} = await supabaseClient.auth.getUser();

This structure ensures consistency, maintainability, and follows established patterns throughout the codebase.