"use server"; import { db } from "@/db"; import { proposals, leads, clients } from "@/db/schema"; import { nanoid } from "nanoid"; import { eq, and, isNull } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; import { generateProposalContent } from "@/lib/proposal/agent"; import { assembleProposal } from "@/lib/proposal/assemble"; import { getOfferEditorData } from "@/lib/offer-queries"; import { getTranscripts } from "@/lib/lead-service"; async function requireAdmin() { const session = await getServerSession(authOptions); if (!session) throw new Error("Non autorizzato"); } // Genera una bozza di preventivo con AI e salva nel DB export async function generateProposalDraft(formData: FormData) { await requireAdmin(); const leadId = formData.get("lead_id") as string | null; const clientId = formData.get("client_id") as string | null; const offerMacroId = formData.get("offer_macro_id") as string; if (!offerMacroId) throw new Error("offer_macro_id richiesto"); if (!leadId && !clientId) throw new Error("lead_id o client_id richiesto"); // Carica dati necessari const offer = await getOfferEditorData(offerMacroId); if (!offer) throw new Error("Offerta non trovata"); let lead = undefined; let client = undefined; let transcripts: Awaited> = []; if (leadId) { const [leadRow] = await db .select({ id: leads.id, name: leads.name, email: leads.email, company: leads.company, notes: leads.notes }) .from(leads) .where(eq(leads.id, leadId)) .limit(1); lead = leadRow; transcripts = await getTranscripts(leadId); } else if (clientId) { const [clientRow] = await db .select({ id: clients.id, name: clients.name, brand_name: clients.brand_name, brief: clients.brief }) .from(clients) .where(eq(clients.id, clientId)) .limit(1); client = clientRow; // Trascrizioni del cliente (via client_id) const { clientTranscripts } = await import("@/db/schema"); const { eq: drizzleEq, desc } = await import("drizzle-orm"); transcripts = await db .select() .from(clientTranscripts) .where(drizzleEq(clientTranscripts.client_id, clientId)) .orderBy(desc(clientTranscripts.call_date)); } const MODEL = "claude-opus-4-8"; // Chiama agente AI const { content: aiContent } = await generateProposalContent({ lead, client, transcripts, offer, }); // Assembla contenuto completo const assembled = assembleProposal({ lead, client, offer, aiContent, model: MODEL, }); // Salva nel DB const id = nanoid(); const slug = nanoid(); const title = `${assembled.header.clientName} × ${assembled.header.offerTitle}`; await db.insert(proposals).values({ id, slug, lead_id: leadId ?? null, client_id: clientId ?? null, offer_macro_id: offerMacroId, title, content: assembled as unknown as Record, model: MODEL, state: "draft", }); revalidatePath("/admin/preventivi"); redirect(`/admin/preventivi/${id}`); } // Pubblica la proposta (draft → published) export async function publishProposal(id: string) { await requireAdmin(); await db .update(proposals) .set({ state: "published", updated_at: new Date() }) .where(and(eq(proposals.id, id), eq(proposals.state, "draft"))); revalidatePath(`/admin/preventivi/${id}`); revalidatePath("/admin/preventivi"); } // Aggiorna il titolo della proposta export async function updateProposalTitle(id: string, title: string) { await requireAdmin(); await db .update(proposals) .set({ title: title.trim(), updated_at: new Date() }) .where(eq(proposals.id, id)); revalidatePath(`/admin/preventivi/${id}`); } // Elimina una proposta (solo draft) export async function deleteProposal(id: string) { await requireAdmin(); await db .delete(proposals) .where(and(eq(proposals.id, id), eq(proposals.state, "draft"))); revalidatePath("/admin/preventivi"); redirect("/admin/preventivi"); }