# Prisma → Convex Full Migration Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Migrate all dashboard API routes from direct Prisma DB calls to Convex functions using the strangler fig pattern, one domain at a time, with zero breaking changes.

**Architecture:** Each API route gets a `getDataSource('domain')` branch — when the env var `DS_<DOMAIN>=convex` is set, the route calls the corresponding Convex function instead of Prisma. External services (Stripe, Google Calendar, Gemini, Twilio, ElevenLabs) stay in API routes untouched. Dashboard components continue using `fetch()` — only the route internals change.

**Tech Stack:** Next.js 15 App Router, Convex 1.32, `lib/data-source.ts` toggle, `lib/convex-client.ts` server-side HTTP client (`convexQuery` / `convexMutation`), TypeScript strict mode.

---

## How the Pattern Works

Every route follows this exact shape — copy it, don't invent:

```typescript
// Inside any API route handler:
import { getDataSource } from "@/lib/data-source";
import { convexQuery, convexMutation } from "@/lib/convex-client";
import { api } from "@/convex/_generated/api";

if (getDataSource("bookings") === "convex") {
  const data = await convexQuery(api.bookings.queries.getBookings, { userId });
  return NextResponse.json(data);
}
// ... existing Prisma code unchanged below
```

**Activation:** Add `DS_BOOKINGS=convex` to `.env.local` to flip that domain. Default is `prisma`.

**Never remove Prisma code** until the Convex path is production-validated.

---

## Pre-Flight Checks (run before any task group)

```bash
cd /Users/Asim/Desktop/mawidi_codex/mawidi-site

# 1. Verify Convex is connected
npx convex run auth/queries:getCurrentUser
# Expected: null (no session) or user object — NOT an error

# 2. Verify convex-client exports exist
grep -n "convexQuery\|convexMutation" lib/convex-client.ts
# Expected: function definitions found

# 3. Verify data-source module
grep -n "getDataSource" lib/data-source.ts
# Expected: exported function found

# 4. TypeScript baseline
npx tsc --noEmit 2>&1 | grep "error TS" | wc -l
# Note this number — don't introduce new errors
```

---

## GROUP A: Bookings + Appointments

**Files to modify:**

- `app/api/bookings/route.ts`
- `app/api/bookings/[id]/status/route.ts`
- `app/api/bookings/[id]/notes/route.ts`
- `app/api/bookings/[id]/reschedule/route.ts`
- `app/api/bookings/[id]/mark-paid/route.ts`
- `app/api/bookings/voice/route.ts`
- `app/api/appointments/by-phone/route.ts`

**Convex functions available:**

- `api.bookings.queries.getBookings(userId, status?, limit?)`
- `api.bookings.queries.getById(id)`
- `api.bookings.queries.getByCustomer(userId, customerPhone)`
- `api.bookings.queries.getUpcoming(userId, limit?)`
- `api.bookings.mutations.create(...)`
- `api.bookings.mutations.updateStatus(id, status)`
- `api.bookings.mutations.addNote(bookingId, authorId, authorName, content)`
- `api.bookings.mutations.cancel(id, reason?)`
- `api.bookings.mutations.reschedule(id, appointmentDate, appointmentTime)`

---

### Task A1: Wire GET /api/bookings

**Files:** Modify `app/api/bookings/route.ts`

**Step 1: Read the current file**

```bash
cat app/api/bookings/route.ts | head -80
```

**Step 2: Add Convex branch to the GET handler**

Find the GET handler. After the auth check (where `userId` is established), add:

```typescript
import { getDataSource } from "@/lib/data-source";
import { convexQuery } from "@/lib/convex-client";
import { api } from "@/convex/_generated/api";

// Inside GET handler, after userId is resolved:
if (getDataSource("bookings") === "convex") {
  const status = searchParams.get("status") ?? undefined;
  const limit = searchParams.get("limit")
    ? parseInt(searchParams.get("limit")!)
    : undefined;
  const bookings = await convexQuery(api.bookings.queries.getBookings, {
    userId,
    ...(status && { status }),
    ...(limit && { limit }),
  });
  return NextResponse.json({ bookings });
}
// existing Prisma code continues below unchanged
```

**Step 3: Verify TypeScript**

```bash
npx tsc --noEmit 2>&1 | grep "api/bookings/route"
# Expected: no errors
```

