# Contact System Architecture

**Well-documented, modular, and easy to maintain**

This README explains the complete contact form submission system architecture, making it easy for any developer to understand and modify the code.

---

## 📐 Architecture Overview

### System Flow

```
User Fills Form
    ↓
Frontend Validation (client-side)
    ↓
API Route: /api/contact/submit
    ├─ Rate Limiting
    ├─ Validation (contact.validator.ts)
    ├─ Sanitization (contact.sanitizer.ts)
    ├─ Service Layer (contact.service.ts)
    └─ Dual-Save (dual-database.ts)
        ├─ Prisma contacts table (PRIMARY)
        └─ Supabase contact_submissions (ANALYTICS)
    ↓
Staff Dashboard: /staff/dashboard/contacts
    └─ Reads from Prisma contacts table
```

### Module Organization

```
lib/
├── types/
│   └── contact.types.ts           # Type definitions
├── validators/
│   └── contact.validator.ts       # Validation logic
├── sanitizers/
│   └── contact.sanitizer.ts       # Sanitization logic
├── services/
│   └── contact.service.ts         # Business logic layer
├── config/
│   └── contact.config.ts          # Configuration constants
└── dual-database.ts               # Database abstraction
```

---

## 🗂️ Module Descriptions

### 1. Type Definitions (`lib/types/contact.types.ts`)

**Purpose**: Centralized TypeScript types for type safety

**What's Defined**:

- `ContactFormData` - Raw form data from user
- `SanitizedContactData` - Validated and sanitized data
- `ContactSaveData` - Data for database save
- `PrismaContact` - Prisma database record structure
- `SupabaseContactSubmission` - Supabase database record structure
- `ContactStatus` - Enum for contact lifecycle
- `ContactValidationError` - Error structure
- And more...

**When to Modify**:

- Adding new fields to contact form
- Changing database schema
- Adding new status types

**Example**:

```typescript
import type { ContactFormData } from "@/lib/types/contact.types";

function handleSubmit(data: ContactFormData) {
  // TypeScript ensures all required fields are present
}
```

---

### 2. Validation Module (`lib/validators/contact.validator.ts`)

**Purpose**: All validation rules in one place

**Main Functions**:

- `validateContactForm(data)` - Validates all fields, returns errors
- `isContactDataValid(data)` - Quick true/false check
- `validateField(field, value)` - Single field validation
- `isValidTopic(topic)` - Topic validation
- `isValidLanguage(lang)` - Language validation

**Configuration**:

```typescript
export const CONTACT_VALIDATION_RULES = {
  NAME_MIN_LENGTH: 2,           // ← Change here
  NAME_MAX_LENGTH: 100,         // ← Change here
  MESSAGE_MIN_LENGTH: 10,       // ← Change here
  MESSAGE_MAX_LENGTH: 10000,    // ← Change here
  ALLOWED_TOPICS: [...],        // ← Add/remove topics here
  ALLOWED_LANGUAGES: ['ar', 'en'], // ← Add languages here
};
```

**When to Modify**:

- Changing field validation rules
- Adding/removing allowed topics
- Adjusting length limits

**Example Usage**:

```typescript
import { validateContactForm } from "@/lib/validators/contact.validator";

const errors = validateContactForm(formData);
if (errors.length > 0) {
  return Response.json({ errors }, { status: 400 });
}
```

---

### 3. Sanitization Module (`lib/sanitizers/contact.sanitizer.ts`)

**Purpose**: XSS prevention and data cleaning

**Main Functions**:

- `sanitizeContactData(data)` - Sanitizes all fields at once
- `sanitizeText(input)` - Removes HTML tags
- `sanitizeEmail(email)` - Normalizes email addresses
- `sanitizePhone(phone)` - Cleans phone numbers
- `sanitizeMessage(message)` - Sanitizes message content
- `containsXSSPatterns(text)` - Detects XSS attempts

**XSS Protection**:

```typescript
const safe = sanitizeText('<script>alert("xss")</script>John');
// Returns: 'John' (script tags stripped)
```

**When to Modify**:

- Adjusting sanitization rules
- Adding new field types
- Changing XSS detection patterns

**Example Usage**:

```typescript
import { sanitizeContactData } from "@/lib/sanitizers/contact.sanitizer";

const sanitized = sanitizeContactData(rawFormData);
// All fields now safe for database storage
```

---

### 4. Service Layer (`lib/services/contact.service.ts`)

**Purpose**: Business logic encapsulation

**Main Functions**:

