# Phase 20: Knowledge Base Cliente — Context **Gathered:** 2026-06-19 **Status:** Ready for planning ## 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 ## 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. ## 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`) ## 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 `` - `src/components/admin/leads/LeadDetail.tsx` — aggiungere prop `transcripts` e sezione "Transcript" dopo `` - `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)` ## Specific Ideas - Il testo del transcript può essere molto lungo (trascrizioni complete di call). Il form deve avere una `