se agregaron conexiones
This commit is contained in:
+46
-13
@@ -1,28 +1,57 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import FuenteCard from "@/components/fuenteCard";
|
||||
import CargaArchivo from "@/components/cargaArchivo";
|
||||
import Button from "@/components/button";
|
||||
import { useAuthRedirect } from "@/hooks/auth";
|
||||
|
||||
const fuentes = [
|
||||
{ nombre: "Fuente 1", fecha: "Martes 3 de mayo 2025", activa: true },
|
||||
{ nombre: "Fuente 2", fecha: "Martes 3 de mayo 2025", activa: true },
|
||||
{ nombre: "Fuente 3", fecha: "Martes 3 de mayo 2025", activa: true },
|
||||
{ nombre: "Fuente 4", fecha: "Martes 3 de mayo 2025", activa: false },
|
||||
];
|
||||
interface Fuente {
|
||||
nombre: string;
|
||||
fecha: string;
|
||||
activa: boolean;
|
||||
id_mov: number;
|
||||
}
|
||||
|
||||
export default function Dashboard() {
|
||||
const loading = useAuthRedirect();
|
||||
const [fuentes, setFuentes] = useState<Fuente[]>([]);
|
||||
|
||||
if (loading) {
|
||||
return <p></p>;
|
||||
}
|
||||
useEffect(() => {
|
||||
const fetchMovimientos = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) return;
|
||||
|
||||
const res = await fetch("http://localhost:4000/excel/movimientos", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
const fuentesConvertidas: Fuente[] = data.map((mov: any) => ({
|
||||
nombre: `Movimiento ${mov.id_mov}`,
|
||||
id_mov: mov.id_mov,
|
||||
fecha: new Date(mov.fecha_mov).toLocaleString("es-MX", {
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}),
|
||||
activa: true, // o ajusta según lógica
|
||||
|
||||
|
||||
}));
|
||||
|
||||
setFuentes(fuentesConvertidas);
|
||||
};
|
||||
|
||||
fetchMovimientos();
|
||||
}, []);
|
||||
|
||||
const handleDownload = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
if (!token) {
|
||||
alert("Token no encontrado. Inicia sesión.");
|
||||
return;
|
||||
@@ -57,6 +86,8 @@ export default function Dashboard() {
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <p>Cargando...</p>;
|
||||
|
||||
return (
|
||||
<main
|
||||
className="roboto"
|
||||
@@ -85,6 +116,7 @@ export default function Dashboard() {
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
gap: "16px",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
{fuentes.map((f, idx) => (
|
||||
@@ -93,16 +125,17 @@ export default function Dashboard() {
|
||||
nombre={f.nombre}
|
||||
fecha={f.fecha}
|
||||
activa={f.activa}
|
||||
id_mov={f.id_mov}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mb-4">Carga</h2>
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">Carga</h2>
|
||||
<div>
|
||||
<CargaArchivo />
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mb-4">Descarga de Datos</h2>
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">Descarga de Datos</h2>
|
||||
<Button onClick={handleDownload} className="mb-3">
|
||||
Descarga Base de Datos
|
||||
</Button>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,24 +1,61 @@
|
||||
import React from "react";
|
||||
|
||||
type FuenteCardProps = {
|
||||
nombre: string;
|
||||
fecha: string;
|
||||
activa: boolean;
|
||||
id_mov: number;
|
||||
};
|
||||
|
||||
const FuenteCard: React.FC<FuenteCardProps> = ({ nombre, fecha, activa }) => {
|
||||
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"
|
||||
}`}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user