Feat Tabla Resumen Nacional
This commit is contained in:
@@ -5,7 +5,8 @@ import type {
|
||||
ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple,
|
||||
TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker,
|
||||
ApiResponseResultadosPorSeccion, PanelElectoralDto, ResumenProvincia,
|
||||
CategoriaResumenHome, ResultadoFila, ResultadoSeccion
|
||||
CategoriaResumenHome, ResultadoFila, ResultadoSeccion,
|
||||
ProvinciaResumen
|
||||
} from './types/types';
|
||||
|
||||
/**
|
||||
@@ -347,4 +348,9 @@ export const getTablaConurbano = async (eleccionId: number): Promise<ResultadoFi
|
||||
export const getTablaSecciones = async (eleccionId: number): Promise<ResultadoSeccion[]> => {
|
||||
const { data } = await apiClient.get(`/elecciones/${eleccionId}/tabla-secciones`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getResumenNacionalPorProvincia = async (eleccionId: number, categoriaId: number): Promise<ProvinciaResumen[]> => {
|
||||
const response = await apiClient.get(`/elecciones/${eleccionId}/resumen-nacional-por-provincia?categoriaId=${categoriaId}`);
|
||||
return response.data;
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import './DevAppStyle.css'
|
||||
import { HomeCarouselNacionalWidget } from './nacionales/HomeCarouselNacionalWidget';
|
||||
import { TablaConurbanoWidget } from './nacionales/TablaConurbanoWidget';
|
||||
import { TablaSeccionesWidget } from './nacionales/TablaSeccionesWidget';
|
||||
import { ResumenNacionalWidget } from './nacionales/ResumenNacionalWidget';
|
||||
|
||||
// --- NUEVO COMPONENTE REUTILIZABLE PARA CONTENIDO COLAPSABLE ---
|
||||
const CollapsibleWidgetWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
@@ -174,6 +175,11 @@ export const DevAppLegislativas = () => {
|
||||
<h2>Widget: Tabla de Resultados por Sección Electoral</h2>
|
||||
<TablaSeccionesWidget />
|
||||
</div>
|
||||
|
||||
<div style={sectionStyle}>
|
||||
<h2>Resumen Nacional de Resultados por Provincia</h2>
|
||||
<ResumenNacionalWidget />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/* src/components/widgets/ResumenNacionalWidget.module.css */
|
||||
.widgetContainer {
|
||||
font-family: sans-serif;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
max-width: 1000px;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.categoriaSelector {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.listaProvincias {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.provinciaItem {
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.provinciaItem:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.provinciaHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.provinciaNombre {
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.provinciaEscrutado {
|
||||
font-size: 0.8rem;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.resultadosLista {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.resultadoItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.partidoNombre {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.partidoPorcentaje {
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// src/components/widgets/ResumenNacionalWidget.tsx
|
||||
import { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Select from 'react-select';
|
||||
import { getResumenNacionalPorProvincia } from '../../../apiService';
|
||||
import styles from './ResumenNacionalWidget.module.css';
|
||||
|
||||
const ELECCION_ID = 2; // Exclusivo para elecciones nacionales
|
||||
const CATEGORIAS_NACIONALES = [
|
||||
{ value: 3, label: 'Diputados Nacionales' },
|
||||
{ value: 2, label: 'Senadores Nacionales' },
|
||||
];
|
||||
|
||||
export const ResumenNacionalWidget = () => {
|
||||
const [categoria, setCategoria] = useState(CATEGORIAS_NACIONALES[0]);
|
||||
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['resumenNacional', ELECCION_ID, categoria.value],
|
||||
queryFn: () => getResumenNacionalPorProvincia(ELECCION_ID, categoria.value),
|
||||
refetchInterval: 60000,
|
||||
});
|
||||
|
||||
const formatPercent = (num: number) => `${num.toFixed(2)}%`;
|
||||
|
||||
return (
|
||||
<div className={styles.widgetContainer}>
|
||||
<div className={styles.header}>
|
||||
<h3>{categoria.label}</h3>
|
||||
<Select
|
||||
className={styles.categoriaSelector}
|
||||
options={CATEGORIAS_NACIONALES}
|
||||
value={categoria}
|
||||
onChange={(opt) => setCategoria(opt!)}
|
||||
isSearchable={false}
|
||||
/>
|
||||
</div>
|
||||
{isLoading && <p>Cargando resumen nacional...</p>}
|
||||
{error && <p style={{ color: 'red' }}>Error al cargar los datos.</p>}
|
||||
{data && (
|
||||
<ul className={styles.listaProvincias}>
|
||||
{data.map((provincia) => (
|
||||
<li key={provincia.provinciaId} className={styles.provinciaItem}>
|
||||
<div className={styles.provinciaHeader}>
|
||||
<span className={styles.provinciaNombre}>{provincia.provinciaNombre}</span>
|
||||
<span className={styles.provinciaEscrutado}>ESCR. {formatPercent(provincia.porcentajeEscrutado)}</span>
|
||||
</div>
|
||||
<ul className={styles.resultadosLista}>
|
||||
{provincia.resultados.map((partido, index) => (
|
||||
<li key={index} className={styles.resultadoItem}>
|
||||
<span className={styles.partidoNombre}>{partido.nombre}</span>
|
||||
<span className={styles.partidoPorcentaje}>{formatPercent(partido.porcentaje)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -31,6 +31,7 @@ import { CongresoNacionalWidget } from './features/legislativas/nacionales/Congr
|
||||
import { HomeCarouselNacionalWidget } from './features/legislativas/nacionales/HomeCarouselNacionalWidget';
|
||||
import { TablaConurbanoWidget } from './features/legislativas/nacionales/TablaConurbanoWidget';
|
||||
import { TablaSeccionesWidget } from './features/legislativas/nacionales/TablaSeccionesWidget';
|
||||
import { ResumenNacionalWidget } from './features/legislativas/nacionales/ResumenNacionalWidget';
|
||||
|
||||
import { DevAppLegislativas } from './features/legislativas/DevAppLegislativas';
|
||||
|
||||
@@ -66,6 +67,7 @@ const WIDGET_MAP: Record<string, React.ElementType> = {
|
||||
'composicion-congreso-nacional': CongresoNacionalWidget,
|
||||
'tabla-conurbano': TablaConurbanoWidget,
|
||||
'tabla-secciones': TablaSeccionesWidget,
|
||||
'resumen-nacional': ResumenNacionalWidget,
|
||||
};
|
||||
|
||||
// Vite establece `import.meta.env.DEV` a `true` cuando ejecutamos 'npm run dev'
|
||||
|
||||
@@ -196,4 +196,16 @@ export interface ResultadoSeccion {
|
||||
seccionId: string;
|
||||
nombre: string;
|
||||
municipios: ResultadoFila[];
|
||||
}
|
||||
|
||||
export interface PartidoResumen {
|
||||
nombre: string;
|
||||
porcentaje: number;
|
||||
}
|
||||
|
||||
export interface ProvinciaResumen {
|
||||
provinciaId: string;
|
||||
provinciaNombre: string;
|
||||
porcentajeEscrutado: number;
|
||||
resultados: PartidoResumen[];
|
||||
}
|
||||
Reference in New Issue
Block a user