This commit is contained in:
Your Name
2025-06-25 05:19:07 -06:00
3 changed files with 143 additions and 23 deletions
+29 -7
View File
@@ -3,6 +3,7 @@ import React, { useState } from "react";
const CargaArchivo: React.FC = () => {
const [archivo, setArchivo] = useState<File | null>(null);
const [errores, setErrores] = useState<string[] | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
@@ -12,22 +13,20 @@ const CargaArchivo: React.FC = () => {
const handleUpload = async () => {
if (!archivo) return;
const token = localStorage.getItem("token"); // 👈 Token JWT guardado en login
const token = localStorage.getItem("token");
if (!token) {
alert("No hay token disponible. Inicia sesión.");
return;
}
const formData = new FormData();
formData.append("file", archivo); // 👈 ¡Debe coincidir con FileInterceptor('file')
formData.append("file", archivo);
try {
const res = await fetch("http://localhost:4000/excel/load", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`, // 👈 Incluye el token en el header
Authorization: `Bearer ${token}`,
},
body: formData,
});
@@ -35,8 +34,11 @@ const CargaArchivo: React.FC = () => {
const data = await res.json();
if (res.ok) {
alert("Archivo subido correctamente");
alert(`Archivo subido correctamente (ID Movimiento: ${data.id_movimiento})`);
setArchivo(null);
if (data.errors && data.errors.length > 0) {
setErrores(data.errors); // ← Mostrar ventana si hay errores
}
} else {
console.error(data);
alert(`Error al subir archivo: ${JSON.stringify(data)}`);
@@ -47,7 +49,7 @@ const CargaArchivo: React.FC = () => {
};
return (
<div className="w-full">
<div className="w-full relative">
<input
type="file"
accept=".xls,.xlsx"
@@ -65,6 +67,26 @@ const CargaArchivo: React.FC = () => {
>
Subir archivo
</button>
{/* Modal de errores */}
{errores && (
<div className="fixed top-10 right-10 bg-white border border-red-400 text-red-700 p-4 rounded-lg shadow-lg w-96 z-50">
<div className="flex justify-between items-center mb-2">
<strong className="text-lg">Reporte de Errores</strong>
<button
onClick={() => setErrores(null)}
className="text-red-600 font-bold text-xl hover:text-red-800"
>
×
</button>
</div>
<ul className="list-disc list-inside text-sm max-h-60 overflow-y-auto">
{errores.map((error, index) => (
<li key={index}>{error}</li>
))}
</ul>
</div>
)}
</div>
);
};
+63
View File
@@ -0,0 +1,63 @@
import React from "react";
type FuenteCardProps = {
nombre: string;
fecha: string;
activa: boolean;
id_mov: number;
};
const FuenteCard: React.FC<FuenteCardProps> = ({ nombre, fecha, activa, id_mov }) => {
const handleActivar = async () => {
const token = localStorage.getItem("token");
if (!token) {
alert("Token no encontrado.");
return;
}
try {
const res = await fetch(`http://localhost:4000/excel/activar-servicios/${id_mov}`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();
if (res.ok) {
alert("Usuarios validados correctamente");
} else {
alert(`Error: ${data.message || "No se pudo validar"}`);
}
} catch (error: any) {
alert(`Error en la solicitud: ${error.message}`);
}
};
return (
<div className="border rounded shadow-sm p-4 bg-white w-44">
<div className="flex items-center justify-between mb-2">
<span className="font-semibold text-sm">{nombre}</span>
<span
className={`w-3 h-3 rounded-full ${activa ? "bg-green-500" : "bg-red-500"}`}
/>
</div>
<p className="text-xs text-gray-700 leading-tight">
Fecha de carga:<br />
{fecha}
</p>
{activa && (
<button
onClick={handleActivar}
className="mt-3 px-3 py-1 btn btn-success btn-sm rounded-pill"
>
Validar
</button>
)}
</div>
);
};
export default FuenteCard;