# Phase 20: Knowledge Base Cliente — Context

**Gathered:** 2026-06-19
**Status:** Ready for planning

<domain>
## Phase Boundary

Aggiungere uno store di transcript datati per lead: l'admin incolla il testo grezzo di ogni call con data e titolo libero, la lista appare in ordine cronologico nel dettaglio lead, e i dati sono pronti per essere letti dall'agente AI in Phase 21.

**In scope:** schema `client_transcripts` + server actions + UI nel LeadDetail
**Out of scope:** UI per clienti (client_id FK presente ma non esposta), ricerca full-text sui transcript, trascrizione automatica da audio

</domain>

<decisions>
## Implementation Decisions

### Schema — client_transcripts

- **D-01:** La tabella `client_transcripts` ha **entrambe** le FK nullable: `lead_id` (references leads, onDelete cascade) e `client_id` (references clients, onDelete cascade). Ragionamento: prepara la struttura per transcript post-conversione senza richiedere una migration futura.
- **D-02:** Campi: `id` (nanoid PK), `lead_id` (nullable FK), `client_id` (nullable FK), `title` (text, optional — titolo libero es. "Discovery call 12 giugno"), `content` (text NOT NULL — testo incollato, lunghezza illimitata), `call_date` (date NOT NULL — giorno della call, non timestamp), `created_at` (timestamp with timezone, defaultNow).
- **D-03:** Migration mano-scritta come 0009 — drizzle-kit generate è rotto. Applicata a prod via SSH **prima** di pushare il codice dipendente (invariante bloccante del progetto).

### UI — Solo lato lead in Phase 20

- **D-04:** In Phase 20 la UI espone solo il lato lead. La FK `client_id` è nello schema ma **non ha form o lista** in questa fase — si aggiunge in futuro se serve la pagina `/admin/clients/[id]`.
- **D-05:** I transcript sono listati in ordine `call_date DESC` nel dettaglio lead (chiamata più recente in cima).

### Claude's Discretion

Le seguenti aree non sono state discusse — Claude ha flessibilità:

- **Metadati:** No tipo enum — `title` libero (optional) è sufficiente. Il testo del transcript parla da solo.
- **Placement UI:** Nuova sezione "Transcript" collassabile in `LeadDetail`, dopo la sezione Attività esistente. Stessa convenzione visiva (Card + lista). Form/modal per aggiungere seguendo il pattern `LogActivityModal`.
- **Nessun limite di lunghezza:** `content` è `text` PostgreSQL (illimitato). Textarea nel form senza troncatura.

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Requirements & Roadmap
- `.planning/ROADMAP.md` — Phase 20 goal, success criteria, schema spec (`client_transcripts` con `lead_id/client_id, testo, data, titolo/tipo, created_at`)
- `.planning/REQUIREMENTS.md` — KB-01 (schema additivo transcript), KB-02 (UI incolla/elenca)

### Schema & Migrations
- `src/db/schema.ts` — pattern tabelle append-only CRM: `activities` (lead_id, type, notes, activity_date), `reminders` (lead_id, due_date). La nuova `client_transcripts` segue questo pattern.
- `src/db/migrations/0008_offer_tier_schema.sql` — ultimo esempio di SQL migration a mano (struttura e convenzioni da seguire)
- `src/db/migrations/0005_phase_10_crm_leads_activities_reminders.sql` — migration originale di `activities` e `reminders` (pattern più vicino alla nuova tabella)

### UI & Components
- `src/components/admin/leads/LeadDetail.tsx` — struttura UI esistente del dettaglio lead (dove va inserita la sezione Transcript)
- `src/components/admin/leads/LogActivityModal.tsx` — pattern modal per aggiungere dati CRM (da seguire per il form transcript)
- `src/app/admin/leads/[id]/page.tsx` — pattern page con `Promise.all` per fetch parallele (aggiungere `getTranscripts(id)`)

### Query & Actions Layer
- `src/lib/lead-service.ts` — pattern query layer CRM (`getActivityLog`, `getUpcomingReminders` — aggiungere `getTranscripts`)
- `src/app/admin/leads/actions.ts` — pattern server actions con `requireAdmin` guard (da seguire per `addTranscript`, `deleteTranscript`)

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `LogActivityModal.tsx` — modal con form controllato (date picker + textarea + submit), pronto da clonare per il form transcript
- `activities` / `reminders` pattern in `schema.ts` — FK `lead_id` con `onDelete: cascade`, `nanoid()` PK, `created_at` defaultNow — copia esatta per `client_transcripts`
- `getActivityLog(leadId)` in `lead-service.ts` — query Drizzle con `where(eq(activities.lead_id, leadId))` e `orderBy(desc(activities.activity_date))` — pattern identico per `getTranscripts`
- `Card`, `CardContent`, `CardHeader`, `CardTitle` da shadcn/ui — già usati nel LeadDetail per ogni sezione

### Established Patterns
- **Migration a mano**: ogni schema change è SQL scritto a mano (`CREATE TABLE IF NOT EXISTS`, tipi Postgres espliciti, FK con `ON DELETE CASCADE`). NON usare `drizzle-kit generate`.
- **requireAdmin guard**: tutte le server actions in `actions.ts` iniziano con `await requireAdmin()` — obbligatorio anche per le nuove actions transcript.
- **`revalidatePath`** dopo ogni mutation: `revalidatePath(\`/admin/leads/${leadId}\`)`.
- **Promise.all fetch**: `page.tsx` del dettaglio lead usa `await Promise.all([...])` per fetch parallele — aggiungere `getTranscripts(id)` nello stesso array.

### Integration Points
- `src/app/admin/leads/[id]/page.tsx` — aggiungere `getTranscripts(id)` nel `Promise.all`, passare `transcripts` a `<LeadDetail />`
- `src/components/admin/leads/LeadDetail.tsx` — aggiungere prop `transcripts` e sezione "Transcript" dopo `<ActivitySection>`
- `src/db/schema.ts` — aggiungere definizione `client_transcripts` + relazioni Drizzle
- `src/lib/lead-service.ts` — aggiungere `getTranscripts(leadId: string)`
- `src/app/admin/leads/actions.ts` — aggiungere `addTranscript(leadId, data)` e `deleteTranscript(transcriptId)`

</code_context>

<specifics>
## Specific Ideas

- Il testo del transcript può essere molto lungo (trascrizioni complete di call). Il form deve avere una `<textarea>` con altezza generosa (es. min-h-48 o più) senza limite di caratteri.
- Il `call_date` è una `date` (non timestamp) — l'admin sceglie il giorno della call, non l'ora. Nel DB: `DATE` type PostgreSQL.
- La lista transcript deve mostrare: `call_date` formattata (es. "12 giugno 2026"), `title` se presente, anteprima delle prime righe del `content`, e un'azione "Elimina". Espansione/collapse del testo completo.
- Phase 21 leggerà i transcript via `getTranscripts(leadId)` — la query deve restituire tutti i campi incluso `content` completo.

</specifics>

<deferred>
## Deferred Ideas

- **UI transcript per clienti** (`/admin/clients/[id]`): la FK `client_id` è nella tabella, ma la UI lato cliente è deferred a una fase futura (post v2.2 o su richiesta).
- **Ricerca full-text sui transcript**: fuori scope v2.2 — i transcript vengono letti dall'AI in blocco, non cercati dall'admin.
- **Trascrizione automatica da audio**: fuori scope — l'admin incolla manualmente il testo.

</deferred>

---

*Phase: 20-Knowledge Base Cliente*
*Context gathered: 2026-06-19*