**Step 4: Test the Prisma path still works (default)**

```bash
curl -s http://localhost:9000/api/bookings \
  -H "Cookie: $(cat /tmp/test-cookie 2>/dev/null || echo '')" | jq '.bookings | length'
# Expected: number (no 500 error)
```

**Step 5: Commit**

```bash
git add app/api/bookings/route.ts
git commit -m "feat(migration): add Convex branch to GET /api/bookings"
```

---

### Task A2: Wire POST /api/bookings

**Files:** Modify `app/api/bookings/route.ts`

**Step 1: Add Convex branch to the POST handler**

After body validation and userId resolution:

```typescript
if (getDataSource("bookings") === "convex") {
  const booking = await convexMutation(api.bookings.mutations.create, {
    userId,
    customerName: body.customerName,
    customerPhone: body.customerPhone,
    customerEmail: body.customerEmail ?? "",
    service: body.service,
    appointmentDate: new Date(body.appointmentDate).getTime(),
    appointmentTime: body.appointmentTime,
    duration: body.duration ?? 60,
    staffMember: body.staffMember,
    totalAmount: body.totalAmount ?? 0,
    depositAmount: body.depositAmount ?? 0,
    source: body.source ?? "manual",
    notes: body.notes,
  });
  return NextResponse.json({ booking }, { status: 201 });
}
// existing Prisma + Stripe code continues below unchanged
```

**Step 2: TypeScript check**

```bash
npx tsc --noEmit 2>&1 | grep "api/bookings/route"
```

**Step 3: Commit**

```bash
git add app/api/bookings/route.ts
git commit -m "feat(migration): add Convex branch to POST /api/bookings"
```

---

### Task A3: Wire PATCH /api/bookings/[id]/status

**Files:** Modify `app/api/bookings/[id]/status/route.ts`

**Step 1: Add Convex branch**

```typescript
if (getDataSource("bookings") === "convex") {
  const updated = await convexMutation(api.bookings.mutations.updateStatus, {
    id: params.id as Id<"bookings">,
    status: body.status,
  });
  return NextResponse.json({ booking: updated });
}
```

**Step 2: Import `Id` type**

```typescript
import type { Id } from "@/convex/_generated/dataModel";
```

**Step 3: TypeScript + commit**

```bash
npx tsc --noEmit 2>&1 | grep "bookings/\[id\]/status"
git add app/api/bookings/[id]/status/route.ts
git commit -m "feat(migration): add Convex branch to PATCH /api/bookings/[id]/status"
```

---

### Task A4: Wire POST /api/bookings/[id]/notes + PATCH reschedule

**Files:** Modify both note and reschedule routes.

**Notes POST:**

```typescript
if (getDataSource("bookings") === "convex") {
  const note = await convexMutation(api.bookings.mutations.addNote, {
    bookingId: params.id as Id<"bookings">,
    authorId: userId,
    authorName: session.user.name ?? "Staff",
    content: body.content,
  });
  return NextResponse.json({ note });
}
```

**Reschedule PATCH:**

```typescript
if (getDataSource("bookings") === "convex") {
  const updated = await convexMutation(api.bookings.mutations.reschedule, {
    id: params.id as Id<"bookings">,
    appointmentDate: new Date(body.appointmentDate).getTime(),
    appointmentTime: body.appointmentTime,
    ...(body.staffMember && { staffMember: body.staffMember }),
  });
  return NextResponse.json({ booking: updated });
}
```

**Step 2: TypeScript + commit**

```bash
npx tsc --noEmit 2>&1 | grep "bookings/\[id\]"
git add app/api/bookings/[id]/notes/route.ts app/api/bookings/[id]/reschedule/route.ts
git commit -m "feat(migration): add Convex branches to booking notes + reschedule routes"
```

---

## GROUP B: Customers (New Convex Functions Required)

The `customers` Convex domain has a schema table but **no queries.ts or mutations.ts**. Write them first.

**Files to create:**

- `convex/customers/queries.ts`
- `convex/customers/mutations.ts`

**Files to modify:**

- `app/api/user/customers/route.ts`
- `app/api/user/customers/[id]/route.ts`

---

### Task B1: Write Convex customer queries

**Files:** Create `convex/customers/queries.ts`

