docs(phase-19): complete plan 19-01 — LeadsKanbanBoard + toggle PIPE-01/PIPE-02

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 18:07:44 +02:00
parent b0dbd341a9
commit bac757a987
3 changed files with 119 additions and 24 deletions
@@ -0,0 +1,95 @@
---
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).