Fix Tabla Columnas
This commit is contained in:
		| @@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'; | ||||
| import Select from 'react-select'; | ||||
| import { getSeccionesElectorales, getRankingMunicipiosPorSeccion } from '../apiService'; | ||||
| import type { MunicipioSimple, ApiResponseRankingMunicipio, RankingPartido } from '../types/types'; | ||||
| import './ResultadosTablaSeccionWidget.css';  | ||||
| import './ResultadosTablaSeccionWidget.css'; | ||||
|  | ||||
| type DisplayMode = 'porcentaje' | 'votos' | 'ambos'; | ||||
| type DisplayOption = { | ||||
| @@ -18,8 +18,8 @@ const displayModeOptions: readonly DisplayOption[] = [ | ||||
| ]; | ||||
|  | ||||
| const customSelectStyles = { | ||||
|   control: (base: any) => ({ ...base, minWidth: '200px', border: '1px solid #ced4da', boxShadow: 'none', '&:hover': { borderColor: '#86b7fe' } }), | ||||
|   menu: (base: any) => ({ ...base, zIndex: 10 }), | ||||
|     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('.', ',')}%`; | ||||
| @@ -50,41 +50,41 @@ const CellRenderer = ({ partido, mode }: { partido?: RankingPartido, mode: Displ | ||||
|  | ||||
|  | ||||
| export const ResultadosRankingMunicipioWidget = () => { | ||||
|   const [secciones, setSecciones] = useState<MunicipioSimple[]>([]); | ||||
|   const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null); | ||||
|   const [displayMode, setDisplayMode] = useState<DisplayOption>(displayModeOptions[0]); | ||||
|     const [secciones, setSecciones] = useState<MunicipioSimple[]>([]); | ||||
|     const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null); | ||||
|     const [displayMode, setDisplayMode] = useState<DisplayOption>(displayModeOptions[0]); | ||||
|  | ||||
|   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; | ||||
|     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 }); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         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]); | ||||
|         fetchSecciones(); | ||||
|     }, [selectedSeccion]); | ||||
|  | ||||
|   const seccionOptions = useMemo(() => secciones.map(s => ({ value: s.id, label: s.nombre })), [secciones]); | ||||
|     const seccionOptions = useMemo(() => secciones.map(s => ({ value: s.id, label: s.nombre })), [secciones]); | ||||
|  | ||||
|   const { data: rankingData, isLoading } = useQuery<ApiResponseRankingMunicipio>({ | ||||
|     queryKey: ['rankingMunicipiosPorSeccion', selectedSeccion?.value], | ||||
|     queryFn: () => getRankingMunicipiosPorSeccion(selectedSeccion!.value), | ||||
|     enabled: !!selectedSeccion, | ||||
|   }); | ||||
|     const { data: rankingData, isLoading } = useQuery<ApiResponseRankingMunicipio>({ | ||||
|         queryKey: ['rankingMunicipiosPorSeccion', selectedSeccion?.value], | ||||
|         queryFn: () => getRankingMunicipiosPorSeccion(selectedSeccion!.value), | ||||
|         enabled: !!selectedSeccion, | ||||
|     }); | ||||
|  | ||||
|   return ( | ||||
|     return ( | ||||
|         <div className="tabla-resultados-widget"> | ||||
|             <div className="tabla-header"> | ||||
|                 <h3>Resultados por Municipio</h3> | ||||
| @@ -92,11 +92,7 @@ export const ResultadosRankingMunicipioWidget = () => { | ||||
|                     <Select | ||||
|                         options={displayModeOptions} | ||||
|                         value={displayMode} | ||||
|                          | ||||
|                         // --- CORRECCIÓN EN ONCHANGE --- | ||||
|                         // 'option' ahora es del tipo correcto, por lo que no necesitamos aserción. | ||||
|                         onChange={(option) => setDisplayMode(option as DisplayOption)} | ||||
|                          | ||||
|                         styles={customSelectStyles} | ||||
|                         isSearchable={false} | ||||
|                     /> | ||||
| @@ -115,16 +111,29 @@ export const ResultadosRankingMunicipioWidget = () => { | ||||
|                 {isLoading ? <p>Cargando...</p> : !rankingData || rankingData.categorias.length === 0 ? <p>No hay datos.</p> : ( | ||||
|                     <table> | ||||
|                         <thead> | ||||
|                             {/* --- Fila 1: Nombres de Categorías --- */} | ||||
|                             <tr> | ||||
|                                 <th rowSpan={2} className="sticky-col municipio-header">Municipio</th> | ||||
|                                 <th rowSpan={3} className="sticky-col municipio-header">Municipio</th> | ||||
|                                 {rankingData.categorias.map(cat => ( | ||||
|                                     <th key={cat.id} colSpan={2} className="categoria-header">{cat.nombre}</th> | ||||
|                                     <th key={cat.id} colSpan={4} className="categoria-header"> | ||||
|                                         {cat.nombre} | ||||
|                                     </th> | ||||
|                                 ))} | ||||
|                             </tr> | ||||
|                             {/* --- Fila 2: Puestos --- */} | ||||
|                             <tr> | ||||
|                                 {rankingData.categorias.flatMap(cat => [ | ||||
|                                     <th key={`${cat.id}-1`} className="partido-header">1° Puesto</th>, | ||||
|                                     <th key={`${cat.id}-2`} className="partido-header category-divider-header">2° Puesto</th> | ||||
|                                     <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> | ||||
|                                 ])} | ||||
|                             </tr> | ||||
|                         </thead> | ||||
| @@ -138,17 +147,20 @@ export const ResultadosRankingMunicipioWidget = () => { | ||||
|                                         const segundoPuesto = resCategoria?.ranking[1]; | ||||
|  | ||||
|                                         return [ | ||||
|                                             <td key={`${municipio.municipioId}-${cat.id}-1`} className="category-divider"> | ||||
|                                                 <div className="cell-content"> | ||||
|                                                     <span className="cell-partido-nombre">{primerPuesto?.nombreCorto || '-'}</span> | ||||
|                                                     <CellRenderer partido={primerPuesto} mode={displayMode.value} /> | ||||
|                                                 </div> | ||||
|                                             // --- 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}-2`}> | ||||
|                                                 <div className="cell-content"> | ||||
|                                                     <span className="cell-partido-nombre">{segundoPuesto?.nombreCorto || '-'}</span> | ||||
|                                                     <CellRenderer partido={segundoPuesto} mode={displayMode.value} /> | ||||
|                                                 </div> | ||||
|                                             <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 || '-'} | ||||
|                                             </td>, | ||||
|                                             <td key={`${municipio.municipioId}-${cat.id}-p2-porc`} className="cell-porcentaje"> | ||||
|                                                 {segundoPuesto ? <CellRenderer partido={segundoPuesto} mode={displayMode.value} /> : '-'} | ||||
|                                             </td> | ||||
|                                         ]; | ||||
|                                     })} | ||||
|   | ||||
| @@ -106,12 +106,14 @@ | ||||
| .tabla-container .cell-partido { | ||||
|     text-align: left; | ||||
|     font-size: 0.85rem; | ||||
|     padding-left: 15px; /* Añade un poco de espacio a la izquierda */ | ||||
|     border-right: 1px solid #e9ecef; /* Línea fina entre partido y % */ | ||||
| } | ||||
| .tabla-container .cell-porcentaje { | ||||
|     text-align: right; | ||||
|     font-family: 'Roboto Mono', 'Courier New', monospace; | ||||
|     font-weight: 500; | ||||
|     border-right: 1px solid #dee2e6; | ||||
|     padding-right: 15px; /* Añade un poco de espacio a la derecha */ | ||||
| } | ||||
|  | ||||
| .tabla-container th.sub-header-init { | ||||
| @@ -123,6 +125,10 @@ | ||||
|     border-left: 2px solid #adb5bd; | ||||
| } | ||||
|  | ||||
| .tabla-container .category-divider { | ||||
|     border-left: 2px solid #adb5bd; | ||||
| } | ||||
|  | ||||
| /* Columna fija de Municipio */ | ||||
| .tabla-container th.sticky-col, | ||||
| .tabla-container td.sticky-col { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user