From f5f90cd64385de0a82eafcc046043713993512e8 Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Mon, 22 Jun 2026 08:18:41 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20lead=20=E2=86=92=20client=20conversion?= =?UTF-8?q?=20+=20client=20contact=20fields=20(email/phone)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocco A+B (milestone v2.3). Migration 0011 is additive (ADD COLUMN only): clients.email, clients.phone, leads.archived. - createClientCore() extracted from createClient and reused by conversion - clients.email/phone added to create + edit forms and shown on client header (never exposed on the public /client/[token] page) - convertLeadToClient(): reuses core, carries over lead transcripts (client_transcripts.client_id), links project.created_from_lead_id, archives the lead while keeping its "won" status; idempotent - "Converti in cliente" button on won leads; "Convertito → Apri cliente" once converted (clientId resolved via created_from_lead_id) - archived leads hidden from list/kanban; detail page fetches by id incl. archived Migration must be applied to the prod DB BEFORE this is deployed. Co-Authored-By: Claude Opus 4.8 --- src/app/admin/clients/[id]/actions.ts | 9 +++ src/app/admin/clients/[id]/edit/page.tsx | 23 ++++++ src/app/admin/clients/[id]/page.tsx | 15 ++++ src/app/admin/clients/new/actions.ts | 80 ++++++++++--------- src/app/admin/clients/new/page.tsx | 20 +++++ src/app/admin/leads/[id]/page.tsx | 14 ++-- src/app/admin/leads/actions.ts | 53 +++++++++++- src/components/admin/leads/LeadDetail.tsx | 38 ++++++++- .../0011_client_contact_lead_archived.sql | 8 ++ src/db/schema.ts | 7 ++ src/lib/admin-queries.ts | 50 ++++++++++++ 11 files changed, 273 insertions(+), 44 deletions(-) create mode 100644 src/db/migrations/0011_client_contact_lead_archived.sql diff --git a/src/app/admin/clients/[id]/actions.ts b/src/app/admin/clients/[id]/actions.ts index a5f66b5..4802d67 100644 --- a/src/app/admin/clients/[id]/actions.ts +++ b/src/app/admin/clients/[id]/actions.ts @@ -50,10 +50,17 @@ async function resolveEntity(id: string): Promise<{ projectId: string | null; pa // ── CLIENT CRUD ─────────────────────────────────────────────────────────────── +const emptyToNull = (v: unknown) => { + const s = typeof v === "string" ? v.trim() : ""; + return s === "" ? null : s; +}; + const clientSchema = z.object({ name: z.string().min(1, "Nome richiesto"), brand_name: z.string().min(1, "Brand name richiesto"), brief: z.string(), + email: z.string().optional().transform(emptyToNull), + phone: z.string().optional().transform(emptyToNull), slug: z .string() .regex(/^[a-z0-9-]{3,50}$/, "Slug non valido (es. mario-rossi, min 3 max 50 caratteri)") @@ -68,6 +75,8 @@ export async function updateClient(clientId: string, formData: FormData) { name: formData.get("name"), brand_name: formData.get("brand_name"), brief: formData.get("brief") ?? "", + email: formData.get("email") ?? "", + phone: formData.get("phone") ?? "", slug: formData.get("slug") ?? "", }); if (!parsed.success) throw new Error(parsed.error.issues[0].message); diff --git a/src/app/admin/clients/[id]/edit/page.tsx b/src/app/admin/clients/[id]/edit/page.tsx index 0cda1bb..357fbc4 100644 --- a/src/app/admin/clients/[id]/edit/page.tsx +++ b/src/app/admin/clients/[id]/edit/page.tsx @@ -62,6 +62,29 @@ export default async function EditClientPage({ /> +
+
+ + +
+
+ + +
+
+