Preparación Legislativas Nacionales 2025
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
// src/apiService.ts
|
// src/apiService.ts
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import type { ApiResponseRankingMunicipio, ApiResponseRankingSeccion, ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker, ApiResponseResultadosPorSeccion } from './types/types';
|
import type { ApiResponseRankingMunicipio, ApiResponseRankingSeccion, ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker, ApiResponseResultadosPorSeccion, PanelElectoralDto } from './types/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL base para las llamadas a la API.
|
* URL base para las llamadas a la API.
|
||||||
@@ -84,14 +84,14 @@ export interface ResultadoDetalleSeccion {
|
|||||||
color: string | null;
|
color: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getResumenProvincial = async (): Promise<CategoriaResumen[]> => {
|
export const getResumenProvincial = async (eleccionId: number): Promise<CategoriaResumen[]> => {
|
||||||
const response = await apiClient.get('/resultados/provincia/02');
|
const response = await apiClient.get(`/elecciones/${eleccionId}/provincia/02`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBancasPorSeccion = async (seccionId: string, camara: 'diputados' | 'senadores'): Promise<ProyeccionBancas> => {
|
export const getBancasPorSeccion = async (eleccionId: number, seccionId: string, camara: 'diputados' | 'senadores'): Promise<ProyeccionBancas> => {
|
||||||
const { data } = await apiClient.get(`/resultados/bancas-por-seccion/${seccionId}/${camara}`);
|
const { data } = await apiClient.get(`/elecciones/${eleccionId}/bancas-por-seccion/${seccionId}/${camara}`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,13 +140,13 @@ export const getMesasPorEstablecimiento = async (establecimientoId: string): Pro
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getComposicionCongreso = async (): Promise<ComposicionData> => {
|
export const getComposicionCongreso = async (eleccionId: number): Promise<ComposicionData> => {
|
||||||
const response = await apiClient.get('/resultados/composicion-congreso');
|
const response = await apiClient.get(`/elecciones/${eleccionId}/composicion-congreso`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBancadasDetalle = async (): Promise<BancadaDetalle[]> => {
|
export const getBancadasDetalle = async (eleccionId: number): Promise<BancadaDetalle[]> => {
|
||||||
const response = await apiClient.get('/resultados/bancadas-detalle');
|
const response = await apiClient.get(`/elecciones/${eleccionId}/bancadas-detalle`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,24 +155,18 @@ export const getConfiguracionPublica = async (): Promise<ConfiguracionPublica> =
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getResultadosPorSeccion = async (seccionId: string, categoriaId: number): Promise<ApiResponseResultadosPorSeccion> => {
|
export const getResultadosPorSeccion = async (eleccionId: number, seccionId: string, categoriaId: number): Promise<ApiResponseResultadosPorSeccion> => {
|
||||||
const response = await apiClient.get(`/resultados/seccion-resultados/${seccionId}?categoriaId=${categoriaId}`);
|
const response = await apiClient.get(`/elecciones/${eleccionId}/seccion-resultados/${seccionId}?categoriaId=${categoriaId}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDetalleSeccion = async (seccionId: string, categoriaId: number): Promise<ResultadoDetalleSeccion[]> => {
|
export const getDetalleSeccion = async (eleccionId: number, seccionId: string, categoriaId: number): Promise<ResultadoDetalleSeccion[]> => {
|
||||||
const response = await apiClient.get(`/resultados/seccion/${seccionId}?categoriaId=${categoriaId}`);
|
const response = await apiClient.get(`/elecciones/${eleccionId}/seccion/${seccionId}?categoriaId=${categoriaId}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getResultadosPorMunicipioYCategoria = async (municipioId: string, categoriaId: number): Promise<ResultadoTicker[]> => {
|
export const getResultadosPorMunicipio = async (eleccionId: number, municipioId: string, categoriaId: number): Promise<ResultadoTicker[]> => {
|
||||||
const response = await apiClient.get(`/resultados/partido/${municipioId}?categoriaId=${categoriaId}`);
|
const response = await apiClient.get(`/elecciones/${eleccionId}/partido/${municipioId}?categoriaId=${categoriaId}`);
|
||||||
return response.data.resultados;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getResultadosPorMunicipio = async (municipioId: string, categoriaId: number): Promise<ResultadoTicker[]> => {
|
|
||||||
const response = await apiClient.get(`/resultados/partido/${municipioId}?categoriaId=${categoriaId}`);
|
|
||||||
// La respuesta es un objeto, nosotros extraemos el array de resultados
|
|
||||||
return response.data.resultados;
|
return response.data.resultados;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -214,3 +208,17 @@ export const getEstablecimientosPorMunicipio = async (municipioId: string): Prom
|
|||||||
const response = await apiClient.get(`/catalogos/establecimientos-por-municipio/${municipioId}`);
|
const response = await apiClient.get(`/catalogos/establecimientos-por-municipio/${municipioId}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPanelElectoral = async (eleccionId: number, ambitoId: string | null, categoriaId: number): Promise<PanelElectoralDto> => {
|
||||||
|
|
||||||
|
// Construimos la URL base
|
||||||
|
let url = ambitoId
|
||||||
|
? `/elecciones/${eleccionId}/panel/${ambitoId}`
|
||||||
|
: `/elecciones/${eleccionId}/panel`;
|
||||||
|
|
||||||
|
// Añadimos categoriaId como un query parameter
|
||||||
|
url += `?categoriaId=${categoriaId}`;
|
||||||
|
|
||||||
|
const { data } = await apiClient.get(url);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
// src/components/DevApp.tsx
|
// src/components/common/DevApp.tsx
|
||||||
import { BancasWidget } from './BancasWidget'
|
import { BancasWidget } from '../../features/legislativas/provinciales/BancasWidget'
|
||||||
import { CongresoWidget } from './CongresoWidget'
|
import { CongresoWidget } from '../../features/legislativas/provinciales/CongresoWidget'
|
||||||
import MapaBsAs from './MapaBsAs'
|
import MapaBsAs from '../../features/legislativas/provinciales/MapaBsAs'
|
||||||
import { DipSenTickerWidget } from './DipSenTickerWidget'
|
import { DipSenTickerWidget } from '../../features/legislativas/provinciales/DipSenTickerWidget'
|
||||||
import { TelegramaWidget } from './TelegramaWidget'
|
import { TelegramaWidget } from '../../features/legislativas/provinciales/TelegramaWidget'
|
||||||
import { ConcejalesWidget } from './ConcejalesWidget'
|
import { ConcejalesWidget } from '../../features/legislativas/provinciales/ConcejalesWidget'
|
||||||
import MapaBsAsSecciones from './MapaBsAsSecciones'
|
import MapaBsAsSecciones from '../../features/legislativas/provinciales/MapaBsAsSecciones'
|
||||||
import { SenadoresWidget } from './SenadoresWidget'
|
import { SenadoresWidget } from '../../features/legislativas/provinciales/SenadoresWidget'
|
||||||
import { DiputadosWidget } from './DiputadosWidget'
|
import { DiputadosWidget } from '../../features/legislativas/provinciales/DiputadosWidget'
|
||||||
import { ResumenGeneralWidget } from './ResumenGeneralWidget'
|
import { ResumenGeneralWidget } from '../../features/legislativas/provinciales/ResumenGeneralWidget'
|
||||||
import { SenadoresTickerWidget } from './SenadoresTickerWidget'
|
import { SenadoresTickerWidget } from '../../features/legislativas/provinciales/SenadoresTickerWidget'
|
||||||
import { DiputadosTickerWidget } from './DiputadosTickerWidget'
|
import { DiputadosTickerWidget } from '../../features/legislativas/provinciales/DiputadosTickerWidget'
|
||||||
import { ConcejalesTickerWidget } from './ConcejalesTickerWidget'
|
import { ConcejalesTickerWidget } from '../../features/legislativas/provinciales/ConcejalesTickerWidget'
|
||||||
import { DiputadosPorSeccionWidget } from './DiputadosPorSeccionWidget'
|
import { DiputadosPorSeccionWidget } from '../../features/legislativas/provinciales/DiputadosPorSeccionWidget'
|
||||||
import { SenadoresPorSeccionWidget } from './SenadoresPorSeccionWidget'
|
import { SenadoresPorSeccionWidget } from '../../features/legislativas/provinciales/SenadoresPorSeccionWidget'
|
||||||
import { ConcejalesPorSeccionWidget } from './ConcejalesPorSeccionWidget'
|
import { ConcejalesPorSeccionWidget } from '../../features/legislativas/provinciales/ConcejalesPorSeccionWidget'
|
||||||
import { ResultadosTablaDetalladaWidget } from './ResultadosTablaDetalladaWidget'
|
import { ResultadosTablaDetalladaWidget } from '../../features/legislativas/provinciales/ResultadosTablaDetalladaWidget'
|
||||||
import { ResultadosRankingMunicipioWidget } from './ResultadosRankingMunicipioWidget'
|
import { ResultadosRankingMunicipioWidget } from '../../features/legislativas/provinciales/ResultadosRankingMunicipioWidget'
|
||||||
import '../App.css';
|
import '../../App.css';
|
||||||
|
|
||||||
|
|
||||||
export const DevApp = () => {
|
export const DevApp = () => {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/components/ImageWithFallback.tsx
|
// src/components/common/ImageWithFallback.tsx
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
interface Props extends React.ImgHTMLAttributes<HTMLImageElement> {
|
interface Props extends React.ImgHTMLAttributes<HTMLImageElement> {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/components/ParliamentLayout.tsx
|
// src/components/common/ParliamentLayout.tsx
|
||||||
import React, { useLayoutEffect } from 'react';
|
import React, { useLayoutEffect } from 'react';
|
||||||
import { assetBaseUrl } from '../apiService';
|
import { assetBaseUrl } from '../../apiService';
|
||||||
import { handleImageFallback } from './imageFallback';
|
import { handleImageFallback } from './imageFallback';
|
||||||
|
|
||||||
// Interfaces (no cambian)
|
// Interfaces (no cambian)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// src/components/SenateLayout.tsx
|
// src/components/common/SenateLayout.tsx
|
||||||
import React, { useLayoutEffect } from 'react';
|
import React, { useLayoutEffect } from 'react';
|
||||||
import { handleImageFallback } from './imageFallback';
|
import { handleImageFallback } from './imageFallback';
|
||||||
import { assetBaseUrl } from '../apiService';
|
import { assetBaseUrl } from '../../apiService';
|
||||||
|
|
||||||
// Interfaces
|
// Interfaces
|
||||||
interface SeatFillData {
|
interface SeatFillData {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/components/imageFallback.ts
|
// src/components/common/imageFallback.ts
|
||||||
|
|
||||||
export function handleImageFallback(selector: string, fallbackImageUrl: string) {
|
export function handleImageFallback(selector: string, fallbackImageUrl: string) {
|
||||||
// Le decimos a TypeScript que el resultado será una lista de elementos de imagen HTML
|
// Le decimos a TypeScript que el resultado será una lista de elementos de imagen HTML
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// src/features/legislativas/rovinciales/DevAppLegislativas.tsx
|
||||||
|
import { PanelNacionalWidget } from './nacionales/PanelNacionalWidget';
|
||||||
|
import './DevAppStyle.css'
|
||||||
|
|
||||||
|
export const DevAppLegislativas = () => {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<h1>Il visualizzatore di widget - Elecciones Nacionales 2025</h1>
|
||||||
|
|
||||||
|
{/* Le pasamos el ID de la elección que queremos visualizar.
|
||||||
|
Para tus datos de prueba provinciales, este ID es 1. */}
|
||||||
|
<PanelNacionalWidget eleccionId={2} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.container{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
/* src/features/legislativas/nacionales/PanelNaciona.css */
|
||||||
|
.panel-nacional-container {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nuevo contenedor para alinear título y selector */
|
||||||
|
.header-top-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoria-selector {
|
||||||
|
min-width: 220px;
|
||||||
|
/* Ancho del selector */
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-link {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #007bff;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-separator {
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-main-content {
|
||||||
|
display: flex;
|
||||||
|
height: 75vh;
|
||||||
|
min-height: 500px;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Columna del mapa */
|
||||||
|
.mapa-column {
|
||||||
|
flex: 2; /* Por defecto, ocupa 2/3 del espacio */
|
||||||
|
position: relative;
|
||||||
|
transition: flex 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Columna de resultados */
|
||||||
|
.resultados-column {
|
||||||
|
flex: 1; /* Por defecto, ocupa 1/3 */
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
min-width: 320px; /* Un ancho mínimo para que no se comprima demasiado */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- NUEVOS ESTILOS --- */
|
||||||
|
.mapa-componente-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapa-volver-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-fila {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
gap: 1rem; /* Añade un espacio entre logo, info y stats */
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-logo {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-logo img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-info-wrapper {
|
||||||
|
flex-grow: 1; /* Permite que esta sección crezca */
|
||||||
|
flex-shrink: 1; /* Permite que se encoja si es necesario */
|
||||||
|
min-width: 0; /* <-- TRUCO CLAVE DE FLEXBOX para que text-overflow funcione */
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-nombre {
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap; /* <-- No permitir que el texto salte de línea */
|
||||||
|
overflow: hidden; /* <-- Ocultar el texto que se desborda */
|
||||||
|
text-overflow: ellipsis; /* <-- Añadir "..." al final */
|
||||||
|
}
|
||||||
|
|
||||||
|
.candidato-nombre {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-barra-background {
|
||||||
|
height: 15px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-barra-foreground {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: width 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-stats {
|
||||||
|
flex-shrink: 0; /* <-- MUY IMPORTANTE: Evita que este bloque se encoja */
|
||||||
|
text-align: right;
|
||||||
|
min-width: 100px; /* Asegura que siempre tenga espacio suficiente */
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-porcentaje {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-votos {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-estado-recuento {
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.estado-item {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.estado-item span {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.estado-item strong {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rsm-zoomable-group {
|
||||||
|
transition: transform 0.75s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
* Contenedor principal del contenido */
|
||||||
|
.panel-main-content {
|
||||||
|
display: flex;
|
||||||
|
height: 70vh;
|
||||||
|
min-height: 500px;
|
||||||
|
transition: all 0.5s ease-in-out; /* Transición suave para el layout */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Columna del mapa */
|
||||||
|
.mapa-column {
|
||||||
|
flex: 2; /* Por defecto, ocupa 2/3 del espacio */
|
||||||
|
position: relative;
|
||||||
|
transition: flex 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Columna de resultados */
|
||||||
|
.resultados-column {
|
||||||
|
flex: 1; /* Por defecto, ocupa 1/3 */
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
min-width: 320px; /* Un ancho mínimo para que no se comprima demasiado */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ESTADO COLAPSADO --- */
|
||||||
|
/* Cuando el panel principal tiene la clase 'panel-collapsed' */
|
||||||
|
.panel-main-content.panel-collapsed .mapa-column {
|
||||||
|
flex: 1 1 100%; /* El mapa ocupa todo el ancho */
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-main-content.panel-collapsed .resultados-column {
|
||||||
|
flex-basis: 0;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden; /* Oculta el contenido para que no se desborde */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Estilo del botón para colapsar --- */
|
||||||
|
.panel-toggle-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 10px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
width: 30px;
|
||||||
|
height: 50px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-toggle-btn:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rsm-geography {
|
||||||
|
cursor: pointer;
|
||||||
|
stroke: #000000;
|
||||||
|
stroke-width: 0.2px;
|
||||||
|
outline: none;
|
||||||
|
transition: filter 0.2s ease-in-out, stroke 0.2s ease-in-out, stroke-width 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ESTADO HOVER (Sutil) --- */
|
||||||
|
/* Se aplica solo si la geografía NO está seleccionada */
|
||||||
|
.rsm-geography:not(.selected):hover {
|
||||||
|
filter: brightness(1.10);
|
||||||
|
stroke: #0000ff;
|
||||||
|
stroke-width: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ESTADO SELECCIONADO (Foco) --- */
|
||||||
|
/* Clase que añadiremos desde React para el municipio en foco */
|
||||||
|
.rsm-geography.selected {
|
||||||
|
stroke: #0000ff; /* Borde negro para el seleccionado */
|
||||||
|
stroke-width: 0.5px; /* <-- Borde más grueso para destacar */
|
||||||
|
filter: none; /* Quitamos cualquier otro filtro para que se vea nítido */
|
||||||
|
pointer-events: none; /* Desactivamos eventos para que no interfiera el hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reglas para los mapas atenuados (sin cambios) */
|
||||||
|
.rsm-geography-faded,
|
||||||
|
.rsm-geography-faded-municipality {
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rsm-geography-faded:hover,
|
||||||
|
.rsm-geography-faded-municipality:hover {
|
||||||
|
filter: none;
|
||||||
|
stroke: #FFFFFF;
|
||||||
|
stroke-width: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partido-barra-foreground {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: width 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spinner para la transición entre mapas */
|
||||||
|
.transition-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.5); /* Fondo blanco semitransparente */
|
||||||
|
z-index: 20;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
/* Estilo del spinner en sí mismo */
|
||||||
|
.transition-spinner::after {
|
||||||
|
content: '';
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 5px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-top-color: #007bff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
// src/features/legislativas/nacionales/PanelNacionalWidget.tsx
|
||||||
|
import { useMemo, useState, Suspense } from 'react';
|
||||||
|
import { useSuspenseQuery } from '@tanstack/react-query'; // <-- CAMBIO CLAVE
|
||||||
|
import { getPanelElectoral } from '../../../apiService';
|
||||||
|
import { MapaNacional } from './components/MapaNacional';
|
||||||
|
import { PanelResultados } from './components/PanelResultados';
|
||||||
|
import { Breadcrumbs } from './components/Breadcrumbs';
|
||||||
|
import './PanelNacional.css';
|
||||||
|
import Select from 'react-select';
|
||||||
|
import type { PanelElectoralDto } from '../../../types/types';
|
||||||
|
|
||||||
|
interface PanelNacionalWidgetProps {
|
||||||
|
eleccionId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AmbitoState = {
|
||||||
|
id: string | null;
|
||||||
|
nivel: 'pais' | 'provincia' | 'municipio';
|
||||||
|
nombre: string;
|
||||||
|
provinciaNombre?: string;
|
||||||
|
provinciaDistritoId?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CATEGORIAS_NACIONALES = [
|
||||||
|
{ value: 2, label: 'Diputados Nacionales' },
|
||||||
|
{ value: 1, label: 'Senadores Nacionales' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Creamos un componente interno para poder usar Suspense correctamente
|
||||||
|
const PanelContenido = ({ eleccionId, ambitoActual, categoriaId }: { eleccionId: number, ambitoActual: AmbitoState, categoriaId: number }) => {
|
||||||
|
// Este hook ahora suspenderá el renderizado si los datos no están listos
|
||||||
|
const { data } = useSuspenseQuery<PanelElectoralDto>({
|
||||||
|
queryKey: ['panelElectoral', eleccionId, ambitoActual.id, categoriaId],
|
||||||
|
queryFn: () => getPanelElectoral(eleccionId, ambitoActual.id, categoriaId),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PanelResultados
|
||||||
|
resultados={data.resultadosPanel}
|
||||||
|
estadoRecuento={data.estadoRecuento}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PanelNacionalWidget = ({ eleccionId }: PanelNacionalWidgetProps) => {
|
||||||
|
const [ambitoActual, setAmbitoActual] = useState<AmbitoState>({ id: null, nivel: 'pais', nombre: 'Argentina', provinciaDistritoId: null });
|
||||||
|
const [categoriaId, setCategoriaId] = useState<number>(2);
|
||||||
|
const [isPanelOpen, setIsPanelOpen] = useState(true);
|
||||||
|
|
||||||
|
const handleAmbitoSelect = (nuevoAmbitoId: string, nuevoNivel: 'provincia' | 'municipio', nuevoNombre: string) => {
|
||||||
|
setAmbitoActual(prev => ({
|
||||||
|
id: nuevoAmbitoId,
|
||||||
|
nivel: nuevoNivel,
|
||||||
|
nombre: nuevoNombre,
|
||||||
|
provinciaNombre: nuevoNivel === 'municipio' ? prev.nombre : (nuevoNivel === 'provincia' ? nuevoNombre : undefined),
|
||||||
|
provinciaDistritoId: nuevoNivel === 'provincia' ? nuevoAmbitoId : prev.provinciaDistritoId
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetToPais = () => {
|
||||||
|
setAmbitoActual({ id: null, nivel: 'pais', nombre: 'Argentina', provinciaDistritoId: null });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleVolverAProvincia = () => {
|
||||||
|
if (ambitoActual.provinciaDistritoId && ambitoActual.provinciaNombre) {
|
||||||
|
setAmbitoActual({
|
||||||
|
id: ambitoActual.provinciaDistritoId,
|
||||||
|
nivel: 'provincia',
|
||||||
|
nombre: ambitoActual.provinciaNombre,
|
||||||
|
provinciaDistritoId: ambitoActual.provinciaDistritoId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleResetToPais();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedCategoria = useMemo(() =>
|
||||||
|
CATEGORIAS_NACIONALES.find(c => c.value === categoriaId),
|
||||||
|
[categoriaId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="panel-nacional-container">
|
||||||
|
<header className="panel-header">
|
||||||
|
<div className="header-top-row">
|
||||||
|
<h1>Resultados elecciones {ambitoActual.nombre}</h1>
|
||||||
|
<Select
|
||||||
|
options={CATEGORIAS_NACIONALES}
|
||||||
|
value={selectedCategoria}
|
||||||
|
onChange={(option) => option && setCategoriaId(option.value)}
|
||||||
|
className="categoria-selector"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Breadcrumbs
|
||||||
|
nivel={ambitoActual.nivel}
|
||||||
|
nombreAmbito={ambitoActual.nombre}
|
||||||
|
nombreProvincia={ambitoActual.provinciaNombre}
|
||||||
|
onReset={handleResetToPais}
|
||||||
|
onVolverProvincia={handleVolverAProvincia}
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
<main className={`panel-main-content ${!isPanelOpen ? 'panel-collapsed' : ''}`}>
|
||||||
|
<div className="mapa-column">
|
||||||
|
<button
|
||||||
|
className="panel-toggle-btn"
|
||||||
|
onClick={() => setIsPanelOpen(!isPanelOpen)}
|
||||||
|
title={isPanelOpen ? "Ocultar panel" : "Mostrar panel"}
|
||||||
|
>
|
||||||
|
{isPanelOpen ? '›' : '‹'}
|
||||||
|
</button>
|
||||||
|
<Suspense fallback={<div className="spinner" />}>
|
||||||
|
<MapaNacional
|
||||||
|
eleccionId={eleccionId}
|
||||||
|
categoriaId={categoriaId}
|
||||||
|
nivel={ambitoActual.nivel}
|
||||||
|
nombreAmbito={ambitoActual.nombre}
|
||||||
|
provinciaDistritoId={ambitoActual.provinciaDistritoId ?? null}
|
||||||
|
onAmbitoSelect={handleAmbitoSelect}
|
||||||
|
onVolver={ambitoActual.nivel === 'municipio' ? handleVolverAProvincia : handleResetToPais}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
<div className="resultados-column">
|
||||||
|
<Suspense fallback={<div className="spinner" />}>
|
||||||
|
<PanelContenido
|
||||||
|
eleccionId={eleccionId}
|
||||||
|
ambitoActual={ambitoActual}
|
||||||
|
categoriaId={categoriaId}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// src/features/legislativas/nacionales/components/AnimatedNumber.tsx
|
||||||
|
import { useAnimatedNumber } from '../hooks/useAnimatedNumber';
|
||||||
|
|
||||||
|
interface AnimatedNumberProps {
|
||||||
|
value: number;
|
||||||
|
formatter: (value: number) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AnimatedNumber = ({ value, formatter }: AnimatedNumberProps) => {
|
||||||
|
const animatedValue = useAnimatedNumber(value);
|
||||||
|
return <span>{formatter(animatedValue)}</span>;
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// src/features/legislativas/nacionales/components/Breadcrumbs.tsx
|
||||||
|
interface BreadcrumbsProps {
|
||||||
|
nivel: 'pais' | 'provincia' | 'municipio';
|
||||||
|
nombreAmbito: string;
|
||||||
|
nombreProvincia?: string;
|
||||||
|
onReset: () => void;
|
||||||
|
onVolverProvincia: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Breadcrumbs = ({ nivel, nombreAmbito, nombreProvincia, onReset, onVolverProvincia }: BreadcrumbsProps) => {
|
||||||
|
return (
|
||||||
|
<div className="breadcrumbs">
|
||||||
|
{nivel !== 'pais' && (
|
||||||
|
<>
|
||||||
|
<button onClick={onReset} className="breadcrumb-link">Argentina</button>
|
||||||
|
<span className="breadcrumb-separator">{'>'}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{nivel === 'municipio' && nombreProvincia && (
|
||||||
|
<>
|
||||||
|
<button onClick={onVolverProvincia} className="breadcrumb-link">{nombreProvincia}</button>
|
||||||
|
<span className="breadcrumb-separator">{'>'}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<span className="breadcrumb-actual">{nombreAmbito}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// src/features/legislativas/nacionales/components/MapaNacional.tsx
|
||||||
|
import axios from 'axios';
|
||||||
|
import { Suspense, useState, useEffect, useCallback } from 'react'; // <-- Asegúrate de que useCallback esté importado
|
||||||
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
|
||||||
|
import { Tooltip } from 'react-tooltip';
|
||||||
|
import { API_BASE_URL, assetBaseUrl } from '../../../../apiService';
|
||||||
|
import type { ResultadoMapaDto, AmbitoGeography } from '../../../../types/types';
|
||||||
|
import { MapaProvincial } from './MapaProvincial';
|
||||||
|
|
||||||
|
const DEFAULT_MAP_COLOR = '#E0E0E0';
|
||||||
|
const FADED_BACKGROUND_COLOR = '#F0F0F0';
|
||||||
|
const normalizarTexto = (texto: string = '') => texto.trim().toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
||||||
|
|
||||||
|
type PointTuple = [number, number];
|
||||||
|
|
||||||
|
interface MapaNacionalProps {
|
||||||
|
eleccionId: number;
|
||||||
|
categoriaId: number;
|
||||||
|
nivel: 'pais' | 'provincia' | 'municipio';
|
||||||
|
nombreAmbito: string;
|
||||||
|
provinciaDistritoId: string | null;
|
||||||
|
onAmbitoSelect: (ambitoId: string, nivel: 'provincia' | 'municipio', nombre: string) => void;
|
||||||
|
onVolver: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, provinciaDistritoId, onAmbitoSelect, onVolver }: MapaNacionalProps) => {
|
||||||
|
const [position, setPosition] = useState({ zoom: 1, center: [-65, -40] as PointTuple });
|
||||||
|
|
||||||
|
const { data: mapaDataNacional } = useSuspenseQuery<ResultadoMapaDto[]>({
|
||||||
|
queryKey: ['mapaResultados', eleccionId, categoriaId, null],
|
||||||
|
queryFn: async () => {
|
||||||
|
const url = `${API_BASE_URL}/elecciones/${eleccionId}/mapa-resultados?categoriaId=${categoriaId}`;
|
||||||
|
const response = await axios.get(url);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: geoDataNacional } = useSuspenseQuery<any>({
|
||||||
|
queryKey: ['geoDataNacional'],
|
||||||
|
queryFn: () => axios.get(`${assetBaseUrl}/maps/argentina-provincias.topojson`).then(res => res.data),
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultadosNacionalesPorNombre = new Map<string, ResultadoMapaDto>(mapaDataNacional.map(d => [normalizarTexto(d.ambitoNombre), d]));
|
||||||
|
|
||||||
|
const nombreMunicipioSeleccionado = nivel === 'municipio' ? nombreAmbito : null;
|
||||||
|
|
||||||
|
// El useEffect para el zoom provincial y nacional sigue siendo correcto.
|
||||||
|
useEffect(() => {
|
||||||
|
if (nivel === 'pais') {
|
||||||
|
setPosition({ zoom: 1, center: [-65, -40] });
|
||||||
|
} else if (nivel === 'provincia') {
|
||||||
|
setPosition({ zoom: 7, center: [-60.5, -37] });
|
||||||
|
}
|
||||||
|
// La lógica de centrado en municipio se delega al hijo, que llamará a `handleCalculatedCenter`
|
||||||
|
}, [nivel]);
|
||||||
|
|
||||||
|
// **LA SOLUCIÓN CLAVE**: Estabilizamos la función que se pasa al hijo.
|
||||||
|
const handleCalculatedCenter = useCallback((center: PointTuple, zoom: number) => {
|
||||||
|
setPosition({ center, zoom });
|
||||||
|
}, []); // El array de dependencias vacío asegura que la función nunca cambie
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mapa-componente-container">
|
||||||
|
{nivel !== 'pais' && <button onClick={onVolver} className="mapa-volver-btn">← Volver</button>}
|
||||||
|
<ComposableMap projection="geoMercator" projectionConfig={{ scale: 700, center: [-65, -40] }} style={{ width: "100%", height: "100%" }}>
|
||||||
|
<ZoomableGroup center={position.center} zoom={position.zoom} filterZoomEvent={() => false}>
|
||||||
|
<Geographies geography={geoDataNacional}>
|
||||||
|
{({ geographies }: { geographies: AmbitoGeography[] }) => geographies.map((geo) => {
|
||||||
|
const resultado = resultadosNacionalesPorNombre.get(normalizarTexto(geo.properties.nombre));
|
||||||
|
const esProvinciaActiva = provinciaDistritoId && resultado?.ambitoId === provinciaDistritoId;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Geography
|
||||||
|
key={geo.rsmKey} geography={geo}
|
||||||
|
className={`rsm-geography ${nivel !== 'pais' ? 'rsm-geography-faded' : ''}`}
|
||||||
|
style={{ visibility: esProvinciaActiva ? 'hidden' : 'visible' }}
|
||||||
|
fill={nivel === 'pais' ? (resultado?.colorGanador || DEFAULT_MAP_COLOR) : FADED_BACKGROUND_COLOR}
|
||||||
|
onClick={() => resultado && onAmbitoSelect(resultado.ambitoId, 'provincia', resultado.ambitoNombre)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Geographies>
|
||||||
|
|
||||||
|
{provinciaDistritoId && (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<MapaProvincial
|
||||||
|
eleccionId={eleccionId}
|
||||||
|
categoriaId={categoriaId}
|
||||||
|
distritoId={provinciaDistritoId}
|
||||||
|
nombreProvincia={"BUENOS AIRES"} // Esto se podría hacer dinámico si fuera necesario
|
||||||
|
nombreMunicipioSeleccionado={nombreMunicipioSeleccionado}
|
||||||
|
onMunicipioSelect={(ambitoId, nombre) => onAmbitoSelect(ambitoId, 'municipio', nombre)}
|
||||||
|
onCalculatedCenter={handleCalculatedCenter} // Pasamos la función estabilizada
|
||||||
|
nivel={nivel as 'provincia' | 'municipio'} // El cast de tipo sigue siendo necesario y correcto
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
|
</ZoomableGroup>
|
||||||
|
</ComposableMap>
|
||||||
|
<Tooltip id="mapa-tooltip" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// src/features/legislativas/nacionales/components/MapaProvincial.tsx
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||||
|
import { Geographies, Geography } from 'react-simple-maps';
|
||||||
|
import { geoCentroid } from 'd3-geo';
|
||||||
|
import { feature } from 'topojson-client';
|
||||||
|
import { API_BASE_URL, assetBaseUrl } from '../../../../apiService';
|
||||||
|
import type { ResultadoMapaDto, AmbitoGeography } from '../../../../types/types';
|
||||||
|
|
||||||
|
const DEFAULT_MAP_COLOR = '#E0E0E0';
|
||||||
|
const normalizarTexto = (texto: string = ''): string => texto.trim().toUpperCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
||||||
|
type PointTuple = [number, number];
|
||||||
|
|
||||||
|
interface MapaProvincialProps {
|
||||||
|
eleccionId: number;
|
||||||
|
categoriaId: number;
|
||||||
|
distritoId: string;
|
||||||
|
nombreProvincia: string;
|
||||||
|
nombreMunicipioSeleccionado: string | null;
|
||||||
|
nivel: 'provincia' | 'municipio';
|
||||||
|
onMunicipioSelect: (ambitoId: string, nombre: string) => void;
|
||||||
|
onCalculatedCenter: (center: PointTuple, zoom: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MapaProvincial = ({ eleccionId, categoriaId, distritoId, nombreProvincia, nombreMunicipioSeleccionado, nivel, onMunicipioSelect, onCalculatedCenter }: MapaProvincialProps) => {
|
||||||
|
const { data: mapaData = [] } = useSuspenseQuery<ResultadoMapaDto[]>({
|
||||||
|
queryKey: ['mapaResultados', eleccionId, categoriaId, distritoId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const url = `${API_BASE_URL}/elecciones/${eleccionId}/mapa-resultados?categoriaId=${categoriaId}&distritoId=${distritoId}`;
|
||||||
|
const response = await axios.get(url);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// El nombre del archivo ahora es completamente dinámico
|
||||||
|
const { data: geoData } = useSuspenseQuery<any>({
|
||||||
|
queryKey: ['geoDataProvincial', nombreProvincia],
|
||||||
|
queryFn: async () => {
|
||||||
|
const nombreNormalizado = nombreProvincia.toLowerCase().replace(/ /g, '_');
|
||||||
|
const mapFile = `departamentos-${nombreNormalizado}.topojson`;
|
||||||
|
return axios.get(`${assetBaseUrl}/maps/${mapFile}`).then(res => res.data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// useEffect para calcular y "exportar" la posición del municipio al padre
|
||||||
|
useEffect(() => {
|
||||||
|
if (nivel === 'municipio' && geoData?.objects && nombreMunicipioSeleccionado) {
|
||||||
|
const geometries = geoData.objects[Object.keys(geoData.objects)[0]].geometries;
|
||||||
|
const municipioGeo = geometries.find((g: any) => normalizarTexto(g.properties.departamento) === normalizarTexto(nombreMunicipioSeleccionado));
|
||||||
|
if (municipioGeo) {
|
||||||
|
const municipioFeature = feature(geoData, municipioGeo);
|
||||||
|
const centroid = geoCentroid(municipioFeature);
|
||||||
|
// Usamos un zoom genérico alto para cualquier municipio
|
||||||
|
onCalculatedCenter(centroid as PointTuple, 40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [nivel, nombreMunicipioSeleccionado, geoData, onCalculatedCenter]);
|
||||||
|
|
||||||
|
const resultadosPorNombre = new Map<string, ResultadoMapaDto>(mapaData.map(d => [normalizarTexto(d.ambitoNombre), d]));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Geographies geography={geoData}>
|
||||||
|
{({ geographies }: { geographies: AmbitoGeography[] }) => geographies.map((geo) => {
|
||||||
|
const resultado = resultadosPorNombre.get(normalizarTexto(geo.properties.departamento));
|
||||||
|
const esSeleccionado = nombreMunicipioSeleccionado ? normalizarTexto(geo.properties.departamento) === normalizarTexto(nombreMunicipioSeleccionado) : false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Geography
|
||||||
|
key={geo.rsmKey}
|
||||||
|
geography={geo}
|
||||||
|
className={`rsm-geography ${esSeleccionado ? 'selected' : ''} ${nombreMunicipioSeleccionado && !esSeleccionado ? 'rsm-geography-faded-municipality' : ''}`}
|
||||||
|
fill={resultado?.colorGanador || DEFAULT_MAP_COLOR}
|
||||||
|
onClick={resultado ? () => onMunicipioSelect(resultado.ambitoId.toString(), resultado.ambitoNombre) : undefined}
|
||||||
|
data-tooltip-id="mapa-tooltip"
|
||||||
|
data-tooltip-content={geo.properties.departamento}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Geographies>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// src/features/legislativas/nacionales/components/PanelResultados.tsx
|
||||||
|
import type { ResultadoTicker, EstadoRecuentoTicker } from '../../../../types/types';
|
||||||
|
import { ImageWithFallback } from '../../../../components/common/ImageWithFallback';
|
||||||
|
import { assetBaseUrl } from '../../../../apiService';
|
||||||
|
import { AnimatedNumber } from './AnimatedNumber';
|
||||||
|
|
||||||
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
|
const formatVotes = (num: number) => Math.round(num).toLocaleString('es-AR');
|
||||||
|
|
||||||
|
interface PanelResultadosProps {
|
||||||
|
resultados: ResultadoTicker[];
|
||||||
|
estadoRecuento: EstadoRecuentoTicker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PanelResultados = ({ resultados, estadoRecuento }: PanelResultadosProps) => {
|
||||||
|
return (
|
||||||
|
<div className="panel-resultados">
|
||||||
|
<div className="panel-partidos-container">
|
||||||
|
{resultados.map(partido => (
|
||||||
|
<div key={partido.id} className="partido-fila">
|
||||||
|
<div className="partido-logo">
|
||||||
|
<ImageWithFallback src={partido.logoUrl || undefined} fallbackSrc={`${assetBaseUrl}/default-avatar.png`} alt={partido.nombre} />
|
||||||
|
</div>
|
||||||
|
<div className="partido-info-wrapper">
|
||||||
|
<span className="partido-nombre">{partido.nombreCorto || partido.nombre}</span>
|
||||||
|
{partido.nombreCandidato && <span className="candidato-nombre">{partido.nombreCandidato}</span>}
|
||||||
|
<div className="partido-barra-background">
|
||||||
|
<div className="partido-barra-foreground" style={{ width: `${partido.porcentaje}%`, backgroundColor: partido.color || '#888' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="partido-stats">
|
||||||
|
<span className="partido-porcentaje">
|
||||||
|
<AnimatedNumber value={partido.porcentaje} formatter={formatPercent} />
|
||||||
|
</span>
|
||||||
|
<span className="partido-votos">
|
||||||
|
<AnimatedNumber value={partido.votos} formatter={formatVotes} /> votos
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="panel-estado-recuento">
|
||||||
|
<div className="estado-item">
|
||||||
|
<span>Participación</span>
|
||||||
|
<strong><AnimatedNumber value={estadoRecuento.participacionPorcentaje} formatter={formatPercent} /></strong>
|
||||||
|
</div>
|
||||||
|
<div className="estado-item">
|
||||||
|
<span>Mesas Escrutadas</span>
|
||||||
|
<strong><AnimatedNumber value={estadoRecuento.mesasTotalizadasPorcentaje} formatter={formatPercent} /></strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// src/features/legislativas/nacionales/components/hooks/useAnimatedNumber.ts
|
||||||
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
const easeOutQuad = (t: number) => t * (2 - t);
|
||||||
|
|
||||||
|
export const useAnimatedNumber = (
|
||||||
|
endValue: number,
|
||||||
|
duration: number = 700 // Duración de la animación en milisegundos
|
||||||
|
) => {
|
||||||
|
const [currentValue, setCurrentValue] = useState(endValue);
|
||||||
|
const previousValueRef = useRef(endValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const startValue = previousValueRef.current;
|
||||||
|
let animationFrameId: number;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
const animate = () => {
|
||||||
|
const elapsedTime = Date.now() - startTime;
|
||||||
|
const progress = Math.min(elapsedTime / duration, 1);
|
||||||
|
const easedProgress = easeOutQuad(progress);
|
||||||
|
|
||||||
|
const newAnimatedValue = startValue + (endValue - startValue) * easedProgress;
|
||||||
|
setCurrentValue(newAnimatedValue);
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
} else {
|
||||||
|
// Asegurarse de que el valor final sea exacto
|
||||||
|
setCurrentValue(endValue);
|
||||||
|
previousValueRef.current = endValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
|
previousValueRef.current = endValue;
|
||||||
|
};
|
||||||
|
}, [endValue, duration]);
|
||||||
|
|
||||||
|
return currentValue;
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* src/components/BancasWidget.css
|
/* src/features/legislativas/rovinciales/BancasWidget.css
|
||||||
|
|
||||||
/* Contenedor principal del widget */
|
/* Contenedor principal del widget */
|
||||||
.bancas-widget-container {
|
.bancas-widget-container {
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// src/components/BancasWidget.tsx (Corregido)
|
// src/features/legislativas/provinciales/BancasWidget.tsx (Corregido)
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select'; // --- CAMBIO: Importar react-select ---
|
import Select from 'react-select'; // --- CAMBIO: Importar react-select ---
|
||||||
import { getBancasPorSeccion, getSeccionesElectoralesConCargos } from '../apiService';
|
import { getBancasPorSeccion, getSeccionesElectoralesConCargos } from '../../../apiService';
|
||||||
import type { ProyeccionBancas, MunicipioSimple } from '../types/types';
|
import type { ProyeccionBancas, MunicipioSimple } from '../../../types/types';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
import './BancasWidget.css';
|
import './BancasWidget.css';
|
||||||
import type { Property } from 'csstype';
|
import type { Property } from 'csstype';
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// src/components/ConcejalesPorSeccionWidget.tsx
|
// src/features/legislativas/provinciales/ConcejalesPorSeccionWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getSeccionesElectorales, getResultadosPorSeccion, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getSeccionesElectorales, getResultadosPorSeccion, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ResultadoTicker, ApiResponseResultadosPorSeccion } from '../types/types';
|
import type { MunicipioSimple, ResultadoTicker, ApiResponseResultadosPorSeccion } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css'; // Reutilizamos los estilos del ticker
|
import './TickerWidget.css'; // Reutilizamos los estilos del ticker
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// src/components/ConcejalesTickerWidget.tsx
|
// src/features/legislativas/provinciales/ConcejalesTickerWidget.tsx
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
import type { CategoriaResumen, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css'; // Reutilizamos los mismos estilos
|
import './TickerWidget.css'; // Reutilizamos los mismos estilos
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// src/components/ConcejalesWidget.tsx
|
// src/features/legislativas/provinciales/ConcejalesWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ResultadoTicker } from '../types/types';
|
import type { MunicipioSimple, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* src/components/CongresoWidget.css */
|
/* src/features/legislativas/provinciales/CongresoWidget.css */
|
||||||
.congreso-container {
|
.congreso-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
/* Se reduce ligeramente el espacio entre el gráfico y el panel */
|
/* Se reduce ligeramente el espacio entre el gráfico y el panel */
|
||||||
@@ -1,28 +1,32 @@
|
|||||||
// src/components/CongresoWidget.tsx
|
// src/features/legislativas/provinciales/CongresoWidget.tsx
|
||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { ParliamentLayout } from './ParliamentLayout';
|
import { ParliamentLayout } from '../../../components/common/ParliamentLayout';
|
||||||
import { SenateLayout } from './SenateLayout';
|
import { SenateLayout } from '../../../components/common/SenateLayout';
|
||||||
import { getComposicionCongreso, getBancadasDetalle } from '../apiService';
|
import { getComposicionCongreso, getBancadasDetalle } from '../../../apiService';
|
||||||
import type { ComposicionData, BancadaDetalle } from '../apiService';
|
import type { ComposicionData, BancadaDetalle } from '../../../apiService';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
import './CongresoWidget.css';
|
import './CongresoWidget.css';
|
||||||
|
|
||||||
type CamaraType = 'diputados' | 'senadores';
|
type CamaraType = 'diputados' | 'senadores';
|
||||||
const DEFAULT_COLOR = '#808080';
|
const DEFAULT_COLOR = '#808080';
|
||||||
|
|
||||||
export const CongresoWidget = () => {
|
interface CongresoWidgetProps {
|
||||||
|
eleccionId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CongresoWidget = ({ eleccionId }: CongresoWidgetProps) => {
|
||||||
const [camaraActiva, setCamaraActiva] = useState<CamaraType>('diputados');
|
const [camaraActiva, setCamaraActiva] = useState<CamaraType>('diputados');
|
||||||
|
|
||||||
const { data: composicionData, isLoading: isLoadingComposicion, error: errorComposicion } = useQuery<ComposicionData>({
|
const { data: composicionData, isLoading: isLoadingComposicion, error: errorComposicion } = useQuery<ComposicionData>({
|
||||||
queryKey: ['composicionCongreso'],
|
queryKey: ['composicionCongreso', eleccionId],
|
||||||
queryFn: getComposicionCongreso,
|
queryFn: () => getComposicionCongreso(eleccionId),
|
||||||
refetchInterval: 180000,
|
refetchInterval: 180000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: bancadasDetalle = [] } = useQuery<BancadaDetalle[]>({
|
const { data: bancadasDetalle = [] } = useQuery<BancadaDetalle[]>({
|
||||||
queryKey: ['bancadasDetalle'],
|
queryKey: ['bancadasDetalle', eleccionId],
|
||||||
queryFn: getBancadasDetalle,
|
queryFn: () => getBancadasDetalle(eleccionId),
|
||||||
enabled: !!composicionData,
|
enabled: !!composicionData,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
// src/components/DipSenTickerWidget.tsx
|
// src/features/legislativas/provinciales/DipSenTickerWidget.tsx
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
import type { CategoriaResumen, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// src/components/DiputadosPorSeccionWidget.tsx
|
// src/features/legislativas/provinciales/DiputadosPorSeccionWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getSeccionesElectorales, getResultadosPorSeccion, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getSeccionesElectorales, getResultadosPorSeccion, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ResultadoTicker, ApiResponseResultadosPorSeccion } from '../types/types';
|
import type { MunicipioSimple, ResultadoTicker, ApiResponseResultadosPorSeccion } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// src/components/DiputadosTickerWidget.tsx
|
// src/features/legislativas/provinciales/DiputadosTickerWidget.tsx
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
import type { CategoriaResumen, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// src/components/DiputadosWidget.tsx
|
// src/features/legislativas/provinciales/DiputadosWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ResultadoTicker } from '../types/types';
|
import type { MunicipioSimple, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* src/components/MapaBsAs.css */
|
/* src/features/legislativas/provinciales/MapaBsAs.css */
|
||||||
:root {
|
:root {
|
||||||
--primary-accent-color: #0073e6;
|
--primary-accent-color: #0073e6;
|
||||||
--background-panel-color: #ffffff;
|
--background-panel-color: #ffffff;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/components/MapaBsAs.tsx
|
// src/features/legislativas/provinciales/MapaBsAs.tsx
|
||||||
import { useState, useMemo, useCallback, useEffect } from 'react';
|
import { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
|
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
@@ -7,7 +7,7 @@ import axios from 'axios';
|
|||||||
import { feature } from 'topojson-client';
|
import { feature } from 'topojson-client';
|
||||||
import type { Feature, Geometry } from 'geojson';
|
import type { Feature, Geometry } from 'geojson';
|
||||||
import { geoCentroid } from 'd3-geo';
|
import { geoCentroid } from 'd3-geo';
|
||||||
import { API_BASE_URL, assetBaseUrl } from '../apiService';
|
import { API_BASE_URL, assetBaseUrl } from '../../../apiService';
|
||||||
import './MapaBsAs.css';
|
import './MapaBsAs.css';
|
||||||
|
|
||||||
// --- Interfaces y Tipos ---
|
// --- Interfaces y Tipos ---
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
// src/components/MapaBsAsSecciones.tsx
|
// src/features/legislativas/provinciales/MapaBsAsSecciones.tsx
|
||||||
import { useState, useMemo, useCallback, useEffect } from 'react';
|
import { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
|
import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { geoCentroid } from 'd3-geo';
|
import { geoCentroid } from 'd3-geo';
|
||||||
import { getDetalleSeccion, API_BASE_URL, assetBaseUrl } from '../apiService';
|
import { getDetalleSeccion, API_BASE_URL, assetBaseUrl } from '../../../apiService';
|
||||||
import { type ResultadoDetalleSeccion } from '../apiService';
|
import { type ResultadoDetalleSeccion } from '../../../apiService';
|
||||||
import './MapaBsAs.css';
|
import './MapaBsAs.css';
|
||||||
|
|
||||||
// --- Interfaces y Tipos ---
|
// --- Interfaces y Tipos ---
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
// src/features/legislativas/provinciales/ResultaosRankingMunicipioWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getSeccionesElectorales, getRankingMunicipiosPorSeccion } from '../apiService';
|
import { getSeccionesElectorales, getRankingMunicipiosPorSeccion } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ApiResponseRankingMunicipio, RankingPartido } from '../types/types';
|
import type { MunicipioSimple, ApiResponseRankingMunicipio, RankingPartido } from '../../../types/types';
|
||||||
import './ResultadosTablaSeccionWidget.css';
|
import './ResultadosTablaSeccionWidget.css';
|
||||||
|
|
||||||
type DisplayMode = 'porcentaje' | 'votos' | 'ambos';
|
type DisplayMode = 'porcentaje' | 'votos' | 'ambos';
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
// src/features/legislativas/provinciales/ResultadosTablaDetalladaWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getSeccionesElectorales, getResultadosTablaDetallada } from '../apiService';
|
import { getSeccionesElectorales, getResultadosTablaDetallada } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ApiResponseTablaDetallada } from '../types/types';
|
import type { MunicipioSimple, ApiResponseTablaDetallada } from '../../../types/types';
|
||||||
import './ResultadosTablaSeccionWidget.css';
|
import './ResultadosTablaSeccionWidget.css';
|
||||||
|
|
||||||
const customSelectStyles = {
|
const customSelectStyles = {
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
/* ==========================================================================
|
/* src/features/legislativas/provinciales/ResultadosTablaSeccionWidget.css */
|
||||||
ResultadosTablaSeccionWidget.css
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
.tabla-resultados-widget {
|
.tabla-resultados-widget {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// src/components/ResumenGeneralWidget.tsx
|
// src/features/legislativas/provinciales/ResumenGeneralWidget.tsx
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
import type { CategoriaResumen, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// src/components/SenadoresPorSeccionWidget.tsx
|
// src/features/legislativas/provinciales/SenadoresPorSeccionWidget.tsx
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { getSeccionesElectorales, getResultadosPorSeccion, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getSeccionesElectorales, getResultadosPorSeccion, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { MunicipioSimple, ResultadoTicker, ApiResponseResultadosPorSeccion } from '../types/types';
|
import type { MunicipioSimple, ResultadoTicker, ApiResponseResultadosPorSeccion } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// src/components/SenadoresTickerWidget.tsx
|
// src/features/legislativas/provinciales/SenadoresTickerWidget.tsx
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../apiService';
|
import { getResumenProvincial, getConfiguracionPublica, assetBaseUrl } from '../../../apiService';
|
||||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
import type { CategoriaResumen, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css'; // Reutilizamos los mismos estilos
|
import './TickerWidget.css'; // Reutilizamos los mismos estilos
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// src/components/SenadoresWidget.tsx
|
// src/features/legislativas/provinciales/SenadoresWidget.tsx
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import Select from 'react-select'; // Importamos react-select
|
import Select from 'react-select'; // Importamos react-select
|
||||||
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica, assetBaseUrl } from '../apiService'; // Usamos las funciones genéricas
|
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica, assetBaseUrl } from '../../../apiService'; // Usamos las funciones genéricas
|
||||||
import type { MunicipioSimple, ResultadoTicker } from '../types/types';
|
import type { MunicipioSimple, ResultadoTicker } from '../../../types/types';
|
||||||
import { ImageWithFallback } from './ImageWithFallback';
|
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||||
import './TickerWidget.css';
|
import './TickerWidget.css';
|
||||||
|
|
||||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* src/components/TelegramaWidget.css */
|
/* src/features/legislativas/provinciales/TelegramaWidget.css */
|
||||||
.telegrama-container {
|
.telegrama-container {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #e0e0e0;
|
border: 1px solid #e0e0e0;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/components/TelegramaWidget.tsx
|
// src/features/legislativas/provinciales/TelegramaWidget.tsx
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
import Select, { type FilterOptionOption } from 'react-select'; // <-- Importar react-select
|
import Select, { type FilterOptionOption } from 'react-select'; // <-- Importar react-select
|
||||||
import {
|
import {
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
getMesasPorEstablecimiento,
|
getMesasPorEstablecimiento,
|
||||||
getTelegramaPorId,
|
getTelegramaPorId,
|
||||||
assetBaseUrl
|
assetBaseUrl
|
||||||
} from '../apiService';
|
} from '../../../apiService';
|
||||||
import type { TelegramaData, CatalogoItem } from '../types/types';
|
import type { TelegramaData, CatalogoItem } from '../../../types/types';
|
||||||
import './TelegramaWidget.css';
|
import './TelegramaWidget.css';
|
||||||
|
|
||||||
import { pdfjs, Document, Page } from 'react-pdf';
|
import { pdfjs, Document, Page } from 'react-pdf';
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
/* ==========================================================================
|
/* src/features/legislativas/provinciales/TickerWidget.css */
|
||||||
TickerWidget.css (Versión Mejorada y Responsiva)
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/* --- Contenedor Principal del Widget --- */
|
/* --- Contenedor Principal del Widget --- */
|
||||||
.ticker-card {
|
.ticker-card {
|
||||||
@@ -3,26 +3,27 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { BancasWidget } from './components/BancasWidget'
|
import { BancasWidget } from './features/legislativas/provinciales/BancasWidget'
|
||||||
import { CongresoWidget } from './components/CongresoWidget'
|
import { CongresoWidget } from './features/legislativas/provinciales/CongresoWidget'
|
||||||
import MapaBsAs from './components/MapaBsAs'
|
import MapaBsAs from './features/legislativas/provinciales/MapaBsAs'
|
||||||
import { DipSenTickerWidget } from './components/DipSenTickerWidget'
|
import { DipSenTickerWidget } from './features/legislativas/provinciales/DipSenTickerWidget'
|
||||||
import { TelegramaWidget } from './components/TelegramaWidget'
|
import { TelegramaWidget } from './features/legislativas/provinciales/TelegramaWidget'
|
||||||
import { ConcejalesWidget } from './components/ConcejalesWidget'
|
import { ConcejalesWidget } from './features/legislativas/provinciales/ConcejalesWidget'
|
||||||
import MapaBsAsSecciones from './components/MapaBsAsSecciones'
|
import MapaBsAsSecciones from './features/legislativas/provinciales/MapaBsAsSecciones'
|
||||||
import { SenadoresWidget } from './components/SenadoresWidget'
|
import { SenadoresWidget } from './features/legislativas/provinciales/SenadoresWidget'
|
||||||
import { DiputadosWidget } from './components/DiputadosWidget'
|
import { DiputadosWidget } from './features/legislativas/provinciales/DiputadosWidget'
|
||||||
import { ResumenGeneralWidget } from './components/ResumenGeneralWidget'
|
import { ResumenGeneralWidget } from './features/legislativas/provinciales/ResumenGeneralWidget'
|
||||||
import { SenadoresTickerWidget } from './components/SenadoresTickerWidget'
|
import { SenadoresTickerWidget } from './features/legislativas/provinciales/SenadoresTickerWidget'
|
||||||
import { DiputadosTickerWidget } from './components/DiputadosTickerWidget'
|
import { DiputadosTickerWidget } from './features/legislativas/provinciales/DiputadosTickerWidget'
|
||||||
import { ConcejalesTickerWidget } from './components/ConcejalesTickerWidget'
|
import { ConcejalesTickerWidget } from './features/legislativas/provinciales/ConcejalesTickerWidget'
|
||||||
import { DiputadosPorSeccionWidget } from './components/DiputadosPorSeccionWidget'
|
import { DiputadosPorSeccionWidget } from './features/legislativas/provinciales/DiputadosPorSeccionWidget'
|
||||||
import { SenadoresPorSeccionWidget } from './components/SenadoresPorSeccionWidget'
|
import { SenadoresPorSeccionWidget } from './features/legislativas/provinciales/SenadoresPorSeccionWidget'
|
||||||
import { ConcejalesPorSeccionWidget } from './components/ConcejalesPorSeccionWidget'
|
import { ConcejalesPorSeccionWidget } from './features/legislativas/provinciales/ConcejalesPorSeccionWidget'
|
||||||
import { ResultadosTablaDetalladaWidget } from './components/ResultadosTablaDetalladaWidget';
|
import { ResultadosTablaDetalladaWidget } from './features/legislativas/provinciales/ResultadosTablaDetalladaWidget';
|
||||||
import { ResultadosRankingMunicipioWidget } from './components/ResultadosRankingMunicipioWidget';
|
import { ResultadosRankingMunicipioWidget } from './features/legislativas/provinciales/ResultadosRankingMunicipioWidget';
|
||||||
import { DevApp } from './components/DevApp';
|
//import { DevApp } from './components/common/DevApp';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
import { DevAppLegislativas } from './features/legislativas/DevAppLegislativas';
|
||||||
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
@@ -56,7 +57,8 @@ if (import.meta.env.DEV) {
|
|||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<DevApp />
|
<DevAppLegislativas />
|
||||||
|
{/* <DevApp /> */}
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
// src/types/types.ts
|
// src/types/types.ts
|
||||||
|
import type { Feature as GeoJsonFeature, Geometry } from 'geojson';
|
||||||
|
|
||||||
|
|
||||||
|
// Definimos nuestras propiedades personalizadas
|
||||||
|
export interface GeoProperties {
|
||||||
|
nombre: string;
|
||||||
|
id: string;
|
||||||
|
[key: string]: any; // Permite otras propiedades
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nuestro tipo de geografía ahora se basa en el tipo estándar de GeoJSON
|
||||||
|
// y le añadimos la propiedad 'rsmKey' que 'react-simple-maps' añade.
|
||||||
|
export type AmbitoGeography = GeoJsonFeature<Geometry, GeoProperties> & { rsmKey: string };
|
||||||
|
|
||||||
// Tipos para la respuesta de la API de resultados por municipio
|
// Tipos para la respuesta de la API de resultados por municipio
|
||||||
export interface AgrupacionResultadoDto {
|
export interface AgrupacionResultadoDto {
|
||||||
@@ -33,13 +46,12 @@ export interface MapaDto {
|
|||||||
export interface GeographyObject {
|
export interface GeographyObject {
|
||||||
rsmKey: string;
|
rsmKey: string;
|
||||||
properties: {
|
properties: {
|
||||||
// CORRECCIÓN: Se cambia 'nombre' por 'NAME_2' para coincidir con el archivo topojson
|
|
||||||
NAME_2: string;
|
NAME_2: string;
|
||||||
[key: string]: any; // Permite otras propiedades que puedan venir
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MunicipioSimple { id: string; nombre: string; camarasDisponibles?: ('diputados' | 'senadores')[];}
|
export interface MunicipioSimple { id: string; nombre: string; camarasDisponibles?: ('diputados' | 'senadores')[]; }
|
||||||
|
|
||||||
export interface ResultadoTicker {
|
export interface ResultadoTicker {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -53,8 +65,8 @@ export interface ResultadoTicker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface EstadoRecuentoTicker {
|
export interface EstadoRecuentoTicker {
|
||||||
mesasTotalizadasPorcentaje: number;
|
mesasTotalizadasPorcentaje: number;
|
||||||
participacionPorcentaje: number;
|
participacionPorcentaje: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CategoriaResumen {
|
export interface CategoriaResumen {
|
||||||
@@ -109,8 +121,8 @@ export interface CatalogoItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiResponseResultadosPorSeccion {
|
export interface ApiResponseResultadosPorSeccion {
|
||||||
ultimaActualizacion: string;
|
ultimaActualizacion: string;
|
||||||
resultados: ResultadoTicker[];
|
resultados: ResultadoTicker[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResultadoTablaAgrupacion {
|
export interface ResultadoTablaAgrupacion {
|
||||||
@@ -220,3 +232,17 @@ export interface ApiResponseRankingMunicipio {
|
|||||||
categorias: { id: number; nombre: string }[];
|
categorias: { id: number; nombre: string }[];
|
||||||
resultados: RankingMunicipio[];
|
resultados: RankingMunicipio[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ResultadoMapaDto {
|
||||||
|
ambitoId: string;
|
||||||
|
ambitoNombre: string;
|
||||||
|
agrupacionGanadoraId: string;
|
||||||
|
colorGanador: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PanelElectoralDto {
|
||||||
|
ambitoNombre: string;
|
||||||
|
mapaData: ResultadoMapaDto[];
|
||||||
|
resultadosPanel: ResultadoTicker[]; // Reutilizamos el tipo que ya tienes
|
||||||
|
estadoRecuento: EstadoRecuentoTicker; // Reutilizamos el tipo que ya tienes
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// src/Elecciones.Api/Controllers/ResultadosController.cs
|
// src/Elecciones.Api/Controllers/ResultadosController.cs
|
||||||
|
using Elecciones.Core.DTOs;
|
||||||
using Elecciones.Core.DTOs.ApiResponses;
|
using Elecciones.Core.DTOs.ApiResponses;
|
||||||
using Elecciones.Database;
|
using Elecciones.Database;
|
||||||
using Elecciones.Database.Entities;
|
using Elecciones.Database.Entities;
|
||||||
@@ -8,7 +9,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
namespace Elecciones.Api.Controllers;
|
namespace Elecciones.Api.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/elecciones/{eleccionId}")]
|
||||||
public class ResultadosController : ControllerBase
|
public class ResultadosController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly EleccionesDbContext _dbContext;
|
private readonly EleccionesDbContext _dbContext;
|
||||||
@@ -46,7 +47,7 @@ public class ResultadosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("partido/{municipioId}")]
|
[HttpGet("partido/{municipioId}")]
|
||||||
public async Task<IActionResult> GetResultadosPorPartido(string municipioId, [FromQuery] int categoriaId)
|
public async Task<IActionResult> GetResultadosPorPartido([FromRoute] int eleccionId, string municipioId, [FromQuery] int categoriaId)
|
||||||
{
|
{
|
||||||
var ambito = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
var ambito = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(a => a.SeccionId == municipioId && a.NivelId == 30);
|
.FirstOrDefaultAsync(a => a.SeccionId == municipioId && a.NivelId == 30);
|
||||||
@@ -57,7 +58,7 @@ public class ResultadosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
var estadoRecuento = await _dbContext.EstadosRecuentos.AsNoTracking()
|
var estadoRecuento = await _dbContext.EstadosRecuentos.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(e => e.AmbitoGeograficoId == ambito.Id && e.CategoriaId == categoriaId);
|
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.AmbitoGeograficoId == ambito.Id && e.CategoriaId == categoriaId);
|
||||||
|
|
||||||
var agrupacionIds = await _dbContext.ResultadosVotos
|
var agrupacionIds = await _dbContext.ResultadosVotos
|
||||||
.Where(rv => rv.AmbitoGeograficoId == ambito.Id && rv.CategoriaId == categoriaId)
|
.Where(rv => rv.AmbitoGeograficoId == ambito.Id && rv.CategoriaId == categoriaId)
|
||||||
@@ -71,7 +72,7 @@ public class ResultadosController : ControllerBase
|
|||||||
|
|
||||||
var resultadosVotos = await _dbContext.ResultadosVotos.AsNoTracking()
|
var resultadosVotos = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||||
.Include(rv => rv.AgrupacionPolitica)
|
.Include(rv => rv.AgrupacionPolitica)
|
||||||
.Where(rv => rv.AmbitoGeograficoId == ambito.Id && rv.CategoriaId == categoriaId)
|
.Where(rv => rv.EleccionId == eleccionId && rv.AmbitoGeograficoId == ambito.Id && rv.CategoriaId == categoriaId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var candidatosRelevantes = await _dbContext.CandidatosOverrides.AsNoTracking()
|
var candidatosRelevantes = await _dbContext.CandidatosOverrides.AsNoTracking()
|
||||||
@@ -119,7 +120,7 @@ public class ResultadosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("provincia/{distritoId}")]
|
[HttpGet("provincia/{distritoId}")]
|
||||||
public async Task<IActionResult> GetResultadosProvinciales(string distritoId)
|
public async Task<IActionResult> GetResultadosProvinciales([FromRoute] int eleccionId, string distritoId)
|
||||||
{
|
{
|
||||||
var provincia = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
var provincia = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
|
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
|
||||||
@@ -131,13 +132,13 @@ public class ResultadosController : ControllerBase
|
|||||||
|
|
||||||
var estadosPorCategoria = await _dbContext.EstadosRecuentosGenerales.AsNoTracking()
|
var estadosPorCategoria = await _dbContext.EstadosRecuentosGenerales.AsNoTracking()
|
||||||
.Include(e => e.CategoriaElectoral)
|
.Include(e => e.CategoriaElectoral)
|
||||||
.Where(e => e.AmbitoGeograficoId == provincia.Id)
|
.Where(e => e.EleccionId == eleccionId && e.AmbitoGeograficoId == provincia.Id)
|
||||||
.ToDictionaryAsync(e => e.CategoriaId);
|
.ToDictionaryAsync(e => e.CategoriaId);
|
||||||
|
|
||||||
var resultadosPorMunicipio = await _dbContext.ResultadosVotos
|
var resultadosPorMunicipio = await _dbContext.ResultadosVotos
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(r => r.AgrupacionPolitica)
|
.Include(r => r.AgrupacionPolitica)
|
||||||
.Where(r => r.AmbitoGeografico.NivelId == 30) // Nivel 30 = Municipio
|
.Where(r => r.EleccionId == eleccionId && r.AmbitoGeografico.NivelId == 30) // Nivel 30 = Municipio
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
// Obtenemos TODOS los logos relevantes en una sola consulta
|
// Obtenemos TODOS los logos relevantes en una sola consulta
|
||||||
@@ -194,8 +195,8 @@ public class ResultadosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("bancas-por-seccion/{seccionId}/{camara}")] // <-- CAMBIO 1: Modificar la ruta
|
[HttpGet("bancas-por-seccion/{seccionId}/{camara}")]
|
||||||
public async Task<IActionResult> GetBancasPorSeccion(string seccionId, string camara) // <-- CAMBIO 2: Añadir el nuevo parámetro
|
public async Task<IActionResult> GetBancasPorSeccion([FromRoute] int eleccionId, string seccionId, string camara)
|
||||||
{
|
{
|
||||||
// Convertimos el string de la cámara a un enum o un valor numérico para la base de datos
|
// Convertimos el string de la cámara a un enum o un valor numérico para la base de datos
|
||||||
// 0 = Diputados, 1 = Senadores. Esto debe coincidir con cómo lo guardas en la DB.
|
// 0 = Diputados, 1 = Senadores. Esto debe coincidir con cómo lo guardas en la DB.
|
||||||
@@ -228,7 +229,7 @@ public class ResultadosController : ControllerBase
|
|||||||
var proyecciones = await _dbContext.ProyeccionesBancas
|
var proyecciones = await _dbContext.ProyeccionesBancas
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Include(p => p.AgrupacionPolitica)
|
.Include(p => p.AgrupacionPolitica)
|
||||||
.Where(p => p.AmbitoGeograficoId == seccion.Id && p.CategoriaId == CategoriaId) // <-- AÑADIDO EL FILTRO
|
.Where(p => p.EleccionId == eleccionId && p.AmbitoGeograficoId == seccion.Id && p.CategoriaId == CategoriaId)
|
||||||
.Select(p => new
|
.Select(p => new
|
||||||
{
|
{
|
||||||
AgrupacionId = p.AgrupacionPolitica.Id, // Añadir para el 'key' en React
|
AgrupacionId = p.AgrupacionPolitica.Id, // Añadir para el 'key' en React
|
||||||
@@ -347,7 +348,7 @@ public class ResultadosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("composicion-congreso")]
|
[HttpGet("composicion-congreso")]
|
||||||
public async Task<IActionResult> GetComposicionCongreso()
|
public async Task<IActionResult> GetComposicionCongreso([FromRoute] int eleccionId)
|
||||||
{
|
{
|
||||||
var config = await _dbContext.Configuraciones
|
var config = await _dbContext.Configuraciones
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
@@ -360,23 +361,24 @@ public class ResultadosController : ControllerBase
|
|||||||
if (usarDatosOficiales)
|
if (usarDatosOficiales)
|
||||||
{
|
{
|
||||||
// Si el interruptor está en 'true', llama a este método
|
// Si el interruptor está en 'true', llama a este método
|
||||||
return await GetComposicionDesdeBancadasOficiales(config);
|
return await GetComposicionDesdeBancadasOficiales(config, eleccionId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Si está en 'false' o no existe, llama a este otro
|
// Si está en 'false' o no existe, llama a este otro
|
||||||
return await GetComposicionDesdeProyecciones(config);
|
return await GetComposicionDesdeProyecciones(config, eleccionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// En ResultadosController.cs
|
// En ResultadosController.cs
|
||||||
private async Task<IActionResult> GetComposicionDesdeBancadasOficiales(Dictionary<string, string> config)
|
private async Task<IActionResult> GetComposicionDesdeBancadasOficiales(Dictionary<string, string> config, int eleccionId)
|
||||||
{
|
{
|
||||||
config.TryGetValue("MostrarOcupantes", out var mostrarOcupantesValue);
|
config.TryGetValue("MostrarOcupantes", out var mostrarOcupantesValue);
|
||||||
bool mostrarOcupantes = mostrarOcupantesValue == "true";
|
bool mostrarOcupantes = mostrarOcupantesValue == "true";
|
||||||
|
|
||||||
IQueryable<Bancada> bancadasQuery = _dbContext.Bancadas.AsNoTracking()
|
IQueryable<Bancada> bancadasQuery = _dbContext.Bancadas.AsNoTracking()
|
||||||
.Include(b => b.AgrupacionPolitica);
|
.Where(b => b.EleccionId == eleccionId)
|
||||||
|
.Include(b => b.AgrupacionPolitica);
|
||||||
if (mostrarOcupantes)
|
if (mostrarOcupantes)
|
||||||
{
|
{
|
||||||
bancadasQuery = bancadasQuery.Include(b => b.Ocupante);
|
bancadasQuery = bancadasQuery.Include(b => b.Ocupante);
|
||||||
@@ -459,7 +461,7 @@ public class ResultadosController : ControllerBase
|
|||||||
return Ok(new { Diputados = diputados, Senadores = senadores });
|
return Ok(new { Diputados = diputados, Senadores = senadores });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> GetComposicionDesdeProyecciones(Dictionary<string, string> config)
|
private async Task<IActionResult> GetComposicionDesdeProyecciones(Dictionary<string, string> config, int eleccionId)
|
||||||
{
|
{
|
||||||
// --- INICIO DE LA CORRECCIÓN ---
|
// --- INICIO DE LA CORRECCIÓN ---
|
||||||
// 1. Obtenemos el ID del ámbito provincial para usarlo en el filtro.
|
// 1. Obtenemos el ID del ámbito provincial para usarlo en el filtro.
|
||||||
@@ -480,8 +482,7 @@ public class ResultadosController : ControllerBase
|
|||||||
|
|
||||||
var bancasPorAgrupacion = await _dbContext.ProyeccionesBancas
|
var bancasPorAgrupacion = await _dbContext.ProyeccionesBancas
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
// --- CAMBIO CLAVE: Añadimos el filtro por AmbitoGeograficoId ---
|
.Where(p => p.EleccionId == eleccionId && p.AmbitoGeograficoId == provincia.Id)
|
||||||
.Where(p => p.AmbitoGeograficoId == provincia.Id)
|
|
||||||
.GroupBy(p => new { p.AgrupacionPoliticaId, p.CategoriaId })
|
.GroupBy(p => new { p.AgrupacionPoliticaId, p.CategoriaId })
|
||||||
.Select(g => new
|
.Select(g => new
|
||||||
{
|
{
|
||||||
@@ -551,7 +552,7 @@ public class ResultadosController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("bancadas-detalle")]
|
[HttpGet("bancadas-detalle")]
|
||||||
public async Task<IActionResult> GetBancadasConOcupantes()
|
public async Task<IActionResult> GetBancadasConOcupantes([FromRoute] int eleccionId)
|
||||||
{
|
{
|
||||||
var config = await _dbContext.Configuraciones.AsNoTracking().ToDictionaryAsync(c => c.Clave, c => c.Valor);
|
var config = await _dbContext.Configuraciones.AsNoTracking().ToDictionaryAsync(c => c.Clave, c => c.Valor);
|
||||||
|
|
||||||
@@ -565,6 +566,7 @@ public class ResultadosController : ControllerBase
|
|||||||
// Si el modo oficial SÍ está activo, devolvemos los detalles.
|
// Si el modo oficial SÍ está activo, devolvemos los detalles.
|
||||||
var bancadasConOcupantes = await _dbContext.Bancadas
|
var bancadasConOcupantes = await _dbContext.Bancadas
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
|
.Where(b => b.EleccionId == eleccionId)
|
||||||
.Include(b => b.Ocupante)
|
.Include(b => b.Ocupante)
|
||||||
.Select(b => new
|
.Select(b => new
|
||||||
{
|
{
|
||||||
@@ -1012,4 +1014,342 @@ public class ResultadosController : ControllerBase
|
|||||||
Resultados = resultadosPorMunicipio
|
Resultados = resultadosPorMunicipio
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("panel/{ambitoId?}")]
|
||||||
|
public async Task<IActionResult> GetPanelElectoral(int eleccionId, string? ambitoId, [FromQuery] int categoriaId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(ambitoId))
|
||||||
|
{
|
||||||
|
// CASO 1: No hay ID -> Vista Nacional
|
||||||
|
return await GetPanelNacional(eleccionId, categoriaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASO 2: El ID es un número (y no un string corto como "02") -> Vista Municipal
|
||||||
|
// La condición clave es que los IDs de distrito son cortos. Los IDs de BD son más largos.
|
||||||
|
// O simplemente, un ID de distrito nunca será un ID de municipio.
|
||||||
|
if (int.TryParse(ambitoId, out int idNumerico) && ambitoId.Length > 2)
|
||||||
|
{
|
||||||
|
return await GetPanelMunicipal(eleccionId, idNumerico, categoriaId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// CASO 3: El ID es un string corto como "02" o "06" -> Vista Provincial
|
||||||
|
return await GetPanelProvincial(eleccionId, ambitoId, categoriaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IActionResult> GetPanelMunicipal(int eleccionId, int ambitoId, int categoriaId)
|
||||||
|
{
|
||||||
|
// 1. Validar y obtener la entidad del municipio
|
||||||
|
var municipio = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(a => a.Id == ambitoId && a.NivelId == 30);
|
||||||
|
|
||||||
|
if (municipio == null) return NotFound($"No se encontró el municipio con ID {ambitoId}.");
|
||||||
|
|
||||||
|
// 2. Obtener los votos solo para ESE municipio
|
||||||
|
var resultadosCrudos = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||||
|
.Include(r => r.AgrupacionPolitica)
|
||||||
|
.Where(r => r.EleccionId == eleccionId &&
|
||||||
|
r.CategoriaId == categoriaId &&
|
||||||
|
r.AmbitoGeograficoId == ambitoId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!resultadosCrudos.Any())
|
||||||
|
{
|
||||||
|
// Devolver un DTO vacío pero válido si no hay resultados
|
||||||
|
return Ok(new PanelElectoralDto
|
||||||
|
{
|
||||||
|
AmbitoNombre = municipio.Nombre,
|
||||||
|
MapaData = new List<ResultadoMapaDto>(), // El mapa estará vacío en la vista de un solo municipio
|
||||||
|
ResultadosPanel = new List<AgrupacionResultadoDto>(),
|
||||||
|
EstadoRecuento = new EstadoRecuentoDto()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Calcular los resultados para el panel lateral (son los mismos datos crudos)
|
||||||
|
var totalVotosMunicipio = (decimal)resultadosCrudos.Sum(r => r.CantidadVotos);
|
||||||
|
var resultadosPanel = resultadosCrudos
|
||||||
|
.Select(g => new AgrupacionResultadoDto
|
||||||
|
{
|
||||||
|
Id = g.AgrupacionPolitica.Id,
|
||||||
|
Nombre = g.AgrupacionPolitica.Nombre,
|
||||||
|
NombreCorto = g.AgrupacionPolitica.NombreCorto,
|
||||||
|
Color = g.AgrupacionPolitica.Color,
|
||||||
|
Votos = g.CantidadVotos,
|
||||||
|
Porcentaje = totalVotosMunicipio > 0 ? (g.CantidadVotos / totalVotosMunicipio) * 100 : 0
|
||||||
|
})
|
||||||
|
.OrderByDescending(r => r.Votos)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var estadoRecuento = await _dbContext.EstadosRecuentos
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.AmbitoGeograficoId == ambitoId && e.CategoriaId == categoriaId);
|
||||||
|
|
||||||
|
var respuesta = new PanelElectoralDto
|
||||||
|
{
|
||||||
|
AmbitoNombre = municipio.Nombre,
|
||||||
|
MapaData = new List<ResultadoMapaDto>(), // El mapa no muestra sub-geografías aquí
|
||||||
|
ResultadosPanel = resultadosPanel,
|
||||||
|
EstadoRecuento = new EstadoRecuentoDto
|
||||||
|
{
|
||||||
|
ParticipacionPorcentaje = estadoRecuento?.ParticipacionPorcentaje ?? 0,
|
||||||
|
MesasTotalizadasPorcentaje = estadoRecuento?.MesasTotalizadasPorcentaje ?? 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(respuesta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Este método se ejecutará cuando la URL sea, por ejemplo, /api/elecciones/2/panel/02?categoriaId=2
|
||||||
|
private async Task<IActionResult> GetPanelProvincial(int eleccionId, string distritoId, int categoriaId)
|
||||||
|
{
|
||||||
|
// 1. Validar y obtener la entidad de la provincia
|
||||||
|
var provincia = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
|
||||||
|
|
||||||
|
if (provincia == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Panel Provincial: La provincia {DistritoId} no fue encontrada en AmbitosGeograficos.", distritoId);
|
||||||
|
return NotFound($"No se encontró la provincia con distritoId {distritoId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Panel Provincial: Provincia {Nombre} ({Id}) encontrada. Buscando municipios...", provincia.Nombre, provincia.Id);
|
||||||
|
|
||||||
|
// 2. Obtener los votos de TODOS los municipios de ESA provincia.
|
||||||
|
var resultadosCrudos = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||||
|
.Include(r => r.AgrupacionPolitica)
|
||||||
|
.Include(r => r.AmbitoGeografico)
|
||||||
|
.Where(r => r.EleccionId == eleccionId &&
|
||||||
|
r.CategoriaId == categoriaId &&
|
||||||
|
r.AmbitoGeografico.DistritoId == distritoId &&
|
||||||
|
r.AmbitoGeografico.NivelId == 30)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!resultadosCrudos.Any())
|
||||||
|
{
|
||||||
|
return Ok(new PanelElectoralDto
|
||||||
|
{
|
||||||
|
AmbitoNombre = provincia.Nombre,
|
||||||
|
MapaData = new List<ResultadoMapaDto>(),
|
||||||
|
ResultadosPanel = new List<AgrupacionResultadoDto>(),
|
||||||
|
EstadoRecuento = new EstadoRecuentoDto()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Calcular los resultados para el PANEL LATERAL (agregado provincial)
|
||||||
|
var totalVotosProvincia = (decimal)resultadosCrudos.Sum(r => r.CantidadVotos);
|
||||||
|
var resultadosPanel = resultadosCrudos
|
||||||
|
.GroupBy(r => r.AgrupacionPolitica.Id)
|
||||||
|
.Select(g => new AgrupacionResultadoDto
|
||||||
|
{
|
||||||
|
Id = g.Key,
|
||||||
|
Nombre = g.First().AgrupacionPolitica.Nombre,
|
||||||
|
NombreCorto = g.First().AgrupacionPolitica.NombreCorto,
|
||||||
|
Color = g.First().AgrupacionPolitica.Color,
|
||||||
|
Votos = g.Sum(r => r.CantidadVotos),
|
||||||
|
Porcentaje = totalVotosProvincia > 0 ? (g.Sum(r => r.CantidadVotos) / totalVotosProvincia) * 100 : 0
|
||||||
|
})
|
||||||
|
.OrderByDescending(r => r.Votos)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// --- CORRECCIÓN DEFINITIVA DE LA LÓGICA DEL MAPA ---
|
||||||
|
var mapaData = resultadosCrudos
|
||||||
|
.GroupBy(r => r.AmbitoGeografico) // 1. Agrupar por la entidad del municipio
|
||||||
|
.Select(g =>
|
||||||
|
{
|
||||||
|
// 2. DENTRO de cada grupo (municipio), encontrar la fila con más votos
|
||||||
|
var ganador = g.OrderByDescending(r => r.CantidadVotos).First();
|
||||||
|
|
||||||
|
// 3. Crear UN SOLO objeto ResultadoMapaDto para este municipio
|
||||||
|
return new ResultadoMapaDto
|
||||||
|
{
|
||||||
|
AmbitoId = g.Key.SeccionId!,
|
||||||
|
AmbitoNombre = g.Key.Nombre,
|
||||||
|
AgrupacionGanadoraId = ganador.AgrupacionPolitica.Id,
|
||||||
|
ColorGanador = ganador.AgrupacionPolitica.Color ?? "#808080"
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var estadoRecuento = await _dbContext.EstadosRecuentosGenerales.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.AmbitoGeograficoId == provincia.Id && e.CategoriaId == categoriaId);
|
||||||
|
|
||||||
|
var respuesta = new PanelElectoralDto
|
||||||
|
{
|
||||||
|
AmbitoNombre = provincia.Nombre,
|
||||||
|
MapaData = mapaData,
|
||||||
|
ResultadosPanel = resultadosPanel,
|
||||||
|
EstadoRecuento = new EstadoRecuentoDto() // Simplificado para el ejemplo
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(respuesta);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IActionResult> GetPanelNacional(int eleccionId, int categoriaId)
|
||||||
|
{
|
||||||
|
var resultadosCrudos = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||||
|
.Include(r => r.AgrupacionPolitica)
|
||||||
|
.Include(r => r.AmbitoGeografico)
|
||||||
|
.Where(r => r.EleccionId == eleccionId && r.CategoriaId == categoriaId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!resultadosCrudos.Any())
|
||||||
|
{
|
||||||
|
return Ok(new PanelElectoralDto
|
||||||
|
{
|
||||||
|
AmbitoNombre = "Argentina",
|
||||||
|
MapaData = new List<ResultadoMapaDto>(),
|
||||||
|
ResultadosPanel = new List<AgrupacionResultadoDto>(),
|
||||||
|
EstadoRecuento = new EstadoRecuentoDto()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Calcular los resultados para el panel
|
||||||
|
var resultadosPanel = resultadosCrudos
|
||||||
|
.GroupBy(r => r.AgrupacionPolitica.Id)
|
||||||
|
.Select(g => new AgrupacionResultadoDto
|
||||||
|
{
|
||||||
|
// g.Key ahora es el ID de la agrupación
|
||||||
|
Id = g.Key,
|
||||||
|
// Tomamos los datos de la agrupación del primer elemento del grupo
|
||||||
|
Nombre = g.First().AgrupacionPolitica.Nombre,
|
||||||
|
NombreCorto = g.First().AgrupacionPolitica.NombreCorto,
|
||||||
|
Color = g.First().AgrupacionPolitica.Color,
|
||||||
|
Votos = g.Sum(r => r.CantidadVotos),
|
||||||
|
Porcentaje = (decimal)g.Sum(r => r.CantidadVotos) * 100 / resultadosCrudos.Sum(r => r.CantidadVotos)
|
||||||
|
})
|
||||||
|
.OrderByDescending(r => r.Votos)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 3. Calcular los datos para el mapa (ganador por provincia/distrito)
|
||||||
|
var mapaData = resultadosCrudos
|
||||||
|
.Where(r => !string.IsNullOrEmpty(r.AmbitoGeografico.DistritoId))
|
||||||
|
.GroupBy(r => r.AmbitoGeografico.DistritoId)
|
||||||
|
.Select(g =>
|
||||||
|
{
|
||||||
|
var ganador = g.GroupBy(r => r.AgrupacionPoliticaId)
|
||||||
|
.Select(pg => new { Votos = pg.Sum(r => r.CantidadVotos), Agrupacion = pg.First().AgrupacionPolitica })
|
||||||
|
.OrderByDescending(x => x.Votos)
|
||||||
|
.First();
|
||||||
|
|
||||||
|
var provinciaAmbito = _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||||
|
.FirstOrDefault(a => a.DistritoId == g.Key && a.NivelId == 10);
|
||||||
|
|
||||||
|
return new ResultadoMapaDto
|
||||||
|
{
|
||||||
|
AmbitoId = g.Key!,
|
||||||
|
AmbitoNombre = provinciaAmbito?.Nombre ?? "Desconocido", // <-- ENVIAMOS EL NOMBRE DE LA PROVINCIA
|
||||||
|
AgrupacionGanadoraId = ganador.Agrupacion.Id,
|
||||||
|
ColorGanador = ganador.Agrupacion.Color ?? "#808080"
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 4. Obtener el estado del recuento general
|
||||||
|
// Asumimos que existe un registro en EstadosRecuentosGenerales para el ámbito de la elección
|
||||||
|
var estadoRecuento = await _dbContext.EstadosRecuentosGenerales
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId);
|
||||||
|
|
||||||
|
var respuesta = new PanelElectoralDto
|
||||||
|
{
|
||||||
|
AmbitoNombre = "Argentina",
|
||||||
|
MapaData = mapaData,
|
||||||
|
ResultadosPanel = resultadosPanel,
|
||||||
|
EstadoRecuento = new EstadoRecuentoDto()
|
||||||
|
};
|
||||||
|
return Ok(respuesta);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("mapa-resultados")]
|
||||||
|
public async Task<IActionResult> GetResultadosMapaPorMunicipio(
|
||||||
|
[FromRoute] int eleccionId,
|
||||||
|
[FromQuery] int categoriaId,
|
||||||
|
[FromQuery] string? distritoId = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(distritoId))
|
||||||
|
{
|
||||||
|
// --- VISTA NACIONAL (Ya corregida y funcionando) ---
|
||||||
|
var votosAgregadosPorProvincia = await _dbContext.ResultadosVotos
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(r => r.EleccionId == eleccionId
|
||||||
|
&& r.CategoriaId == categoriaId
|
||||||
|
&& r.AmbitoGeografico.NivelId == 30
|
||||||
|
&& r.AmbitoGeografico.DistritoId != null)
|
||||||
|
.GroupBy(r => new { r.AmbitoGeografico.DistritoId, r.AgrupacionPoliticaId })
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
g.Key.DistritoId,
|
||||||
|
g.Key.AgrupacionPoliticaId,
|
||||||
|
TotalVotos = g.Sum(r => r.CantidadVotos)
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var agrupacionesInfo = await _dbContext.AgrupacionesPoliticas.AsNoTracking().ToDictionaryAsync(a => a.Id);
|
||||||
|
var provinciasInfo = await _dbContext.AmbitosGeograficos.AsNoTracking().Where(a => a.NivelId == 10).ToListAsync();
|
||||||
|
|
||||||
|
var ganadoresPorProvincia = votosAgregadosPorProvincia
|
||||||
|
.GroupBy(r => r.DistritoId)
|
||||||
|
.Select(g => g.OrderByDescending(x => x.TotalVotos).First())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var mapaDataNacional = ganadoresPorProvincia.Select(g => new ResultadoMapaDto
|
||||||
|
{
|
||||||
|
AmbitoId = g.DistritoId!,
|
||||||
|
AmbitoNombre = provinciasInfo.FirstOrDefault(p => p.DistritoId == g.DistritoId)?.Nombre ?? "Desconocido",
|
||||||
|
AgrupacionGanadoraId = g.AgrupacionPoliticaId,
|
||||||
|
ColorGanador = agrupacionesInfo.GetValueOrDefault(g.AgrupacionPoliticaId)?.Color ?? "#808080"
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return Ok(mapaDataNacional);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// --- VISTA PROVINCIAL (AHORA CORREGIDA CON LA MISMA LÓGICA) ---
|
||||||
|
|
||||||
|
// PASO 1: Agrupar por IDs y sumar votos en la base de datos.
|
||||||
|
var votosAgregadosPorMunicipio = await _dbContext.ResultadosVotos
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(r => r.EleccionId == eleccionId
|
||||||
|
&& r.CategoriaId == categoriaId
|
||||||
|
&& r.AmbitoGeografico.DistritoId == distritoId
|
||||||
|
&& r.AmbitoGeografico.NivelId == 30)
|
||||||
|
// Agrupamos por los IDs (int y string)
|
||||||
|
.GroupBy(r => new { r.AmbitoGeograficoId, r.AgrupacionPoliticaId })
|
||||||
|
.Select(g => new
|
||||||
|
{
|
||||||
|
g.Key.AmbitoGeograficoId,
|
||||||
|
g.Key.AgrupacionPoliticaId,
|
||||||
|
TotalVotos = g.Sum(r => r.CantidadVotos)
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// PASO 2: Encontrar el ganador para cada municipio en memoria.
|
||||||
|
var ganadoresPorMunicipio = votosAgregadosPorMunicipio
|
||||||
|
.GroupBy(r => r.AmbitoGeograficoId)
|
||||||
|
.Select(g => g.OrderByDescending(x => x.TotalVotos).First())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// PASO 3: Hidratar con los nombres y colores (muy rápido).
|
||||||
|
var idsMunicipios = ganadoresPorMunicipio.Select(g => g.AmbitoGeograficoId).ToList();
|
||||||
|
var idsAgrupaciones = ganadoresPorMunicipio.Select(g => g.AgrupacionPoliticaId).ToList();
|
||||||
|
|
||||||
|
var municipiosInfo = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||||
|
.Where(a => idsMunicipios.Contains(a.Id)).ToDictionaryAsync(a => a.Id);
|
||||||
|
|
||||||
|
var agrupacionesInfo = await _dbContext.AgrupacionesPoliticas.AsNoTracking()
|
||||||
|
.Where(a => idsAgrupaciones.Contains(a.Id)).ToDictionaryAsync(a => a.Id);
|
||||||
|
|
||||||
|
// Mapeo final a DTO.
|
||||||
|
var mapaDataProvincial = ganadoresPorMunicipio.Select(g => new ResultadoMapaDto
|
||||||
|
{
|
||||||
|
AmbitoId = g.AmbitoGeograficoId.ToString(),
|
||||||
|
AmbitoNombre = municipiosInfo.GetValueOrDefault(g.AmbitoGeograficoId)?.Nombre ?? "Desconocido",
|
||||||
|
AgrupacionGanadoraId = g.AgrupacionPoliticaId,
|
||||||
|
ColorGanador = agrupacionesInfo.GetValueOrDefault(g.AgrupacionPoliticaId)?.Color ?? "#808080"
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return Ok(mapaDataProvincial);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -215,6 +215,116 @@ using (var scope = app.Services.CreateScope())
|
|||||||
Console.WriteLine("--> Seeded default configurations.");
|
Console.WriteLine("--> Seeded default configurations.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- SEEDER FINAL Y AUTOSUFICIENTE: Resultados Nacionales de Simulación para todo el país ---
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var services = scope.ServiceProvider;
|
||||||
|
var context = services.GetRequiredService<EleccionesDbContext>();
|
||||||
|
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||||
|
|
||||||
|
const int eleccionNacionalId = 2;
|
||||||
|
|
||||||
|
if (!context.ResultadosVotos.Any(r => r.EleccionId == eleccionNacionalId))
|
||||||
|
{
|
||||||
|
logger.LogInformation("--> No se encontraron datos para la elección nacional ID {EleccionId}. Generando datos de simulación para TODO EL PAÍS...", eleccionNacionalId);
|
||||||
|
|
||||||
|
var eleccionNacional = await context.Elecciones.FindAsync(eleccionNacionalId) ?? new Eleccion { Id = eleccionNacionalId, Nombre = "Elecciones Nacionales 2025", Nivel = "Nacional", DistritoId = "00", Fecha = new DateOnly(2025, 10, 26) };
|
||||||
|
if (!context.Elecciones.Local.Any(e => e.Id == eleccionNacionalId)) context.Elecciones.Add(eleccionNacional);
|
||||||
|
|
||||||
|
var categoriaDiputadosNac = await context.CategoriasElectorales.FindAsync(2) ?? new CategoriaElectoral { Id = 2, Nombre = "DIPUTADOS NACIONALES", Orden = 3 };
|
||||||
|
if (!context.CategoriasElectorales.Local.Any(c => c.Id == 2)) context.CategoriasElectorales.Add(categoriaDiputadosNac);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var provinciasMaestras = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "01", "CABA" }, { "02", "BUENOS AIRES" }, { "03", "CATAMARCA" }, { "04", "CORDOBA" },
|
||||||
|
{ "05", "CORRIENTES" },{ "06", "CHACO" }, { "07", "CHUBUT" }, { "08", "ENTRE RIOS" },
|
||||||
|
{ "09", "FORMOSA" }, { "10", "JUJUY" }, { "11", "LA PAMPA" }, { "12", "LA RIOJA" },
|
||||||
|
{ "13", "MENDOZA" }, { "14", "MISIONES" }, { "15", "NEUQUEN" }, { "16", "RIO NEGRO" },
|
||||||
|
{ "17", "SALTA" }, { "18", "SAN JUAN" }, { "19", "SAN LUIS" }, { "20", "SANTA CRUZ" },
|
||||||
|
{ "21", "SANTA FE" }, { "22", "SANTIAGO DEL ESTERO" }, { "23", "TIERRA DEL FUEGO" }, { "24", "TUCUMAN" }
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var p in provinciasMaestras)
|
||||||
|
{
|
||||||
|
if (!await context.AmbitosGeograficos.AnyAsync(a => a.NivelId == 10 && a.DistritoId == p.Key))
|
||||||
|
{
|
||||||
|
context.AmbitosGeograficos.Add(new AmbitoGeografico { Nombre = p.Value, NivelId = 10, DistritoId = p.Key });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
logger.LogInformation("--> Verificados/creados los 24 ámbitos geográficos de Nivel 10.");
|
||||||
|
|
||||||
|
// --- INICIO DE LA LÓGICA DE CREACIÓN DE MUNICIPIOS DE EJEMPLO ---
|
||||||
|
logger.LogInformation("--> Verificando existencia de municipios (Nivel 30) para cada provincia...");
|
||||||
|
var provinciasEnDb = await context.AmbitosGeograficos.AsNoTracking().Where(a => a.NivelId == 10).ToListAsync();
|
||||||
|
foreach (var provincia in provinciasEnDb)
|
||||||
|
{
|
||||||
|
bool existenMunicipios = await context.AmbitosGeograficos.AnyAsync(a => a.NivelId == 30 && a.DistritoId == provincia.DistritoId);
|
||||||
|
if (!existenMunicipios)
|
||||||
|
{
|
||||||
|
logger.LogWarning("--> No se encontraron municipios para {Provincia}. Creando 5 municipios de ejemplo...", provincia.Nombre);
|
||||||
|
for (int i = 1; i <= 5; i++)
|
||||||
|
{
|
||||||
|
context.AmbitosGeograficos.Add(new AmbitoGeografico
|
||||||
|
{
|
||||||
|
Nombre = $"{provincia.Nombre} - Municipio de Ejemplo {i}",
|
||||||
|
NivelId = 30,
|
||||||
|
DistritoId = provincia.DistritoId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
// --- FIN DE LA LÓGICA DE CREACIÓN DE MUNICIPIOS DE EJEMPLO ---
|
||||||
|
|
||||||
|
var todosLosPartidos = await context.AgrupacionesPoliticas.Take(5).ToListAsync();
|
||||||
|
if (!todosLosPartidos.Any()) {
|
||||||
|
logger.LogWarning("--> No hay agrupaciones políticas en la BD. No se pueden generar votos de simulación.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nuevosResultados = new List<ResultadoVoto>();
|
||||||
|
var rand = new Random();
|
||||||
|
|
||||||
|
foreach (var provincia in provinciasEnDb)
|
||||||
|
{
|
||||||
|
var municipiosDeProvincia = await context.AmbitosGeograficos.AsNoTracking()
|
||||||
|
.Where(a => a.NivelId == 30 && a.DistritoId == provincia.DistritoId).ToListAsync();
|
||||||
|
|
||||||
|
if (!municipiosDeProvincia.Any()) continue;
|
||||||
|
|
||||||
|
logger.LogInformation("--> Generando votos para {Count} municipios en {Provincia}...", municipiosDeProvincia.Count, provincia.Nombre);
|
||||||
|
|
||||||
|
int partidoIndex = rand.Next(todosLosPartidos.Count);
|
||||||
|
foreach (var municipio in municipiosDeProvincia)
|
||||||
|
{
|
||||||
|
var partidoGanador = todosLosPartidos[partidoIndex % todosLosPartidos.Count];
|
||||||
|
partidoIndex++;
|
||||||
|
nuevosResultados.Add(new ResultadoVoto {
|
||||||
|
EleccionId = eleccionNacional.Id, AmbitoGeograficoId = municipio.Id, CategoriaId = categoriaDiputadosNac.Id,
|
||||||
|
AgrupacionPoliticaId = partidoGanador.Id, CantidadVotos = rand.Next(25000, 70000)
|
||||||
|
});
|
||||||
|
var otrosPartidos = todosLosPartidos.Where(p => p.Id != partidoGanador.Id).OrderBy(p => rand.Next()).Take(rand.Next(3, 6));
|
||||||
|
foreach (var competidor in otrosPartidos) {
|
||||||
|
nuevosResultados.Add(new ResultadoVoto {
|
||||||
|
EleccionId = eleccionNacional.Id, AmbitoGeograficoId = municipio.Id, CategoriaId = categoriaDiputadosNac.Id,
|
||||||
|
AgrupacionPoliticaId = competidor.Id, CantidadVotos = rand.Next(1000, 24000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nuevosResultados.Any()) {
|
||||||
|
await context.ResultadosVotos.AddRangeAsync(nuevosResultados);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
logger.LogInformation("--> Se generaron {Count} registros de votos de simulación para todo el país.", nuevosResultados.Count);
|
||||||
|
} else {
|
||||||
|
logger.LogWarning("--> No se encontraron municipios en la BD para generar votos de simulación.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configurar el pipeline de peticiones HTTP.
|
// Configurar el pipeline de peticiones HTTP.
|
||||||
// Añadimos el logging de peticiones de Serilog aquí.
|
// Añadimos el logging de peticiones de Serilog aquí.
|
||||||
app.UseSerilogRequestLogging();
|
app.UseSerilogRequestLogging();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+843c0f725893a48eae1236473cb5eeb00ef3d91c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+64dc7ef440f585cb5e7723585b6327d5387f1b32")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","/f\u002B\u002BdIRysg7dipW05N4RtxXuPBXZZIhhi3aMiCZ\u002BB2w="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","xf7fvi/A0tUBZMxhPJDER8vuJQH2E3gyAnDWXXhOyD0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","sfQbd729CZjIZCr20t2H\u002BLd7hI/GEf6\u002BdyvRnmo8MpU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","/f\u002B\u002BdIRysg7dipW05N4RtxXuPBXZZIhhi3aMiCZ\u002BB2w="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","xf7fvi/A0tUBZMxhPJDER8vuJQH2E3gyAnDWXXhOyD0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","sfQbd729CZjIZCr20t2H\u002BLd7hI/GEf6\u002BdyvRnmo8MpU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Elecciones.Core.DTOs;
|
||||||
|
using Elecciones.Core.DTOs.ApiResponses;
|
||||||
|
|
||||||
|
public class PanelElectoralDto
|
||||||
|
{
|
||||||
|
public required string AmbitoNombre { get; set; }
|
||||||
|
public required List<ResultadoMapaDto> MapaData { get; set; }
|
||||||
|
public required List<AgrupacionResultadoDto> ResultadosPanel { get; set; }
|
||||||
|
public required EstadoRecuentoDto EstadoRecuento { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
public class ResultadoMapaDto
|
||||||
|
{
|
||||||
|
public required string AmbitoId { get; set; } // ID de la provincia o municipio
|
||||||
|
public required string AmbitoNombre { get; set; }
|
||||||
|
public required string AgrupacionGanadoraId { get; set; }
|
||||||
|
public required string ColorGanador { get; set; }
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+843c0f725893a48eae1236473cb5eeb00ef3d91c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+64dc7ef440f585cb5e7723585b6327d5387f1b32")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public class EleccionesDbContext(DbContextOptions<EleccionesDbContext> options)
|
|||||||
public DbSet<OcupanteBanca> OcupantesBancas { get; set; }
|
public DbSet<OcupanteBanca> OcupantesBancas { get; set; }
|
||||||
public DbSet<LogoAgrupacionCategoria> LogosAgrupacionesCategorias { get; set; }
|
public DbSet<LogoAgrupacionCategoria> LogosAgrupacionesCategorias { get; set; }
|
||||||
public DbSet<CandidatoOverride> CandidatosOverrides { get; set; }
|
public DbSet<CandidatoOverride> CandidatosOverrides { get; set; }
|
||||||
|
public DbSet<Eleccion> Elecciones { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ public class Bancada
|
|||||||
|
|
||||||
// Relación uno a uno con OcupanteBanca
|
// Relación uno a uno con OcupanteBanca
|
||||||
public OcupanteBanca? Ocupante { get; set; }
|
public OcupanteBanca? Ocupante { get; set; }
|
||||||
|
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -31,4 +31,5 @@ public class CandidatoOverride
|
|||||||
[Required]
|
[Required]
|
||||||
[MaxLength(255)]
|
[MaxLength(255)]
|
||||||
public string NombreCandidato { get; set; } = null!;
|
public string NombreCandidato { get; set; } = null!;
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// src/Elecciones.Database/Entities/CategoriaElectoral.cs
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
|||||||
14
Elecciones-Web/src/Elecciones.Database/Entities/Eleccion.cs
Normal file
14
Elecciones-Web/src/Elecciones.Database/Entities/Eleccion.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
public class Eleccion
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Nombre { get; set; } = null!; // Ej: "Elecciones Provinciales BA 2025"
|
||||||
|
[Required]
|
||||||
|
public string Nivel { get; set; } = null!; // Ej: "Provincial" o "Nacional"
|
||||||
|
[Required]
|
||||||
|
public string DistritoId { get; set; } = null!; // Ej: "02" para PBA, "00" para Nacional
|
||||||
|
public DateOnly Fecha { get; set; }
|
||||||
|
}
|
||||||
@@ -24,4 +24,5 @@ public class EstadoRecuento
|
|||||||
public decimal VotosNulosPorcentaje { get; set; }
|
public decimal VotosNulosPorcentaje { get; set; }
|
||||||
public decimal VotosEnBlancoPorcentaje { get; set; }
|
public decimal VotosEnBlancoPorcentaje { get; set; }
|
||||||
public decimal VotosRecurridosPorcentaje { get; set; }
|
public decimal VotosRecurridosPorcentaje { get; set; }
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,9 @@ public class EstadoRecuentoGeneral
|
|||||||
public int CantidadVotantes { get; set; }
|
public int CantidadVotantes { get; set; }
|
||||||
public decimal ParticipacionPorcentaje { get; set; }
|
public decimal ParticipacionPorcentaje { get; set; }
|
||||||
|
|
||||||
// --- Propiedades de navegación (Opcional pero recomendado) ---
|
// --- Propiedad de navegación (Opcional pero recomendado) ---
|
||||||
[ForeignKey("CategoriaId")]
|
[ForeignKey("CategoriaId")]
|
||||||
public CategoriaElectoral CategoriaElectoral { get; set; } = null!;
|
public CategoriaElectoral CategoriaElectoral { get; set; } = null!;
|
||||||
|
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -13,4 +13,5 @@ public class LogoAgrupacionCategoria
|
|||||||
public int CategoriaId { get; set; }
|
public int CategoriaId { get; set; }
|
||||||
public string? LogoUrl { get; set; }
|
public string? LogoUrl { get; set; }
|
||||||
public int? AmbitoGeograficoId { get; set; }
|
public int? AmbitoGeograficoId { get; set; }
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -21,4 +21,6 @@ public class OcupanteBanca
|
|||||||
public string? FotoUrl { get; set; }
|
public string? FotoUrl { get; set; }
|
||||||
|
|
||||||
public string? Periodo { get; set; }
|
public string? Periodo { get; set; }
|
||||||
|
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -24,4 +24,5 @@ public class ProyeccionBanca
|
|||||||
// Cantidad de bancas obtenidas
|
// Cantidad de bancas obtenidas
|
||||||
public int NroBancas { get; set; }
|
public int NroBancas { get; set; }
|
||||||
public DateTime FechaTotalizacion { get; set; }
|
public DateTime FechaTotalizacion { get; set; }
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -24,4 +24,6 @@ public class ResultadoVoto
|
|||||||
public long CantidadVotos { get; set; }
|
public long CantidadVotos { get; set; }
|
||||||
|
|
||||||
public decimal PorcentajeVotos { get; set; }
|
public decimal PorcentajeVotos { get; set; }
|
||||||
|
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -24,4 +24,6 @@ public class ResumenVoto
|
|||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public decimal VotosPorcentaje { get; set; }
|
public decimal VotosPorcentaje { get; set; }
|
||||||
|
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
@@ -18,4 +18,6 @@ public class Telegrama
|
|||||||
|
|
||||||
public DateTime FechaEscaneo { get; set; }
|
public DateTime FechaEscaneo { get; set; }
|
||||||
public DateTime FechaTotalizacion { get; set; }
|
public DateTime FechaTotalizacion { get; set; }
|
||||||
|
|
||||||
|
public int EleccionId { get; set; }
|
||||||
}
|
}
|
||||||
675
Elecciones-Web/src/Elecciones.Database/Migrations/20250911152547_AddEleccionEntities.Designer.cs
generated
Normal file
675
Elecciones-Web/src/Elecciones.Database/Migrations/20250911152547_AddEleccionEntities.Designer.cs
generated
Normal file
@@ -0,0 +1,675 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Elecciones.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Elecciones.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(EleccionesDbContext))]
|
||||||
|
[Migration("20250911152547_AddEleccionEntities")]
|
||||||
|
partial class AddEleccionEntities
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.UseCollation("Modern_Spanish_CI_AS")
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.8")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Eleccion", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DistritoId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Fecha")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<string>("Nivel")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Nombre")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Elecciones");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.AdminUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordSalt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("AdminUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Color")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("IdTelegrama")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Nombre")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("NombreCorto")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int?>("OrdenDiputados")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("OrdenSenadores")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("AgrupacionesPoliticas");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.AmbitoGeografico", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("CircuitoId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("DistritoId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("EstablecimientoId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("MesaId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("MunicipioId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("NivelId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Nombre")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SeccionId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SeccionProvincialId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("AmbitosGeograficos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AgrupacionPoliticaId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("Camara")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("NumeroBanca")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.ToTable("Bancadas");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AgrupacionPoliticaId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int?>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("NombreCandidato")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("nvarchar(255)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AmbitoGeograficoId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoriaId");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId", "CategoriaId", "AmbitoGeograficoId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[AmbitoGeograficoId] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("CandidatosOverrides");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Nombre")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("Orden")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CategoriasElectorales");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.Configuracion", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Clave")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Valor")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.HasKey("Clave");
|
||||||
|
|
||||||
|
b.ToTable("Configuraciones");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CantidadElectores")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CantidadVotantes")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("MesasEsperadas")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("MesasTotalizadas")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("MesasTotalizadasPorcentaje")
|
||||||
|
.HasPrecision(5, 2)
|
||||||
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ParticipacionPorcentaje")
|
||||||
|
.HasPrecision(5, 2)
|
||||||
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
|
b.Property<long>("VotosEnBlanco")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<decimal>("VotosEnBlancoPorcentaje")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<long>("VotosNulos")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<decimal>("VotosNulosPorcentaje")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<long>("VotosRecurridos")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<decimal>("VotosRecurridosPorcentaje")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.HasKey("AmbitoGeograficoId", "CategoriaId");
|
||||||
|
|
||||||
|
b.ToTable("EstadosRecuentos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CantidadElectores")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CantidadVotantes")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("MesasEsperadas")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("MesasTotalizadas")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("MesasTotalizadasPorcentaje")
|
||||||
|
.HasPrecision(5, 2)
|
||||||
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
|
b.Property<decimal>("ParticipacionPorcentaje")
|
||||||
|
.HasPrecision(5, 2)
|
||||||
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
|
b.HasKey("AmbitoGeograficoId", "CategoriaId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoriaId");
|
||||||
|
|
||||||
|
b.ToTable("EstadosRecuentosGenerales");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.LogoAgrupacionCategoria", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AgrupacionPoliticaId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int?>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("LogoUrl")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId", "CategoriaId", "AmbitoGeograficoId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasFilter("[AmbitoGeograficoId] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("LogosAgrupacionesCategorias");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("BancadaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("FotoUrl")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("NombreOcupante")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Periodo")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BancadaId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("OcupantesBancas");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AgrupacionPoliticaId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("NroBancas")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ProyeccionesBancas");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AgrupacionPoliticaId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<long>("CantidadVotos")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("PorcentajeVotos")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ResultadosVotos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AgrupacionPoliticaId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<long>("Votos")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<decimal>("VotosPorcentaje")
|
||||||
|
.HasPrecision(5, 2)
|
||||||
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.ToTable("ResumenesVotos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.Telegrama", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ContenidoBase64")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FechaEscaneo")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Telegramas");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.Navigation("AgrupacionPolitica");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgrupacionPoliticaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AmbitoGeograficoId");
|
||||||
|
|
||||||
|
b.HasOne("Elecciones.Database.Entities.CategoriaElectoral", "CategoriaElectoral")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoriaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AgrupacionPolitica");
|
||||||
|
|
||||||
|
b.Navigation("AmbitoGeografico");
|
||||||
|
|
||||||
|
b.Navigation("CategoriaElectoral");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AmbitoGeograficoId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AmbitoGeografico");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.CategoriaElectoral", "CategoriaElectoral")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoriaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("CategoriaElectoral");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.Bancada", "Bancada")
|
||||||
|
.WithOne("Ocupante")
|
||||||
|
.HasForeignKey("Elecciones.Database.Entities.OcupanteBanca", "BancadaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Bancada");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgrupacionPoliticaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AmbitoGeograficoId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AgrupacionPolitica");
|
||||||
|
|
||||||
|
b.Navigation("AmbitoGeografico");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgrupacionPoliticaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AmbitoGeograficoId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AgrupacionPolitica");
|
||||||
|
|
||||||
|
b.Navigation("AmbitoGeografico");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgrupacionPoliticaId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AgrupacionPolitica");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Ocupante");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Elecciones.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddEleccionEntities : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "Telegramas",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "ResumenesVotos",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "ResultadosVotos",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "ProyeccionesBancas",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "OcupantesBancas",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "LogosAgrupacionesCategorias",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "EstadosRecuentosGenerales",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "CandidatosOverrides",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "Bancadas",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Elecciones",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
Nombre = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
|
Nivel = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
|
DistritoId = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
|
Fecha = table.Column<DateOnly>(type: "date", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Elecciones", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Elecciones");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "Telegramas");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "ResumenesVotos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "ResultadosVotos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "ProyeccionesBancas");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "OcupantesBancas");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "LogosAgrupacionesCategorias");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "EstadosRecuentosGenerales");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "EstadosRecuentos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "CandidatosOverrides");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "EleccionId",
|
||||||
|
table: "Bancadas");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,34 @@ namespace Elecciones.Database.Migrations
|
|||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Eleccion", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DistritoId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Fecha")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<string>("Nivel")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Nombre")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Elecciones");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Elecciones.Database.Entities.AdminUser", b =>
|
modelBuilder.Entity("Elecciones.Database.Entities.AdminUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -134,6 +162,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("Camara")
|
b.Property<int>("Camara")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("NumeroBanca")
|
b.Property<int>("NumeroBanca")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -162,6 +193,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("CategoriaId")
|
b.Property<int>("CategoriaId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("NombreCandidato")
|
b.Property<string>("NombreCandidato")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(255)
|
.HasMaxLength(255)
|
||||||
@@ -227,6 +261,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("CantidadVotantes")
|
b.Property<int>("CantidadVotantes")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime>("FechaTotalizacion")
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@@ -284,6 +321,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("CantidadVotantes")
|
b.Property<int>("CantidadVotantes")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime>("FechaTotalizacion")
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@@ -326,6 +366,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("CategoriaId")
|
b.Property<int>("CategoriaId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("LogoUrl")
|
b.Property<string>("LogoUrl")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
@@ -349,6 +392,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("BancadaId")
|
b.Property<int>("BancadaId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("FotoUrl")
|
b.Property<string>("FotoUrl")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
@@ -385,6 +431,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("CategoriaId")
|
b.Property<int>("CategoriaId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime>("FechaTotalizacion")
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@@ -422,6 +471,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("CategoriaId")
|
b.Property<int>("CategoriaId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<decimal>("PorcentajeVotos")
|
b.Property<decimal>("PorcentajeVotos")
|
||||||
.HasPrecision(18, 4)
|
.HasPrecision(18, 4)
|
||||||
.HasColumnType("decimal(18,4)");
|
.HasColumnType("decimal(18,4)");
|
||||||
@@ -451,6 +503,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<int>("AmbitoGeograficoId")
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<long>("Votos")
|
b.Property<long>("Votos")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
@@ -477,6 +532,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("EleccionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime>("FechaEscaneo")
|
b.Property<DateTime>("FechaEscaneo")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+843c0f725893a48eae1236473cb5eeb00ef3d91c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+64dc7ef440f585cb5e7723585b6327d5387f1b32")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+843c0f725893a48eae1236473cb5eeb00ef3d91c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+64dc7ef440f585cb5e7723585b6327d5387f1b32")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Target Name="GetEFProjectMetadata">
|
||||||
|
<MSBuild Condition=" '$(TargetFramework)' == '' "
|
||||||
|
Projects="$(MSBuildProjectFile)"
|
||||||
|
Targets="GetEFProjectMetadata"
|
||||||
|
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);EFProjectMetadataFile=$(EFProjectMetadataFile)" />
|
||||||
|
<ItemGroup Condition=" '$(TargetFramework)' != '' ">
|
||||||
|
<EFProjectMetadata Include="AssemblyName: $(AssemblyName)" />
|
||||||
|
<EFProjectMetadata Include="Language: $(Language)" />
|
||||||
|
<EFProjectMetadata Include="OutputPath: $(OutputPath)" />
|
||||||
|
<EFProjectMetadata Include="Platform: $(Platform)" />
|
||||||
|
<EFProjectMetadata Include="PlatformTarget: $(PlatformTarget)" />
|
||||||
|
<EFProjectMetadata Include="ProjectAssetsFile: $(ProjectAssetsFile)" />
|
||||||
|
<EFProjectMetadata Include="ProjectDir: $(ProjectDir)" />
|
||||||
|
<EFProjectMetadata Include="RootNamespace: $(RootNamespace)" />
|
||||||
|
<EFProjectMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
|
||||||
|
<EFProjectMetadata Include="TargetFileName: $(TargetFileName)" />
|
||||||
|
<EFProjectMetadata Include="TargetFrameworkMoniker: $(TargetFrameworkMoniker)" />
|
||||||
|
<EFProjectMetadata Include="Nullable: $(Nullable)" />
|
||||||
|
<EFProjectMetadata Include="TargetFramework: $(TargetFramework)" />
|
||||||
|
<EFProjectMetadata Include="TargetPlatformIdentifier: $(TargetPlatformIdentifier)" />
|
||||||
|
</ItemGroup>
|
||||||
|
<WriteLinesToFile Condition=" '$(TargetFramework)' != '' "
|
||||||
|
File="$(EFProjectMetadataFile)"
|
||||||
|
Lines="@(EFProjectMetadata)" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user