+ {/* Header */}
+
+
+
+
+
+
+
Lecturas
+
Registrar lecturas de medidores
+
+
+
+
+ {/* Content */}
+
+ {/* Info */}
+
+
+ Campos requeridos: meter_serial, reading_value
+
+
+ El medidor debe existir previamente. La fecha es opcional (por defecto: ahora).
+
+
+
+ {/* Template Download */}
+
+
+ {/* File Dropzone */}
+
+
+ {/* Upload Button */}
+
+
+ {/* Error Message */}
+ {error && (
+
+ )}
+
+ {/* Results */}
+
+
+
+ );
+}
diff --git a/upload-panel/src/components/ResultsDisplay.tsx b/upload-panel/src/components/ResultsDisplay.tsx
new file mode 100644
index 0000000..37bbd7e
--- /dev/null
+++ b/upload-panel/src/components/ResultsDisplay.tsx
@@ -0,0 +1,105 @@
+import { CheckCircle, XCircle, AlertTriangle } from 'lucide-react';
+import type { UploadResult } from '../api/upload';
+
+interface ResultsDisplayProps {
+ result: UploadResult | null;
+ type: 'meters' | 'readings';
+}
+
+export function ResultsDisplay({ result, type }: ResultsDisplayProps) {
+ if (!result) return null;
+
+ const hasErrors = result.errors.length > 0;
+ const processedCount = type === 'meters'
+ ? result.inserted + result.updated
+ : result.inserted;
+
+ return (
+
+ {/* Summary Header */}
+
+
+ {hasErrors ? (
+
+ ) : (
+
+ )}
+
+ Resultado de la carga
+
+
+
+
+ {/* Stats */}
+
+
+
+ {result.total} registros procesados
+
+
+ {type === 'meters' ? (
+ <>
+
+
+ {result.inserted} insertados
+
+
+
+ {result.updated} actualizados
+
+ >
+ ) : (
+
+
+ {result.inserted} lecturas insertadas
+
+ )}
+
+ {hasErrors && (
+
+
+ {result.errors.length} errores
+
+ )}
+
+
+ {/* Success message if no errors */}
+ {!hasErrors && processedCount > 0 && (
+
+
+ Todos los registros fueron procesados correctamente.
+
+
+ )}
+
+ {/* Error List */}
+ {hasErrors && (
+
+
+ Errores encontrados:
+
+
+
+
+
+ | Fila |
+ Campo |
+ Error |
+
+
+
+ {result.errors.map((error, index) => (
+
+ | {error.row} |
+ {error.field || '-'} |
+ {error.message} |
+
+ ))}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/upload-panel/src/index.css b/upload-panel/src/index.css
new file mode 100644
index 0000000..f1d8c73
--- /dev/null
+++ b/upload-panel/src/index.css
@@ -0,0 +1 @@
+@import "tailwindcss";
diff --git a/upload-panel/src/main.tsx b/upload-panel/src/main.tsx
new file mode 100644
index 0000000..2339d59
--- /dev/null
+++ b/upload-panel/src/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+import './index.css';
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+