- `submitContactForm(formData, metadata)` - Complete submission flow
- `processContactSubmission(formData, metadata)` - With detailed logging
- `validateContactFormData(formData)` - Validation only
- `getContactStatistics(timeframe)` - Analytics helper

**What It Does**:

```typescript
// Handles complete flow internally:
export async function submitContactForm(formData, metadata) {
  // 1. Validate
  const errors = validateContactForm(formData);
  if (errors.length > 0) return { success: false, error: "..." };

  // 2. Sanitize
  const sanitized = sanitizeContactData(formData);

  // 3. Save to both databases
  const contact = await saveContactToBoth(sanitized);

  // 4. Return result
  return { success: true, data: contact };
}
```

**When to Modify**:

- Adding business logic rules
- Implementing new features (auto-assignment, notifications)
- Changing submission workflow

**Example Usage**:

```typescript
import { submitContactForm } from "@/lib/services/contact.service";

const result = await submitContactForm(formData, {
  ipAddress: request.ip,
  userAgent: request.headers.get("user-agent"),
});

if (result.success) {
  return Response.json({ submissionId: result.data.id });
}
```

---

### 5. Configuration (`lib/config/contact.config.ts`)

**Purpose**: Centralized configuration constants

**What's Configured**:

- `CONTACT_RATE_LIMIT` - Rate limiting settings
- `CONTACT_VALIDATION` - Validation rules
- `CONTACT_FILE_UPLOAD` - File upload limits
- `CONTACT_RECAPTCHA` - Bot protection settings
- `CONTACT_STATUSES` - Status definitions
- `CONTACT_DATABASES` - Database configuration
- `CONTACT_SLA` - Response time SLAs
- `CONTACT_NOTIFICATIONS` - Notification settings

**Easy Modifications**:

```typescript
// Change rate limit
export const CONTACT_RATE_LIMIT = {
  MAX_REQUESTS: 5, // ← Change from 3 to 5
  WINDOW: "10 m", // ← Change from 5min to 10min
};

// Add new topic
export const CONTACT_VALIDATION = {
  topic: {
    allowedValues: [
      "sales",
      "support",
      "consulting", // ← Add new topic
      // ...
    ],
  },
};
```

**When to Modify**:

- Adjusting rate limits
- Changing validation rules
- Updating SLA times
- Configuring notifications

---

### 6. Dual-Database Helper (`lib/dual-database.ts`)

**Purpose**: Abstract dual-database complexity

**Main Function**:

```typescript
/**
 * Saves contact to BOTH databases:
 * 1. Prisma (staff dashboard) - Source of truth
 * 2. Supabase (analytics) - Backup & analytics
 */
export async function saveContactToBoth(contactData) {
  // 1. Save to Prisma FIRST
  const contact = await prisma.contact.create({...});

  // 2. Sync to Supabase SECOND
  await supabaseAdmin.from('contact_submissions').insert({...});

  return contact;
}
```

**Field Mapping**:
| Common Field | Prisma | Supabase |
|--------------|--------|----------|
| Language | `language` | `preferred_language` |
| Timestamps | `createdAt` (camelCase) | `created_at` (snake_case) |
| Status | `status` (enum) | `status` (text) |

**When to Modify**:

- Changing field mappings
- Adding new fields to save
- Adjusting error handling

---

## 🔄 Data Flow (Detailed)

### 1. User Submits Form

**Location**: `app/[lang]/contact/page.tsx`
**Component**: `components/contact/SimpleContactForm.tsx`

```typescript
// User fills form and submits
const formData = {
  name: "John Doe",
  email: "john@example.com",
  topic: "sales",
  message: "I need help...",
  preferredLanguage: "en",
  consent: true,
};

// Frontend submits to API
fetch("/api/contact/submit", {
  method: "POST",
  body: formData,
});
```

### 2. API Processes Request

**Location**: `app/api/contact/submit/route.ts`

```typescript
// Step 1: Extract data from FormData
const rawData = extractFromFormData(request);

// Step 2: Validate (using validator module)
const errors = validateContactForm(rawData);
if (errors.length > 0) {
  return Response.json({ error: "Validation failed", errors }, { status: 400 });
}

// Step 3: Sanitize (using sanitizer module)
const sanitized = sanitizeContactData(rawData);

// Step 4: Save to both databases (using dual-database helper)
const contact = await saveContactToBoth({
  ...sanitized,
  ipAddress: request.ip,
  userAgent: request.headers.get("user-agent"),
});

// Step 5: Return success
return Response.json({ success: true, submissionId: contact.id });
```

### 3. Dual-Save Executes

**Location**: `lib/dual-database.ts`

