# GAP Analysis: Manejo de Fechas Nulas en Admin Portal **ID:** GAP-DATE-HANDLING-001 **Fecha:** 2025-11-24 **Severidad:** ALTA (P1) **Analista:** Architecture-Analyst **Estado:** En corrección --- ## 1. RESUMEN EJECUTIVO Se identificó un **patrón inseguro de conversión de fechas** en múltiples componentes del Admin Portal donde se llama `new Date(value)` sin validar si `value` es `null` o `undefined`, resultando en "Invalid Date". --- ## 2. CAUSA RAÍZ ### El campo `lastLogin` puede ser null Cuando un usuario nunca ha iniciado sesión, `lastLogin` es `null`. Al ejecutar: ```typescript new Date(null) // → Thu Jan 01 1970 00:00:00 (epoch) new Date(undefined) // → Invalid Date ``` El error "date invalid" aparece cuando `toLocaleDateString()` se llama sobre un `Invalid Date`. ### Flujo del error: ``` Backend envía: { lastLogin: null } // Usuario nunca ha iniciado sesión ↓ Frontend: new Date(null).toLocaleDateString() ↓ Resultado: "1/1/1970" (incorrecto) o "Invalid Date" (error) ``` --- ## 3. INSTANCIAS DEL PROBLEMA ### Archivo Principal (Error Reportado) | Archivo | Línea | Código Problemático | |---------|-------|---------------------| | `UserManagementTable.tsx` | 106 | `{new Date(user.lastLogin).toLocaleDateString()}` | ### Otros Archivos con Mismo Patrón | # | Archivo | Línea | Campo | |---|---------|-------|-------| | 1 | `TeacherClassroomsTab.tsx` | 181 | `classroom.assignedAt` | | 2 | `ClassroomTeachersTab.tsx` | 214 | `teacher.assignedAt` | | 3 | `ErrorTrackingTab.tsx` | 212 | `trend.time_bucket` | | 4 | `UserActivityMonitor.tsx` | 211 | `activity.timestamp` | | 5 | `SystemHealthIndicators.tsx` | 215 | `incident.timestamp` | | 6 | `SystemPerformanceDashboard.tsx` | 219 | `metrics.timestamp` | | 7 | `AdminContentPage.tsx` | 141, 228, 275 | `row.createdAt`, `row.uploadedAt` | | 8 | `AdminInstitutionsPage.tsx` | 180 | `row.createdAt` | | 9 | `AdminGamificationPage.tsx` | 114 | `stats.lastModified` | | 10 | `TenantManagementPanel.tsx` | 349, 355 | `createdAt`, `lastActive` | | 11 | `FeatureFlagControls.tsx` | 345, 357, 365, 396 | Múltiples campos | | 12 | `ABTestingDashboard.tsx` | 306, 314 | `startDate`, `endDate` | | 13 | `MediaLibraryManager.tsx` | 260 | `file.uploadedAt` | | 14 | `EconomicInterventionPanel.tsx` | 384, 388 | `startDate`, `endDate` | | 15 | `ContentApprovalQueue.tsx` | 165, 284 | `item.submittedAt` | | 16 | `ContentVersionControl.tsx` | 114, 138, 192, 234 | `version.timestamp` | | 17 | `AdminDashboardHero.tsx` | 171 | `health.lastCheck` | | 18 | `SystemAlertsPanel.tsx` | 204 | `selectedAlert.timestamp` | | 19 | `RecentActionsTable.tsx` | 318, 418 | `action.timestamp` | | 20 | `OrganizationsTable.tsx` | 140 | `org.createdAt` | | 21 | `SystemLogsViewer.tsx` | 135 | `log.timestamp` | --- ## 4. PATRÓN CORRECTO ### Implementación Segura (Ya existe en AdminUsersPage.tsx:370) ```typescript // ✅ CORRECTO - Con validación {usr.lastLogin ? new Date(usr.lastLogin).toLocaleDateString('es-ES') : 'Nunca'} ``` ### Función Utilitaria Recomendada ```typescript // apps/frontend/src/shared/utils/formatters.ts /** * Formatea una fecha de forma segura, manejando null/undefined * @param dateValue - Valor de fecha (string, Date, null, undefined) * @param options - Opciones de formato * @param fallback - Texto a mostrar si la fecha es inválida * @returns Fecha formateada o fallback */ export function formatDateSafe( dateValue: string | Date | null | undefined, options: Intl.DateTimeFormatOptions = { dateStyle: 'short' }, fallback: string = 'N/A' ): string { if (!dateValue) return fallback; const date = new Date(dateValue); if (isNaN(date.getTime())) return fallback; return date.toLocaleDateString('es-ES', options); } ``` --- ## 5. PLAN DE CORRECCIÓN ### Prioridad 1: Error Reportado - **Archivo:** `UserManagementTable.tsx` - **Línea:** 106 - **Acción:** Agregar validación de null ### Prioridad 2: Componentes de Alta Visibilidad - `RecentActionsTable.tsx` (Dashboard principal) - `OrganizationsTable.tsx` (Dashboard principal) - `SystemAlertsPanel.tsx` (Dashboard principal) ### Prioridad 3: Resto de Componentes - Aplicar patrón seguro a todos los archivos listados --- ## 6. CRITERIOS DE ACEPTACIÓN - [ ] `UserManagementTable.tsx` no genera "Invalid Date" - [ ] Todos los componentes usan el patrón `value ? new Date(value)... : fallback` - [ ] `npm run type-check` pasa sin errores relacionados - [ ] Las fechas null muestran texto apropiado ("Nunca", "N/A", "Sin fecha", etc.) --- ## 7. ARCHIVOS A MODIFICAR | # | Archivo | Tipo de Cambio | |---|---------|----------------| | 1 | `UserManagementTable.tsx` | Agregar validación null para lastLogin | | 2 | `TeacherClassroomsTab.tsx` | Agregar validación null para assignedAt | | 3 | `ClassroomTeachersTab.tsx` | Agregar validación null para assignedAt | | 4+ | (Ver lista completa arriba) | Mismo patrón | --- **Versión:** 1.0 **Estado:** Listo para implementación