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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user