**Step 1: Check the schema first**

```bash
grep -A 30 "customers" convex/schema.ts | head -40
```

**Step 2: Write queries.ts**

```typescript
// convex/customers/queries.ts
import { query } from "../_generated/server";
import { v } from "convex/values";

export const getCustomers = query({
  args: {
    userId: v.string(),
    page: v.optional(v.number()),
    limit: v.optional(v.number()),
    search: v.optional(v.string()),
  },
  handler: async (ctx, { userId, page = 1, limit = 20, search }) => {
    let q = ctx.db
      .query("customers")
      .withIndex("by_userId", (q) => q.eq("userId", userId));

    const all = await q.collect();

    const filtered = search
      ? all.filter(
          (c) =>
            c.name?.toLowerCase().includes(search.toLowerCase()) ||
            c.phone?.includes(search) ||
            c.email?.toLowerCase().includes(search.toLowerCase()),
        )
      : all;

    const total = filtered.length;
    const start = (page - 1) * limit;
    return {
      customers: filtered.slice(start, start + limit),
      total,
      page,
      totalPages: Math.ceil(total / limit),
    };
  },
});

export const getCustomerById = query({
  args: { id: v.id("customers"), userId: v.string() },
  handler: async (ctx, { id, userId }) => {
    const customer = await ctx.db.get(id);
    if (!customer || customer.userId !== userId) return null;
    return customer;
  },
});
```

**Step 3: Run TypeScript check**

```bash
npx tsc --noEmit 2>&1 | grep "convex/customers"
# Expected: no errors
```

**Step 4: Commit**

```bash
git add convex/customers/queries.ts
git commit -m "feat(convex): add customers queries — getCustomers, getCustomerById"
```

---

### Task B2: Write Convex customer mutations

**Files:** Create `convex/customers/mutations.ts`

```typescript
// convex/customers/mutations.ts
import { mutation } from "../_generated/server";
import { v } from "convex/values";

export const createCustomer = mutation({
  args: {
    userId: v.string(),
    name: v.string(),
    phone: v.string(),
    email: v.optional(v.string()),
    notes: v.optional(v.string()),
    tags: v.optional(v.array(v.string())),
  },
  handler: async (ctx, args) => {
    // Duplicate phone check
    const existing = await ctx.db
      .query("customers")
      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
      .filter((q) => q.eq(q.field("phone"), args.phone))
      .first();
    if (existing) throw new Error("DUPLICATE_PHONE");

    return await ctx.db.insert("customers", {
      ...args,
      createdAt: Date.now(),
      updatedAt: Date.now(),
    });
  },
});

export const updateCustomer = mutation({
  args: {
    id: v.id("customers"),
    userId: v.string(),
    name: v.optional(v.string()),
    phone: v.optional(v.string()),
    email: v.optional(v.string()),
    notes: v.optional(v.string()),
    tags: v.optional(v.array(v.string())),
  },
  handler: async (ctx, { id, userId, ...fields }) => {
    const customer = await ctx.db.get(id);
    if (!customer || customer.userId !== userId) throw new Error("NOT_FOUND");
    await ctx.db.patch(id, { ...fields, updatedAt: Date.now() });
    return await ctx.db.get(id);
  },
});

export const deleteCustomer = mutation({
  args: { id: v.id("customers"), userId: v.string() },
  handler: async (ctx, { id, userId }) => {
    const customer = await ctx.db.get(id);
    if (!customer || customer.userId !== userId) throw new Error("NOT_FOUND");
    await ctx.db.delete(id);
    return { deleted: true };
  },
});
```

**Step 2: Push to Convex cloud**

```bash
cd /Users/Asim/Desktop/mawidi_codex/mawidi-site
npx convex dev --once 2>&1 | tail -10
# Expected: "✔ Convex functions ready"
```

**Step 3: Commit**

```bash
git add convex/customers/mutations.ts
git commit -m "feat(convex): add customers mutations — create, update, delete"
```

---

### Task B3: Wire /api/user/customers routes

**Files:** Modify `app/api/user/customers/route.ts` and `app/api/user/customers/[id]/route.ts`

**GET /api/user/customers:**

