> For the complete documentation index, see [llms.txt](https://docs.lesarconsults.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lesarconsults.org/lesar-consults-website-technical-documentation-guide.md).

# Lesar Consults Website - Technical Documentation Guide

<p align="center"><a href="https://www.lesarconsults.org/"><strong>https://www.lesarconsults.org/</strong></a></p>

## 1. High-Level System Overview

The website code is hosted on GitHub in the repository below. Kindly email <mark style="color:$primary;"><billjmageni@gmail.com></mark> for access to this repository.

{% embed url="<https://github.com/Bjoseph23/lesar-consults-v2>" %}

### 1.1 What the Website Does

Lesar Consults is a professional consultancy firm website serving two primary purposes:

* **Public-facing platform**: Showcases services, team, projects, and resources for potential clients and partners
* **Admin dashboard**: Enables content management, lead tracking, and resource publishing

### 1.2 Core Features

| Feature           | Description                                                                    |
| ----------------- | ------------------------------------------------------------------------------ |
| Service Showcase  | Seven specialized consultancy services with detailed pages                     |
| Team Profiles     | Individual team member profiles with education, expertise, and project history |
| Project Portfolio | Featured and archived projects with metrics and achievements                   |
| Resource Library  | Gated and ungated downloadable resources (reports, tools, case studies)        |
| Contact Wizard    | Multi-step form with file uploads and email notifications                      |
| Admin Dashboard   | Lead management, resource editing, analytics overview                          |

### 1.3 User Flows

**Public Users:**

```markdown
Landing Page -> Browse Services/Team/Projects -> View Resources -> Submit Contact Form -> Receive Confirmation
```

**Admin Users:**

```
Login (/admin/login) -> Dashboard -> Manage Resources -> Review Leads -> Export Data
```

### 1.4 Architecture Diagram

```
+------------------+     +-------------------+     +------------------+
|   React Frontend |<--->| Supabase Backend  |<--->| Edge Functions   |
|   (Vite + TS)    |     | (PostgreSQL + RLS)|     | (Deno Runtime)   |
+------------------+     +-------------------+     +------------------+
        |                        |                        |
        v                        v                        v
   Tailwind CSS            Storage Buckets         Email Webhooks
   shadcn/ui               (leads-uploads,         (n8n/Railway)
   TipTap Editor           resources)
```

***

## 2. Technology Stack

### 2.1 Frontend Framework

| Technology | Version | Purpose                   |
| ---------- | ------- | ------------------------- |
| React      | 18.3.1  | UI component library      |
| TypeScript | 5.x     | Type-safe JavaScript      |
| Vite       | 5.x     | Build tool and dev server |

**Why React + Vite**: Fast development iteration with HMR, excellent TypeScript support, and optimised production builds.

### 2.2 Styling System

| Technology          | Purpose                     |
| ------------------- | --------------------------- |
| Tailwind CSS        | Utility-first styling       |
| tailwindcss-animate | Animation utilities         |
| shadcn/ui           | Pre-built component library |
| CSS Variables       | Design token system         |

**Design System Colors** (from `index.css`):

* `--navy: 227 57% 19%` (#162356) - Primary brand color
* `--cream: 37 65% 91%` (#FAEED4) - Secondary background
* `--dark-red: 0 50% 33%` (#8B1E1E) - Accent color

### 2.3 Routing

| Package          | Purpose             |
| ---------------- | ------------------- |
| react-router-dom | Client-side routing |

Routes are defined in `App.tsx` with protected routes using `ProtectedRoute` component.

### 2.4 Backend Services (Supabase)

| Service             | Usage                                           |
| ------------------- | ----------------------------------------------- |
| PostgreSQL Database | Leads, resources, profiles tables               |
| Supabase Auth       | Admin authentication                            |
| Storage             | File uploads (leads-uploads, resources buckets) |
| Edge Functions      | Signed URLs, email forwarding                   |

### 2.5 Email System

* Edge function `send-lead-email` forwards lead data to an external webhook (n8n on Railway)
* Non-blocking: Email failures don't prevent form submission

### 2.6 Deployment

| Platform       | Purpose                         |
| -------------- | ------------------------------- |
| Vercel         | Frontend hosting with analytics |
| Supabase Cloud | Backend hosting                 |

Configuration files: `vercel.json`, `public/_redirects`

***

## 3. Frontend Architecture

### 3.1 Folder Structure

```
src/
├── components/           # Reusable UI components
│   ├── admin/           # Admin-specific components
│   │   ├── AdminLayout.tsx
│   │   ├── ProtectedRoute.tsx
│   │   └── ResourceEditor/
│   ├── contact/         # Contact form components
│   │   ├── ContactWizard.tsx
│   │   ├── ProgressBar.tsx
│   │   ├── SuccessOptions.tsx
│   │   └── steps/       # Form step components
│   ├── resources/       # Resource library components
│   ├── team/            # Team page components
│   └── ui/              # shadcn/ui primitives
├── contexts/            # React contexts
│   └── AuthContext.tsx  # Authentication state
├── data/                # Static JSON data files
│   ├── about.json
│   ├── services.json
│   ├── projects.json
│   └── team.json
├── hooks/               # Custom React hooks
├── integrations/        # External service clients
│   └── supabase/
├── lib/                 # Utility functions
├── pages/               # Route components
│   ├── admin/           # Admin pages
│   └── ...              # Public pages
└── index.css            # Global styles + design system
```

### 3.2 Component Categories

**Page Components** (`src/pages/`):

* Full-page layouts with SEO metadata
* Compose multiple UI components
* Handle data fetching

**Layout Components**:

* `Navbar.tsx`: Responsive navigation with dropdown menus
* `Footer.tsx`: Site footer with newsletter signup
* `AdminLayout.tsx`: Admin sidebar and header

**Feature Components**:

* `ContactWizard.tsx`: Multi-step form orchestrator
* `ResourceEditor/index.tsx`: Rich text editor for resources

**UI Primitives** (`src/components/ui/`):

* Button, Card, Dialog, Input, etc.
* Built on Radix UI primitives
* Styled with Tailwind CSS

### 3.3 Styling Conventions

```css
/* Button variants (index.css) */
.btn-primary   /* Navy background, white text */
.btn-secondary /* Cream background, navy text */
.btn-accent    /* Dark red background */

/* Animation classes */
.animate-fade-in
.animate-slide-up
.cta-glisten   /* Shimmer effect on CTA buttons */

/* Card variants */
.card-elevated
.card-service
```

***

## 4. Page-by-Page Breakdown

### 4.1 Home Page (`/`)

**File**: `src/pages/Index.tsx`

**Components**:

* `Hero`: Full-width hero with CTA buttons
* `PartnersMarquee`: Animated logo carousel
* `ServicesStrip`: Service overview cards
* `Counters`: Animated impact statistics
* `FeaturedCase`: Highlighted project
* `ResourcesGrid`: Latest resources preview
* `WhyChoose`: Value propositions

**Data Source**: Static imports, Supabase for resources

### 4.2 About Page (`/about`)

**File**: `src/pages/About.tsx`

**Components**:

* `AboutHero`: Hero with headline from JSON
* `ValuesGrid`: Company values cards
* `ApproachTimeline`: Methodology steps
* `Image3DCarousel`: Work showcase
* `QASection`: Quality assurance points
* `PartnersMarquee`: Client logos

**Data Source**: `src/data/about.json`

### 4.3 Services Page (`/services`)

**File**: `src/pages/Services.tsx`

**Features**:

* Service grid with alternating layouts
* Icon mapping for each service type
* Links to individual service detail pages

**Data Source**: `src/data/services.json`

### 4.4 Team Page (`/team`)

**File**: `src/pages/Team.tsx`

**Components**:

* `TeamGrid`: Grid of team member cards
* `Timeline`: Company milestones
* Links to individual member profiles

**Data Source**: `src/data/team.json`, `src/data/milestones.json`

### 4.5 Projects Page (`/projects`)

**File**: `src/pages/Projects.tsx`

**Features**:

* Featured projects with full details
* Additional projects in card grid
* Metrics and achievements display
* Download button for certain projects

**Data Source**: `src/data/projects.json`

### 4.6 Resources Page (`/resources`)

**File**: `src/pages/Resources.tsx`

**Features**:

* Search and filter functionality
* Category scroller
* Type and year filters
* Resource cards with thumbnails

**Data Source**: Supabase `resources` table

### 4.7 Contact Page (`/contact`)

**File**: `src/pages/Contact.tsx`

Contains `ContactWizard` component (detailed in Section 5).

### 4.8 Admin Dashboard (`/admin/dashboard`)

**File**: `src/pages/admin/AdminDashboard.tsx`

**Features**:

* Statistics cards (resources, leads, downloads)
* Recent activity feed
* Quick action links

**Data Source**: Supabase queries on mount

### 4.9 Admin Leads (`/admin/leads`)

**File**: `src/pages/admin/AdminLeads.tsx`

**Features**:

* Lead table with search/filter
* Export to PDF, CSV, Excel
* Lead detail modal with file attachments
* Delete with cascade (files + records)

***

## 5. Contact Wizard (Detailed)

### 5.1 Wizard Structure

**Main Component**: `src/components/contact/ContactWizard.tsx`

```
ContactWizard
├── ProgressBar         # Visual step indicator
├── Step Components     # 6 individual form steps
├── Navigation          # Back/Next/Submit buttons
└── handleSubmit()      # Submission logic
```

**Step Components** (`src/components/contact/steps/`):

| Step | Component     | Purpose                                   |
| ---- | ------------- | ----------------------------------------- |
| 1    | StepOne.tsx   | Full name collection                      |
| 2    | StepTwo.tsx   | Organization and role                     |
| 3    | StepThree.tsx | Email and phone with country code         |
| 4    | StepFour.tsx  | Service selection and project description |
| 5    | StepFive.tsx  | Budget, timeframe, file uploads           |
| 6    | StepSix.tsx   | Privacy consent                           |

### 5.2 State and Validation

**Form State Shape** (TypeScript interface):

```typescript
interface FormData {
  fullName: string;
  organization: string;
  role: string;
  email: string;
  phone: string;
  countryCode: string;
  services: string[];
  otherService: string;
  description: string;
  budget: string;
  customBudget: string;
  timeframe: string;
  files: File[];
  consent: boolean;
}
```

**Validation Rules** (`isStepValid()` function):

| Step | Validation                                |
| ---- | ----------------------------------------- |
| 1    | `fullName.length > 0`                     |
| 2    | `organization.length > 0`                 |
| 3    | Valid email regex + phone length > 0      |
| 4    | At least one service + description filled |
| 5    | Timeframe selected                        |
| 6    | Consent checkbox checked                  |

**Submission Guards**:

* `isSubmitting` state prevents double-submission
* `isSubmitted` flag disables button after success

### 5.3 Submission Flow (End-to-End)

{% stepper %}
{% step %}

### Submit Request: create lead record

1. User clicks "Submit Request".
2. Generate client-side UUID for `lead_id`.
3. INSERT into `leads` table (lead record stored).
   {% endstep %}

{% step %}

### Upload files (for each file)

1. Request signed upload URL from Edge Function (`create-signed-url`).
2. PUT file directly to the signed upload URL.
3. Request signed download URL from Edge Function (`get-signed-download-url`).
4. INSERT a record into `leads_uploads` with `file_path` (and store signed URL as `file_url` if needed).
   {% endstep %}

{% step %}

### Post-submit notifications and UX

1. Invoke `send-lead-email` Edge Function (non-blocking).
2. Show success toast to user.
3. Navigate to `SuccessOptions` component.
   {% endstep %}
   {% endstepper %}

**Error Handling**:

* File upload failures are logged but don't block submission.
* Email failures are caught and logged silently.
* Database errors show a user-friendly toast.

***

## 6. Supabase Database Schema

### 6.1 Tables

**`leads`** - Contact form submissions

| Column         | Type        | Purpose                |
| -------------- | ----------- | ---------------------- |
| id             | UUID (PK)   | Unique identifier      |
| name           | TEXT        | Contact name           |
| email          | TEXT        | Email address          |
| organization   | TEXT        | Company/institution    |
| role           | TEXT        | Job title              |
| phone          | TEXT        | Phone number           |
| country\_code  | TEXT        | Phone country code     |
| interested\_in | TEXT        | Selected services      |
| other\_service | TEXT        | Custom service input   |
| message        | TEXT        | Project description    |
| budget         | TEXT        | Budget range           |
| timeframe      | TEXT        | Project timeline       |
| resource\_id   | UUID (FK)   | If download-gated lead |
| consent        | BOOLEAN     | Privacy consent        |
| created\_at    | TIMESTAMPTZ | Submission timestamp   |

**`leads_uploads`** - File attachments

| Column      | Type        | Purpose                   |
| ----------- | ----------- | ------------------------- |
| id          | UUID (PK)   | Unique identifier         |
| lead\_id    | UUID (FK)   | Parent lead reference     |
| file\_url   | TEXT        | Signed download URL       |
| file\_path  | TEXT        | Storage path for deletion |
| created\_at | TIMESTAMPTZ | Upload timestamp          |

**`resources`** - Content items

| Column         | Type        | Purpose                                  |
| -------------- | ----------- | ---------------------------------------- |
| id             | TEXT (PK)   | Slug-based identifier                    |
| slug           | TEXT        | URL-friendly identifier                  |
| title          | TEXT        | Resource title                           |
| summary        | TEXT        | Short description                        |
| content\_html  | TEXT        | Rich text content                        |
| categories     | TEXT\[]     | Category array                           |
| tags           | TEXT\[]     | Tag array                                |
| type           | TEXT        | article/case\_study/report/tool/download |
| thumbnail\_url | TEXT        | Cover image URL                          |
| file\_url      | TEXT        | Downloadable file URL                    |
| author         | TEXT        | Content author                           |
| published\_at  | TIMESTAMPTZ | Publication date                         |
| year           | INTEGER     | Publication year                         |
| featured       | BOOLEAN     | Featured flag                            |
| seo            | JSONB       | SEO metadata                             |

**`profiles`** - Admin users

| Column    | Type                        | Purpose              |
| --------- | --------------------------- | -------------------- |
| id        | UUID (PK, FK to auth.users) | User ID              |
| is\_admin | BOOLEAN                     | Admin privilege flag |

### 6.2 Relationships

```
leads ──1:N──> leads_uploads (lead_id FK)
profiles ──1:1──> auth.users (id FK)
leads ──N:1──> resources (resource_id FK, optional)
```

***

## 7. Supabase Storage and Signed URLs

### 7.1 Why Signed URLs

* **Security**: Prevent direct bucket access
* **Expiration**: URLs expire after 1 hour
* **Audit Trail**: Track who requested access
* **RLS Bypass**: Service role generates URLs server-side

### 7.2 Upload Flow

{% stepper %}
{% step %}

### Request signed upload URL

Client: POST /functions/v1/create-signed-url\
Body: { bucket: "leads-uploads", filename: "doc.pdf" }
{% endstep %}

{% step %}

### Edge Function generates upload URL

Edge Function: `createSignedUploadUrl(object_path, 3600)` (service role). Response: { upload\_url: "...", object\_path: "leads/12345\_doc.pdf" }
{% endstep %}

{% step %}

### Client uploads file

Client: PUT file directly to `upload_url`.\
Storage: File saved to bucket at returned `object_path`.
{% endstep %}
{% endstepper %}

### 7.3 Download Flow

{% stepper %}
{% step %}

### Request signed download URL

Client: POST /functions/v1/get-signed-download-url\
Body: { bucket: "leads-uploads", object\_path: "leads/12345\_doc.pdf" }
{% endstep %}

{% step %}

### Edge Function returns signed URL

Edge Function: `createSignedUrl(object_path, 3600)`\
Response: { signed\_download\_url: "..." }
{% endstep %}

{% step %}

### Client uses signed URL

Client: Open `signed_download_url` in new tab (or link for admin).
{% endstep %}
{% endstepper %}

### 7.4 File Path Generation

```javascript
const filenameSafe = `${Date.now()}_${file.name.replace(/\s+/g, "_")}`;
const object_path = `leads/${filenameSafe}`;
```

***

## 8. Supabase Edge Functions

### 8.1 `create-signed-url`

**Purpose**: Generate pre-signed upload URL\
**Location**: `supabase/functions/create-signed-url/index.ts`

**Input**:

```json
{ "bucket": "leads-uploads", "filename": "document.pdf" }
```

**Output**:

```json
{ "upload_url": "https://...", "object_path": "leads/1234_document.pdf" }
```

**Called**: During ContactWizard file upload step

### 8.2 `get-signed-download-url`

**Purpose**: Generate pre-signed download URL\
**Location**: `supabase/functions/get-signed-download-url/index.ts`

**Input**:

```json
{ "bucket": "leads-uploads", "object_path": "leads/1234_document.pdf" }
```

**Output**:

```json
{ "signed_download_url": "https://..." }
```

**Called**: During file upload (for record) and admin file viewing

### 8.3 `send-lead-email`

**Purpose**: Forward lead data to email notification service\
**Location**: `supabase/functions/send-lead-email/index.tsx`<br>

Flow:

{% stepper %}
{% step %}

1. Receive lead payload from client.
   {% endstep %}

{% step %}
2\. Forward to `RAILWAY_WEBHOOK_URL` (n8n) for processing.
{% endstep %}

{% step %}
3\. n8n sends email via configured provider.
{% endstep %}
{% endstepper %}

**Called**: After successful lead insert (non-blocking)

***

## 9. Email System

### 9.1 Architecture

```
ContactWizard -> Edge Function -> n8n Webhook -> Email Provider
                 (send-lead-email)
```

### 9.2 Template Mapping

The lead data object passed to the webhook contains:

* name, email, organization, role
* phone, country\_code
* interested\_in, other\_service
* message, budget, timeframe
* consent, id

n8n transforms this into email templates.

### 9.3 Error Handling

```javascript
try {
  const { error: fnError } = await supabase.functions.invoke("send-lead-email", {
    body: leadData,
  });
  if (fnError) {
    console.warn("Failed to invoke send-lead-email function:", fnError);
  }
} catch (err) {
  console.warn("Exception invoking send-lead-email function:", err);
}
// Form submission continues regardless of email status
```

### 9.4 Non-Blocking Design

Email failures are logged and follow best-effort, they don't prevent:

* Lead database record creation
* File uploads
* User success feedback

***

## 10. Admin Dashboard

### 10.1 Route Protection

**File**: `src/components/admin/ProtectedRoute.tsx`

Logic (stepper):

{% stepper %}
{% step %}

1. Check `AuthContext` loading state.
   {% endstep %}

{% step %}
2\. If loading > 200ms, show skeleton/loading UI.
{% endstep %}

{% step %}
3\. If not loading and no user, redirect to `/admin/login`.
{% endstep %}

{% step %}
4\. If `requireAdmin` prop is true and `!isAdmin`, show "Access Denied".
{% endstep %}

{% step %}
5\. Otherwise, render children.
{% endstep %}
{% endstepper %}

### 10.2 Authentication Flow

**File**: `src/contexts/AuthContext.tsx`

Flow (stepper):

{% stepper %}
{% step %}

1. On mount: `supabase.auth.getSession()` to initialize session.
   {% endstep %}

{% step %}
2\. Listen to `onAuthStateChange()` for auth updates.
{% endstep %}

{% step %}
3\. On user change: call `checkAdminStatus(userId)` which queries `profiles` table for `is_admin` flag.
{% endstep %}

{% step %}
4\. Expose context values: `user`, `session`, `isAdmin`, `signIn`, `signOut`.
{% endstep %}
{% endstepper %}

### 10.3 Lead Data Access

**AdminLeads.tsx**:

```javascript
const { data, error } = await supabase
  .from('leads')
  .select('*')
  .order('created_at', { ascending: false });
```

**File Attachment Access**:

```javascript
const { data: uploads } = await supabase
  .from('leads_uploads')
  .select('*')
  .eq('lead_id', leadId);
```

### 10.4 Export Features

| Format | Library |
| ------ | ------- |
| PDF    | jsPDF   |
| CSV    | XLSX    |
| Excel  | XLSX    |

***

## 11. JSON-Driven Content

### 11.1 Team Page

**File**: `src/data/team.json`

**Structure**:

```json
[
  {
    "slug": "eliud-khayo",
    "name": "Eliud Khayo",
    "title": "Co-Founder & CEO",
    "photo": "/Eliud-Khayo.jpg",
    "expertise": "Health Economics",
    "summary": "...",
    "education": [{ "degree": "...", "description": "..." }],
    "competencies": ["..."],
    "contributions": ["..."],
    "projects": [{ "title": "...", "year": "...", "slug": "...", "impact": "..." }],
    "linkedin": "...",
    "cv": "..."
  }
]
```

**Rendering**:

```tsx
import rawTeamData from "@/data/team.json";
const teamData = rawTeamData as TeamMember[];
// TeamGrid component maps over teamData
```

### 11.2 Services Page

**File**: `src/data/services.json`

**Structure**:

```json
{
  "services": [
    {
      "id": "health-systems-strengthening",
      "slug": "health-systems-strengthening",
      "title": "Health Systems Strengthening",
      "overview": "...",
      "description": "...",
      "challenge": ["..."],
      "approach": ["..."],
      "outcomes": ["..."],
      "miniServices": ["..."],
      "keywords": ["..."],
      "testimonials": [{ "text": "...", "author": "...", "organization": "..." }],
      "metrics": { "value": "28%", "description": "..." }
    }
  ]
}
```

### 11.3 Projects Page

**File**: `src/data/projects.json`

**Rendering**:

```tsx
import projectsData from "@/data/projects.json";
const featuredProjects = projectsData.projects.filter(p => p.featured);
```

### 11.4 About Page

**File**: `src/data/about.json`

Contains: hero content, whoWeAre paragraphs, values, approach steps, qualityAssurance, carousel slides.

### 11.5 Benefits of JSON-Driven Content

* No database dependency for static content
* Version controlled content changes
* Fast build-time imports
* Easy content updates without code changes
* Type-safe with TypeScript interfaces

***

## 12. Routing and Access Control

### 12.1 Route Definitions

**File**: `src/App.tsx`

```tsx
// Public Routes
} />
} />
} />
} />
} />
} />
} />
} />
} />
} />

// Admin Routes (Protected)
} />
} />
} />
} />
} />
} />

// Catch-all
} />
```

### 12.2 Authentication Checks

* `ProtectedRoute` wraps admin pages
* `requireAdmin` prop enforces `is_admin` flag check
* Unauthenticated users redirect to `/admin/login`
* Non-admin authenticated users see "Access Denied"

### 12.3 Redirect Behavior

| Condition                      | Behavior                     |
| ------------------------------ | ---------------------------- |
| Unauthenticated + admin route  | Redirect to /admin/login     |
| Authenticated + /admin/login   | Redirect to /admin/dashboard |
| Not admin + requireAdmin route | Show "Access Denied"         |

***

## 13. Environment Configuration

### 13.1 Frontend Variables (Vite)

| Variable                       | Purpose                 |
| ------------------------------ | ----------------------- |
| VITE\_SUPABASE\_URL            | Supabase project URL    |
| VITE\_SUPABASE\_ANON\_KEY      | Public anonymous key    |
| VITE\_SUPABASE\_FUNCTIONS\_URL | Edge functions base URL |

**Usage**: `import.meta.env.VITE_SUPABASE_URL`

### 13.2 Backend Secrets (Edge Functions)

| Variable                     | Purpose                        |
| ---------------------------- | ------------------------------ |
| SUPABASE\_URL                | Auto-injected Supabase URL     |
| SUPABASE\_SERVICE\_ROLE\_KEY | Auto-injected service role key |
| RAILWAY\_WEBHOOK\_URL        | n8n email webhook endpoint     |

**Usage**: `Deno.env.get("RAILWAY_WEBHOOK_URL")`

### 13.3 Client Configuration

**File**: `src/integrations/supabase/client.ts`

```typescript
const SUPABASE_URL = "https://tfqwiwdjjflzezzrxqbn.supabase.co";
const SUPABASE_PUBLISHABLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

export const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
  auth: {
    storage: localStorage,
    persistSession: true,
    autoRefreshToken: true,
  }
});
```

***

## 14. Error Handling and UX Safeguards

### 14.1 Client-Side Validation

* Per-step validation in ContactWizard
* Email regex validation
* Required field checks
* Inline error messages

### 14.2 Backend Error Handling

```typescript
// Supabase query pattern
const { data, error } = await supabase.from('leads').insert([leadData]);
if (error) {
  console.error("Supabase insert error:", error);
  toast.error("Failed to save request. Please try again.");
  return;
}
```

### 14.3 Graceful Failure Strategies

* File upload failures don't block form submission
* Email failures are logged but silent to user
* Network errors show retry-friendly messages
* 404 pages for missing resources/team members

### 14.4 User Feedback Mechanisms

| Scenario       | Feedback                    |
| -------------- | --------------------------- |
| Form success   | Toast + success modal       |
| Form error     | Toast with error message    |
| Loading states | Spinner animations          |
| Empty states   | "No results found" messages |

***

## 15. Maintenance and Extensibility

### 15.1 Adding New Pages

1. Create page component in `src/pages/`
2. Add route in `App.tsx`
3. Add to navigation in `Navbar.tsx` and `Footer.tsx`
4. Include SEO metadata with react-helmet-async

### 15.2 Adding New Services

1. Add entry to `src/data/services.json`
2. ServiceDetail page auto-renders based on slug
3. Update Navbar dropdown if needed

### 15.3 Extending Contact Wizard

1. Create new step component in `src/components/contact/steps/`
2. Add to step renderer in ContactWizard
3. Update `totalSteps` constant
4. Add validation logic to `isStepValid()`
5. Update `FormData` interface

### 15.4 Updating JSON Content

1. Edit relevant JSON file in `src/data/`
2. TypeScript will catch schema mismatches
3. Changes take effect on next build/deploy

### 15.5 Adding Database Tables

1. Create migration in `supabase/migrations/`
2. Update TypeScript types in `src/integrations/supabase/types.ts`
3. Add RLS policies for security
4. Test locally before deploying

### 15.6 Code Quality Guidelines

* Run `npm run lint` before committing
* Run `npm run typecheck` for type safety
* Follow existing component patterns
* Use shadcn/ui components when possible
* Keep components under 300 lines

***

## Appendix A: Key File Reference

<table><thead><tr><th width="153.66156005859375">Purpose</th><th width="362.99993896484375">File</th></tr></thead><tbody><tr><td>Route definitions and provider setup</td><td><code>src/App.tsx</code></td></tr><tr><td>Authentication state management</td><td><code>src/contexts/AuthContext.tsx</code></td></tr><tr><td>Supabase client configuration</td><td><code>src/integrations/supabase/client.ts</code></td></tr><tr><td>Database TypeScript types</td><td><code>src/integrations/supabase/types.ts</code></td></tr><tr><td>Multi-step form logic</td><td><code>src/components/contact/ContactWizard.tsx</code></td></tr><tr><td>Route protection HOC</td><td><code>src/components/admin/ProtectedRoute.tsx</code></td></tr><tr><td>Design system and global styles</td><td><code>src/index.css</code></td></tr><tr><td>Tailwind configuration</td><td><code>tailwind.config.ts</code></td></tr></tbody></table>
