se modifico la manera en que se traen los datos del back-end para reportes y se creo un componente llamado PeriodoPicker, para poder traer la informacion de los periodos por rango

This commit is contained in:
2026-04-14 14:17:08 -06:00
parent f7297a2f64
commit c4f68aa1e0
3 changed files with 165 additions and 56 deletions
+13 -6
View File
@@ -6,6 +6,7 @@ import SearchDateBetween from "@/app/Components/SearchDateBetween/SearchDateBetw
import PorServicios from "@/app/Components/Reportes/porServicio";
import PorRecibos from "@/app/Components/Reportes/porRecibo";
import MonthYearPicker from "@/app/Components/MonthYearPicker/MonthYearPicker";
import PeriodoPicker from "@/app/Components/PeriodoPicker/periodoPicker";
export default function ReportesClient({
defaultKey,
@@ -14,6 +15,8 @@ export default function ReportesClient({
}) {
const [desde, setDesde] = useState<string | null>(null);
const [hasta, setHasta] = useState<string | null>(null);
const [periodo1, setPeriodo1] = useState<any>(null);
const [periodo2, setPeriodo2] = useState<any>(null);
return (
<section className="containerSection">
@@ -42,16 +45,20 @@ export default function ReportesClient({
label: "Por Servicio",
content: (
<>
<MonthYearPicker
onChange={(d, h) => {
setDesde(d);
setHasta(h);
<PeriodoPicker
onChange={(p1, p2) => {
setPeriodo1(p1);
setPeriodo2(p2);
}}
/>
<PorServicios desde={desde} hasta={hasta} />
<PorServicios
periodo1={periodo1}
periodo2={periodo2}
/>
</>
),
},
}
]}
/>
</section>
@@ -0,0 +1,65 @@
"use client";
import { useEffect, useState } from "react";
import axios from "axios";
import Cookies from "js-cookie";
import { envConfig } from "@/app/lib/config";
interface Periodo {
id_periodo: number;
semestre: string;
fecha_inicio_servicio: string;
fecha_fin_servicio: string;
}
export default function PeriodoPicker({
onChange,
}: {
onChange: (p1: Periodo | null, p2: Periodo | null) => void;
}) {
const [periodos, setPeriodos] = useState<Periodo[]>([]);
const [p1, setP1] = useState<number | null>(null);
const [p2, setP2] = useState<number | null>(null);
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
useEffect(() => {
const fetchPeriodos = async () => {
const res = await axios.get(`${envConfig.apiUrl}/periodo`, { headers });
setPeriodos(res.data);
};
fetchPeriodos();
}, []);
useEffect(() => {
const periodo1 = periodos.find(p => p.id_periodo === p1) || null;
const periodo2 = periodos.find(p => p.id_periodo === p2) || null;
onChange(periodo1, periodo2);
}, [p1, p2]);
return (
<div className="flex gap-4">
{/* Periodo 1 */}
<select onChange={(e) => setP1(Number(e.target.value))}>
<option value="">Periodo 1</option>
{periodos.map(p => (
<option key={p.id_periodo} value={p.id_periodo}>
{p.semestre}
</option>
))}
</select>
{/* Periodo 2 */}
<select onChange={(e) => setP2(Number(e.target.value))}>
<option value="">Periodo 2</option>
{periodos.map(p => (
<option key={p.id_periodo} value={p.id_periodo}>
{p.semestre}
</option>
))}
</select>
</div>
);
}
+87 -50
View File
@@ -4,84 +4,121 @@ import { useEffect, useState } from "react";
import axios from "axios";
import Cookies from "js-cookie";
interface ServicioReporte {
id_servicio: number;
servicio: string;
total: string;
interface Periodo {
id_periodo: number;
semestre: string;
fecha_inicio_servicio: string;
fecha_fin_servicio: string;
}
interface Props {
desde: string | null;
hasta: string | null;
periodo1: Periodo | null;
periodo2: Periodo | null;
}
export default function PorServicio({ desde, hasta }: Props) {
const [servicios, setServicios] = useState<ServicioReporte[]>([]);
export default function PorServicio({ periodo1, periodo2 }: Props) {
const [data, setData] = useState<any[]>([]);
const [periodos, setPeriodos] = useState<Periodo[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!desde || !hasta) return;
if (!periodo1 || !periodo2) return;
const fetchServicios = async () => {
setLoading(true);
try {
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
const res = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/detalle-servicio/rango`,
{ desde, hasta },
{ headers },
// 🔥 1. traer TODOS los periodos en rango
const resPeriodos = await axios.get(
`${process.env.NEXT_PUBLIC_API_URL}/periodo/range?p1=${periodo1.id_periodo}&p2=${periodo2.id_periodo}`,
{ headers }
);
setServicios(res.data);
const periodosRango = resPeriodos.data;
setPeriodos(periodosRango);
// 🔥 2. hacer requests por cada periodo
const resultados = await Promise.all(
periodosRango.map((p: Periodo) =>
axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/detalle-servicio/rango`,
{
desde: p.fecha_inicio_servicio,
hasta: p.fecha_fin_servicio,
},
{ headers }
)
)
);
// 🔥 3. merge dinámico
const map = new Map();
periodosRango.forEach((p: Periodo, index: number) => {
resultados[index].data.forEach((item: any) => {
if (!map.has(item.servicio)) {
map.set(item.servicio, {
servicio: item.servicio,
});
}
map.get(item.servicio)[p.semestre] = Number(item.total);
});
});
const finalData = Array.from(map.values());
setData(finalData);
} catch (error) {
console.error("Error al obtener reporte por servicio", error);
console.error("Error comparando servicios", error);
} finally {
setLoading(false);
}
};
fetchServicios();
}, [desde, hasta]);
}, [periodo1, periodo2]);
return (
<div style={{ position: "relative" }}>
<div style={{ overflow: "auto", height: "300px" }}>
{loading && <p>Cargando...</p>}
<div
style={{
overflow: "auto",
height: "270px",
scrollbarColor: "#2563eb white",
}}
>
{loading && <p>Cargando...</p>}
<table>
<thead>
<tr>
<th>Servicio</th>
<table>
<thead>
<tr>
<th>Servicio</th>
<th>Monto total</th>
</tr>
</thead>
<tbody>
{!loading && servicios.length === 0 && (
<tr>
<td colSpan={2}>No hay servicios en este rango</td>
</tr>
)}
{servicios.map((servicio) => (
<tr key={servicio.id_servicio}>
<td>{servicio.servicio}</td>
<td>${Number(servicio.total).toFixed(2)}</td>
</tr>
{periodos.map((p) => (
<th key={p.id_periodo}>{p.semestre}</th>
))}
</tbody>
</table>
</div>
</tr>
</thead>
<tbody>
{!loading && data.length === 0 && (
<tr>
<td colSpan={periodos.length + 1}>
No hay datos en este rango
</td>
</tr>
)}
{data.map((row) => (
<tr key={row.servicio}>
<td>{row.servicio}</td>
{periodos.map((p) => (
<td key={p.id_periodo}>
${(row[p.semestre] || 0).toFixed(2)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
//IO
}