```typescript
if (getDataSource("customers") === "convex") {
  const page = parseInt(searchParams.get("page") ?? "1");
  const limit = parseInt(searchParams.get("limit") ?? "20");
  const search = searchParams.get("search") ?? undefined;
  const result = await convexQuery(api.customers.queries.getCustomers, {
    userId,
    page,
    limit,
    ...(search && { search }),
  });
  return NextResponse.json(result);
}
```

**POST /api/user/customers:**

```typescript
if (getDataSource("customers") === "convex") {
  try {
    const id = await convexMutation(api.customers.mutations.createCustomer, {
      userId,
      name: body.name,
      phone: body.phone,
      email: body.email,
      notes: body.notes,
    });
    return NextResponse.json({ id }, { status: 201 });
  } catch (e: any) {
    if (e.message?.includes("DUPLICATE_PHONE")) {
      return NextResponse.json(
        { error: "Phone number already exists" },
        { status: 409 },
      );
    }
    throw e;
  }
}
```

**Step 2: TypeScript + commit**

```bash
npx tsc --noEmit 2>&1 | grep "user/customers"
git add app/api/user/customers/route.ts app/api/user/customers/[id]/route.ts
git commit -m "feat(migration): wire /api/user/customers to Convex"
```

---

## GROUP C: Analytics + Reviews

**Key insight:** Both domains already have `getDataSource()` branches in some routes. This group completes the remaining routes.

---

### Task C1: Complete Analytics routes

**Files:** Modify `app/api/analytics/metrics/route.ts`, `app/api/analytics/revenue-trends/route.ts`

**Step 1: Add missing Convex function to analytics mutations**

The existing `api.analytics.queries.getEvents` covers events. For metrics aggregation, add to `convex/analytics/queries.ts`:

```typescript
export const getMetricsSummary = query({
  args: { userId: v.string(), period: v.optional(v.string()) },
  handler: async (ctx, { userId, period = "month" }) => {
    const events = await ctx.db
      .query("analytics_events")
      .withIndex("by_userId", (q) => q.eq("userId", userId))
      .collect();
    // Return aggregated metrics
    return {
      totalBookings: events.filter((e) => e.category === "booking").length,
      totalRevenue: events
        .filter((e) => e.category === "payment")
        .reduce((sum, e) => sum + (e.value ?? 0), 0),
    };
  },
});
```

**Step 2: Wire GET /api/analytics/metrics**

```typescript
if (getDataSource("analytics") === "convex") {
  const metrics = await convexQuery(api.analytics.queries.getMetricsSummary, {
    userId,
    period: searchParams.get("period") ?? "month",
  });
  return NextResponse.json(metrics);
}
```

**Step 3: TypeScript + push + commit**

```bash
npx convex dev --once 2>&1 | tail -5
npx tsc --noEmit 2>&1 | grep "analytics"
git add convex/analytics/queries.ts app/api/analytics/metrics/route.ts
git commit -m "feat(migration): wire analytics metrics to Convex"
```

---

### Task C2: Wire Reviews routes

**Files:** `app/api/dashboard/reviews/route.ts`, `app/api/dashboard/reviews/widget-settings/route.ts`

Both routes already call `convexQuery` via the service layer when enabled. Verify the service layer toggles are complete:

**Step 1: Check service layer**

```bash
grep -n "getDataSource\|convexQuery" lib/services/reviews.service.ts | head -20
```

**Step 2: If toggle missing in listReviews(), add it:**

```typescript
// In lib/services/reviews.service.ts — listReviews()
if (getDataSource("reviews") === "convex") {
  const result = await convexQuery(api.reviews.queries.getReviews, { userId });
  return result;
}
// existing Prisma code...
```

**Step 3: TypeScript + commit**

```bash
npx tsc --noEmit 2>&1 | grep "reviews.service"
git add lib/services/reviews.service.ts
git commit -m "feat(migration): complete Convex toggle in reviews service layer"
```

---

## GROUP D: WhatsApp + Voice Agent

---

### Task D1: Wire WhatsApp conversation routes

**Files:** Modify `app/api/whatsapp/bookings/route.ts`, add Convex branch

**Step 1: Wire POST /api/whatsapp/bookings**

