Fix Tabla Columnas

This commit is contained in:
2025-09-08 15:23:18 -03:00
parent 2fac830528
commit f4d4cd173f
2 changed files with 70 additions and 52 deletions

View File

@@ -18,8 +18,8 @@ const displayModeOptions: readonly DisplayOption[] = [
]; ];
const customSelectStyles = { const customSelectStyles = {
control: (base: any) => ({ ...base, minWidth: '200px', border: '1px solid #ced4da', boxShadow: 'none', '&:hover': { borderColor: '#86b7fe' } }), control: (base: any) => ({ ...base, minWidth: '200px', border: '1px solid #ced4da', boxShadow: 'none', '&:hover': { borderColor: '#86b7fe' } }),
menu: (base: any) => ({ ...base, zIndex: 10 }), menu: (base: any) => ({ ...base, zIndex: 10 }),
}; };
const formatPercent = (porcentaje: number) => `${porcentaje.toFixed(2).replace('.', ',')}%`; const formatPercent = (porcentaje: number) => `${porcentaje.toFixed(2).replace('.', ',')}%`;
@@ -50,41 +50,41 @@ const CellRenderer = ({ partido, mode }: { partido?: RankingPartido, mode: Displ
export const ResultadosRankingMunicipioWidget = () => { export const ResultadosRankingMunicipioWidget = () => {
const [secciones, setSecciones] = useState<MunicipioSimple[]>([]); const [secciones, setSecciones] = useState<MunicipioSimple[]>([]);
const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null); const [selectedSeccion, setSelectedSeccion] = useState<{ value: string; label: string } | null>(null);
const [displayMode, setDisplayMode] = useState<DisplayOption>(displayModeOptions[0]); const [displayMode, setDisplayMode] = useState<DisplayOption>(displayModeOptions[0]);
useEffect(() => { useEffect(() => {
const fetchSecciones = async () => { const fetchSecciones = async () => {
const seccionesData = await getSeccionesElectorales(); const seccionesData = await getSeccionesElectorales();
if (seccionesData && seccionesData.length > 0) { if (seccionesData && seccionesData.length > 0) {
const orden = new Map([ const orden = new Map([
['Capital', 0], ['Primera', 1], ['Segunda', 2], ['Tercera', 3], ['Capital', 0], ['Primera', 1], ['Segunda', 2], ['Tercera', 3],
['Cuarta', 4], ['Quinta', 5], ['Sexta', 6], ['Séptima', 7] ['Cuarta', 4], ['Quinta', 5], ['Sexta', 6], ['Séptima', 7]
]); ]);
const getOrden = (nombre: string) => { const getOrden = (nombre: string) => {
const match = nombre.match(/Capital|Primera|Segunda|Tercera|Cuarta|Quinta|Sexta|Séptima/); const match = nombre.match(/Capital|Primera|Segunda|Tercera|Cuarta|Quinta|Sexta|Séptima/);
return match ? orden.get(match[0]) ?? 99 : 99; 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)); fetchSecciones();
setSecciones(seccionesData); }, [selectedSeccion]);
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 seccionOptions = useMemo(() => secciones.map(s => ({ value: s.id, label: s.nombre })), [secciones]);
const { data: rankingData, isLoading } = useQuery<ApiResponseRankingMunicipio>({ const { data: rankingData, isLoading } = useQuery<ApiResponseRankingMunicipio>({
queryKey: ['rankingMunicipiosPorSeccion', selectedSeccion?.value], queryKey: ['rankingMunicipiosPorSeccion', selectedSeccion?.value],
queryFn: () => getRankingMunicipiosPorSeccion(selectedSeccion!.value), queryFn: () => getRankingMunicipiosPorSeccion(selectedSeccion!.value),
enabled: !!selectedSeccion, enabled: !!selectedSeccion,
}); });
return ( return (
<div className="tabla-resultados-widget"> <div className="tabla-resultados-widget">
<div className="tabla-header"> <div className="tabla-header">
<h3>Resultados por Municipio</h3> <h3>Resultados por Municipio</h3>
@@ -92,11 +92,7 @@ export const ResultadosRankingMunicipioWidget = () => {
<Select <Select
options={displayModeOptions} options={displayModeOptions}
value={displayMode} 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)} onChange={(option) => setDisplayMode(option as DisplayOption)}
styles={customSelectStyles} styles={customSelectStyles}
isSearchable={false} isSearchable={false}
/> />
@@ -115,16 +111,29 @@ export const ResultadosRankingMunicipioWidget = () => {
{isLoading ? <p>Cargando...</p> : !rankingData || rankingData.categorias.length === 0 ? <p>No hay datos.</p> : ( {isLoading ? <p>Cargando...</p> : !rankingData || rankingData.categorias.length === 0 ? <p>No hay datos.</p> : (
<table> <table>
<thead> <thead>
{/* --- Fila 1: Nombres de Categorías --- */}
<tr> <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 => ( {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> </tr>
{/* --- Fila 2: Puestos --- */}
<tr> <tr>
{rankingData.categorias.flatMap(cat => [ {rankingData.categorias.flatMap(cat => [
<th key={`${cat.id}-1`} className="partido-header">1° Puesto</th>, <th key={`${cat.id}-p1`} colSpan={2} className="puesto-header">1° Puesto</th>,
<th key={`${cat.id}-2`} className="partido-header category-divider-header">2° 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> </tr>
</thead> </thead>
@@ -138,17 +147,20 @@ export const ResultadosRankingMunicipioWidget = () => {
const segundoPuesto = resCategoria?.ranking[1]; const segundoPuesto = resCategoria?.ranking[1];
return [ return [
<td key={`${municipio.municipioId}-${cat.id}-1`} className="category-divider"> // --- Celdas para el 1° Puesto ---
<div className="cell-content"> <td key={`${municipio.municipioId}-${cat.id}-p1-partido`} className="cell-partido">
<span className="cell-partido-nombre">{primerPuesto?.nombreCorto || '-'}</span> {primerPuesto?.nombreCorto || '-'}
<CellRenderer partido={primerPuesto} mode={displayMode.value} />
</div>
</td>, </td>,
<td key={`${municipio.municipioId}-${cat.id}-2`}> <td key={`${municipio.municipioId}-${cat.id}-p1-porc`} className="cell-porcentaje">
<div className="cell-content"> {primerPuesto ? <CellRenderer partido={primerPuesto} mode={displayMode.value} /> : '-'}
<span className="cell-partido-nombre">{segundoPuesto?.nombreCorto || '-'}</span> </td>,
<CellRenderer partido={segundoPuesto} mode={displayMode.value} />
</div> // --- 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> </td>
]; ];
})} })}

View File

@@ -106,12 +106,14 @@
.tabla-container .cell-partido { .tabla-container .cell-partido {
text-align: left; text-align: left;
font-size: 0.85rem; 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 { .tabla-container .cell-porcentaje {
text-align: right; text-align: right;
font-family: 'Roboto Mono', 'Courier New', monospace; font-family: 'Roboto Mono', 'Courier New', monospace;
font-weight: 500; 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 { .tabla-container th.sub-header-init {
@@ -123,6 +125,10 @@
border-left: 2px solid #adb5bd; border-left: 2px solid #adb5bd;
} }
.tabla-container .category-divider {
border-left: 2px solid #adb5bd;
}
/* Columna fija de Municipio */ /* Columna fija de Municipio */
.tabla-container th.sticky-col, .tabla-container th.sticky-col,
.tabla-container td.sticky-col { .tabla-container td.sticky-col {