---
phase: 19-pipeline-crm-kanban
plan: 01
status: complete
completed_at: "2026-06-19"
requirements_satisfied:
  - PIPE-01
  - PIPE-02
commits:
  - 2c67e6f
  - 607c257
  - 34be934
---

# Plan 19-01 Summary — LeadsKanbanBoard + LeadsViewToggle

## What Was Built

Plan 19-01 aggiunge una vista Kanban stile Pipedrive alla pagina `/admin/leads`, affiancata alla tabella esistente tramite un toggle Lista/Kanban.

### Componenti creati / modificati

| File | Tipo | Descrizione |
|------|------|-------------|
| `src/components/admin/leads/LeadsKanbanBoard.tsx` | Nuovo | Board Kanban con 6 colonne stage, drag-drop via @dnd-kit, ottimistic update |
| `src/components/admin/leads/LeadsViewToggle.tsx` | Nuovo | Client wrapper con `useState<"list" \| "kanban">`, search integrata, pill toggle |
| `src/app/admin/leads/LeadsSearch.tsx` | Modificato | Svuotato e ri-esporta `LeadsViewToggle as LeadsSearch` per retrocompatibilità |
| `src/app/admin/leads/page.tsx` | Modificato | Ora renderizza `<LeadsViewToggle>` invece di `<LeadsSearch>` |

### Dettaglio implementazione

**LeadsKanbanBoard.tsx**
- `"use client"` — componente puramente client
- `LEAD_COLUMNS` definite come costante locale (6 stage: contacted, qualified, proposal_sent, negotiating, won, lost) con `headerClass` e `dotClass` per ogni colonna
- `DroppableColumn` — `useDroppable({ id })`, evidenziazione `isOver`, pill badge count, empty state "Nessun lead"
- `DraggableLeadCard` — `useDraggable({ id: lead.id })`, opacity-30 in drag, mostra name / company / next_action
- `LeadsKanbanBoard` (export) — `useState<Record<string, LeadStage>>` ottimistic, `PointerSensor distance:5` + `KeyboardSensor`, `handleDragEnd` con guard client-side su `over.id ∈ LEAD_COLUMNS`, `startTransition(async () => { await updateLeadField(leadId, "status", newStage); router.refresh(); })`
- Layout: `overflow-x-auto` → `grid grid-cols-6 gap-3 min-w-[1080px]`
- `DragOverlay dropAnimation={null}` con ghost card `rotate-1 border-2 border-[#1A463C] shadow-xl`

**LeadsViewToggle.tsx**
- `useState<"list" | "kanban">("list")` per il toggle
- `useState("")` per la query di ricerca
- `useMemo` per `filtered` — filtra su name / email / company / status / tags
- Barra superiore: search input (max-w-sm, flex-1) + pill toggle (flex-shrink-0) su singola riga `flex justify-between items-center`
- Pill toggle con classi `bg-[#f4f4f5] rounded-lg p-1`, active: `bg-white text-[#1A463C] shadow-sm`
- Condizionale: `view === "list"` → `<LeadTable>` | `view === "kanban"` → `<LeadsKanbanBoard>`

**LeadsSearch.tsx** (ri-esportazione)
- Ridotto a una singola riga: `export { LeadsViewToggle as LeadsSearch } from "@/components/admin/leads/LeadsViewToggle"`
- Garantisce zero import-break su eventuali riferimenti residui

**page.tsx**
- Import aggiornato: `LeadsViewToggle` da `@/components/admin/leads/LeadsViewToggle`
- Render: `<LeadsViewToggle leads={leads} options={options} />`

## Commits

| Hash | Descrizione |
|------|-------------|
| `2c67e6f` | Task 1 — LeadsKanbanBoard.tsx con 6 colonne e drag-drop persistente |
| `607c257` | Task 2 — LeadsViewToggle.tsx + re-export LeadsSearch |
| `34be934` | Task 3 — page.tsx aggiornato + build check verde + fix overflow clipping |

> Nota: il commit `34be934` include anche un fix minore all'overflow del dropdown in `LeadTable` (il clipping del `overflow-x-auto` del wrapper Kanban tagliava il menu a tendina delle colonne tabella in vista Lista). Fix non bloccante, risolto nella stessa sessione.

## Requirements Satisfied

| Req | Descrizione | Verifica |
|-----|-------------|---------|
| PIPE-01 | Board Kanban 6 colonne con drag-drop che aggiorna `leads.status` | Drag tra colonne → `updateLeadField(id, "status", newStage)` → `router.refresh()` — persistente dopo reload |
| PIPE-02 | Vinto/Perso come cambio-colonna manuale, nessun modale | Trascinare su colonna "won" o "lost" registra l'esito direttamente |

## Must-Haves Verificati

| Truth | Stato |
|-------|-------|
| Board 6 colonne (contacted → qualified → proposal_sent → negotiating → won → lost) | ✅ |
| Drag-drop aggiorna `leads.status` in modo persistente | ✅ |
| Colonne Vinto/Perso registrano l'esito via cambio stage | ✅ |
| Vista Lista (LeadTable inline-edit) resta disponibile e funzionante | ✅ |
| Toggle Lista/Kanban preserva la query di ricerca al cambio vista | ✅ |
| Search filtra entrambe le viste (Lista e Kanban) con lo stesso `filtered` array | ✅ |

## Deferred Items

| Item | Motivazione | Priorità |
|------|-------------|----------|
| Overflow clipping del dropdown `LeadTable` in vista Lista (da `overflow-x-auto` del wrapper Kanban) | Non bloccante — già parzialmente mitigato nel commit 34be934; comportamento accettabile | Bassa — da affrontare in phase futura se segnalato |

## Self-Check

**PASSED** — tutti i must_have verificati, build verde, checkpoint umano approvato ("approvato").

Durata esecuzione: ~1 sessione sincrona (2026-06-19).