```typescript
if (getDataSource("whatsapp") === "convex") {
  const booking = await convexMutation(api.bookings.mutations.create, {
    userId: orgOwnerId,
    customerName: body.customerName,
    customerPhone: body.customerPhone,
    source: "whatsapp",
    service: body.service,
    appointmentDate: new Date(body.appointmentDate).getTime(),
    appointmentTime: body.appointmentTime,
    totalAmount: 0,
  });
  return NextResponse.json({ booking, message: "Booking confirmed" });
}
```

**Step 2: Wire GET conversations**

```typescript
// app/api/whatsapp/conversations/route.ts (if exists)
if (getDataSource("whatsapp") === "convex") {
  const conversations = await convexQuery(
    api.whatsapp.queries.getConversations,
    { userId },
  );
  return NextResponse.json({ conversations });
}
```

**Step 3: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "whatsapp"
git add app/api/whatsapp/bookings/route.ts
git commit -m "feat(migration): wire WhatsApp bookings to Convex"
```

---

### Task D2: Wire Voice Agent routes

**Files:** Modify `app/api/voice-agent/conversations/route.ts`, `app/api/voice-agent/analytics/route.ts`

**Step 1: Wire GET /api/voice-agent/conversations**

```typescript
if (getDataSource("voice_agents") === "convex") {
  const logs = await convexQuery(api.voice_agents.queries.getConversationLogs, {
    userId,
  });
  return NextResponse.json({ conversations: logs });
}
```

**Step 2: Wire phone config**

```typescript
// app/api/voice-agent/auth/route.ts — GET handler
if (getDataSource("voice_agents") === "convex") {
  const config = await convexQuery(api.voice_agents.queries.getPhoneConfig, {
    userId,
  });
  return NextResponse.json({ config });
}
```

**Step 3: TypeScript + commit**

```bash
npx tsc --noEmit 2>&1 | grep "voice-agent"
git add app/api/voice-agent/conversations/route.ts app/api/voice-agent/analytics/route.ts
git commit -m "feat(migration): wire voice-agent conversations to Convex"
```

---

## GROUP E: Support + Team/Organizations

---

### Task E1: Wire Support tickets

**Files:** Modify `app/api/support/tickets/route.ts`, `app/api/support/tickets/[id]/route.ts`

**Step 1: Wire GET /api/support/tickets**

```typescript
if (getDataSource("support") === "convex") {
  const result = await convexQuery(api.support.queries.getTickets, {
    userId,
    status: searchParams.get("status") ?? undefined,
  });
  return NextResponse.json(result);
}
```

**Step 2: Wire POST /api/support/tickets**

```typescript
if (getDataSource("support") === "convex") {
  const ticket = await convexMutation(api.support.mutations.createTicket, {
    userId,
    subject: body.subject,
    category: body.category,
    priority: body.priority ?? "medium",
    description: body.description,
  });
  return NextResponse.json({ ticket }, { status: 201 });
}
```

**Step 3: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "support/tickets"
git add app/api/support/tickets/route.ts app/api/support/tickets/[id]/route.ts
git commit -m "feat(migration): wire support tickets to Convex"
```

---

### Task E2: Wire Organization/Team routes

**Files:** Modify `app/api/organization/route.ts`, `app/api/organization/invitations/route.ts`

**Step 1: Wire GET /api/organization**

```typescript
if (getDataSource("organizations") === "convex") {
  const [org, members, invitations] = await Promise.all([
    convexQuery(api.organizations.queries.getOrganization, { id: orgId }),
    convexQuery(api.organizations.queries.getMembers, {
      organizationId: orgId,
    }),
    convexQuery(api.organizations.queries.getInvitations, {
      organizationId: orgId,
      status: "pending",
    }),
  ]);
  return NextResponse.json({ organization: org, members, invitations });
}
```

**Step 2: Wire POST /api/organization/invitations**

```typescript
if (getDataSource("organizations") === "convex") {
  await convexMutation(api.organizations.mutations.addMember, {
    organizationId: orgId,
    userId: invitedUserId,
    role: body.role,
  });
  return NextResponse.json({ success: true }, { status: 201 });
}
```