```typescript
// Prisma save (FIRST - source of truth)
const prismaContact = await prisma.contact.create({
  data: {
    id: contactId,
    name: "John Doe",
    email: "john@example.com",
    message: "I need help...",
    language: "en",
    status: "NEW",
  },
});

// Supabase save (SECOND - analytics)
await supabaseAdmin.from("contact_submissions").insert({
  id: contactId, // SAME ID as Prisma
  name: "John Doe",
  email: "john@example.com",
  message: "I need help...",
  preferred_language: "en",
  status: "new",
});
```

### 4. Staff Views Dashboard

**Location**: `app/staff/dashboard/contacts/page.tsx`
**API**: `app/api/staff/dashboard/contacts/route.ts`

```typescript
// Staff dashboard API reads from Prisma
const contacts = await prisma.contact.findMany({
  where: { status: "NEW" },
  orderBy: { createdAt: "desc" },
});

// Returns contacts to dashboard
// Dashboard displays all submissions ✅
```

---

## 🛠️ How to Modify the System

### Adding a New Field

**1. Update Types** (`lib/types/contact.types.ts`):

```typescript
export interface ContactFormData {
  // ... existing fields
  preferredContact Method: 'email' | 'phone'; // NEW
}
```

**2. Update Validation** (`lib/validators/contact.validator.ts`):

```typescript
export const CONTACT_VALIDATION_RULES = {
  // ... existing rules
  ALLOWED_CONTACT_METHODS: ["email", "phone"],
};

// Add validation in validateContactForm():
if (
  !data.preferredContactMethod ||
  !ALLOWED_CONTACT_METHODS.includes(data.preferredContactMethod)
) {
  errors.push({
    field: "preferredContactMethod",
    message: "Invalid contact method",
  });
}
```

**3. Update Sanitization** (`lib/sanitizers/contact.sanitizer.ts`):

```typescript
export function sanitizeContactData(
  data: ContactFormData,
): SanitizedContactData {
  return {
    // ... existing fields
    preferredContactMethod: sanitizeText(data.preferredContactMethod),
  };
}
```

**4. Update Dual-Save** (`lib/dual-database.ts`):

```typescript
export async function saveContactToBoth(contactData: {
  // ... existing fields
  preferredContactMethod?: string; // Add parameter
}) {
  // Save to Prisma
  const prismaContact = await prisma.contact.create({
    data: {
      // ... existing fields
      preferredContactMethod: contactData.preferredContactMethod,
    },
  });

  // Save to Supabase
  await supabaseAdmin.from("contact_submissions").insert({
    // ... existing fields
    preferred_contact_method: contactData.preferredContactMethod,
  });
}
```

**5. Update Database Schemas**:

- Add column to Prisma schema
- Run `npx prisma migrate dev`
- Add column to Supabase migration
- Apply migration

---

### Changing Validation Rules

**Easy - just modify config**:

```typescript
// File: lib/validators/contact.validator.ts
export const CONTACT_VALIDATION_RULES = {
  NAME_MIN_LENGTH: 3, // ← Changed from 2 to 3
  MESSAGE_MIN_LENGTH: 20, // ← Changed from 10 to 20

  ALLOWED_TOPICS: [
    "sales",
    "support",
    "consulting", // ← Added new topic
    // ...
  ],
};
```

That's it! No need to change validation logic.

---

### Changing Rate Limits

**Easy - modify configuration**:

```typescript
// File: lib/config/contact.config.ts
export const CONTACT_RATE_LIMIT = {
  MAX_REQUESTS: 5, // ← Changed from 3 to 5
  WINDOW: "10 m", // ← Changed from 5min to 10min
};
```

---

### Adding a New Status

**1. Update Type** (`lib/types/contact.types.ts`):

```typescript
export enum ContactStatus {
  // ... existing statuses
  ESCALATED = "ESCALATED", // NEW
}
```

**2. Update Config** (`lib/config/contact.config.ts`):

```typescript
export const CONTACT_STATUSES = {
  // ... existing statuses
  ESCALATED: {
    label: "Escalated",
    color: "red",
    description: "Escalated to senior staff",
    allowedNextStatuses: ["IN_PROGRESS", "RESPONDED"],
  },
};
```

**3. Update Database**:

- Add to Prisma enum
- Run migration

---

## 📚 Module Usage Examples

### Example 1: Submit Contact Form (Simple)

```typescript
import { submitContactForm } from "@/lib/services/contact.service";

const result = await submitContactForm(formData, {
  ipAddress: request.ip,
  userAgent: request.headers.get("user-agent"),
});

if (result.success) {
  return Response.json({ submissionId: result.data.id });
} else {
  return Response.json({ error: result.error }, { status: 400 });
}
```

