diff --git a/app/(private)/Reportes/ReportesClient.tsx b/app/(private)/Reportes/ReportesClient.tsx index c812b09..8919e67 100644 --- a/app/(private)/Reportes/ReportesClient.tsx +++ b/app/(private)/Reportes/ReportesClient.tsx @@ -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(null); const [hasta, setHasta] = useState(null); + const [periodo1, setPeriodo1] = useState(null); + const [periodo2, setPeriodo2] = useState(null); return (
@@ -42,16 +45,20 @@ export default function ReportesClient({ label: "Por Servicio", content: ( <> - { - setDesde(d); - setHasta(h); + { + setPeriodo1(p1); + setPeriodo2(p2); }} /> - + + ), - }, + } ]} />
diff --git a/app/Components/PeriodoPicker/periodoPicker.tsx b/app/Components/PeriodoPicker/periodoPicker.tsx new file mode 100644 index 0000000..0b45e3d --- /dev/null +++ b/app/Components/PeriodoPicker/periodoPicker.tsx @@ -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([]); + const [p1, setP1] = useState(null); + const [p2, setP2] = useState(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 ( +
+ {/* Periodo 1 */} + + + {/* Periodo 2 */} + +
+ ); +} \ No newline at end of file diff --git a/app/Components/Reportes/porServicio.tsx b/app/Components/Reportes/porServicio.tsx index d0f5237..c1e85f3 100644 --- a/app/Components/Reportes/porServicio.tsx +++ b/app/Components/Reportes/porServicio.tsx @@ -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([]); +export default function PorServicio({ periodo1, periodo2 }: Props) { + const [data, setData] = useState([]); + const [periodos, setPeriodos] = useState([]); 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 ( -
+
+ {loading &&

Cargando...

} -
- {loading &&

Cargando...

} + + + + -
Servicio
- - - - - - - - - {!loading && servicios.length === 0 && ( - - - - )} - - {servicios.map((servicio) => ( - - - - + {periodos.map((p) => ( + ))} - -
ServicioMonto total
No hay servicios en este rango
{servicio.servicio}${Number(servicio.total).toFixed(2)}
{p.semestre}
-
+ + + + + {!loading && data.length === 0 && ( + + + No hay datos en este rango + + + )} + + {data.map((row) => ( + + {row.servicio} + + {periodos.map((p) => ( + + ${(row[p.semestre] || 0).toFixed(2)} + + ))} + + ))} + +
); -} -//IO \ No newline at end of file +} \ No newline at end of file