# Plan — Campaign module rework

Reworks the campaign UX to the quality bar already set by the **templates**
module (rich detail + `TemplateStatsPanel`). Grounded in `specs/05-campaigns.md`
(FR-05.*/AC-05.*) and the gap inventory. Backend send + analytics already exist;
this is mostly **surfacing** capability and closing UX gaps. TDD throughout.

## Research findings (spec vs. current)

The send/queue/stats backend satisfies FR-05.1–05.15. The gaps are UX/exposure:

1. **Creation — CSV select-from-contacts has no variable-mapping UI.** With a
   variable template every recipient is skipped (`missing_variable`). The
   backend skip is correct per FR-05.2/AC-05.4; the UI just never lets you map.
   (The object-shape resolver bug is already fixed + deployed.)
2. **Detail/info is thin.** Shows name, status, template name, segment name.
   Missing: template **preview**, **variable-mapping summary**, scheduled time,
   created/started/completed timestamps, created-by, audience size, cost.
3. **Stats are shallow.** Only the 5 progress tiles. The richer
   `GET /api/analytics/campaigns/:id` (audienceSize, counts, delivery/read rates,
   duration, failure breakdown) + `getCampaignDetail()` client **already exist
   but are never rendered**. No funnel, trend, or failure table like templates.
4. **Builders** lack a live template preview; audience exclusions only shown at
   create time.

## Design

Mirror the template-detail pattern. **Single scrolling detail page** (no tabs)
with stacked sections, matching `TemplateDetail.tsx`:

```
[back] Campaign name           [Send now | Edit | Pause | … lifecycle]
status badge · type · template · segment · schedule
┌ Overview (2-col) ───────────────────────────────────────────┐
│  Info card (left, ~360px)        │  Template preview (right) │
│   template, audience size,        │   <TemplatePreview/>      │
│   variable mapping summary,       │   with mapping applied    │
│   schedule, created/started/      │                           │
│   completed, created-by, cost     │                           │
└──────────────────────────────────┴───────────────────────────┘
CampaignProgress (existing tiles)
CampaignStatsPanel  ← NEW: KPIs, funnel, trend, failure table
Recipients table (existing) + export
```

## Tasks (TDD: test → implement → verify → commit)

### A. CSV variable mapping (completes the send fix)
- **A1** `VariableMappingEditor` is reused in `CsvBroadcast.tsx` select-from-
  contacts path. Show it when the chosen template has variables; pass the
  mapping in the `contactIds` create body. Disable submit until every variable
  is mapped (mirror broadcast). Test: builder renders the editor + blocks submit.
- **A2** Manual-CSV path already maps via columns — leave as is, but surface a
  warning if a template variable is unmapped.

### B. CampaignStatsPanel (surface the orphaned analytics)
- **B1** New `components/campaigns/CampaignStatsPanel.tsx` consuming
  `getCampaignDetail(id)` + `getMessageMetrics({campaignId})`. KPI grid (sent,
  delivered+rate, read+rate, failed+rate, skipped, audience), conversation
  **funnel** (sent→delivered→read), **trend** chart (reuse template approach),
  **failure breakdown** table. Reuse the visual language of `TemplateStatsPanel`.
  Tests: renders KPIs/funnel/failures from mocked data; empty state.
- **B2** Embed it in `CampaignDetail.tsx`.

### C. Detail/info enrichment
- **C1** New `components/campaigns/CampaignInfoCard.tsx`: template (link),
  audience size (from analytics), variable-mapping summary (position → source),
  scheduled time, created/started/completed timestamps, created-by, type.
- **C2** Add `<TemplatePreview>` on the right, fed by the campaign's template
  components + a sample row from the variable mapping.
- **C3** Detail API must return what's needed: ensure `GET /api/campaigns/:id`
  includes `template.components`, `variableMapping`, `createdBy`, timestamps.
  Extend the service/select + DTO + `campaigns-api.ts` types as needed (test the
  service returns the new fields).

### D. Builder polish
- **D1** Live `<TemplatePreview>` in `CampaignBuilder` + `CsvBroadcast` once a
  template is picked.
- **D2** Re-show sendable/excluded audience consistently.

### E. Verify
- `pnpm typecheck/lint/test/build` all green; new component tests pass.
- Live re-test the select-from-contacts send with a **variable** template now
  that the UI maps it (operator number / `test_template`).
- Rebuild + `pm2 restart channels-api`; user runs `sudo scripts/publish-web.sh`.

## Out of scope (flag, don't build)
- API-campaign "finalize/close" action (recipients grow unbounded by design).
- Cost-rate configuration UI.
