Conectar funnel B2C real sin claves: captura → fotos → presupuesto

El formulario de la landing ahora crea un lead real en BD y redirige a
/solicitud/[id]/fotos, donde el cliente sube fotos y datos de la reforma.
El orquestador simula los pasos de IA (pre-llamada, llamada, render) y
calcula el presupuesto DE VERDAD con el catálogo del reformista, dejando
el lead listo en el panel con render y desglose.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-05-31 14:29:21 +02:00
parent b95c588efe
commit b582f3ac33
10 changed files with 733 additions and 19 deletions

View File

@@ -1,6 +1,8 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { crearLead } from '@/app/solicitud/actions';
type FormData = {
name: string;
@@ -39,11 +41,13 @@ function validateForm(data: FormData): FormErrors {
}
function LeadForm() {
const router = useRouter();
const [formData, setFormData] = useState<FormData>(initialData);
const [consents, setConsents] = useState(initialConsents);
const [errors, setErrors] = useState<FormErrors>({});
const [touched, setTouched] = useState<Partial<Record<keyof FormData, boolean>>>({});
const [status, setStatus] = useState<SubmitStatus>('idle');
const [submitError, setSubmitError] = useState<string | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
@@ -74,12 +78,20 @@ function LeadForm() {
if (!consentsGranted) return;
setStatus('loading');
await new Promise((resolve) => setTimeout(resolve, 1500));
setStatus('success');
setFormData(initialData);
setConsents(initialConsents);
setTouched({});
setErrors({});
setSubmitError(null);
const result = await crearLead({
nombre: formData.name,
email: formData.email,
telefono: formData.phone,
consentPrivacidad: consents.privacy,
consentContratacion: consents.contracting,
});
if (!result.ok) {
setStatus('error');
setSubmitError(result.error);
return;
}
router.push(`/solicitud/${result.leadId}/fotos`);
};
const handleReset = () => {
@@ -273,6 +285,12 @@ function LeadForm() {
</label>
</fieldset>
{submitError && (
<p className="text-xs text-error font-medium" role="alert">
{submitError}
</p>
)}
{/* Submit */}
<button
type="submit"