new tabla and new dowload

This commit is contained in:
2026-03-18 18:38:01 -05:00
parent c1a9e5d195
commit 6c7be89a01
12 changed files with 353 additions and 62 deletions
+4 -4
View File
@@ -8,7 +8,7 @@ export default function DownloadReporteXLSX() {
const [loading, setLoading] = useState(false);
const handleDownload = async () => {
setLoading(true); // 🔹 Activa loader
setLoading(true);
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
@@ -37,7 +37,7 @@ export default function DownloadReporteXLSX() {
console.error(error);
alert("Hubo un problema al descargar el archivo.");
} finally {
setLoading(false); // 🔹 Reactiva botón y oculta loader
setLoading(false);
}
};
@@ -45,10 +45,10 @@ export default function DownloadReporteXLSX() {
<button
className={styles.downloadBtn}
onClick={handleDownload}
disabled={loading} // 🔹 Botón deshabilitado
disabled={loading}
>
{loading ? (
<div className={styles.loader}></div> // 🔹 Icono de cargando
<div className={styles.loader}></div>
) : (
<img src="/excel.svg" alt="Excel" className={styles.iconExcel} width={30} height={30}/>
)}
+74
View File
@@ -0,0 +1,74 @@
"use client";
import styles from "./style.module.css";
import Cookies from "js-cookie";
import { useState } from "react";
import axios from "axios";
interface Props {
filtros: any;
count?: boolean;
}
export default function DownloadTable({ filtros,count }: Props) {
const [loading, setLoading] = useState(false);
const handleDownload = async () => {
setLoading(true);
const token = Cookies.get("token");
try {
const endpoint = count
? "/equipos/excel/tabla/count"
: "/equipos/excel/tabla";
const response = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}${endpoint}`,
filtros,
{
headers: {
Authorization: `Bearer ${token}`,
},
responseType: "blob",
}
);
const url = window.URL.createObjectURL(new Blob([response.data]));
const a = document.createElement("a");
a.href = url;
a.download = "reporte.xlsx";
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error(error);
alert("Hubo un problema al descargar el archivo.");
} finally {
setLoading(false);
}
};
return (
<button
className={styles.downloadBtn}
onClick={handleDownload}
disabled={loading}
>
{loading ? (
<div className={styles.loader}></div>
) : (
<img
src="/excel.svg"
alt="Excel"
className={styles.iconExcel}
width={30}
height={30}
/>
)}
</button>
);
}
+57
View File
@@ -0,0 +1,57 @@
"use client";
import styles from "./style.module.css";
import Cookies from "js-cookie";
import { useState } from "react";
export default function DownloadTableCount() {
const [loading, setLoading] = useState(false);
const handleDownload = async () => {
setLoading(true);
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/equipos/excel/tabla/count`,
{ headers }
);
if (!response.ok) {
throw new Error("Error al descargar el archivo");
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "reporte.xlsx";
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error(error);
alert("Hubo un problema al descargar el archivo.");
} finally {
setLoading(false);
}
};
return (
<button
className={styles.downloadBtn}
onClick={handleDownload}
disabled={loading}
>
{loading ? (
<div className={styles.loader}></div>
) : (
<img src="/excel.svg" alt="Excel" className={styles.iconExcel} width={30} height={30}/>
)}
</button>
);
}
@@ -25,12 +25,12 @@ interface AntiguedadData {
}
const colores = [
"#095bd6",
"#e63946",
"#2a9d8f",
"#f4a261",
"#8d5cf6",
"#ff006e"
"#818CF8",
"#34D399",
"#FBBF24",
"#F87171",
"#67E8F9",
"#C084FC"
]
const api_url = process.env.NEXT_PUBLIC_API_URL;
@@ -90,7 +90,6 @@ export default function Antiguedad({ filtros }: Props) {
minWidth: "570px",
background: "white",
borderRadius: "10px",
margin: "5px auto",
padding: "1rem",
maxWidth: "100%"
}}
@@ -109,7 +108,9 @@ export default function Antiguedad({ filtros }: Props) {
<XAxis dataKey="antiguedad" fontSize={12} />
<YAxis />
<Tooltip />
<Tooltip
formatter={(value) => [`${value}`]}
/>
<Legend />
{tieneAdscripciones ? (
@@ -15,12 +15,12 @@ interface procesador {
}
const colores = [
"#095bd6",
"#e63946",
"#2a9d8f",
"#f4a261",
"#8d5cf6",
"#ff006e"
"#818CF8",
"#34D399",
"#FBBF24",
"#F87171",
"#67E8F9",
"#C084FC"
]
const api_url = process.env.NEXT_PUBLIC_API_URL;
@@ -64,7 +64,7 @@ export default function Procesador({ filtros }: Props) {
return (
<div style={{ width: "100%", background: "white", borderRadius: "10px", margin: "5px auto", padding: "1rem" }}>
<h1 style={{ margin: "0" }}>Procesador</h1>
<ResponsiveContainer width="100%" aspect={3.5} minHeight={270}>
<ResponsiveContainer width="100%" aspect={3.5}>
<BarChart data={dataTransformada}
width={500}
height={300}
@@ -76,7 +76,9 @@ export default function Procesador({ filtros }: Props) {
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="procesador" fontSize={12} angle={40} textAnchor="start" hide />
<YAxis />
<Tooltip />
<Tooltip
formatter={(value) => [`${value}`]}
/>
<Legend />
{tieneAdscripciones ? (
@@ -15,12 +15,12 @@ interface so {
}
const colores = [
"#095bd6",
"#e63946",
"#2a9d8f",
"#f4a261",
"#8d5cf6",
"#ff006e"
"#818CF8",
"#34D399",
"#FBBF24",
"#F87171",
"#67E8F9",
"#C084FC"
]
const api_url = process.env.NEXT_PUBLIC_API_URL;
@@ -75,7 +75,9 @@ export default function SistemaOperativo({ filtros }: Props) {
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="so" fontSize={12} angle={30} textAnchor="start" hide />
<YAxis />
<Tooltip />
<Tooltip
formatter={(value) => [`${value}`]}
/>
<Legend />
{tieneAdscripciones ? (
@@ -15,12 +15,12 @@ interface UsoData {
}
const colores = [
"#095bd6",
"#e63946",
"#2a9d8f",
"#f4a261",
"#8d5cf6",
"#ff006e"
"#818CF8",
"#34D399",
"#FBBF24",
"#F87171",
"#67E8F9",
"#C084FC"
]
const api_url = process.env.NEXT_PUBLIC_API_URL;
@@ -64,7 +64,7 @@ export default function Uso({ filtros }: Props) {
}
return (
<div style={{ minWidth: "570px", background: "white", borderRadius: "10px", margin: "5px auto", padding: "1rem" }}>
<div style={{ minWidth: "570px", background: "white", borderRadius: "10px", padding: "1rem" }}>
<h1 style={{ margin: "0" }}>Uso</h1>
<ResponsiveContainer width="100%" aspect={2.5} minHeight={220}>
<BarChart data={dataTransformada}
@@ -77,7 +77,9 @@ export default function Uso({ filtros }: Props) {
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="uso" fontSize={12} />
<YAxis />
<Tooltip />
<Tooltip
formatter={(value) => [`${value}`]}
/>
<Legend />
{tieneAdscripciones ? (
@@ -42,51 +42,81 @@
.chip {
display: flex;
align-items: center;
background: #f1f3f5;
background: linear-gradient(135deg, #2563eb, #7c3aed);
border-radius: 6px;
padding: 5px 10px;
font-size: 13px;
text-align: start;
color: white;
}
.remove {
border: none;
background: none;
margin-left: 6px;
background: rgba(255,255,255,0.2);
margin-left: 8px;
cursor: pointer;
color: #777;
color: white;
border-radius: 50%;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
transition: all 0.2s ease;
}
.remove:hover {
color: #c0392b;
background: rgba(255,255,255,0.4);
}
.button {
margin-top: 20px;
padding: 8px 14px;
padding: 12px 20px;
border: none;
border-radius: 6px;
background: #2563eb;
border-radius: 10px;
background: linear-gradient(135deg, #2563eb, #4f46e5);
color: white;
font-size: 13px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.25s ease;
box-shadow: 0 6px 15px rgba(37, 99, 235, 0.3);
}
.button:hover {
background: #1d4ed8;
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(37, 99, 235, 0.4);
}
.graficas {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 1rem;
}
.graficas > div {
background: white;
border-radius: 16px !important;
padding: 15px !important;
box-shadow: 0 10px 25px rgba(0,0,0,0.08);
transition: all 0.3s ease;
}
.graficas > div:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0,0,0,0.12);
}
.filtro {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: start;
justify-content: space-between;
}
.containerSelection {
width: 100%;
min-width: 200px
width: 200px
}
@@ -0,0 +1,83 @@
'use client'
import { useEffect, useState } from 'react'
import styles from './TableAntiguedad.module.css'
import axios from 'axios'
import DownloadTable from '@/components/Dowload/tabla'
interface Props {
filtros: any
count: boolean
}
const api_url = process.env.NEXT_PUBLIC_API_URL;
export default function TableAntiguedad({ filtros, count }: Props) {
const [data, setData] = useState<any[]>([])
useEffect(() => {
const getUso = async () => {
const endpoint = count
? `${api_url}/equipos/tabla/count`
: `${api_url}/equipos/tabla`
const response = await axios.post(endpoint, filtros)
setData(response.data)
}
getUso()
}, [filtros, count])
const columnas = data.length > 0 ? Object.keys(data[0]) : []
return (
<div className={styles.container}>
{count ? <DownloadTable filtros={filtros} count={true} /> : <DownloadTable filtros={filtros} count={false} />}
<table className={styles.table}>
<thead>
<tr>
{columnas.map((col) => (
<th key={col}>{formatearTitulo(col)}</th>
))}
</tr>
</thead>
<tbody>
{data.length === 0 ? (
<tr>
<td colSpan={columnas.length} className={styles.empty}>
No hay datos
</td>
</tr>
) : (
data.map((item, index) => (
<tr key={index}>
{columnas.map((col) => (
<td key={col}>
{item[col] ?? '-'}
</td>
))}
</tr>
))
)}
</tbody>
</table>
</div>
)
}
function formatearTitulo(col: string) {
const nombres: any = {
inventario: 'No. Inventario',
adscripcion: 'Adscripción',
antiguedad: 'Antigüedad',
procesador: 'Procesador',
sistema_operativo: 'Sistema Operativo',
uso: 'Uso'
}
return nombres[col] || col
}
@@ -0,0 +1,44 @@
.container {
width: 100%;
overflow-x: auto;
}
.table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
background: #ffffff;
border-radius: 12px;
overflow: hidden;
}
.table thead {
background: linear-gradient(135deg, #a5a5a5, #878787);
color: white;
}
.table th {
padding: 12px;
text-align: center;
}
.table td {
padding: 10px 12px;
border-bottom: 1px solid #eee;
}
.table tbody tr:hover {
background-color: #f5f8ff;
transition: 0.2s;
}
.total {
font-weight: bold;
color: #095bd6;
}
.empty {
text-align: center;
padding: 20px;
color: #999;
}
+10 -6
View File
@@ -11,6 +11,7 @@ import SistemaOperativo from "./Graficas/SistemaOperativo"
import Uso from "./Graficas/Uso"
import Cookies from "js-cookie";
import TableAntiguedad from "./Tablas/Antiguedad"
const api_url = process.env.NEXT_PUBLIC_API_URL;
type AdscripcionOption = {
@@ -58,18 +59,18 @@ export default function Reporte() {
useEffect(() => {
const cargarDatos = async () => {
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
const [
adscripcionRes,
procesadorRes,
usoRes,
soRes,
] = await Promise.all([
axios.get(`${api_url}/equipos/adscripciones`,{ headers }),
axios.get(`${api_url}/equipos/procesador-tipo-equipos`,{ headers }),
axios.get(`${api_url}/equipos/usos`,{ headers }),
axios.get(`${api_url}/equipos/sistemas-operativos`,{ headers }),
axios.get(`${api_url}/equipos/adscripciones`, { headers }),
axios.get(`${api_url}/equipos/procesador-tipo-equipos`, { headers }),
axios.get(`${api_url}/equipos/usos`, { headers }),
axios.get(`${api_url}/equipos/sistemas-operativos`, { headers }),
])
setAdscripciones(adscripcionRes.data)
@@ -350,6 +351,9 @@ export default function Reporte() {
filtros={filtros}
/>
<TableAntiguedad filtros={filtros} count={true}/>
<TableAntiguedad filtros={filtros} count={false}/>
</section>
+1 -9
View File
@@ -35,15 +35,7 @@ export function proxy(request: NextRequest) {
export const config = {
matcher: [
"/escaner",
"/agregarEquipo",
"/editar",
"/crearCuenta",
"/equipoComputo",
"/perifericos",
"/ranking",
"/historial",
"/cambiarPass"
],
};
//IO