feat: Interesado en leads, lada MX/USA, orígenes FRASA y subcategoría de descarte

- Mis leads: botón Interesado (label sembrado en Monday, mueve a Leads
  Progreso); el botón Agenda cita ahora solo vive en Mis oportunidades
- Descarte: además de la categoría, subcategoría del catálogo de Monday
  (workspace de referencia CRM FRASA); se guarda en la columna
  Subcategoria del tablero Motivos No Compra y el item queda en el grupo
  de su categoría
- Teléfonos con selector de lada 🇲🇽 +52 / 🇺🇸 +1 en captura rápida (Mi
  día), Alta Afluencia y Alta de Cliente (tel. casa y celular); Monday
  escribe countryShortName MX/US y el número compacto (rechaza espacios);
  el dedup sigue comparando por los últimos 10 dígitos
- Origen nuevo en captura rápida y afluencia: PASE, PROSPECCION ASESOR,
  ACTIVACION, FACEBOOK FRASA, REFERIDO, OTRO (sembrados en Leads.Origen y
  Oportunidades.Origen del Lead)
- Tests: 461 API + 16 mi-dia + 116 alta-clientes, verde
This commit is contained in:
2026-09-04 23:28:56 +00:00
parent 5d7c6f58cd
commit 27ba9ea8fa
14 changed files with 279 additions and 72 deletions

View File

@@ -89,6 +89,7 @@ interface CampoSelectProps extends CampoBaseProps {
opciones: { valor: string; etiqueta: string }[]
}
/** CampoSelect (dropdown) conectado a react-hook-form */
export function CampoSelect({ nombre, etiqueta, opciones, requerido, className }: CampoSelectProps) {
const { register, formState: { errors } } = useFormContext()
return (
@@ -106,3 +107,50 @@ export function CampoSelect({ nombre, etiqueta, opciones, requerido, className }
</label>
)
}
/**
* Teléfono con selector de lada (México +52 / USA +1). El campo del formulario
* guarda el valor compuesto "<prefijo> <número>" (p. ej. "+52 6641234567");
* el dedup del API compara por los últimos 10 dígitos, así que no afecta.
*/
export function CampoTelefono({ nombre, etiqueta, requerido, className }: CampoBaseProps) {
const { watch, setValue, formState: { errors } } = useFormContext()
const valor = ((watch(nombre) as string) ?? '').trim()
const lada = valor.startsWith('+1') ? '+1' : '+52'
const numero = valor.startsWith('+1') ? valor.slice(2).trim() : valor.startsWith('+52') ? valor.slice(3).trim() : valor
const actualizar = (nuevaLada: string, nuevoNumero: string) => {
const limpio = nuevoNumero.trim()
// Vacío se guarda vacío (sin prefijo huérfano); con número, compuesto
setValue(nombre, limpio === '' ? '' : `${nuevaLada} ${limpio}`, { shouldValidate: true })
}
return (
<div className={className ?? ''}>
<label htmlFor={nombre}>
<Etiqueta etiqueta={etiqueta} requerido={requerido} />
</label>
<div className="flex gap-2">
<select
id={`${nombre}-lada`}
aria-label="Lada"
value={lada}
onChange={(e) => actualizar(e.target.value, numero)}
className="w-28 shrink-0 rounded-lg border border-[#D1D5DB] bg-white px-2 py-2.5 text-sm transition-colors focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
>
<option value="+52">🇲🇽 +52</option>
<option value="+1">🇺🇸 +1</option>
</select>
<input
id={nombre}
type="tel"
placeholder="664 123 4567"
value={numero}
onChange={(e) => actualizar(lada, e.target.value)}
className={claseInput(nombre, errors)}
/>
</div>
<MensajeError nombre={nombre} />
</div>
)
}