added new grafics

This commit is contained in:
2026-03-12 18:45:04 -05:00
parent fbac1e4000
commit d02fd04edf
13 changed files with 1083 additions and 40 deletions
+762 -33
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -18,7 +18,9 @@
"next": "^16.0.7",
"react": "19.2.0",
"react-dom": "19.2.0",
"react-hot-toast": "^2.6.0"
"react-hot-toast": "^2.6.0",
"react-select": "^5.10.2",
"recharts": "^3.8.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -0,0 +1,10 @@
import "../../styles/layout/reporte.scss";
export default function Page() {
return (
<div className="scanView_reporte">
<div className="container_reporte">
</div>
</div>
);
}
@@ -0,0 +1,61 @@
"use client";
import { useState } from "react";
import DownloadReporteXLSX from "@/components/Dowload/Reporte";
import Reporte from "@/components/reporteGraficas/reporte";
import Antiguedad from "@/components/AntiguedadEquipo/Antiguedad";
import "../../styles/layout/reporte.scss";
type PreguntaKey = "antiguedad" | "adscripcion";
const LABELS: Record<PreguntaKey, string> = {
antiguedad: "Antigüedad",
adscripcion: "Antigüedad por Adscripcion"
};
export default function Page() {
const [activeTab, setActiveTab] = useState<PreguntaKey>("antiguedad");
const renderPregunta = () => {
switch (activeTab) {
case "antiguedad":
return (
<Antiguedad />
);
case "adscripcion":
return (
<Reporte />
);
default:
return (
<div className="p-6 text-center text-gray-500">
Contenido no disponible aún.
</div>
);
}
};
return (
<div className="scanView_reporte">
<div className="container_reporte">
<div className="main-content_reporte">
<div className="tabs_reporte">
{Object.entries(LABELS).map(([key, label]) => (
<button
key={key}
className={`tab_reporte ${activeTab === key ? "active" : ""}`}
onClick={() => setActiveTab(key as PreguntaKey)}
aria-selected={activeTab === key}
role="tab"
>
{label}
</button>
))}
</div>
<div className={`data-table_reporte`}>{renderPregunta()}</div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,43 @@
'use client'
import axios from "axios";
import { useEffect, useState } from "react";
import { Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
interface antiguedad{
antiguedad:string;
total:number;
}
export default function Antiguedad() {
const [antiguedad,setAntiguedad]=useState<antiguedad[]>([])
useEffect(()=>{
const getAntiguedad = async()=>{
const response = await axios.get("http://localhost:3001/equipos/grafica")
setAntiguedad(response.data)
}
getAntiguedad()
},[])
return (
<ResponsiveContainer width="100%" aspect={2}>
<BarChart data={antiguedad}
width={500}
height={300}
margin={{
top:20,
}}
>
<CartesianGrid strokeDasharray="3 3"/>
<XAxis dataKey="antiguedad"/>
<YAxis/>
<Tooltip/>
<Legend/>
<Bar dataKey="total" fill="#0d6efd"/>
</BarChart>
</ResponsiveContainer>
)
}
@@ -0,0 +1,18 @@
export default function FormEquipo() {
return (
<form>
<label >Selecciona la antiguedad</label>
<select>
<option value=""></option>
</select>
<label>Selecciona la adscripcion</label>
<select>
<option value=""></option>
</select>
<label >Selecciona el edificio</label>
<select>
<option value=""></option>
</select>
</form>
)
}
+5
View File
@@ -49,6 +49,11 @@ function BarNavigation() {
<span>Perifericos</span>
</Link>
</li>
<li className="subMenu" onClick={toggleMenu}>
<Link href="/reportesGraficas" className="links">
<span>Reportes y Graficas</span>
</Link>
</li>
<li className="subMenu">
<button className="logout-button" onClick={cerrarSesion}>
Cerrar sesión
+4 -4
View File
@@ -40,19 +40,19 @@ export default function Pregunta7() {
const antig = item.antiguedad?.toLowerCase().trim();
switch (antig) {
case "Menor a 2 años":
case "menor a 2 años":
menor2 += total;
break;
case "Entre 2 y 3 años":
case "entre 2 y 3 años":
entre2_3 += total;
break;
case "Entre 4 y 5 años":
case "entre 4 y 5 años":
entre4_5 += total;
break;
case "Mayor a 6 años":
case "mayor a 6 años":
mayor6 += total;
break;
}
+1 -1
View File
@@ -18,7 +18,7 @@ function Header() {
"/ranking",
];
const privateNav = ["/equipoComputo", "/crearCuenta", "/perifericos"];
const privateNav = ["/equipoComputo", "/crearCuenta", "/perifericos","/reportesGraficas"];
return (
<header className={header.header}>
@@ -0,0 +1,76 @@
.container{
width: 100%;
margin: 0 auto;
padding:20px;
background:white;
border-radius:10px;
box-shadow:0 2px 8px rgba(0,0,0,0.08);
font-family:Arial, Helvetica, sans-serif;
font-size:14px;
}
.title{
font-size:18px;
margin-bottom:15px;
}
.label{
font-size:13px;
color:#444;
}
.listaContainer{
margin-top:20px;
}
.subtitle{
font-size:14px;
margin-bottom:10px;
}
.empty{
font-size:13px;
color:#777;
}
.lista{
display:flex;
flex-wrap:wrap;
gap:8px;
}
.chip{
display:flex;
align-items:center;
background:#f1f3f5;
border-radius:20px;
padding:5px 10px;
font-size:13px;
}
.remove{
border:none;
background:none;
margin-left:6px;
cursor:pointer;
color:#777;
}
.remove:hover{
color:#c0392b;
}
.button{
margin-top:20px;
padding:8px 14px;
border:none;
border-radius:6px;
background:#2563eb;
color:white;
font-size:13px;
cursor:pointer;
}
.button:hover{
background:#1d4ed8;
}
@@ -0,0 +1,99 @@
"use client"
import Select from "react-select"
import { useState } from "react"
import styles from "./Reporte.module.css"
type AdscripcionOption = {
value: string
label: string
}
export default function Reporte() {
const [adscripcionesSeleccionadas, setAdscripcionesSeleccionadas] = useState<AdscripcionOption[]>([])
const adscripciones: AdscripcionOption[] = [
{ value: "informatica", label: "Informática" },
{ value: "rh", label: "Recursos Humanos" },
{ value: "finanzas", label: "Finanzas" },
{ value: "juridico", label: "Jurídico" }
]
const agregarAdscripcion = (option: AdscripcionOption | null) => {
if (!option) return
const existe = adscripcionesSeleccionadas.some(a => a.value === option.value)
if (!existe) {
setAdscripcionesSeleccionadas([...adscripcionesSeleccionadas, option])
}
}
const eliminarAdscripcion = (value: string) => {
setAdscripcionesSeleccionadas(
adscripcionesSeleccionadas.filter(a => a.value !== value)
)
}
const generarReporte = () => {
console.log("Adscripciones seleccionadas:", adscripcionesSeleccionadas)
}
return (
<div className={styles.container}>
<h2 className={styles.title}>Reporte de Antigüedad de Equipos</h2>
<label className={styles.label}>Buscar adscripción</label>
<Select
options={adscripciones}
placeholder="Escribe para buscar..."
onChange={(value) => agregarAdscripcion(value)}
/>
<div className={styles.listaContainer}>
<h4 className={styles.subtitle}>
Adscripciones seleccionadas ({adscripcionesSeleccionadas.length})
</h4>
{adscripcionesSeleccionadas.length === 0 && (
<p className={styles.empty}>No hay adscripciones seleccionadas</p>
)}
<div className={styles.lista}>
{adscripcionesSeleccionadas.map((ads) => (
<div key={ads.value} className={styles.chip}>
<span>{ads.label}</span>
<button
className={styles.remove}
onClick={() => eliminarAdscripcion(ads.value)}
>
</button>
</div>
))}
</div>
</div>
<button
className={styles.button}
onClick={generarReporte}
>
Generar reporte
</button>
</div>
)
}
+1 -1
View File
@@ -3,7 +3,7 @@ import type { NextRequest } from "next/server";
import { jwtDecode } from "jwt-decode";
const permisos: Record<number, string[]> = {
1: ["/crearCuenta", "/equipoComputo", "/perifericos","/escaner", "/agregarEquipo", "/editar","/historial","/ranking","/cambiarPass"],
1: ["/crearCuenta", "/equipoComputo", "/perifericos","/escaner", "/agregarEquipo", "/editar","/historial","/ranking","/cambiarPass","/reportesGraficas"],
2: ["/escaner", "/agregarEquipo", "/editar","/historial","/ranking","/cambiarPass"],
};