### Example 2: Validate Before Submitting

```typescript
import { validateContactForm } from "@/lib/validators/contact.validator";
import { sanitizeContactData } from "@/lib/sanitizers/contact.sanitizer";

// 1. Validate
const errors = validateContactForm(formData);
if (errors.length > 0) {
  return { error: "Validation failed", errors };
}

// 2. Sanitize
const sanitized = sanitizeContactData(formData);

// 3. Save
const contact = await saveContactToBoth(sanitized);

return { success: true, contact };
```

### Example 3: Custom Validation

```typescript
import { validateField } from "@/lib/validators/contact.validator";

// Validate single field (for real-time feedback)
const emailError = validateField("email", userInput.email, formData);

if (emailError) {
  showError(emailError.message);
}
```

### Example 4: Check if Data is Valid

```typescript
import { isContactDataValid } from "@/lib/validators/contact.validator";

if (!isContactDataValid(formData)) {
  alert("Please fill all required fields");
  return;
}

// Proceed with submission
```

---

## 🗄️ Database Schema

### Prisma Database (Source of Truth)

**Table**: `contacts`
**Location**: `localhost:5432/mawidi_production`
**Used By**: Staff dashboard

```prisma
model Contact {
  id            String        @id @default(cuid())
  name          String
  email         String
  phone         String?
  company       String?
  message       String        @db.Text
  language      String        @default("ar")
  status        ContactStatus @default(NEW)
  assignedTo    String?
  respondedBy   String?
  respondedAt   DateTime?
  internalNotes String?       @db.Text
  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt

  @@index([email])
  @@index([status])
  @@index([createdAt])
  @@map("contacts")
}
```

### Supabase Database (Analytics)

**Table**: `contact_submissions`
**Location**: `localhost:54322/postgres`
**Used By**: Analytics, file attachments

```sql
CREATE TABLE contact_submissions (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  email TEXT NOT NULL,
  phone TEXT,
  company TEXT,
  topic TEXT NOT NULL,
  message TEXT NOT NULL,
  preferred_language TEXT DEFAULT 'en',
  consent BOOLEAN DEFAULT false,
  attachments JSONB DEFAULT '[]',
  ip_address INET,
  user_agent TEXT,
  status TEXT DEFAULT 'new',
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);
```

---

## 🔐 Security Features

### Implemented Security Measures

| Feature                      | Module                 | Description                             |
| ---------------------------- | ---------------------- | --------------------------------------- |
| **Input Validation**         | `contact.validator.ts` | Validates all fields before processing  |
| **XSS Prevention**           | `contact.sanitizer.ts` | Strips HTML, sanitizes all inputs       |
| **Rate Limiting**            | `contact.config.ts`    | 3 submissions per 5 minutes per IP      |
| **Email Validation**         | `contact.validator.ts` | Uses validator.js for proper validation |
| **SQL Injection Prevention** | Prisma/Supabase        | Parameterized queries                   |
| **CSRF Protection**          | API route              | Token validation (if configured)        |
| **Staff Authentication**     | `staff-auth.ts`        | Required for dashboard access           |
| **Audit Logging**            | `logger.ts`            | All actions logged                      |

### Security Best Practices

```typescript
// ✅ GOOD: Use the service layer (handles everything)
const result = await submitContactForm(formData, metadata);

// ❌ BAD: Skip validation
await saveContactToBoth(rawFormData); // Unsafe!

// ✅ GOOD: Validate then sanitize then save
const errors = validateContactForm(data);
if (errors.length === 0) {
  const sanitized = sanitizeContactData(data);
  await saveContactToBoth(sanitized);
}
```

---

## 🧪 Testing the System

### Unit Testing

```typescript
// Test validation
import { validateContactForm } from "@/lib/validators/contact.validator";

const errors = validateContactForm({ name: "A" }); // Too short
expect(errors).toHaveLength(1);
expect(errors[0].field).toBe("name");
```

### Integration Testing

```typescript
// Test complete flow
const result = await submitContactForm(validFormData, metadata);
expect(result.success).toBe(true);

// Verify in both databases
const prismaContact = await prisma.contact.findUnique({
  where: { id: result.data.id },
});
expect(prismaContact).toBeDefined();

const supabaseResult = await supabase
  .from("contact_submissions")
  .select("*")
  .eq("id", result.data.id);
expect(supabaseResult.data.length).toBe(1);
```

### E2E Testing

See test files:

- `tests/staff-contacts-dashboard.spec.ts` - Dual-save verification
- `tests/contact-to-dashboard-flow.spec.ts` - Complete flow
- `tests/contact-form-integration.spec.ts` - Integration tests

