new tabla and new dowload
This commit is contained in:
@@ -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}/>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -35,15 +35,7 @@ export function proxy(request: NextRequest) {
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
"/escaner",
|
||||
"/agregarEquipo",
|
||||
"/editar",
|
||||
"/crearCuenta",
|
||||
"/equipoComputo",
|
||||
"/perifericos",
|
||||
"/ranking",
|
||||
"/historial",
|
||||
"/cambiarPass"
|
||||
|
||||
],
|
||||
};
|
||||
//IO
|
||||
|
||||
Reference in New Issue
Block a user