From 2db20969a18fcbf489d0e7aead982309a11284e8 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Thu, 4 Sep 2025 15:54:00 -0300 Subject: [PATCH] FEat Widgets Tablas --- Elecciones-Web/frontend/src/App.tsx | 3 + Elecciones-Web/frontend/src/apiService.ts | 7 +- .../frontend/src/components/DevApp.tsx | 4 +- .../ResultadosRankingMunicipioWidget.tsx | 129 ++++++++++++++++++ .../ResultadosTablaSeccionWidget.css | 104 ++++++++------ Elecciones-Web/frontend/src/main.tsx | 2 + Elecciones-Web/frontend/src/types/types.ts | 26 +++- .../Controllers/ResultadosController.cs | 79 +++++++++++ .../net9.0/Elecciones.Api.AssemblyInfo.cs | 2 +- .../Debug/net9.0/rjsmcshtml.dswa.cache.json | 2 +- .../Debug/net9.0/rjsmrazor.dswa.cache.json | 2 +- .../net9.0/Elecciones.Core.AssemblyInfo.cs | 2 +- .../Elecciones.Database.AssemblyInfo.cs | 2 +- .../Elecciones.Infrastructure.AssemblyInfo.cs | 2 +- 14 files changed, 313 insertions(+), 53 deletions(-) create mode 100644 Elecciones-Web/frontend/src/components/ResultadosRankingMunicipioWidget.tsx diff --git a/Elecciones-Web/frontend/src/App.tsx b/Elecciones-Web/frontend/src/App.tsx index 6d33218..5273caf 100644 --- a/Elecciones-Web/frontend/src/App.tsx +++ b/Elecciones-Web/frontend/src/App.tsx @@ -17,6 +17,7 @@ import { DiputadosPorSeccionWidget } from './components/DiputadosPorSeccionWidge import { SenadoresPorSeccionWidget } from './components/SenadoresPorSeccionWidget' import { ConcejalesPorSeccionWidget } from './components/ConcejalesPorSeccionWidget' import { ResultadosTablaDetalladaWidget } from './components/ResultadosTablaDetalladaWidget' +import { ResultadosRankingMunicipioWidget } from './components/ResultadosRankingMunicipioWidget' function App() { return ( @@ -56,6 +57,8 @@ function App() {
+
+ ) diff --git a/Elecciones-Web/frontend/src/apiService.ts b/Elecciones-Web/frontend/src/apiService.ts index 325b021..3622f5f 100644 --- a/Elecciones-Web/frontend/src/apiService.ts +++ b/Elecciones-Web/frontend/src/apiService.ts @@ -1,6 +1,6 @@ // src/apiService.ts import axios from 'axios'; -import type { ApiResponseRankingSeccion, ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker, ApiResponseResultadosPorSeccion } from './types/types'; +import type { ApiResponseRankingMunicipio, ApiResponseRankingSeccion, ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker, ApiResponseResultadosPorSeccion } from './types/types'; /** * URL base para las llamadas a la API. @@ -200,4 +200,9 @@ export const getResultadosTablaDetallada = async (seccionId: string): Promise => { const { data } = await apiClient.get(`/resultados/ranking-por-seccion/${seccionId}`); return data; +}; + +export const getRankingMunicipiosPorSeccion = async (seccionId: string): Promise => { + const { data } = await apiClient.get(`/resultados/ranking-municipios-por-seccion/${seccionId}`); + return data; }; \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/components/DevApp.tsx b/Elecciones-Web/frontend/src/components/DevApp.tsx index b7b4664..f84b65e 100644 --- a/Elecciones-Web/frontend/src/components/DevApp.tsx +++ b/Elecciones-Web/frontend/src/components/DevApp.tsx @@ -16,6 +16,7 @@ import { DiputadosPorSeccionWidget } from './DiputadosPorSeccionWidget' import { SenadoresPorSeccionWidget } from './SenadoresPorSeccionWidget' import { ConcejalesPorSeccionWidget } from './ConcejalesPorSeccionWidget' import { ResultadosTablaDetalladaWidget } from './ResultadosTablaDetalladaWidget' +import { ResultadosRankingMunicipioWidget } from './ResultadosRankingMunicipioWidget' import '../App.css'; @@ -42,7 +43,8 @@ export const DevApp = () => { - + + ); diff --git a/Elecciones-Web/frontend/src/components/ResultadosRankingMunicipioWidget.tsx b/Elecciones-Web/frontend/src/components/ResultadosRankingMunicipioWidget.tsx new file mode 100644 index 0000000..c6c5d43 --- /dev/null +++ b/Elecciones-Web/frontend/src/components/ResultadosRankingMunicipioWidget.tsx @@ -0,0 +1,129 @@ +import { useState, useMemo, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import Select from 'react-select'; +import { getSeccionesElectorales, getRankingMunicipiosPorSeccion } from '../apiService'; +import type { MunicipioSimple, ApiResponseRankingMunicipio } from '../types/types'; +import './ResultadosTablaSeccionWidget.css'; // Reutilizamos el mismo estilo + +const customSelectStyles = { + control: (base: any) => ({ ...base, minWidth: '200px', border: '1px solid #ced4da', boxShadow: 'none', '&:hover': { borderColor: '#86b7fe' } }), + menu: (base: any) => ({ ...base, zIndex: 10 }), +}; + +const formatPercent = (porcentaje: number) => `${porcentaje.toFixed(2).replace('.', ',')}%`; + +export const ResultadosRankingMunicipioWidget = () => { + const [secciones, setSecciones] = useState([]); + const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null); + + useEffect(() => { + const fetchSecciones = async () => { + const seccionesData = await getSeccionesElectorales(); + if (seccionesData && seccionesData.length > 0) { + const orden = new Map([ + ['Capital', 0], ['Primera', 1], ['Segunda', 2], ['Tercera', 3], + ['Cuarta', 4], ['Quinta', 5], ['Sexta', 6], ['Séptima', 7] + ]); + const getOrden = (nombre: string) => { + const match = nombre.match(/Capital|Primera|Segunda|Tercera|Cuarta|Quinta|Sexta|Séptima/); + return match ? orden.get(match[0]) ?? 99 : 99; + }; + seccionesData.sort((a, b) => getOrden(a.nombre) - getOrden(b.nombre)); + setSecciones(seccionesData); + if (!selectedSeccion) { + setSelectedSeccion({ value: seccionesData[0].id, label: seccionesData[0].nombre }); + } + } + }; + fetchSecciones(); + }, [selectedSeccion]); + + const seccionOptions = useMemo(() => secciones.map(s => ({ value: s.id, label: s.nombre })), [secciones]); + + const { data: rankingData, isLoading } = useQuery({ + queryKey: ['rankingMunicipiosPorSeccion', selectedSeccion?.value], + queryFn: () => getRankingMunicipiosPorSeccion(selectedSeccion!.value), + enabled: !!selectedSeccion, + }); + + return ( +
+
+

Resultados por Municipio

+