# Leads Detail View

**Date:** 2026-05-31  
**Status:** Draft  
**Spec:** 100  
**Tier:** All tiers (Business+ for sequences/forms)  
**Depends on:** `marketing-leads-pipeline`, `marketing-catalogs-campaigns`, `activity-timeline`, `system-communications-notifications`, `foundation-auth-rbac`  
**Referenced by:** `marketing-leads-pipeline`

---

## Overview

Spec 22 (`marketing-leads-pipeline`) defines the `leads` table and Kanban board. This spec defines the full lead detail page (`/leads/:id`): lead info panel, stage management, activities timeline, linked entities (proposals, contracts, invoice), task panel, and email/call logging.

---

## Lead Detail Layout

`/leads/:id`:

```
┌──────────────────────────────────────────────────────────────┐
│  ← Leads   Acme Corp — John Smith          [Edit] [Convert]  │
│                                                              │
│  ┌─ Info ──────────────────┐  ┌─ Activity ───────────────┐  │
│  │ Stage:   QUALIFIED  [▾] │  │ [+ Log call] [+ Add note]│  │
│  │ Value:   ₪18,000        │  │                          │  │
│  │ Source:  form           │  │ 2026-05-30 14:22         │  │
│  │ Assigned: Dana Levi [▾] │  │ Stage → QUALIFIED        │  │
│  │                         │  │ by Alex Cohen            │  │
│  │ Email:   john@acme.com  │  │                          │  │
│  │ Phone:   +972-54-...    │  │ 2026-05-29 10:05         │  │
│  │ Company: Acme Corp      │  │ Note: "Expressed interest│  │
│  │                         │  │  in monthly retainer"    │  │
│  │ UTM source: google      │  │  by Dana Levi            │  │
│  │ UTM campaign: spring26  │  │                          │  │
│  │                         │  │ 2026-05-28 09:14         │  │
│  │ Created: 2026-05-28     │  │ Lead created (form)      │  │
│  └─────────────────────────┘  └──────────────────────────┘  │
│                                                              │
│  Linked entities                                             │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ Proposals (1)  Contracts (0)  Invoices (0)           │   │
│  │ Q2 Proposal   SENT   ₪18,000   2026-05-30  [View]    │   │
│  │ [+ Create proposal]                                   │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  Tasks                                                       │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ ○ Follow up by phone (due 2026-06-01)  [✓] [✕]       │   │
│  │ [+ Add task]                                          │   │
│  └──────────────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────────────┘
```

---

## Info Panel

Displays all `leads` columns. Inline-edit via `PATCH /api/leads/:id`. Fields:

- Stage (dropdown: NEW / CONTACTED / QUALIFIED / PROPOSAL / WON / LOST)
- Estimated value (₪)
- Assigned to (user picker)
- Email, phone, company, notes
- Source (read-only with badge: manual / form / webhook / facebook / google / zapier / make)
- UTM fields (collapsed, expand on click if any UTM present)

**Stage change** updates `stage` and appends `lead_activities` row (`type = 'stage_changed'`, metadata `{from, to}`), emits `lead.stage_updated` webhook (spec 84).

**Mark won / lost** buttons (replaces stage dropdown when in PROPOSAL):
- Won → stage = 'WON'; prompts "Convert to customer now?" (spec 67 `lead-to-customer-conversion`)
- Lost → stage = 'LOST'; modal asks for `lost_reason` (free text, stored on `leads.lost_reason`)

---

## Activity Timeline

Displays `lead_activities` in reverse-chronological order. Types and rendering:

| Activity type | Icon | Content |
|---|---|---|
| `note` | 📝 | Content text (Markdown rendered) |
| `email_sent` | ✉️ | Subject + preview from `metadata.subject` |
| `call_logged` | 📞 | Duration + notes from `content` |
| `stage_changed` | ↗️ | "Stage: {from} → {to}" |
| `form_submitted` | 📋 | "Lead captured via form {metadata.formName}" |
| `webhook_received` | 🔗 | "Received from {metadata.source}" |

### Add Note

Inline textarea (auto-focus on "+ Add note"):

```
┌──────────────────────────────────────────────────────────────┐
│  Add note                                                    │
│  [Markdown textarea...                                      ]│
│                                [Cancel]  [Save note]         │
└──────────────────────────────────────────────────────────────┘
```

`POST /api/leads/:id/activities` — body: `{ type: 'note', content }`.

### Log Call

Modal:
```
┌──────────────────────────────────────────────────────────────┐
│  Log call                                                    │
│                                                              │
│  Duration:  [30] minutes                                     │
│  Notes:     [___________________________________________]    │
│             [___________________________________________]    │
│                                                              │
│  [Cancel]                         [Log call]                 │
└──────────────────────────────────────────────────────────────┘
```

`POST /api/leads/:id/activities` — body: `{ type: 'call_logged', content: notes, metadata: { duration_minutes } }`.

---

## Linked Entities

Three tabs: **Proposals**, **Contracts**, **Invoices**. Each queries by `lead_id`:

- `proposals WHERE lead_id = :id` (spec 23 — `lead_id` already on proposals table)
- `contracts WHERE lead_id = :id` (spec 48 — `lead_id` added by schema delta below)
- `invoices WHERE lead_id = :id` (added by schema delta below)

Each shows status, amount, date, and quick [View] link. [+ Create proposal] button pre-fills lead as recipient.

---

## Tasks

Lists tasks linked to this lead via `tasks.lead_id` (added by schema delta below). Inline add/complete/delete.

---

## Convert Button

Top-right [Convert] button visible when `lead.customer_id IS NULL` and `stage != 'LOST'`. Launches conversion flow (spec 67 `lead-to-customer-conversion`).

---

## API

```
GET /api/leads/:id
    → lead detail with linked entity counts
      Requires: marketing:read

GET /api/leads/:id/activities
    → paginated activity log
      Requires: marketing:read

POST /api/leads/:id/activities
     → log note, call, or email
       body: { type, content?, metadata? }
       Requires: marketing:write

PATCH /api/leads/:id
      → update lead fields (stage, assigned_to, value, etc.)
        Requires: marketing:write
```

---

## Schema Delta

```sql
-- Add lead_id to contracts for reverse-lookup from lead detail
ALTER TABLE contracts ADD COLUMN lead_id UUID REFERENCES leads(id);
CREATE INDEX idx_contracts_lead ON contracts(lead_id) WHERE lead_id IS NOT NULL;

-- Add lead_id to invoices for reverse-lookup from lead detail
ALTER TABLE invoices ADD COLUMN lead_id UUID REFERENCES leads(id);
CREATE INDEX idx_invoices_lead ON invoices(lead_id) WHERE lead_id IS NOT NULL;

-- Add lead_id to tasks for CRM task context
ALTER TABLE tasks ADD COLUMN lead_id UUID REFERENCES leads(id);
CREATE INDEX idx_tasks_lead ON tasks(lead_id) WHERE lead_id IS NOT NULL;
```

`lead_id` is nullable on all three tables — existing rows unaffected. Populated when creating from the lead detail page or via conversion flow (spec 67).

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Lead activities separate from spec-28 audit log | Both coexist | `lead_activities` is the CRM activity feed (user-facing: notes, calls, stage changes); `tenant_audit_log` is the compliance trail. Same write can log to both |
| UTM fields collapsed by default | Not always visible | UTMs are diagnostic, not primary. Most users don't need them; collapse reduces noise |
| Stage change inline (not separate form) | Not modal | Single-field change; dropdown is fastest path; modal adds unnecessary friction |
