336 lines
9.9 KiB
TypeScript
336 lines
9.9 KiB
TypeScript
"use client";
|
|
import { envConfig } from "@/app/lib/config";
|
|
import { useEffect, useState } from "react";
|
|
import axios from "axios";
|
|
import Cookies from "js-cookie";
|
|
import "./inscripciones.css";
|
|
import * as XLSX from "xlsx";
|
|
import { saveAs } from "file-saver";
|
|
|
|
interface Periodo {
|
|
id_periodo: number;
|
|
semestre: string;
|
|
fecha_inicio_servicio: string;
|
|
}
|
|
|
|
interface ApiResponse {
|
|
semestre: string;
|
|
carrera: string;
|
|
profesor: string;
|
|
femenino: string;
|
|
masculino: string;
|
|
total: string;
|
|
}
|
|
|
|
type Tipo = "periodo" | "mes" | "anio";
|
|
|
|
type TipoConteo = "WINDOWS" | "MACINTOSH" | "LINUX" | "PROFESORES";
|
|
|
|
interface TablaRow {
|
|
carrera: string;
|
|
semestre: string;
|
|
WINDOWS: number;
|
|
MACINTOSH: number;
|
|
LINUX: number;
|
|
PROFESORES: number;
|
|
TOTAL: number;
|
|
femenino: number;
|
|
masculino: number;
|
|
}
|
|
|
|
export default function Inscripciones() {
|
|
const [periodo, setPeriodo] = useState<Periodo[]>([]);
|
|
const [tipo, setTipo] = useState<Tipo>("periodo");
|
|
|
|
const [periodoInicio, setPeriodoInicio] = useState<any>(null);
|
|
const [periodoFin, setPeriodoFin] = useState<any>(null);
|
|
|
|
const [anio, setAnio] = useState<number | null>(null);
|
|
|
|
const [tablaData, setTablaData] = useState<TablaRow[]>([]);
|
|
const [periodosUnicos, setPeriodosUnicos] = useState<string[]>([]);
|
|
const [tablaResumen, setTablaResumen] = useState<any[]>([]);
|
|
const [rawData, setRawData] = useState<ApiResponse[]>([]);
|
|
|
|
const token = Cookies.get("token");
|
|
const headers = { Authorization: `Bearer ${token}` };
|
|
|
|
// Obtener periodos
|
|
useEffect(() => {
|
|
const getPeriodo = async () => {
|
|
const response = await axios.get(`${envConfig.apiUrl}/periodo`, { headers });
|
|
setPeriodo(response.data);
|
|
};
|
|
getPeriodo();
|
|
}, []);
|
|
|
|
// Obtener años desde fechas
|
|
const aniosDisponibles = Array.from(
|
|
new Set(
|
|
periodo.map((p) =>
|
|
new Date(p.fecha_inicio_servicio).getFullYear()
|
|
)
|
|
)
|
|
).sort();
|
|
|
|
// ============================
|
|
// 🔍 BUSCAR
|
|
// ============================
|
|
const handleBuscar = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
let url = "";
|
|
|
|
if (tipo === "periodo") {
|
|
if (!periodoInicio || !periodoFin) {
|
|
alert("Selecciona ambos periodos");
|
|
return;
|
|
}
|
|
|
|
url = `${envConfig.apiUrl}/alumno-inscrito/inscritos/periodo/${periodoInicio}/${periodoFin}`;
|
|
}
|
|
|
|
if (tipo === "anio") {
|
|
if (!periodoInicio || !periodoFin) {
|
|
alert("Selecciona ambos años");
|
|
return;
|
|
}
|
|
|
|
url = `${envConfig.apiUrl}/alumno-inscrito/inscritos/anio/${periodoInicio}/${periodoFin}`;
|
|
}
|
|
|
|
|
|
try {
|
|
console.log("URL:", url);
|
|
|
|
const response = await axios.get(url, { headers });
|
|
const data: ApiResponse[] = response.data;
|
|
|
|
setRawData(data);
|
|
|
|
const agrupado: Record<string, TablaRow> = {};
|
|
|
|
data.forEach((item) => {
|
|
const key = `${item.carrera}-${item.semestre}`;
|
|
|
|
if (!agrupado[key]) {
|
|
agrupado[key] = {
|
|
carrera: item.carrera,
|
|
semestre: item.semestre,
|
|
WINDOWS: 0,
|
|
MACINTOSH: 0,
|
|
LINUX: 0,
|
|
PROFESORES: 0,
|
|
TOTAL: 0,
|
|
femenino: 0,
|
|
masculino: 0,
|
|
};
|
|
}
|
|
|
|
const fila = agrupado[key];
|
|
const tipoProfesor = item.profesor as TipoConteo;
|
|
|
|
const total = Number(item.total);
|
|
const fem = Number(item.femenino);
|
|
const masc = Number(item.masculino);
|
|
|
|
fila[tipoProfesor] += total;
|
|
fila.TOTAL += total;
|
|
fila.femenino += fem;
|
|
fila.masculino += masc;
|
|
});
|
|
|
|
setTablaData(Object.values(agrupado));
|
|
|
|
const periodosUnicosList = Array.from(new Set(data.map((t) => t.semestre)));
|
|
setPeriodosUnicos(periodosUnicosList);
|
|
|
|
const carrerasUnicas = Array.from(new Set(data.map((t) => t.carrera)));
|
|
|
|
const resumen = carrerasUnicas.map((carrera) => {
|
|
const fila: any = { Carrera: carrera };
|
|
let totalFila = 0;
|
|
periodosUnicosList.forEach((periodo) => {
|
|
const items = data.filter((t) => t.carrera === carrera && t.semestre === periodo);
|
|
const totalPeriodo = items.reduce((acc, item) => acc + Number(item.total), 0);
|
|
fila[periodo] = totalPeriodo;
|
|
totalFila += totalPeriodo;
|
|
});
|
|
fila["Total"] = totalFila;
|
|
return fila;
|
|
});
|
|
|
|
setTablaResumen(resumen);
|
|
} catch (error) {
|
|
console.error("Error al obtener inscritos", error);
|
|
}
|
|
};
|
|
|
|
const exportarExcel = () => {
|
|
if (!rawData.length || !tablaResumen.length) return;
|
|
|
|
// HOJA 1: Inscritos (resumen)
|
|
const worksheet1 = XLSX.utils.json_to_sheet(tablaResumen);
|
|
|
|
const carrerasUnicas = Array.from(new Set(rawData.map((t) => t.carrera)));
|
|
|
|
// HOJA 2: genero
|
|
const dataGenero = carrerasUnicas.map((carrera) => {
|
|
const fila: any = { Carrera: carrera };
|
|
periodosUnicos.forEach((periodo) => {
|
|
const items = rawData.filter((t) => t.carrera === carrera && t.semestre === periodo);
|
|
const masc = items.reduce((acc, item) => acc + Number(item.masculino), 0);
|
|
const fem = items.reduce((acc, item) => acc + Number(item.femenino), 0);
|
|
fila[`Masculino - ${periodo}`] = masc;
|
|
fila[`Femenino - ${periodo}`] = fem;
|
|
});
|
|
return fila;
|
|
});
|
|
const worksheet2 = XLSX.utils.json_to_sheet(dataGenero);
|
|
|
|
// HOJA 3: areas
|
|
const dataAreas = carrerasUnicas.map((carrera) => {
|
|
const fila: any = { Carrera: carrera };
|
|
periodosUnicos.forEach((periodo) => {
|
|
const items = rawData.filter((t) => t.carrera === carrera && t.semestre === periodo);
|
|
let windows = 0, mac = 0, linux = 0, profes = 0;
|
|
|
|
items.forEach((item) => {
|
|
if (item.profesor === "WINDOWS") windows += Number(item.total);
|
|
if (item.profesor === "MACINTOSH") mac += Number(item.total);
|
|
if (item.profesor === "LINUX") linux += Number(item.total);
|
|
if (item.profesor === "PROFESORES") profes += Number(item.total);
|
|
});
|
|
|
|
fila[`WINDOWS - ${periodo}`] = windows;
|
|
fila[`MACINTOSH - ${periodo}`] = mac;
|
|
fila[`LINUX - ${periodo}`] = linux;
|
|
fila[`PROFESORES - ${periodo}`] = profes;
|
|
});
|
|
return fila;
|
|
});
|
|
const worksheet3 = XLSX.utils.json_to_sheet(dataAreas);
|
|
|
|
const workbook = XLSX.utils.book_new();
|
|
XLSX.utils.book_append_sheet(workbook, worksheet1, "Inscritos");
|
|
XLSX.utils.book_append_sheet(workbook, worksheet2, "genero");
|
|
XLSX.utils.book_append_sheet(workbook, worksheet3, "areas");
|
|
|
|
const excelBuffer = XLSX.write(workbook, {
|
|
bookType: "xlsx",
|
|
type: "array",
|
|
});
|
|
|
|
const blob = new Blob([excelBuffer], {
|
|
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
});
|
|
|
|
saveAs(blob, "reporte_inscritos.xlsx");
|
|
};
|
|
|
|
return (
|
|
<section className="containerSection">
|
|
<h2 className="title">INSCRITOS</h2>
|
|
|
|
<form className="containerForm" onSubmit={handleBuscar}>
|
|
<label>Filtro</label>
|
|
|
|
{/* Selector tipo */}
|
|
<select value={tipo} onChange={(e) => setTipo(e.target.value as Tipo)}>
|
|
<option value="periodo">Periodo</option>
|
|
<option value="anio">Año</option>
|
|
</select>
|
|
|
|
<div className="groupInput">
|
|
{/* PERIODO */}
|
|
{tipo === "periodo" && (
|
|
<>
|
|
<select onChange={(e) => setPeriodoInicio(Number(e.target.value))}>
|
|
<option value="">Inicio</option>
|
|
{periodo.map((p) => (
|
|
<option key={p.id_periodo} value={p.id_periodo}>
|
|
{p.semestre}
|
|
</option>
|
|
))}
|
|
</select>
|
|
|
|
<select onChange={(e) => setPeriodoFin(Number(e.target.value))}>
|
|
<option value="">Fin</option>
|
|
{periodo.map((p) => (
|
|
<option key={p.id_periodo} value={p.id_periodo}>
|
|
{p.semestre}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</>
|
|
)}
|
|
|
|
{/* AÑO */}
|
|
{tipo === "anio" && (
|
|
<>
|
|
<select onChange={(e) => setPeriodoInicio(Number(e.target.value))}>
|
|
<option value="">Año inicio</option>
|
|
{aniosDisponibles.map((a) => (
|
|
<option key={a} value={a}>
|
|
{a}
|
|
</option>
|
|
))}
|
|
</select>
|
|
|
|
<select onChange={(e) => setPeriodoFin(Number(e.target.value))}>
|
|
<option value="">Año fin</option>
|
|
{aniosDisponibles.map((a) => (
|
|
<option key={a} value={a}>
|
|
{a}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</>
|
|
)}
|
|
<button className="button buttonSearch" type="submit">
|
|
Buscar
|
|
</button>
|
|
|
|
<button type="button" className="button buttonSearch" onClick={exportarExcel}>
|
|
Exportar Excel
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
{/* TABLA */}
|
|
<div className="containerTable">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Carrera</th>
|
|
<th>Periodo</th>
|
|
<th>Femenino</th>
|
|
<th>Masculino</th>
|
|
<th>WINDOWS</th>
|
|
<th>MACINTOSH</th>
|
|
<th>LINUX</th>
|
|
<th>Profesores</th>
|
|
<th>Total</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
{tablaData.map((row, index) => (
|
|
<tr key={index}>
|
|
<td>{row.carrera}</td>
|
|
<td>{row.semestre}</td>
|
|
<td>{row.femenino}</td>
|
|
<td>{row.masculino}</td>
|
|
<td>{row.WINDOWS}</td>
|
|
<td>{row.MACINTOSH}</td>
|
|
<td>{row.LINUX}</td>
|
|
<td>{row.PROFESORES}</td>
|
|
<td>{row.TOTAL}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
);
|
|
} |