**Step 3: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "organization"
git add app/api/organization/route.ts app/api/organization/invitations/route.ts
git commit -m "feat(migration): wire organization + invitations to Convex"
```

---

## GROUP F: Quotations + Billing

---

### Task F1: Wire Quotations portal routes

**Files:** `app/api/quotations/portal/[token]/route.ts` and sub-routes

**Step 1: Wire GET (view quotation)**

```typescript
if (getDataSource("quotations") === "convex") {
  const quotation = await convexQuery(api.quotations.queries.getById, {
    id: quotationId as Id<"quotations">,
  });
  if (!quotation)
    return NextResponse.json({ error: "Not found" }, { status: 404 });
  return NextResponse.json({ quotation });
}
```

**Step 2: Wire accept/reject**

```typescript
// accept route
if (getDataSource("quotations") === "convex") {
  await convexMutation(api.quotations.mutations.acceptQuotation, {
    id: quotationId as Id<"quotations">,
  });
  return NextResponse.json({ success: true });
}

// reject route
if (getDataSource("quotations") === "convex") {
  await convexMutation(api.quotations.mutations.rejectQuotation, {
    id: quotationId as Id<"quotations">,
    reason: body.reason,
  });
  return NextResponse.json({ success: true });
}
```

**Step 3: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "quotations"
git add app/api/quotations/portal/
git commit -m "feat(migration): wire quotations portal routes to Convex"
```

---

### Task F2: Wire Billing routes

**Files:** `app/api/billing/current/route.ts`, `app/api/billing/history/route.ts`, `app/api/billing/invoices/route.ts`

**Important:** Stripe API calls remain in the route — only DB reads switch to Convex.

**Step 1: Wire GET /api/billing/current**

```typescript
if (getDataSource("billing") === "convex") {
  const subscription = await convexQuery(api.billing.queries.getSubscription, {
    subscriptionId: user.stripeSubscriptionId ?? "",
  });
  return NextResponse.json({ subscription });
}
```

**Step 2: Wire GET /api/billing/history**

```typescript
if (getDataSource("billing") === "convex") {
  const history = await convexQuery(api.billing.queries.getBillingHistory, {
    userId,
  });
  return NextResponse.json({ history });
}
```

**Step 3: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "billing"
git add app/api/billing/current/route.ts app/api/billing/history/route.ts
git commit -m "feat(migration): wire billing current + history to Convex"
```

---

## GROUP G: Contact Page + Demo Booking

---

### Task G1: Wire /api/contact routes

**Files:** `app/api/contact/route.ts`, `app/api/contact/submit/route.ts`

**Note:** Contact form has external services (email, file upload). Convex only handles the DB insert.

**Step 1: Add contacts to Convex schema (if not present)**

```bash
grep -n "contacts" convex/schema.ts
```

If missing, add to schema.ts:

```typescript
contacts: defineTable({
  name: v.string(),
  email: v.string(),
  phone: v.optional(v.string()),
  message: v.string(),
  company: v.optional(v.string()),
  source: v.optional(v.string()),
  createdAt: v.number(),
}).index("by_email", ["email"]),
```

**Step 2: Create convex/contacts/mutations.ts**

```typescript
import { mutation } from "../_generated/server";
import { v } from "convex/values";

export const createContact = mutation({
  args: {
    name: v.string(),
    email: v.string(),
    phone: v.optional(v.string()),
    message: v.string(),
    company: v.optional(v.string()),
    source: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    return await ctx.db.insert("contacts", {
      ...args,
      createdAt: Date.now(),
    });
  },
});
```

**Step 3: Wire /api/contact/route.ts**

```typescript
if (getDataSource("contacts") === "convex") {
  const id = await convexMutation(api.contacts.mutations.createContact, {
    name: body.name,
    email: body.email,
    phone: body.phone,
    message: body.message,
    company: body.company,
    source: "contact_form",
  });
  // Email notifications still sent via existing code
  await sendContactNotification(body); // keep this
  return NextResponse.json({ id }, { status: 201 });
}
```

**Step 4: Push schema + commit**

```bash
npx convex dev --once 2>&1 | tail -5
npx tsc --noEmit 2>&1 | grep "contact"
git add convex/schema.ts convex/contacts/ app/api/contact/route.ts app/api/contact/submit/route.ts
git commit -m "feat(migration): wire contact form to Convex, add contacts table"
```

---

### Task G2: Wire Demo Booking routes

**Files:** `app/api/demo/book/route.ts`, `app/api/demo/confirm/route.ts`, `app/api/demo/bookings/route.ts`

**Step 1: Wire POST /api/demo/book**

```typescript
if (getDataSource("demo") === "convex") {
  const booking = await convexMutation(api.bookings.mutations.create, {
    userId: "demo",
    customerName: body.name,
    customerPhone: body.phone,
    customerEmail: body.email,
    service: "Product Demo",
    appointmentDate: new Date(body.preferredDate).getTime(),
    appointmentTime: body.preferredTime,
    source: "demo_booking",
    totalAmount: 0,
  });
  // Keep Google Calendar + n8n webhook + email notifications
  await scheduleGoogleCalendarEvent(booking);
  return NextResponse.json({
    bookingId: booking,
    reference: `DEMO-${Date.now()}`,
  });
}
```

**Step 2: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "api/demo"
git add app/api/demo/
git commit -m "feat(migration): wire demo booking to Convex"
```