---

## 📊 Configuration Reference

### Quick Settings Change Guide

| Want to change...     | Edit this file...      | Section...                 |
| --------------------- | ---------------------- | -------------------------- |
| Field min/max lengths | `contact.validator.ts` | `CONTACT_VALIDATION_RULES` |
| Allowed topics        | `contact.validator.ts` | `ALLOWED_TOPICS`           |
| Rate limit            | `contact.config.ts`    | `CONTACT_RATE_LIMIT`       |
| File upload limits    | `contact.config.ts`    | `CONTACT_FILE_UPLOAD`      |
| Status definitions    | `contact.config.ts`    | `CONTACT_STATUSES`         |
| SLA times             | `contact.config.ts`    | `CONTACT_SLA`              |
| Notification settings | `contact.config.ts`    | `CONTACT_NOTIFICATIONS`    |

---

## 🚀 Quick Start for Developers

### Using the Modules

```typescript
// In your API route
import { submitContactForm } from "@/lib/services/contact.service";

export async function POST(request: Request) {
  const formData = await request.formData();

  // Service layer handles everything
  const result = await submitContactForm(formData, {
    ipAddress: request.headers.get("x-forwarded-for"),
    userAgent: request.headers.get("user-agent"),
  });

  if (result.success) {
    return Response.json({
      success: true,
      submissionId: result.data.id,
    });
  } else {
    return Response.json(
      {
        error: result.error,
      },
      { status: 400 },
    );
  }
}
```

### Using Individual Modules

```typescript
// Validation only
import { validateContactForm } from "@/lib/validators/contact.validator";
const errors = validateContactForm(data);

// Sanitization only
import { sanitizeContactData } from "@/lib/sanitizers/contact.sanitizer";
const clean = sanitizeContactData(data);

// Database save only
const contact = await saveContactToBoth(data);

// Complete flow
import { submitContactForm } from "@/lib/services/contact.service";
const result = await submitContactForm(data, metadata);
```

---

## 📁 File Organization

```
lib/
├── types/
│   └── contact.types.ts                # Type definitions ✅
│
├── validators/
│   └── contact.validator.ts            # Validation logic ✅
│
├── sanitizers/
│   └── contact.sanitizer.ts            # Sanitization logic ✅
│
├── services/
│   └── contact.service.ts              # Business logic ✅
│
├── config/
│   └── contact.config.ts               # Configuration ✅
│
└── dual-database.ts                    # DB abstraction ✅

app/
├── [lang]/contact/
│   └── page.tsx                        # Contact form page
│
├── api/contact/submit/
│   └── route.ts                        # Submission API
│
└── staff/dashboard/contacts/
    └── page.tsx                        # Staff dashboard

tests/
├── staff-contacts-dashboard.spec.ts   # Dual-save tests ✅
├── contact-to-dashboard-flow.spec.ts  # E2E flow tests ✅
├── contact-form-integration.spec.ts   # Integration tests ✅
└── contact-form-regression.spec.ts    # Regression tests ✅
```

---

## 🎓 Best Practices

### DO ✅

- Use the service layer (`contact.service.ts`) for business logic
- Configure behavior in config files, not in code
- Validate before sanitizing
- Sanitize before saving
- Use type definitions for type safety
- Log important operations
- Test after making changes

### DON'T ❌

- Skip validation or sanitization
- Hard-code configuration values
- Save raw user input directly to database
- Bypass the service layer for complex operations
- Modify database directly (use helpers)
- Skip testing after changes

---

## 📖 Further Reading

- **Dual-Database Architecture**: `DUAL_DATABASE_ARCHITECTURE.md`
- **Implementation Guide**: `CONTACT_TO_DASHBOARD_IMPLEMENTATION.md`
- **Bug Fix Report**: `CONTACT_FORM_BUG_FIX_REPORT.md`
- **Test Documentation**: See test files for comprehensive examples

---

## ✅ Summary

This refactored architecture provides:

- ✅ **Clear separation of concerns** - Each module has one responsibility
- ✅ **Easy to modify** - Change config files, not code
- ✅ **Type-safe** - TypeScript ensures correctness
- ✅ **Well-documented** - JSDoc comments on all functions
- ✅ **Testable** - Each module can be tested independently
- ✅ **Maintainable** - Future developers can easily understand and modify

**All tests still passing**: ✅ No functionality changed, just better organized!

---

**Created**: 2025-11-16
**Purpose**: Make contact system easy to understand and modify
**Status**: ✅ Complete
