import { useEffect, useState, type FormEvent } from 'react' import { z } from 'zod' import { crearAfluencia, type LeadResumen } from '../services/api' import BusquedaLead from './BusquedaLead' import { DESARROLLOS_LEAD, ESTADOS_CIVILES, MODELOS_POR_PROYECTO, OPCIONES_CALIFICA, ORIGENES_LEAD, TIPOS_CREDITO, TIPOS_ECONOMIA, TIPOS_EMPLEO, } from '../data/constantes' interface Props { onVolver: () => void /** Conversión directa: tras registrar la afluencia, abrir el alta completa * prellenada con los datos recién capturados (sin rebuscar el lead) */ onPasarAAltaCliente: (lead: LeadResumen) => void /** Prefill desde Mis oportunidades (Mi día): valores iniciales del formulario */ valoresIniciales?: Partial /** Oportunidad de Monday a ACTUALIZAR al enviar (sin duplicar el item) */ oportunidadId?: string } const emailSchema = z.string().trim().email() const inputClase = 'w-full rounded-lg border border-[#D1D5DB] bg-white px-3 dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0] py-2.5 text-sm transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20' const labelClase = 'mb-1 block text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]' interface Errores { nombre?: string telefono?: string email?: string } const FORM_INICIAL = { nombre: '', nombrePila: '', apellidoPaterno: '', apellidoMaterno: '', telefono: '', email: '', origen: '', desarrollo: '', modelo: '', numDependientes: '', edad: '', estadoCivil: '', tipoEmpleo: '', ocupacion: '', rangoSalarial: '', credito: '', tipoEconomia: '', califica: '', loQuierePara: '', notas: '', } /** * Captura ligera del interesado al entrar al embudo (POST /leads): crea el * lead en Monday y su oportunidad en etapa Afluencia. El alta completa de * cliente sigue siendo el wizard del picker. */ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente, valoresIniciales, oportunidadId }: Props) { const [form, setForm] = useState({ ...FORM_INICIAL, ...valoresIniciales }) const [lada, setLada] = useState<'MX' | 'US'>( (valoresIniciales?.telefono ?? '').trim().startsWith('+1') ? 'US' : 'MX', ) const [errores, setErrores] = useState({}) const [enviando, setEnviando] = useState(false) const [leadCreado, setLeadCreado] = useState(null) const [asignacion, setAsignacion] = useState<'capturista' | 'round-robin' | null>(null) const [aviso, setAviso] = useState('') const cambiar = (campo: keyof typeof FORM_INICIAL) => (e: { target: { value: string } }) => { setForm((prev) => ({ ...prev, [campo]: e.target.value })) setErrores((prev) => ({ ...prev, [campo]: undefined })) } // Al cambiar de desarrollo se resetea el modelo (las opciones dependen de él) const cambiarDesarrollo = (e: { target: { value: string } }) => { setForm((prev) => ({ ...prev, desarrollo: e.target.value, modelo: '' })) } const modelosDelDesarrollo = MODELOS_POR_PROYECTO[form.desarrollo as keyof typeof MODELOS_POR_PROYECTO] ?? [] /** * Jala los datos de un lead u oportunidad ya capturados en Monday: * nombre/contacto siempre; origen, desarrollo y modelo solo si el valor del * lead existe entre las opciones del formulario. */ const prellenarDesdeLead = (lead: LeadResumen) => { // Teléfono con prefijo internacional: separa lada y número para el selector const tel = lead.telefono.trim() const esUsa = tel.startsWith('+1') // Strip lada: +52 or +1 prefix let numero = tel if (tel.startsWith('+52 ')) numero = tel.slice(4).trim() else if (tel.startsWith('+52')) numero = tel.slice(3).trim() else if (tel.startsWith('+1 ')) numero = tel.slice(3).trim() else if (tel.startsWith('+1')) numero = tel.slice(2).trim() setLada(esUsa ? 'US' : 'MX') setForm((prev) => { const desarrollo = lead.desarrollo !== undefined && (DESARROLLOS_LEAD as readonly string[]).includes(lead.desarrollo) ? lead.desarrollo : prev.desarrollo const modelos = MODELOS_POR_PROYECTO[desarrollo as keyof typeof MODELOS_POR_PROYECTO] ?? [] const modelo = lead.modeloInteres !== undefined && (modelos as readonly string[]).includes(lead.modeloInteres) ? lead.modeloInteres : '' // Split name: lead.nombre may contain full name or just first name const partes = lead.nombre.trim().split(/\s+/) const nombrePila = partes[0] || '' const apellidoPaterno = partes.length > 1 ? partes[1] : '' const apellidoMaterno = partes.length > 2 ? partes.slice(2).join(' ') : '' return { ...prev, nombre: lead.nombre, nombrePila, apellidoPaterno, apellidoMaterno, telefono: numero, email: lead.email, origen: (ORIGENES_LEAD as readonly string[]).includes(lead.origen) ? lead.origen : prev.origen, desarrollo, modelo, } }) setErrores({}) setAviso('') } const validar = (): boolean => { const nuevos: Errores = {} if (!form.nombre.trim()) nuevos.nombre = 'El nombre es obligatorio' if (!form.telefono.trim()) nuevos.telefono = 'El teléfono es obligatorio' if (form.email.trim() && !emailSchema.safeParse(form.email).success) { nuevos.email = 'Correo electrónico inválido' } setErrores(nuevos) return Object.keys(nuevos).length === 0 } const enviar = async (e: FormEvent) => { e.preventDefault() setAviso('') if (!validar()) return setEnviando(true) // Teléfono con lada: " " (si ya trae +, se respeta tal cual) const numeroTel = form.telefono.trim() const telefono = numeroTel === '' ? '' : numeroTel.startsWith('+') ? numeroTel : `${lada === 'US' ? '+1' : '+52'} ${numeroTel}` const res = await crearAfluencia({ nombre: [form.nombrePila, form.apellidoPaterno, form.apellidoMaterno].filter(Boolean).join(' ').trim() || form.nombre.trim(), telefono, ...(oportunidadId !== undefined ? { oportunidadId } : {}), ...(form.email.trim() ? { email: form.email.trim() } : {}), ...(form.origen ? { origen: form.origen } : {}), ...(form.desarrollo ? { desarrollo: form.desarrollo } : {}), ...(form.modelo ? { modelo: form.modelo } : {}), ...(form.numDependientes ? { numDependientes: form.numDependientes } : {}), ...(form.edad.trim() ? { edad: form.edad.trim() } : {}), ...(form.estadoCivil ? { estadoCivil: form.estadoCivil } : {}), ...(form.tipoEmpleo ? { tipoEmpleo: form.tipoEmpleo } : {}), ...(form.ocupacion.trim() ? { ocupacion: form.ocupacion.trim() } : {}), ...(form.rangoSalarial.trim() ? { rangoSalarial: form.rangoSalarial.trim() } : {}), ...(form.credito ? { credito: form.credito } : {}), ...(form.tipoEconomia ? { tipoEconomia: form.tipoEconomia } : {}), ...(form.califica ? { califica: form.califica } : {}), ...(form.loQuierePara.trim() ? { loQuierePara: form.loQuierePara.trim() } : {}), ...(form.notas.trim() ? { notas: form.notas.trim() } : {}), }) setEnviando(false) if (res.ok) { setLeadCreado({ id: res.lead.id, nombre: [form.nombrePila, form.apellidoPaterno, form.apellidoMaterno].filter(Boolean).join(' ').trim() || form.nombre.trim(), telefono, email: form.email.trim(), estado: res.lead.estado, origen: form.origen, fuente: 'oportunidad', // la captura nueva vive en el tablero Oportunidades }) setAsignacion(res.lead.asignacion ?? 'round-robin') } else if (res.motivo === 'duplicado') { setAviso(`Ya existe un lead con ese teléfono: ${res.nombre}.`) } else { setAviso('No se pudo registrar el lead. Revisa tu conexión e intenta de nuevo.') } } const capturarOtro = () => { setForm(FORM_INICIAL) setLada('MX') setErrores({}) setAviso('') setLeadCreado(null) setAsignacion(null) } // Redirigir a Mi día 2 s después de registrar la afluencia useEffect(() => { if (leadCreado === null) return const timer = setTimeout(() => { window.location.href = '/hoy/' }, 2000) return () => clearTimeout(timer) }, [leadCreado]) if (leadCreado !== null) { return (

{oportunidadId !== undefined ? 'Afluencia actualizada — el perfil completo quedó guardado en la oportunidad' : asignacion === 'capturista' ? 'Registrado en afluencia — asignado a ti' : 'Registrado en afluencia — se asignará por round-robin'}

Redirigiendo a Mi día en unos segundos…

{oportunidadId === undefined && ( )}
) } return (
void enviar(e)} noValidate> {oportunidadId !== undefined ? (

Estás completando la afluencia de {form.nombre || 'esta oportunidad'} — al enviar se actualiza su oportunidad en Monday (no se duplica).

) : (

Si el interesado ya está en Monday (lead u oportunidad), sus datos se prellenan solos.

)}
{errores.telefono &&

{errores.telefono}

}
{aviso && (

{aviso}

)}
) }