diff --git a/api-gateway/src/config/campos-departamento.ts b/api-gateway/src/config/campos-departamento.ts index 5b65aa5..819789a 100644 --- a/api-gateway/src/config/campos-departamento.ts +++ b/api-gateway/src/config/campos-departamento.ts @@ -1,13 +1,19 @@ export const DEPARTAMENTOS = ['OBRA', 'VENTAS', 'ESCRITURACION', 'FINANZAS', 'POSTVENTA'] as const -export const CAMPOS_POR_DEPARTAMENTO: Record = { +export const CAMPOS_POR_DEPARTAMENTO: Record = { OBRA: [ - { campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas' }, - { campo: 'to_c4_fianza', tipo: 'boolean', label: 'TO y C4-Fianza' }, - { campo: 'regimen_inscrito', tipo: 'boolean', label: 'Régimen Inscrito' }, - { campo: 'no_oficial', tipo: 'boolean', label: 'No. Oficial' }, - { campo: 'clave_catastral', tipo: 'boolean', label: 'Clave Catastral' }, - { campo: 'listas_avaluo', tipo: 'boolean', label: 'Listas para Avaluo' }, + { campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas', conFecha: 'certificacion_gas_fecha' }, + { campo: 'certificacion_gas_fecha', tipo: 'date', label: 'Fecha Certificación Gas' }, + { campo: 'to_c4_fianza', tipo: 'boolean', label: 'TO y C4-Fianza', conFecha: 'to_c4_fianza_fecha' }, + { campo: 'to_c4_fianza_fecha', tipo: 'date', label: 'Fecha TO y C4-Fianza' }, + { campo: 'regimen_inscrito', tipo: 'boolean', label: 'Régimen Inscrito', conFecha: 'regimen_inscrito_fecha' }, + { campo: 'regimen_inscrito_fecha', tipo: 'date', label: 'Fecha Régimen Inscrito' }, + { campo: 'no_oficial', tipo: 'boolean', label: 'No. Oficial', conFecha: 'no_oficial_fecha' }, + { campo: 'no_oficial_fecha', tipo: 'date', label: 'Fecha No. Oficial' }, + { campo: 'clave_catastral', tipo: 'boolean', label: 'Clave Catastral', conFecha: 'clave_catastral_fecha' }, + { campo: 'clave_catastral_fecha', tipo: 'date', label: 'Fecha Clave Catastral' }, + { campo: 'listas_avaluo', tipo: 'boolean', label: 'Listas para Avaluo', conFecha: 'listas_avaluo_fecha' }, + { campo: 'listas_avaluo_fecha', tipo: 'date', label: 'Fecha Listas para Avaluo' }, { campo: 'habitabilidad', tipo: 'date', label: 'Habitabilidad a partir de' }, { campo: 'comentarios_obra', tipo: 'text', label: 'Comentarios' }, ], diff --git a/apps/landing/src/components/TablaCaptura.test.tsx b/apps/landing/src/components/TablaCaptura.test.tsx index 5877291..d6a0ae8 100644 --- a/apps/landing/src/components/TablaCaptura.test.tsx +++ b/apps/landing/src/components/TablaCaptura.test.tsx @@ -206,4 +206,49 @@ describe('TablaCaptura', () => { fireEvent.click(screen.getByText('Siguiente')) expect(screen.getByText(/Mostrando 51–60 de 60/)).toBeTruthy() }) + + it('boolean con conFecha: fecha + checkbox en una celda, semáforo y dirty del campo fecha', () => { + const onGuardar = vi.fn() + const camposEmparejados: CampoDef[] = [ + { campo: 'certificacion_gas', tipo: 'boolean', label: 'Certificación Gas', conFecha: 'certificacion_gas_fecha' }, + { campo: 'certificacion_gas_fecha', tipo: 'date', label: 'Fecha Certificación Gas' }, + ] + const datosEmparejados: DatosCaptura = { + // Check activo → fecha en verde + '409-1': { certificacion_gas: 'true', certificacion_gas_fecha: '2026-01-01' }, + // Fecha pasada sin check → fecha en rojo + '409-2': { certificacion_gas: null, certificacion_gas_fecha: '2026-01-01' }, + } + + const { container } = render( + , + ) + + // La columna standalone de fecha se oculta: solo Unidad + Certificación Gas + expect(screen.getAllByRole('columnheader')).toHaveLength(2) + expect(screen.queryByText('Fecha Certificación Gas')).toBeNull() + + // Un date input y un checkbox por unidad + const fechas = container.querySelectorAll('input[type="date"]') + expect(fechas).toHaveLength(2) + expect((fechas[0] as HTMLInputElement).value).toBe('2026-01-01') + + // Semáforo: check activo → verde; fecha pasada sin check → rojo + expect(fechas[0].className).toContain('text-green-600') + expect(fechas[1].className).toContain('text-red-600') + + // Cambiar la fecha marca dirty en el campo _fecha y se guarda en el bulk + fireEvent.change(fechas[1], { target: { value: '2026-12-01' } }) + fireEvent.click(screen.getByText('Guardar cambios')) + + expect(onGuardar).toHaveBeenCalledTimes(1) + const dirty = onGuardar.mock.calls[0][0] + expect(dirty['409-2']).toEqual({ certificacion_gas_fecha: '2026-12-01' }) + }) }) diff --git a/apps/landing/src/components/TablaCaptura.tsx b/apps/landing/src/components/TablaCaptura.tsx index 2cb7fc7..cd9339b 100644 --- a/apps/landing/src/components/TablaCaptura.tsx +++ b/apps/landing/src/components/TablaCaptura.tsx @@ -24,6 +24,14 @@ function valorOriginal(datos: DatosCaptura, unidad: string, campo: string): stri return datos[unidad]?.[campo] ?? null } +/** Fecha de hoy en formato ISO local (YYYY-MM-DD) */ +function hoyISO(): string { + const d = new Date() + const mes = String(d.getMonth() + 1).padStart(2, '0') + const dia = String(d.getDate()).padStart(2, '0') + return `${d.getFullYear()}-${mes}-${dia}` +} + // ─── Componente de celda ───────────────────────────────────────────── interface CeldaProps { @@ -32,21 +40,53 @@ interface CeldaProps { options?: string[] onChange: (nuevo: string | null) => void dirty: boolean + /** Campo de fecha emparejado (booleanos de Obra) */ + conFecha?: string + valorFecha?: string | null + dirtyFecha?: boolean + onFechaChange?: (nuevo: string | null) => void } -function CeldaEditable({ tipo, valor, options, onChange, dirty }: CeldaProps) { +function CeldaEditable({ + tipo, + valor, + options, + onChange, + dirty, + conFecha, + valorFecha, + dirtyFecha, + onFechaChange, +}: CeldaProps) { const bordeDirty = dirty ? 'ring-2 ring-accent/40' : '' if (tipo === 'boolean') { + const checked = valor === 'true' + // Semáforo del campo fecha emparejado (Obra): verde si el check está activo, + // rojo si la fecha ya pasó sin activarse, neutro mientras no llegue la fecha. + const colorFecha = checked + ? 'text-green-600 dark:text-green-400' + : valorFecha && valorFecha < hoyISO() + ? 'text-red-600 dark:text-red-400' + : 'text-[#111827] dark:text-[#E2E8F0]' + const bordeFecha = dirtyFecha ? 'ring-2 ring-accent/40' : '' return ( -