| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | import { useState, useMemo, useEffect } from 'react'; | 
					
						
							|  |  |  | import { useQuery } from '@tanstack/react-query'; | 
					
						
							|  |  |  | import Select from 'react-select'; | 
					
						
							|  |  |  | import { getSeccionesElectorales, getRankingMunicipiosPorSeccion } from '../apiService'; | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  | import type { MunicipioSimple, ApiResponseRankingMunicipio, RankingPartido } from '../types/types'; | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  | import './ResultadosTablaSeccionWidget.css'; | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | type DisplayMode = 'porcentaje' | 'votos' | 'ambos'; | 
					
						
							|  |  |  | type DisplayOption = { | 
					
						
							|  |  |  |     value: DisplayMode; | 
					
						
							|  |  |  |     label: string; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const displayModeOptions: readonly DisplayOption[] = [ | 
					
						
							|  |  |  |     { value: 'porcentaje', label: 'Ver Porcentajes' }, | 
					
						
							|  |  |  |     { value: 'votos', label: 'Ver Votos' }, | 
					
						
							|  |  |  |     { value: 'ambos', label: 'Ver Ambos' }, | 
					
						
							|  |  |  | ]; | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | const customSelectStyles = { | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |     control: (base: any) => ({ ...base, minWidth: '200px', border: '1px solid #ced4da', boxShadow: 'none', '&:hover': { borderColor: '#86b7fe' } }), | 
					
						
							|  |  |  |     menu: (base: any) => ({ ...base, zIndex: 10 }), | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const formatPercent = (porcentaje: number) => `${porcentaje.toFixed(2).replace('.', ',')}%`; | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  | // Nueva función para formatear votos con separador de miles
 | 
					
						
							|  |  |  | const formatVotos = (votos: number) => votos.toLocaleString('es-AR'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // --- NUEVO COMPONENTE HELPER PARA RENDERIZAR CELDAS ---
 | 
					
						
							|  |  |  | const CellRenderer = ({ partido, mode }: { partido?: RankingPartido, mode: DisplayMode }) => { | 
					
						
							|  |  |  |     if (!partido) { | 
					
						
							|  |  |  |         return <span>-</span>; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (mode) { | 
					
						
							|  |  |  |         case 'votos': | 
					
						
							|  |  |  |             return <span>{formatVotos(partido.votos)}</span>; | 
					
						
							|  |  |  |         case 'ambos': | 
					
						
							|  |  |  |             return ( | 
					
						
							|  |  |  |                 <div className="cell-ambos"> | 
					
						
							|  |  |  |                     <span>{formatVotos(partido.votos)}</span> | 
					
						
							|  |  |  |                     <small>{formatPercent(partido.porcentaje)}</small> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |         case 'porcentaje': | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             return <span>{formatPercent(partido.porcentaje)}</span>; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const ResultadosRankingMunicipioWidget = () => { | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |     const [secciones, setSecciones] = useState<MunicipioSimple[]>([]); | 
					
						
							|  |  |  |     const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null); | 
					
						
							|  |  |  |     const [displayMode, setDisplayMode] = useState<DisplayOption>(displayModeOptions[0]); | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |     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 }); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |         fetchSecciones(); | 
					
						
							|  |  |  |     }, [selectedSeccion]); | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |     const seccionOptions = useMemo(() => secciones.map(s => ({ value: s.id, label: s.nombre })), [secciones]); | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |     const { data: rankingData, isLoading } = useQuery<ApiResponseRankingMunicipio>({ | 
					
						
							|  |  |  |         queryKey: ['rankingMunicipiosPorSeccion', selectedSeccion?.value], | 
					
						
							|  |  |  |         queryFn: () => getRankingMunicipiosPorSeccion(selectedSeccion!.value), | 
					
						
							|  |  |  |         enabled: !!selectedSeccion, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |     return ( | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |         <div className="tabla-resultados-widget"> | 
					
						
							|  |  |  |             <div className="tabla-header"> | 
					
						
							|  |  |  |                 <h3>Resultados por Municipio</h3> | 
					
						
							|  |  |  |                 <div className="header-filters"> | 
					
						
							|  |  |  |                     <Select | 
					
						
							|  |  |  |                         options={displayModeOptions} | 
					
						
							|  |  |  |                         value={displayMode} | 
					
						
							|  |  |  |                         onChange={(option) => setDisplayMode(option as DisplayOption)} | 
					
						
							|  |  |  |                         styles={customSelectStyles} | 
					
						
							|  |  |  |                         isSearchable={false} | 
					
						
							|  |  |  |                     /> | 
					
						
							|  |  |  |                     <Select | 
					
						
							|  |  |  |                         options={seccionOptions} | 
					
						
							|  |  |  |                         value={selectedSeccion} | 
					
						
							|  |  |  |                         onChange={(option) => setSelectedSeccion(option)} | 
					
						
							|  |  |  |                         isLoading={secciones.length === 0} | 
					
						
							|  |  |  |                         styles={customSelectStyles} | 
					
						
							|  |  |  |                         isSearchable={false} | 
					
						
							|  |  |  |                     /> | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |             <div className="tabla-container"> | 
					
						
							|  |  |  |                 {isLoading ? <p>Cargando...</p> : !rankingData || rankingData.categorias.length === 0 ? <p>No hay datos.</p> : ( | 
					
						
							|  |  |  |                     <table> | 
					
						
							|  |  |  |                         <thead> | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                             {/* --- Fila 1: Nombres de Categorías --- */} | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                             <tr> | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                                 <th rowSpan={3} className="sticky-col municipio-header">Municipio</th> | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                                 {rankingData.categorias.map(cat => ( | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                                     <th key={cat.id} colSpan={4} className="categoria-header"> | 
					
						
							|  |  |  |                                         {cat.nombre} | 
					
						
							|  |  |  |                                     </th> | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                                 ))} | 
					
						
							|  |  |  |                             </tr> | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                             {/* --- Fila 2: Puestos --- */} | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                             <tr> | 
					
						
							|  |  |  |                                 {rankingData.categorias.flatMap(cat => [ | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                                     <th key={`${cat.id}-p1`} colSpan={2} className="puesto-header">1° Puesto</th>, | 
					
						
							|  |  |  |                                     <th key={`${cat.id}-p2`} colSpan={2} className="puesto-header category-divider-header">2° Puesto</th> | 
					
						
							|  |  |  |                                 ])} | 
					
						
							|  |  |  |                             </tr> | 
					
						
							|  |  |  |                             {/* --- Fila 3: Sub-cabeceras (Partido y %) --- */} | 
					
						
							|  |  |  |                             <tr> | 
					
						
							|  |  |  |                                 {rankingData.categorias.flatMap(cat => [ | 
					
						
							|  |  |  |                                     <th key={`${cat.id}-p1-partido`} className="sub-header">Partido</th>, | 
					
						
							|  |  |  |                                     <th key={`${cat.id}-p1-porc`} className="sub-header">%</th>, | 
					
						
							|  |  |  |                                     <th key={`${cat.id}-p2-partido`} className="sub-header category-divider-header">Partido</th>, | 
					
						
							|  |  |  |                                     <th key={`${cat.id}-p2-porc`} className="sub-header">%</th> | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                                 ])} | 
					
						
							|  |  |  |                             </tr> | 
					
						
							|  |  |  |                         </thead> | 
					
						
							|  |  |  |                         <tbody> | 
					
						
							|  |  |  |                             {rankingData.resultados.map(municipio => ( | 
					
						
							|  |  |  |                                 <tr key={municipio.municipioId}> | 
					
						
							|  |  |  |                                     <td className="sticky-col">{municipio.municipioNombre}</td> | 
					
						
							|  |  |  |                                     {rankingData.categorias.flatMap(cat => { | 
					
						
							|  |  |  |                                         const resCategoria = municipio.resultadosPorCategoria[cat.id]; | 
					
						
							|  |  |  |                                         const primerPuesto = resCategoria?.ranking[0]; | 
					
						
							|  |  |  |                                         const segundoPuesto = resCategoria?.ranking[1]; | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                                         return [ | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                                             // --- Celdas para el 1° Puesto ---
 | 
					
						
							|  |  |  |                                             <td key={`${municipio.municipioId}-${cat.id}-p1-partido`} className="cell-partido"> | 
					
						
							|  |  |  |                                                 {primerPuesto?.nombreCorto || '-'} | 
					
						
							|  |  |  |                                             </td>, | 
					
						
							|  |  |  |                                             <td key={`${municipio.municipioId}-${cat.id}-p1-porc`} className="cell-porcentaje"> | 
					
						
							|  |  |  |                                                 {primerPuesto ? <CellRenderer partido={primerPuesto} mode={displayMode.value} /> : '-'} | 
					
						
							|  |  |  |                                             </td>, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                             // --- Celdas para el 2° Puesto ---
 | 
					
						
							|  |  |  |                                             <td key={`${municipio.municipioId}-${cat.id}-p2-partido`} className="cell-partido category-divider"> | 
					
						
							|  |  |  |                                                 {segundoPuesto?.nombreCorto || '-'} | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                                             </td>, | 
					
						
							| 
									
										
										
										
											2025-09-08 15:23:18 -03:00
										 |  |  |                                             <td key={`${municipio.municipioId}-${cat.id}-p2-porc`} className="cell-porcentaje"> | 
					
						
							|  |  |  |                                                 {segundoPuesto ? <CellRenderer partido={segundoPuesto} mode={displayMode.value} /> : '-'} | 
					
						
							| 
									
										
										
										
											2025-09-08 14:11:05 -03:00
										 |  |  |                                             </td> | 
					
						
							|  |  |  |                                         ]; | 
					
						
							|  |  |  |                                     })} | 
					
						
							|  |  |  |                                 </tr> | 
					
						
							|  |  |  |                             ))} | 
					
						
							|  |  |  |                         </tbody> | 
					
						
							|  |  |  |                     </table> | 
					
						
							|  |  |  |                 )} | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2025-09-04 15:54:00 -03:00
										 |  |  | }; |