feat(afluencia): captura completa de perfil en alta de afluencia

Agrega al formulario ligero y a POST /api/leads: tipo de propiedad,
medio de contacto, edad, estado civil, tipo de empleo, ocupacion,
rango salarial, credito, tipo de economia, califica y lo quiere para.
Selects obligatorios (estado civil, tipo de empleo, tipo de economia,
califica) con las etiquetas sembradas como labels status en el tablero
Leads de Monday.
This commit is contained in:
2026-09-02 01:38:13 +00:00
parent f7070c9c35
commit 52667df041
7 changed files with 388 additions and 27 deletions

View File

@@ -1,7 +1,16 @@
import { useState, type FormEvent } from 'react'
import { z } from 'zod'
import { crearLead, type LeadResumen } from '../services/api'
import { DESARROLLOS_LEAD, ORIGENES_LEAD } from '../data/constantes'
import {
DESARROLLOS_LEAD,
ESTADOS_CIVILES,
OPCIONES_CALIFICA,
ORIGENES_LEAD,
TIPOS_CREDITO,
TIPOS_ECONOMIA,
TIPOS_EMPLEO,
TIPOS_PROPIEDAD_LEAD,
} from '../data/constantes'
interface Props {
onVolver: () => void
@@ -23,7 +32,25 @@ interface Errores {
email?: string
}
const FORM_INICIAL = { nombre: '', telefono: '', email: '', origen: '', desarrollo: '', notas: '' }
const FORM_INICIAL = {
nombre: '',
telefono: '',
email: '',
origen: '',
desarrollo: '',
tipoPropiedad: '',
medioContacto: '',
edad: '',
estadoCivil: '',
tipoEmpleo: '',
ocupacion: '',
rangoSalarial: '',
credito: '',
tipoEconomia: '',
califica: '',
loQuierePara: '',
notas: '',
}
/**
* Captura ligera del interesado al entrar al embudo (POST /leads): crea el
@@ -64,6 +91,17 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro
...(form.email.trim() ? { email: form.email.trim() } : {}),
...(form.origen ? { origen: form.origen } : {}),
...(form.desarrollo ? { desarrollo: form.desarrollo } : {}),
...(form.tipoPropiedad ? { tipoPropiedad: form.tipoPropiedad } : {}),
...(form.medioContacto.trim() ? { medioContacto: form.medioContacto.trim() } : {}),
...(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)
@@ -127,9 +165,9 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro
}
return (
<form onSubmit={(e) => void enviar(e)} noValidate className="max-w-md">
<div className="space-y-6">
<label htmlFor="afluencia-nombre" className="block">
<form onSubmit={(e) => void enviar(e)} noValidate className="max-w-2xl">
<div className="grid gap-6 sm:grid-cols-2">
<label htmlFor="afluencia-nombre" className="block sm:col-span-2">
<span className={labelClase}>
Nombre<span className="text-red-700"> *</span>
</span>
@@ -158,7 +196,7 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro
</label>
<label htmlFor="afluencia-email" className="block">
<span className={labelClase}>Email</span>
<span className={labelClase}>Correo</span>
<input
id="afluencia-email"
type="email"
@@ -169,23 +207,6 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro
{errores.email && <p className="mt-1 text-xs text-red-700">{errores.email}</p>}
</label>
<label htmlFor="afluencia-origen" className="block">
<span className={labelClase}>Origen</span>
<select
id="afluencia-origen"
value={form.origen}
onChange={cambiar('origen')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{ORIGENES_LEAD.map((o) => (
<option key={o} value={o}>
{o}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-desarrollo" className="block">
<span className={labelClase}>Desarrollo de interés</span>
<select
@@ -203,7 +224,183 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro
</select>
</label>
<label htmlFor="afluencia-notas" className="block">
<label htmlFor="afluencia-tipo-propiedad" className="block">
<span className={labelClase}>Departamento o casa de interés</span>
<select
id="afluencia-tipo-propiedad"
value={form.tipoPropiedad}
onChange={cambiar('tipoPropiedad')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{TIPOS_PROPIEDAD_LEAD.map((t) => (
<option key={t} value={t}>
{t}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-medio-contacto" className="block">
<span className={labelClase}>Medio de contacto</span>
<input
id="afluencia-medio-contacto"
type="text"
value={form.medioContacto}
onChange={cambiar('medioContacto')}
className={inputClase}
/>
</label>
<label htmlFor="afluencia-origen" className="block">
<span className={labelClase}>¿Cómo se enteró de nosotros?</span>
<select
id="afluencia-origen"
value={form.origen}
onChange={cambiar('origen')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{ORIGENES_LEAD.map((o) => (
<option key={o} value={o}>
{o}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-edad" className="block">
<span className={labelClase}>Edad</span>
<input
id="afluencia-edad"
type="number"
min={15}
max={120}
value={form.edad}
onChange={cambiar('edad')}
className={inputClase}
/>
</label>
<label htmlFor="afluencia-estado-civil" className="block">
<span className={labelClase}>Estado civil</span>
<select
id="afluencia-estado-civil"
value={form.estadoCivil}
onChange={cambiar('estadoCivil')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{ESTADOS_CIVILES.map((e) => (
<option key={e.valor} value={e.etiqueta}>
{e.etiqueta}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-tipo-empleo" className="block">
<span className={labelClase}>Tipo de empleo</span>
<select
id="afluencia-tipo-empleo"
value={form.tipoEmpleo}
onChange={cambiar('tipoEmpleo')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{TIPOS_EMPLEO.map((t) => (
<option key={t.valor} value={t.etiqueta}>
{t.etiqueta}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-ocupacion" className="block">
<span className={labelClase}>Ocupación</span>
<input
id="afluencia-ocupacion"
type="text"
value={form.ocupacion}
onChange={cambiar('ocupacion')}
className={inputClase}
/>
</label>
<label htmlFor="afluencia-rango-salarial" className="block">
<span className={labelClase}>Rango salarial</span>
<input
id="afluencia-rango-salarial"
type="text"
value={form.rangoSalarial}
onChange={cambiar('rangoSalarial')}
className={inputClase}
/>
</label>
<label htmlFor="afluencia-credito" className="block">
<span className={labelClase}>Crédito</span>
<select
id="afluencia-credito"
value={form.credito}
onChange={cambiar('credito')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{TIPOS_CREDITO.map((c) => (
<option key={c.valor} value={c.etiqueta}>
{c.etiqueta}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-tipo-economia" className="block">
<span className={labelClase}>Tipo de economía</span>
<select
id="afluencia-tipo-economia"
value={form.tipoEconomia}
onChange={cambiar('tipoEconomia')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{TIPOS_ECONOMIA.map((t) => (
<option key={t.valor} value={t.etiqueta}>
{t.etiqueta}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-califica" className="block">
<span className={labelClase}>Califica</span>
<select
id="afluencia-califica"
value={form.califica}
onChange={cambiar('califica')}
className={inputClase}
>
<option value="">— Selecciona —</option>
{OPCIONES_CALIFICA.map((c) => (
<option key={c} value={c}>
{c}
</option>
))}
</select>
</label>
<label htmlFor="afluencia-lo-quiere-para" className="block">
<span className={labelClase}>Lo quiere para</span>
<input
id="afluencia-lo-quiere-para"
type="text"
value={form.loQuierePara}
onChange={cambiar('loQuierePara')}
className={inputClase}
/>
</label>
<label htmlFor="afluencia-notas" className="block sm:col-span-2">
<span className={labelClase}>Notas</span>
<input
id="afluencia-notas"