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 { useState, useRef, useEffect, FormEvent } from 'react';
import { useRouter } from 'next/navigation';
import { crearLead } from '@/app/solicitud/actions';
type FormData = {
name: string;
@@ -44,6 +46,8 @@ export default function ContactForm() {
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 router = useRouter();
const sectionRef = useRef<HTMLElement>(null);
useEffect(() => {
@@ -94,13 +98,20 @@ export default function ContactForm() {
if (!consentsGranted) return;
setStatus('loading');
// TODO: integrar con backend captación (lead -> pending_photos). De momento mock.
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 = () => {
@@ -405,6 +416,12 @@ export default function ContactForm() {
</label>
</fieldset>
{submitError && (
<p className="text-sm text-error font-medium" role="alert">
{submitError}
</p>
)}
{/* Submit */}
<button
type="submit"