fdcc938252
- Migration 0010: tabella proposals (id, slug, lead_id, client_id, offer_macro_id, content jsonb, state, selected_tier, accepted_at) applicata a prod via SSH tunnel - @anthropic-ai/sdk@0.105.0 installato; ANTHROPIC_API_KEY in .env.local - src/lib/proposal/: schema Zod ProposalContent, agente Claude Opus 4.8, assemble (AI + offerta DB + config consulente), queries, profile.ts - Admin: /admin/preventivi lista + /genera (pre-fill ?lead_id=X) + /[id] review - Sidebar: voce Preventivi + CTA globale lime "Genera preventivo" - LeadDetail: pulsante "Genera preventivo" → /admin/preventivi/genera?lead_id=X - Pagina pubblica /preventivo/[slug]: deck 20+ slide light-mode iamcavalli, navigazione frecce + dot + keyboard, accept/reject con guard immutabilità - STATE.md aggiornato (80%), 21-PLAN.md scritto nel formato GSD Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
113 lines
3.9 KiB
TypeScript
113 lines
3.9 KiB
TypeScript
import { z } from "zod";
|
||
|
||
// ── Schema delle sezioni generate dall'AI ──────────────────────────────────
|
||
// Le sezioni da OFFER DB (pricing tier) e CONFIG (bio/testimonianze) NON
|
||
// sono prodotte dall'AI e non fanno parte di questo schema.
|
||
|
||
export const ProblemNodeSchema = z.object({
|
||
id: z.string(), // "01" | "02" | ... | "05"
|
||
title: z.string(), // es. "La voce non è una. Sono tre."
|
||
subtitle: z.string(), // es. "LA VOCE"
|
||
body: z.string(), // paragrafo descrittivo
|
||
risk: z.string(), // testo del box "IL RISCHIO"
|
||
quote: z.string(), // citazione verbatim dal transcript
|
||
quoteAttribution: z.string(), // "NOME COGNOME · DISCOVERY YYYY-MM-DD"
|
||
});
|
||
|
||
export const SolutionNodeSchema = z.object({
|
||
id: z.string(), // specchia il problem node
|
||
title: z.string(), // es. "Tre voci coordinate, un solo brand."
|
||
subtitle: z.string(), // es. "SOLUZIONE 01 · LA VOCE"
|
||
transformation: z.string(), // testo del box "LA TRASFORMAZIONE"
|
||
throughWhat: z.array(z.string()), // bullet "ATTRAVERSO COSA" (3 voci)
|
||
});
|
||
|
||
export const DiagramNodeSchema = z.object({
|
||
id: z.string(),
|
||
label: z.string(),
|
||
});
|
||
|
||
export const ScopeSectionSchema = z.object({
|
||
scopeTitle: z.string(),
|
||
scopeBody: z.string(),
|
||
objectives: z.array(z.string()), // 3–5 bullet con "checkmark" visivo
|
||
});
|
||
|
||
export const DeliverablesSectionSchema = z.object({
|
||
deliverables: z.array(z.string()), // elenco output finali (tier-agnostic, macro)
|
||
outOfScope: z.array(z.string()), // ciò che NON è incluso
|
||
});
|
||
|
||
export const TimelineSectionSchema = z.object({
|
||
totalWeeks: z.number(),
|
||
phases: z.array(z.object({
|
||
label: z.string(), // es. "Audit Strategico"
|
||
startWeek: z.number(),
|
||
endWeek: z.number(),
|
||
color: z.enum(["cyan", "purple", "green"]), // palette fissa light-mode
|
||
})),
|
||
disclaimer: z.string(),
|
||
});
|
||
|
||
export const StagesRecapSchema = z.object({
|
||
stages: z.array(z.object({
|
||
number: z.string(), // "01" | "02" | "03"
|
||
tierLetter: z.string(), // "A" | "B" | "C"
|
||
label: z.string(), // es. "OPZIONE A – AUDIT"
|
||
headline: z.string(), // es. "Si capisce."
|
||
body: z.string(),
|
||
deliverable: z.string(), // es. "Audit decision document"
|
||
})),
|
||
});
|
||
|
||
export const ComparisonRowSchema = z.object({
|
||
component: z.string(), // es. "Brand Architecture System"
|
||
description: z.string(), // sottotitolo
|
||
inA: z.boolean(),
|
||
inB: z.boolean(),
|
||
inC: z.boolean(),
|
||
});
|
||
|
||
// Root schema — contenuto generato dall'AI
|
||
export const ProposalContentSchema = z.object({
|
||
// Slide "dove andiamo" (02)
|
||
vision: z.object({
|
||
headline: z.string(), // es. "Tre voci. Un solo brand."
|
||
body: z.string(), // paragrafo breve
|
||
}),
|
||
|
||
// Cap.02 — 5 nodi problema estratti dai transcript
|
||
problems: z.array(ProblemNodeSchema).min(3).max(5),
|
||
|
||
// Sintesi problema (5 nodi → 1 radice)
|
||
problemSynthesis: z.object({
|
||
rootCause: z.string(), // la domanda strategica non ancora risolta
|
||
nodeLabels: z.array(z.string()).length(5), // label per il diagramma
|
||
}),
|
||
|
||
// Cap.03 — soluzioni mirror
|
||
solutions: z.array(SolutionNodeSchema).min(3).max(5),
|
||
|
||
// Sintesi soluzione (1 direzione → 5 elevazioni)
|
||
solutionSynthesis: z.object({
|
||
direction: z.string(),
|
||
elevations: z.array(DiagramNodeSchema).length(5),
|
||
}),
|
||
|
||
// Cap.04 — scope, deliverable, timeline
|
||
scope: ScopeSectionSchema,
|
||
deliverables: DeliverablesSectionSchema,
|
||
timeline: TimelineSectionSchema,
|
||
|
||
// Slide di recap (3 tappe progressive)
|
||
stagesRecap: StagesRecapSchema,
|
||
|
||
// Tabella comparativa componenti × A/B/C
|
||
comparisonMatrix: z.array(ComparisonRowSchema).min(4).max(10),
|
||
});
|
||
|
||
export type ProposalContent = z.infer<typeof ProposalContentSchema>;
|
||
export type ProblemNode = z.infer<typeof ProblemNodeSchema>;
|
||
export type SolutionNode = z.infer<typeof SolutionNodeSchema>;
|
||
export type ComparisonRow = z.infer<typeof ComparisonRowSchema>;
|