From 57029bdd3d25909c7e93dc73ccb92b8db92a2b4e Mon Sep 17 00:00:00 2001 From: Consult AS Technology Date: Wed, 2 Sep 2026 01:48:45 +0000 Subject: [PATCH] feat(afluencia): el campo de interes muestra los modelos del desarrollo 'Al departamento o casa de interes' ahora es un select dependiente: al elegir desarrollo se cargan sus modelos (MODELOS_POR_PROYECTO) y al cambiar de desarrollo se resetea. En Monday la columna 'Tipo de Propiedad' se renombro a 'Modelo de Interes' con los 16 modelos como labels del status. --- .../src/controllers/leads.controller.test.ts | 4 +- .../src/controllers/leads.controller.ts | 4 +- api-gateway/src/services/monday.service.ts | 4 +- .../src/components/AltaAfluenciaForm.test.tsx | 19 ++++++++-- .../src/components/AltaAfluenciaForm.tsx | 37 ++++++++++++------- apps/alta-clientes/src/data/constantes.ts | 4 +- apps/alta-clientes/src/services/api.ts | 2 +- 7 files changed, 49 insertions(+), 25 deletions(-) diff --git a/api-gateway/src/controllers/leads.controller.test.ts b/api-gateway/src/controllers/leads.controller.test.ts index 7bc6bb5..f91b8ac 100644 --- a/api-gateway/src/controllers/leads.controller.test.ts +++ b/api-gateway/src/controllers/leads.controller.test.ts @@ -105,7 +105,7 @@ describe('crearLead (POST /api/leads)', () => { const monday = mondayMock() const r = await crearLead(monday, { ...datosBase, - tipoPropiedad: 'Casa', + modelo: 'HIMALAYA', medioContacto: 'WhatsApp', edad: 35, estadoCivil: 'Casado(a)', @@ -119,7 +119,7 @@ describe('crearLead (POST /api/leads)', () => { }) expect(r).toEqual({ id: 'lead-nuevo-1', oportunidadId: null }) expect(monday.leadsCreados[0]).toMatchObject({ - tipoPropiedadLabel: 'Casa', + modeloLabel: 'HIMALAYA', medioContacto: 'WhatsApp', edad: 35, estadoCivilLabel: 'Casado(a)', diff --git a/api-gateway/src/controllers/leads.controller.ts b/api-gateway/src/controllers/leads.controller.ts index 28b5e45..c4ff9f6 100644 --- a/api-gateway/src/controllers/leads.controller.ts +++ b/api-gateway/src/controllers/leads.controller.ts @@ -24,7 +24,7 @@ const crearLeadSchema = z.object({ notas: z.string().trim().min(1).optional(), // Campos de calificación de la afluencia (opcionales; los labels fijos del // front coinciden con los sembrados en Monday, no se validan contra el board) - tipoPropiedad: z.string().trim().min(1).optional(), + modelo: z.string().trim().min(1).optional(), medioContacto: z.string().trim().min(1).optional(), edad: z.coerce.number().int().min(15).max(120).optional(), estadoCivil: z.string().trim().min(1).optional(), @@ -125,7 +125,7 @@ export async function crearLead( origenLabel: datos.origen, desarrolloLabel: datos.desarrollo, notas: datos.notas, - tipoPropiedadLabel: datos.tipoPropiedad, + modeloLabel: datos.modelo, medioContacto: datos.medioContacto, edad: datos.edad, estadoCivilLabel: datos.estadoCivil, diff --git a/api-gateway/src/services/monday.service.ts b/api-gateway/src/services/monday.service.ts index 89b0b95..0c09559 100644 --- a/api-gateway/src/services/monday.service.ts +++ b/api-gateway/src/services/monday.service.ts @@ -882,7 +882,7 @@ export class MondayService { Origen: datos.origenLabel, Desarrollo: datos.desarrolloLabel, Notas: datos.notas, - 'Tipo de Propiedad': datos.tipoPropiedadLabel, + 'Modelo de Interés': datos.modeloLabel, 'Medio de Contacto': datos.medioContacto, Edad: datos.edad, 'Estado Civil': datos.estadoCivilLabel, @@ -1095,7 +1095,7 @@ export interface DatosLead { desarrolloLabel?: string | undefined // label real de la columna status 'Desarrollo' del board Leads notas?: string | undefined // Campos de calificación del interesado (labels reales de las columnas status del board Leads) - tipoPropiedadLabel?: string | undefined + modeloLabel?: string | undefined // label de la columna status 'Modelo de Interés' (modelo según desarrollo) medioContacto?: string | undefined edad?: number | undefined estadoCivilLabel?: string | undefined diff --git a/apps/alta-clientes/src/components/AltaAfluenciaForm.test.tsx b/apps/alta-clientes/src/components/AltaAfluenciaForm.test.tsx index 130a8b8..8af133d 100644 --- a/apps/alta-clientes/src/components/AltaAfluenciaForm.test.tsx +++ b/apps/alta-clientes/src/components/AltaAfluenciaForm.test.tsx @@ -71,7 +71,19 @@ describe('AltaAfluenciaForm', () => { const opcionesDe = (label: RegExp) => Array.from((screen.getByLabelText(label) as HTMLSelectElement).options).map((o) => o.value) - expect(opcionesDe(/Departamento o casa de interés/)).toEqual(['', 'Casa', 'Departamento']) + // El select de modelo arranca deshabilitado hasta elegir desarrollo + const selectModelo = screen.getByLabelText(/Departamento o casa de interés/) as HTMLSelectElement + expect(selectModelo.disabled).toBe(true) + fireEvent.change(screen.getByLabelText(/Desarrollo de interés/), { target: { value: 'SENDA' } }) + expect(selectModelo.disabled).toBe(false) + expect(opcionesDe(/Departamento o casa de interés/)).toEqual([ + '', + 'HIMALAYA', + 'SANTIAGO', + 'ESMERALDA PA', + 'ESMERALDA PB', + 'SANTA CRUZ', + ]) expect(opcionesDe(/^Estado civil/)).toEqual(['', 'Soltero(a)', 'Casado(a)', 'Unión libre', 'Divorciado(a)', 'Viudo(a)']) expect(opcionesDe(/Tipo de empleo/)).toEqual(['', 'Asalariado', 'Independiente']) expect(opcionesDe(/^Crédito/)).toEqual([ @@ -93,7 +105,8 @@ describe('AltaAfluenciaForm', () => { fetchMock.mockResolvedValueOnce(respuesta({ id: 'l-1', estado: 'NUEVO', oportunidadId: 'op-1' })) render( undefined} onPasarAAltaCliente={() => undefined} />) llenarRequeridos() - fireEvent.change(screen.getByLabelText(/Departamento o casa de interés/), { target: { value: 'Casa' } }) + fireEvent.change(screen.getByLabelText(/Desarrollo de interés/), { target: { value: 'SENDA' } }) + fireEvent.change(screen.getByLabelText(/Departamento o casa de interés/), { target: { value: 'HIMALAYA' } }) fireEvent.change(screen.getByLabelText(/Medio de contacto/), { target: { value: 'Llamada' } }) fireEvent.change(screen.getByLabelText(/^Edad/), { target: { value: '35' } }) fireEvent.change(screen.getByLabelText(/^Estado civil/), { target: { value: 'Casado(a)' } }) @@ -109,7 +122,7 @@ describe('AltaAfluenciaForm', () => { await screen.findByRole('status') const [, init] = fetchMock.mock.calls[0] as [string, RequestInit] expect(JSON.parse(String(init.body))).toMatchObject({ - tipoPropiedad: 'Casa', + modelo: 'HIMALAYA', medioContacto: 'Llamada', edad: '35', estadoCivil: 'Casado(a)', diff --git a/apps/alta-clientes/src/components/AltaAfluenciaForm.tsx b/apps/alta-clientes/src/components/AltaAfluenciaForm.tsx index 4197121..16802f8 100644 --- a/apps/alta-clientes/src/components/AltaAfluenciaForm.tsx +++ b/apps/alta-clientes/src/components/AltaAfluenciaForm.tsx @@ -4,12 +4,12 @@ import { crearLead, type LeadResumen } from '../services/api' import { DESARROLLOS_LEAD, ESTADOS_CIVILES, + MODELOS_POR_PROYECTO, OPCIONES_CALIFICA, ORIGENES_LEAD, TIPOS_CREDITO, TIPOS_ECONOMIA, TIPOS_EMPLEO, - TIPOS_PROPIEDAD_LEAD, } from '../data/constantes' interface Props { @@ -38,7 +38,7 @@ const FORM_INICIAL = { email: '', origen: '', desarrollo: '', - tipoPropiedad: '', + modelo: '', medioContacto: '', edad: '', estadoCivil: '', @@ -69,6 +69,14 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro 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] ?? [] + const validar = (): boolean => { const nuevos: Errores = {} if (!form.nombre.trim()) nuevos.nombre = 'El nombre es obligatorio' @@ -91,7 +99,7 @@ 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.modelo ? { modelo: form.modelo } : {}), ...(form.medioContacto.trim() ? { medioContacto: form.medioContacto.trim() } : {}), ...(form.edad.trim() ? { edad: form.edad.trim() } : {}), ...(form.estadoCivil ? { estadoCivil: form.estadoCivil } : {}), @@ -212,7 +220,7 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente }: Pro -