---

## GROUP H: Dashboard Overview + Settings

---

### Task H1: Wire Dashboard initial data

**Files:** `app/[lang]/dashboard/page.tsx` or `lib/services/dashboard.service.ts`

**Step 1: Find where initial data is fetched**

```bash
grep -rn "getInitialData\|DashboardService" app/[lang]/dashboard/ | head -10
```

**Step 2: Add Convex branch in DashboardService.getInitialData()**

```typescript
if (getDataSource("auth") === "convex") {
  const [user, bookings, notifications] = await Promise.all([
    convexQuery(api.auth.queries.getUser, { id: userId }),
    convexQuery(api.bookings.queries.getUpcoming, { userId, limit: 5 }),
    convexQuery(api.notifications.queries.getNotifications, { userId }),
  ]);
  return { user, upcomingBookings: bookings, notifications };
}
```

**Step 3: Commit**

```bash
npx tsc --noEmit 2>&1 | grep "dashboard"
git add lib/services/dashboard.service.ts
git commit -m "feat(migration): wire dashboard initial data to Convex"
```

---

### Task H2: Wire Settings routes

**Files:** `app/api/settings/` or `app/api/organization/settings/route.ts`

**Step 1: Find settings routes**

```bash
find app/api -name "route.ts" | xargs grep -l "settings\|organization" | head -10
```

**Step 2: Wire org settings GET**

```typescript
if (getDataSource("organizations") === "convex") {
  const org = await convexQuery(api.organizations.queries.getOrganization, {
    id: orgId,
  });
  return NextResponse.json({ settings: org });
}
```

**Step 3: Wire org settings PATCH**

```typescript
if (getDataSource("organizations") === "convex") {
  // Settings update not in current mutations — use updateOrganization if available
  // Otherwise keep Prisma path for now
}
```

**Step 4: Commit**

```bash
git add app/api/settings/
git commit -m "feat(migration): wire settings org data to Convex"
```

---

## Activation Sequence

After all tasks complete, flip domains one at a time in `.env.local`:

```bash
# Week 1 — lowest risk (read-mostly)
DS_ANALYTICS=convex
DS_REVIEWS=convex

# Week 2 — moderate
DS_SUPPORT=convex
DS_NOTIFICATIONS=convex

# Week 3 — core product
DS_BOOKINGS=convex
DS_CUSTOMERS=convex

# Week 4 — financial + comms
DS_BILLING=convex
DS_QUOTATIONS=convex
DS_ORGANIZATIONS=convex

# Week 5 — integrations
DS_WHATSAPP=convex
DS_VOICE_AGENTS=convex
DS_AUTH=convex
```

After each flip: watch `npx convex logs` for errors, check dashboard visually, roll back by removing the env var.

---

## Final Verification

```bash
# All TS clean
npx tsc --noEmit 2>&1 | grep "error TS" | wc -l
# Expected: same as pre-flight baseline

# Convex functions deployed
npx convex dev --once 2>&1 | grep "functions ready"

# All 8 domain toggles respond without error
for domain in bookings customers analytics reviews whatsapp voice_agents support organizations; do
  echo "Testing $domain..."
done
```

---

## What Is NOT Changed

- `lib/auth.ts` — untouched
- `/login`, `/signup` pages — untouched
- Convex schema (`convex/schema.ts`) — append-only, no deletions
- Stripe SDK calls — stay in API routes
- Google Calendar / Gemini / Twilio / ElevenLabs integrations — stay in API routes
- Dashboard UI components — still use `fetch()` to API routes
