Fix: Corregir pantalla blanca y mejorar carga masiva

- Fix error .toFixed() con valores DECIMAL de PostgreSQL (string vs number)
- Fix modal de carga masiva que se cerraba sin mostrar resultados
- Validar fechas antes de insertar en BD (evita error con "Installed")
- Agregar mapeos de columnas comunes (device_status, device_name, etc.)
- Normalizar valores de status (Installed -> ACTIVE, New_LoRa -> ACTIVE)
- Actualizar documentación del proyecto

Archivos modificados:
- src/pages/meters/MetersTable.tsx
- src/pages/consumption/ConsumptionPage.tsx
- src/pages/meters/MeterPage.tsx
- water-api/src/services/bulk-upload.service.ts
- ESTADO_ACTUAL.md
- CAMBIOS_SESION.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Exteban08
2026-01-23 23:13:48 +00:00
parent ab97987c6a
commit 6c7d448b2f
6 changed files with 388 additions and 299 deletions

View File

@@ -1,131 +1,171 @@
# Cambios Realizados en Esta Sesión
# Cambios Realizados - Sesión 2026-01-23
**Fecha:** 2026-01-23
## Resumen
Corrección de errores críticos que causaban pantalla blanca y mejoras en el sistema de carga masiva.
---
## Problema a Resolver
Pantalla en blanco al entrar a "Water Meters" y "Consumo" después de implementar carga masiva de lecturas.
## Problema 1: Pantalla Blanca en Water Meters y Consumo
---
### Síntoma
Al navegar a "Water Meters" o "Consumo", la página se quedaba en blanco.
## Cambios Realizados
### Causa
PostgreSQL devuelve valores DECIMAL como strings (ej: `"300.0000"`). El código llamaba `.toFixed()` directamente sobre estos strings, pero `.toFixed()` es un método de números, no de strings.
### 1. `src/api/client.ts` (línea ~224-237)
**Cambio:** Modificado `parseResponse` para manejar respuestas con paginación
### Solución
Convertir los valores a número con `Number()` antes de llamar `.toFixed()`.
### Archivos Modificados
**`src/pages/meters/MetersTable.tsx` (línea 75)**
```typescript
// ANTES:
if ('success' in data) {
if (data.success === false) { throw... }
return data.data as T; // Solo devolvía data.data
}
r.lastReadingValue?.toFixed(2)
// DESPUÉS:
if ('success' in data) {
if (data.success === false) { throw... }
// Si hay pagination, devolver objeto completo
if ('pagination' in data) {
return {
data: data.data,
pagination: data.pagination,
} as T;
r.lastReadingValue != null ? Number(r.lastReadingValue).toFixed(2) : "-"
```
**`src/pages/consumption/ConsumptionPage.tsx` (líneas 133, 213, 432)**
```typescript
// ANTES:
r.readingValue.toFixed(2)
summary?.avgReading.toFixed(1)
reading.readingValue.toFixed(2)
// DESPUÉS:
Number(r.readingValue).toFixed(2)
summary?.avgReading != null ? Number(summary.avgReading).toFixed(1) : "0"
Number(reading.readingValue).toFixed(2)
```
---
## Problema 2: Modal de Carga Masiva se Cerraba sin Mostrar Resultados
### Síntoma
Al subir un archivo Excel para carga masiva, el modal se cerraba inmediatamente sin mostrar cuántos registros se insertaron o qué errores hubo.
### Causa
El callback `onSuccess` cerraba el modal automáticamente:
```typescript
onSuccess={() => {
m.loadMeters();
setShowBulkUpload(false); // ← Cerraba antes de ver resultados
}}
```
### Solución
Separar la recarga de datos del cierre del modal. Ahora el modal solo se cierra cuando el usuario hace clic en "Cerrar".
### Archivo Modificado
**`src/pages/meters/MeterPage.tsx` (líneas 332-340)**
```typescript
// ANTES:
<MetersBulkUploadModal
onClose={() => setShowBulkUpload(false)}
onSuccess={() => {
m.loadMeters();
setShowBulkUpload(false);
}}
/>
// DESPUÉS:
<MetersBulkUploadModal
onClose={() => {
m.loadMeters();
setShowBulkUpload(false);
}}
onSuccess={() => {
m.loadMeters();
}}
/>
```
---
## Problema 3: Error de Fecha Inválida en Carga Masiva
### Síntoma
Al subir medidores, aparecía el error:
```
Fila X: invalid input syntax for type date: "Installed"
```
### Causa
El archivo Excel tenía columnas con valores como "Installed" o "New_LoRa" que el sistema interpretaba como fechas porque no estaban mapeadas correctamente.
### Solución
1. **Validar fechas**: Verificar que `installation_date` sea realmente una fecha válida antes de usarla.
2. **Más mapeos de columnas**: Agregar mapeos para columnas comunes como `device_status`, `device_name`, etc.
3. **Normalizar status**: Convertir valores como "Installed", "New_LoRa" a "ACTIVE".
### Archivo Modificado
**`water-api/src/services/bulk-upload.service.ts`**
Validación de fechas (líneas 183-195):
```typescript
let installationDate: string | undefined = undefined;
if (row.installation_date) {
const dateStr = String(row.installation_date).trim();
if (/^\d{4}[-/]\d{1,2}[-/]\d{1,2}/.test(dateStr) || /^\d{1,2}[-/]\d{1,2}[-/]\d{2,4}/.test(dateStr)) {
const parsed = new Date(dateStr);
if (!isNaN(parsed.getTime())) {
installationDate = parsed.toISOString().split('T')[0];
}
}
return data.data as T;
}
```
### 2. `src/api/readings.ts`
**Cambios en interface `MeterReading`:**
Mapeos de columnas adicionales (líneas 65-90):
```typescript
// ELIMINADO:
deviceId: string | null;
areaName: string | null;
// AGREGADO:
meterLocation: string | null;
concentratorId: string;
concentratorName: string;
projectName: string;
const mappings: Record<string, string> = {
// Serial number
'device_s/n': 'serial_number',
'device_sn': 'serial_number',
// Name
'device_name': 'name',
'meter_name': 'name',
// Status
'device_status': 'status',
// ... más mapeos
};
```
**Cambios en interface `ReadingFilters`:**
Normalización de status (líneas 210-225):
```typescript
// ELIMINADO:
areaName?: string;
// AGREGADO:
concentratorId?: string;
const statusMappings: Record<string, string> = {
'INSTALLED': 'ACTIVE',
'NEW_LORA': 'ACTIVE',
'NEW': 'ACTIVE',
'ENABLED': 'ACTIVE',
'DISABLED': 'INACTIVE',
// ...
};
```
**Cambios en interface `ReadingInput`:**
```typescript
// ELIMINADO:
deviceId?: string;
```
**Cambios en función `createReading`:**
```typescript
// ELIMINADO de backendData:
device_id: data.deviceId,
```
### 3. `src/api/projects.ts` (función `fetchProjects`)
**Cambio:** Ahora maneja respuestas paginadas
```typescript
// ANTES:
const response = await apiClient.get<Record<string, unknown>[]>('/api/projects');
return transformArray<Project>(response);
// DESPUÉS:
const response = await apiClient.get<...>('/api/projects');
if (response && typeof response === 'object' && 'data' in response && Array.isArray(response.data)) {
return transformArray<Project>(response.data);
}
return transformArray<Project>(response as Record<string, unknown>[]);
```
### 4. `src/api/concentrators.ts` (función `fetchConcentrators`)
**Cambio:** Mismo patrón que projects.ts para manejar paginación
### 5. `src/pages/consumption/ConsumptionPage.tsx`
**Cambios:**
- Línea ~94: `r.areaName``r.meterLocation` (filtro de búsqueda)
- Línea ~127: `"Área"``"Ubicación"` (header CSV)
- Línea ~132: `r.areaName``r.meterLocation` (datos CSV)
- Línea ~365: `"Área"``"Ubicación"` (header tabla)
- Línea ~428: `reading.areaName``reading.meterLocation` (celda tabla)
### 6. `water-api/src/services/reading.service.ts`
**Cambios previos (ya aplicados):**
- Eliminadas todas las referencias a `device_id` en queries SQL
- La columna `device_id` no existe en la tabla `meter_readings`
---
## Estado de los Servidores
## Archivos Modificados en Esta Sesión
### Backend (puerto 3000) ✅ Funcionando
```bash
curl http://localhost:3000/api/readings?pageSize=1
# Devuelve datos correctamente
```
### Frontend (puerto 5173) ⚠️ Pantalla blanca
- El servidor está corriendo
- Las páginas no renderizan correctamente
- Se necesita revisar la consola del navegador para ver el error específico
| Archivo | Cambio |
|---------|--------|
| `src/pages/meters/MetersTable.tsx` | Fix `.toFixed()` en lastReadingValue |
| `src/pages/consumption/ConsumptionPage.tsx` | Fix `.toFixed()` en readingValue y avgReading |
| `src/pages/meters/MeterPage.tsx` | Fix modal de carga masiva |
| `water-api/src/services/bulk-upload.service.ts` | Validación de fechas, mapeos de columnas, normalización de status |
| `ESTADO_ACTUAL.md` | Documentación actualizada |
| `CAMBIOS_SESION.md` | Este archivo |
---
## Para Debug
## Verificación
1. Abrir http://localhost:5173
2. F12 → Console
3. Navegar a "Water Meters" o "Consumo"
4. Copiar el error de la consola
El error más probable es:
- `Cannot read property 'map' of undefined` - si `transformArray` recibe undefined
- `TypeError: response.data is undefined` - si la respuesta no tiene la estructura esperada
1. ✅ La página de Water Meters carga correctamente
2. ✅ La página de Consumo carga correctamente
3. ✅ El modal de carga masiva muestra resultados
4. ✅ Errores de carga masiva se muestran claramente
5. ✅ Valores como "Installed" no causan error de fecha