diff --git a/Elecciones-Web/frontend-admin/src/components/AgrupacionesManager.tsx b/Elecciones-Web/frontend-admin/src/components/AgrupacionesManager.tsx index 6fc6686..8a14c57 100644 --- a/Elecciones-Web/frontend-admin/src/components/AgrupacionesManager.tsx +++ b/Elecciones-Web/frontend-admin/src/components/AgrupacionesManager.tsx @@ -1,113 +1,133 @@ // src/components/AgrupacionesManager.tsx import { useState, useEffect } from 'react'; -import { getAgrupaciones, updateAgrupacion } from '../services/apiService'; -import type { AgrupacionPolitica, UpdateAgrupacionData } from '../types'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { getAgrupaciones, updateAgrupacion, getLogos, updateLogos } from '../services/apiService'; +import type { AgrupacionPolitica, LogoAgrupacionCategoria } from '../types'; import './AgrupacionesManager.css'; +const SENADORES_ID = 5; +const DIPUTADOS_ID = 6; +const CONCEJALES_ID = 7; + export const AgrupacionesManager = () => { - const [agrupaciones, setAgrupaciones] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [editingId, setEditingId] = useState(null); - const [formData, setFormData] = useState({ - nombreCorto: '', - color: '#000000', - logoUrl: '', + const queryClient = useQueryClient(); + + const [editedAgrupaciones, setEditedAgrupaciones] = useState>>({}); + const [editedLogos, setEditedLogos] = useState([]); + + // Query 1: Obtener agrupaciones + const { data: agrupaciones = [], isLoading: isLoadingAgrupaciones } = useQuery({ + queryKey: ['agrupaciones'], + queryFn: getAgrupaciones, }); - useEffect(() => { - fetchAgrupaciones(); - }, []); + // Query 2: Obtener logos + const { data: logos = [], isLoading: isLoadingLogos } = useQuery({ + queryKey: ['logos'], + queryFn: getLogos, + }); - const fetchAgrupaciones = async () => { - try { - setLoading(true); - const data = await getAgrupaciones(); - setAgrupaciones(data); - } catch (err) { - setError('No se pudieron cargar las agrupaciones.'); - } finally { - setLoading(false); + // Usamos useEffect para reaccionar cuando los datos de 'logos' se cargan o cambian. + useEffect(() => { + if (logos) { + setEditedLogos(logos); } + }, [logos]); + + // Usamos otro useEffect para reaccionar a los datos de 'agrupaciones'. + useEffect(() => { + if (agrupaciones) { + const initialEdits = Object.fromEntries(agrupaciones.map(a => [a.id, {}])); + setEditedAgrupaciones(initialEdits); + } + }, [agrupaciones]); + + const handleInputChange = (id: string, field: 'nombreCorto' | 'color', value: string) => { + setEditedAgrupaciones(prev => ({ + ...prev, + [id]: { ...prev[id], [field]: value } + })); }; - const handleEdit = (agrupacion: AgrupacionPolitica) => { - setEditingId(agrupacion.id); - setFormData({ - nombreCorto: agrupacion.nombreCorto || '', - color: agrupacion.color || '#000000', - logoUrl: agrupacion.logoUrl || '', + const handleLogoChange = (agrupacionId: string, categoriaId: number, value: string) => { + setEditedLogos(prev => { + const newLogos = [...prev]; + const existing = newLogos.find(l => l.agrupacionPoliticaId === agrupacionId && l.categoriaId === categoriaId); + if (existing) { + existing.logoUrl = value; + } else { + newLogos.push({ id: 0, agrupacionPoliticaId: agrupacionId, categoriaId, logoUrl: value }); + } + return newLogos; }); }; - const handleCancel = () => { - setEditingId(null); - }; - - const handleSave = async (id: string) => { + const handleSaveAll = async () => { try { - await updateAgrupacion(id, formData); - setEditingId(null); - fetchAgrupaciones(); // Recargar datos para ver los cambios + const agrupacionPromises = Object.entries(editedAgrupaciones).map(([id, changes]) => { + if (Object.keys(changes).length > 0) { + const original = agrupaciones.find(a => a.id === id); + if (original) { // Chequeo de seguridad + return updateAgrupacion(id, { ...original, ...changes }); + } + } + return Promise.resolve(); + }); + + const logoPromise = updateLogos(editedLogos); + + await Promise.all([...agrupacionPromises, logoPromise]); + + queryClient.invalidateQueries({ queryKey: ['agrupaciones'] }); + queryClient.invalidateQueries({ queryKey: ['logos'] }); + + alert('¡Todos los cambios han sido guardados!'); } catch (err) { - alert('Error al guardar los cambios.'); + console.error("Error al guardar todo:", err); + alert("Ocurrió un error al guardar los cambios."); } }; - const handleChange = (e: React.ChangeEvent) => { - setFormData({ ...formData, [e.target.name]: e.target.value }); + const isLoading = isLoadingAgrupaciones || isLoadingLogos; + + const getLogoUrl = (agrupacionId: string, categoriaId: number) => { + return editedLogos.find(l => l.agrupacionPoliticaId === agrupacionId && l.categoriaId === categoriaId)?.logoUrl || ''; }; - - if (loading) return

Cargando agrupaciones...

; - if (error) return

{error}

; return (
-

Gestión de Agrupaciones Políticas

- - - - - - - - - - - - {agrupaciones.map((agrupacion) => ( - - {editingId === agrupacion.id ? ( - <> +

Gestión de Agrupaciones y Logos

+ {isLoading ?

Cargando...

: ( + <> +
NombreNombre CortoColorLogo URLAcciones
+ + + + + + + + + + + + {agrupaciones.map(agrupacion => ( + - - - - - - ) : ( - <> - - - - - - - )} - - ))} - -
NombreNombre CortoColorLogo SenadoresLogo DiputadosLogo Concejales
{agrupacion.nombre} - - - {agrupacion.nombre}{agrupacion.nombreCorto} -
-
- {agrupacion.color} -
-
{agrupacion.logoUrl} - -
+ handleInputChange(agrupacion.id, 'nombreCorto', e.target.value)} /> + handleInputChange(agrupacion.id, 'color', e.target.value)} /> + handleLogoChange(agrupacion.id, SENADORES_ID, e.target.value)} /> + handleLogoChange(agrupacion.id, DIPUTADOS_ID, e.target.value)} /> + handleLogoChange(agrupacion.id, CONCEJALES_ID, e.target.value)} /> + + ))} + + + + + )}
); }; \ No newline at end of file diff --git a/Elecciones-Web/frontend-admin/src/components/ConfiguracionGeneral.tsx b/Elecciones-Web/frontend-admin/src/components/ConfiguracionGeneral.tsx index 1ab43c6..171afbc 100644 --- a/Elecciones-Web/frontend-admin/src/components/ConfiguracionGeneral.tsx +++ b/Elecciones-Web/frontend-admin/src/components/ConfiguracionGeneral.tsx @@ -10,6 +10,8 @@ export const ConfiguracionGeneral = () => { const [agrupaciones, setAgrupaciones] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [tickerCantidad, setTickerCantidad] = useState('3'); + const [concejalesCantidad, setConcejalesCantidad] = useState('5'); const [presidenciaSenadoId, setPresidenciaSenadoId] = useState(''); // Renombramos el estado para mayor claridad @@ -24,6 +26,8 @@ export const ConfiguracionGeneral = () => { setAgrupaciones(agrupacionesData); setPresidenciaSenadoId(configData.PresidenciaSenadores || ''); setModoOficialActivo(configData.UsarDatosDeBancadasOficiales === 'true'); + setTickerCantidad(configData.TickerResultadosCantidad || '3'); + setConcejalesCantidad(configData.ConcejalesResultadosCantidad || '5'); } catch (err) { console.error("Error al cargar datos de configuración:", err); setError("No se pudieron cargar los datos necesarios para la configuración."); @@ -36,7 +40,9 @@ export const ConfiguracionGeneral = () => { try { await updateConfiguracion({ "PresidenciaSenadores": presidenciaSenadoId, - "UsarDatosDeBancadasOficiales": modoOficialActivo.toString() + "UsarDatosDeBancadasOficiales": modoOficialActivo.toString(), + "TickerResultadosCantidad": tickerCantidad, + "ConcejalesResultadosCantidad": concejalesCantidad }); await queryClient.invalidateQueries({ queryKey: ['composicionCongreso'] }); await queryClient.invalidateQueries({ queryKey: ['bancadasDetalle'] }); @@ -89,7 +95,7 @@ export const ConfiguracionGeneral = () => { Seleccione el partido político al que pertenece el Vicegobernador. El asiento presidencial del Senado se pintará con el color de este partido.

-
+

Presidencia Cámara de Diputados

@@ -97,6 +103,19 @@ export const ConfiguracionGeneral = () => { Esta banca se asigna y colorea automáticamente según la agrupación política con la mayoría de bancas totales en la cámara.

+
+ + setTickerCantidad(e.target.value)} /> +
+
+ + setConcejalesCantidad(e.target.value)} + /> +
diff --git a/Elecciones-Web/frontend-admin/src/services/apiService.ts b/Elecciones-Web/frontend-admin/src/services/apiService.ts index 7216e21..40b34c2 100644 --- a/Elecciones-Web/frontend-admin/src/services/apiService.ts +++ b/Elecciones-Web/frontend-admin/src/services/apiService.ts @@ -1,7 +1,7 @@ // src/services/apiService.ts import axios from 'axios'; import { triggerLogout } from '../context/authUtils'; -import type { AgrupacionPolitica, UpdateAgrupacionData, Bancada } from '../types'; +import type { AgrupacionPolitica, UpdateAgrupacionData, Bancada, LogoAgrupacionCategoria } from '../types'; const AUTH_API_URL = 'http://localhost:5217/api/auth'; const ADMIN_API_URL = 'http://localhost:5217/api/admin'; @@ -94,4 +94,13 @@ export const getConfiguracion = async (): Promise => { export const updateConfiguracion = async (data: Record): Promise => { await adminApiClient.put('/configuracion', data); +}; + +export const getLogos = async (): Promise => { + const response = await adminApiClient.get('/logos'); + return response.data; +}; + +export const updateLogos = async (data: LogoAgrupacionCategoria[]): Promise => { + await adminApiClient.put('/logos', data); }; \ No newline at end of file diff --git a/Elecciones-Web/frontend-admin/src/types/index.ts b/Elecciones-Web/frontend-admin/src/types/index.ts index a875434..a0605e0 100644 --- a/Elecciones-Web/frontend-admin/src/types/index.ts +++ b/Elecciones-Web/frontend-admin/src/types/index.ts @@ -6,7 +6,6 @@ export interface AgrupacionPolitica { nombre: string; nombreCorto: string | null; color: string | null; - logoUrl: string | null; ordenDiputados: number | null; ordenSenadores: number | null; } @@ -14,7 +13,6 @@ export interface AgrupacionPolitica { export interface UpdateAgrupacionData { nombreCorto: string | null; color: string | null; - logoUrl: string | null; } export const TipoCamara = { @@ -40,4 +38,11 @@ export interface Bancada { agrupacionPoliticaId: string | null; agrupacionPolitica: AgrupacionPolitica | null; ocupante: OcupanteBanca | null; +} + +export interface LogoAgrupacionCategoria { + id: number; + agrupacionPoliticaId: string; + categoriaId: number; + logoUrl: string | null; } \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/App.tsx b/Elecciones-Web/frontend/src/App.tsx index 92f53de..dba97b7 100644 --- a/Elecciones-Web/frontend/src/App.tsx +++ b/Elecciones-Web/frontend/src/App.tsx @@ -5,6 +5,7 @@ import { CongresoWidget } from './components/CongresoWidget' import MapaBsAs from './components/MapaBsAs' import { TickerWidget } from './components/TickerWidget' import { TelegramaWidget } from './components/TelegramaWidget' +import { ConcejalesWidget } from './components/ConcejalesWidget' function App() { return ( @@ -12,6 +13,7 @@ function App() {

Resultados Electorales - Provincia de Buenos Aires

+ diff --git a/Elecciones-Web/frontend/src/apiService.ts b/Elecciones-Web/frontend/src/apiService.ts index 22241b0..f3429ae 100644 --- a/Elecciones-Web/frontend/src/apiService.ts +++ b/Elecciones-Web/frontend/src/apiService.ts @@ -1,6 +1,13 @@ // src/apiService.ts import axios from 'axios'; -import type { ResumenProvincial, ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem } from './types/types'; +import type { ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker } from './types/types'; + +const API_BASE_URL = 'http://localhost:5217/api'; + +const apiClient = axios.create({ + baseURL: API_BASE_URL, + headers: { 'Content-Type': 'application/json' }, +}); interface PartidoData { id: string; @@ -47,14 +54,13 @@ export interface BancadaDetalle { ocupante: OcupanteBanca | null; } -const API_BASE_URL = 'http://localhost:5217/api'; +export interface ConfiguracionPublica { + TickerResultadosCantidad?: string; + ConcejalesResultadosCantidad?: string; + // ... otras claves públicas que pueda añadir en el futuro +} -const apiClient = axios.create({ - baseURL: API_BASE_URL, - headers: { 'Content-Type': 'application/json' }, -}); - -export const getResumenProvincial = async (): Promise => { +export const getResumenProvincial = async (): Promise => { const response = await apiClient.get('/resultados/provincia/02'); return response.data; }; @@ -113,4 +119,14 @@ export const getComposicionCongreso = async (): Promise => { export const getBancadasDetalle = async (): Promise => { const response = await apiClient.get('/resultados/bancadas-detalle'); return response.data; +}; + +export const getConfiguracionPublica = async (): Promise => { + const response = await apiClient.get('/resultados/configuracion-publica'); + return response.data; +}; + +export const getResultadosConcejales = async (seccionId: string): Promise => { + const response = await apiClient.get(`/resultados/concejales/${seccionId}`); + return response.data; }; \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx b/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx new file mode 100644 index 0000000..b1ecc64 --- /dev/null +++ b/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx @@ -0,0 +1,103 @@ +// src/components/ConcejalesWidget.tsx +import { useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { getSeccionesElectorales, getResultadosConcejales, getConfiguracionPublica } from '../apiService'; +import type { MunicipioSimple, ResultadoTicker } from '../types/types'; +import { ImageWithFallback } from './ImageWithFallback'; +import './TickerWidget.css'; // Reutilizamos los estilos del ticker + +const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`; + +export const ConcejalesWidget = () => { + const [secciones, setSecciones] = useState([]); + const [seccionActualId, setSeccionActualId] = useState(''); + + // Query para la configuración (para saber cuántos resultados mostrar) + const { data: configData } = useQuery({ + queryKey: ['configuracionPublica'], + queryFn: getConfiguracionPublica, + staleTime: 0, + }); + + // Calculamos la cantidad a mostrar desde la configuración + const cantidadAMostrar = parseInt(configData?.ConcejalesResultadosCantidad || '5', 10) + 1; + + useEffect(() => { + getSeccionesElectorales().then(seccionesData => { + if (seccionesData && seccionesData.length > 0) { + const orden = new Map([ + ['Capital', 0], ['Primera', 1], ['Segunda', 2], ['Tercera', 3], + ['Cuarta', 4], ['Quinta', 5], ['Sexta', 6], ['Séptima', 7] + ]); + const getOrden = (nombre: string) => { + const match = nombre.match(/Capital|Primera|Segunda|Tercera|Cuarta|Quinta|Sexta|Séptima/); + return match ? orden.get(match[0]) ?? 99 : 99; + }; + seccionesData.sort((a, b) => getOrden(a.nombre) - getOrden(b.nombre)); + setSecciones(seccionesData); + // Al estar los datos ya ordenados, el [0] será "Sección Capital" + setSeccionActualId(seccionesData[0].id); + } + }); + }, []); // El array de dependencias vacío asegura que esto solo se ejecute una vez + + // Query para obtener los resultados de la sección seleccionada + const { data: resultados, isLoading } = useQuery({ + queryKey: ['resultadosConcejales', seccionActualId], + queryFn: () => getResultadosConcejales(seccionActualId), + enabled: !!seccionActualId, + }); + + // --- INICIO DE LA LÓGICA DE PROCESAMIENTO "OTROS" --- + let displayResults: ResultadoTicker[] = resultados || []; + if (resultados && resultados.length > cantidadAMostrar) { + const topParties = resultados.slice(0, cantidadAMostrar - 1); + const otherParties = resultados.slice(cantidadAMostrar - 1); + const otrosPorcentaje = otherParties.reduce((sum, party) => sum + (party.votosPorcentaje || 0), 0); + + const otrosEntry: ResultadoTicker = { + id: `otros-concejales-${seccionActualId}`, + nombre: 'Otros', + nombreCorto: 'Otros', + color: '#888888', + logoUrl: null, + votos: 0, // No es relevante para la visualización del porcentaje + votosPorcentaje: otrosPorcentaje, + }; + displayResults = [...topParties, otrosEntry]; + } else if (resultados) { + displayResults = resultados.slice(0, cantidadAMostrar); + } + // --- FIN DE LA LÓGICA DE PROCESAMIENTO "OTROS" --- + + return ( +
+
+

CONCEJALES - LA PLATA

+ +
+
+ {isLoading ?

Cargando...

: + displayResults.map(partido => ( +
+
+ +
+
+
+ {partido.nombreCorto || partido.nombre} + {formatPercent(partido.votosPorcentaje)} +
+
+
+
+
+
+ )) + } +
+
+ ); +}; \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/components/CongresoWidget.tsx b/Elecciones-Web/frontend/src/components/CongresoWidget.tsx index 7b39bc7..826eb59 100644 --- a/Elecciones-Web/frontend/src/components/CongresoWidget.tsx +++ b/Elecciones-Web/frontend/src/components/CongresoWidget.tsx @@ -80,17 +80,13 @@ export const CongresoWidget = () => { {camaraActiva === 'diputados' ? ( ) : ( )}
diff --git a/Elecciones-Web/frontend/src/components/ImageWithFallback.tsx b/Elecciones-Web/frontend/src/components/ImageWithFallback.tsx new file mode 100644 index 0000000..cfe5a15 --- /dev/null +++ b/Elecciones-Web/frontend/src/components/ImageWithFallback.tsx @@ -0,0 +1,28 @@ +// src/components/ImageWithFallback.tsx +import { useState, useEffect } from 'react'; + +interface Props extends React.ImgHTMLAttributes { + fallbackSrc: string; +} + +export const ImageWithFallback = ({ src, fallbackSrc, ...props }: Props) => { + const [imgSrc, setImgSrc] = useState(src); + const [error, setError] = useState(false); + + useEffect(() => { + setError(false); + setImgSrc(src); + }, [src]); + + const handleError = () => { + setError(true); + }; + + return ( + + ); +}; \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/components/TelegramaWidget.tsx b/Elecciones-Web/frontend/src/components/TelegramaWidget.tsx index 0e5ef56..43b7c90 100644 --- a/Elecciones-Web/frontend/src/components/TelegramaWidget.tsx +++ b/Elecciones-Web/frontend/src/components/TelegramaWidget.tsx @@ -87,7 +87,7 @@ export const TelegramaWidget = () => { .finally(() => setLoading(false)); } }, [selectedMesa]); - + return (

Consulta de Telegramas por Ubicación

diff --git a/Elecciones-Web/frontend/src/components/TickerWidget.css b/Elecciones-Web/frontend/src/components/TickerWidget.css index 4c41195..de70dc5 100644 --- a/Elecciones-Web/frontend/src/components/TickerWidget.css +++ b/Elecciones-Web/frontend/src/components/TickerWidget.css @@ -1,22 +1,20 @@ /* src/components/TickerWidget.css */ -.ticker-container { - /* Se cambia a un fondo claro con borde y sombra sutil */ - background-color: #ffffff; - border: 1px solid #e0e0e0; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); - padding: 15px 20px; - border-radius: 8px; - max-width: 800px; - margin: 20px auto; - font-family: "Public Sans", system-ui, Avenir, Helvetica, Arial, sans-serif; - color: #333333; /* Color de texto por defecto */ +.ticker-wrapper { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 1.5rem; + width: 100%; + max-width: 1280px; + margin: 20px auto; } - -.ticker-container.loading, .ticker-container.error { - text-align: center; - padding: 30px; - font-style: italic; - color: #757575; /* Color de texto atenuado */ +.ticker-card { + background-color: #ffffff; + border: 1px solid #e0e0e0; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); + padding: 15px 20px; + border-radius: 8px; + display: flex; + flex-direction: column; } .ticker-header { @@ -48,9 +46,9 @@ } .ticker-results { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 20px; + display: flex; + flex-direction: column; + gap: 12px; /* Espacio entre partidos */ } .ticker-party .party-info { @@ -84,4 +82,31 @@ border-radius: 4px; transition: width 0.5s ease-in-out; /* El color de fondo se sigue aplicando desde el componente, esto es correcto */ +} + +.ticker-results { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); /* Aumentamos el tamaño mínimo */ + gap: 20px; +} +.ticker-party { + display: flex; + align-items: center; + gap: 10px; /* Espacio entre logo y detalles */ +} +.party-logo { + flex-shrink: 0; + width: 50px; + height: 50px; +} +.party-logo img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 4px; + border: 1px solid #ddd; +} +.party-details { + flex-grow: 1; + min-width: 0; /* Previene que el flex item se desborde */ } \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/components/TickerWidget.tsx b/Elecciones-Web/frontend/src/components/TickerWidget.tsx index 4f017fb..5ae22e1 100644 --- a/Elecciones-Web/frontend/src/components/TickerWidget.tsx +++ b/Elecciones-Web/frontend/src/components/TickerWidget.tsx @@ -1,78 +1,92 @@ // src/components/TickerWidget.tsx -import { useState, useEffect } from 'react'; -import { getResumenProvincial } from '../apiService'; -import type { ResumenProvincial } from '../types/types'; +import { useQuery } from '@tanstack/react-query'; +import { getResumenProvincial, getConfiguracionPublica } from '../apiService'; +import type { CategoriaResumen, ResultadoTicker } from '../types/types'; +import { ImageWithFallback } from './ImageWithFallback'; import './TickerWidget.css'; -const formatPercent = (num: number) => `${num.toFixed(2).replace('.', ',')}%`; -const COLORS = [ - "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", - "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf" -]; +const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`; export const TickerWidget = () => { - const [data, setData] = useState(null); - const [loading, setLoading] = useState(true); - // Se añade un nuevo estado para manejar errores de forma explícita - const [error, setError] = useState(null); + const { data: categorias, isLoading, error } = useQuery({ + queryKey: ['resumenProvincial'], + queryFn: getResumenProvincial, + refetchInterval: 30000, + }); - useEffect(() => { - const fetchData = async () => { - // Se resetea el error en cada intento de carga - setError(null); - try { - const result = await getResumenProvincial(); - setData(result); - } catch (err) { - console.error("Error cargando resumen provincial:", err); - // Se guarda el mensaje de error para mostrarlo en la UI - setError("No se pudo conectar con el servidor para obtener el resumen provincial."); - } finally { - setLoading(false); - } - }; + const { data: configData } = useQuery({ + queryKey: ['configuracionPublica'], + queryFn: getConfiguracionPublica, + staleTime: 0, + }); - fetchData(); // Carga inicial - const intervalId = setInterval(fetchData, 30000); // Actualiza cada 30 segundos + const cantidadAMostrar = parseInt(configData?.TickerResultadosCantidad || '5', 10) + 1; - return () => clearInterval(intervalId); // Limpia el intervalo al desmontar el componente - }, []); + if (isLoading) return
Cargando resumen...
; + if (error || !categorias) return
No hay datos disponibles.
; - if (loading) { - return
Cargando resultados provinciales...
; - } - - // Si hay un error, se muestra el mensaje correspondiente - if (error) { - return
{error}
; - } - - if (!data) { - return
No hay datos disponibles.
; - } + const categoriasFiltradas = categorias.filter(c => c.categoriaId !== 7); return ( -
-
-

PROVINCIA BS. AS.

-
- Mesas Escrutadas: {formatPercent(data.porcentajeEscrutado)} - Participación Total: {formatPercent(data.porcentajeParticipacion)} -
-
-
- {data.resultados.slice(0, 3).map((partido, index) => ( -
-
- {partido.nombre} - {formatPercent(partido.porcentaje)} +
+ {categoriasFiltradas.map(categoria => { + + let displayResults: ResultadoTicker[] = categoria.resultados; + + if (categoria.resultados.length > cantidadAMostrar) { + const topParties = categoria.resultados.slice(0, cantidadAMostrar - 1); + const otherParties = categoria.resultados.slice(cantidadAMostrar - 1); + const otrosPorcentaje = otherParties.reduce((sum, party) => sum + party.votosPorcentaje, 0); + + const otrosEntry: ResultadoTicker = { + id: `otros-${categoria.categoriaId}`, + nombre: 'Otros', + nombreCorto: 'Otros', + color: '#888888', + logoUrl: null, + votos: 0, + votosPorcentaje: otrosPorcentaje, + }; + + displayResults = [...topParties, otrosEntry]; + } else { + displayResults = categoria.resultados.slice(0, cantidadAMostrar); + } + + return ( +
+
+

{categoria.categoriaNombre}

+
+ Mesas: {formatPercent(categoria.estadoRecuento?.mesasTotalizadasPorcentaje ?? 0)} + Part: {formatPercent(categoria.estadoRecuento?.participacionPorcentaje ?? 0)} +
-
-
+
+ {displayResults.map(partido => ( +
+
+ +
+
+
+ {partido.nombreCorto || partido.nombre} + {formatPercent(partido.votosPorcentaje)} +
+
+
+
+
+
+ ))}
- ))} -
+ ); + })}
); }; \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/types/types.ts b/Elecciones-Web/frontend/src/types/types.ts index 0dde52b..2dc209b 100644 --- a/Elecciones-Web/frontend/src/types/types.ts +++ b/Elecciones-Web/frontend/src/types/types.ts @@ -40,22 +40,46 @@ export interface GeographyObject { } export interface MunicipioSimple { id: string; nombre: string; } -export interface AgrupacionResultado { nombre: string; votos: number; porcentaje: number; } + +export interface ResultadoTicker { + id: string; + nombre: string; + nombreCorto: string | null; + color: string | null; + logoUrl: string | null; + votos: number; + votosPorcentaje: number; +} + +export interface EstadoRecuentoTicker { + mesasTotalizadasPorcentaje: number; + participacionPorcentaje: number; +} + +export interface CategoriaResumen { + categoriaId: number; + categoriaNombre: string; + estadoRecuento: EstadoRecuentoTicker | null; + resultados: ResultadoTicker[]; +} + export interface VotosAdicionales { enBlanco: number; nulos: number; recurridos: number; } + export interface MunicipioDetalle { municipioNombre: string; ultimaActualizacion: string; porcentajeEscrutado: number; porcentajeParticipacion: number; - resultados: AgrupacionResultado[]; + resultados: CategoriaResumen[]; votosAdicionales: VotosAdicionales; } + export interface ResumenProvincial { provinciaNombre: string; ultimaActualizacion: string; porcentajeEscrutado: number; porcentajeParticipacion: number; - resultados: AgrupacionResultado[]; + resultados: CategoriaResumen[]; votosAdicionales: VotosAdicionales; } diff --git a/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs b/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs index 9efedcd..ae22c7e 100644 --- a/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs +++ b/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs @@ -50,7 +50,6 @@ public class AdminController : ControllerBase // Actualizamos las propiedades de la entidad con los valores del DTO. agrupacion.NombreCorto = agrupacionDto.NombreCorto; agrupacion.Color = agrupacionDto.Color; - agrupacion.LogoUrl = agrupacionDto.LogoUrl; // Guardamos los cambios en la base de datos. await _dbContext.SaveChangesAsync(); @@ -178,4 +177,32 @@ public class AdminController : ControllerBase return NoContent(); } + + [HttpGet("logos")] + public async Task GetLogos() + { + return Ok(await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().ToListAsync()); + } + + [HttpPut("logos")] + public async Task UpdateLogos([FromBody] List logos) + { + // Lógica de "Upsert" + foreach (var logo in logos) + { + var logoExistente = await _dbContext.LogosAgrupacionesCategorias + .FirstOrDefaultAsync(l => l.AgrupacionPoliticaId == logo.AgrupacionPoliticaId && l.CategoriaId == logo.CategoriaId); + + if (logoExistente != null) + { + logoExistente.LogoUrl = logo.LogoUrl; + } + else if (!string.IsNullOrEmpty(logo.LogoUrl)) + { + _dbContext.LogosAgrupacionesCategorias.Add(logo); + } + } + await _dbContext.SaveChangesAsync(); + return NoContent(); + } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs b/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs index 0e7004e..31e8ed2 100644 --- a/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs +++ b/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs @@ -92,59 +92,69 @@ public class ResultadosController : ControllerBase [HttpGet("provincia/{distritoId}")] public async Task GetResultadosProvinciales(string distritoId) { - _logger.LogInformation("Solicitud de resultados para la provincia con distritoId: {DistritoId}", distritoId); - - // PASO 1: Encontrar el ámbito geográfico de la provincia. var provincia = await _dbContext.AmbitosGeograficos.AsNoTracking() .FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10); - if (provincia == null) - { - _logger.LogWarning("No se encontró la provincia con distritoId: {DistritoId}", distritoId); - return NotFound(new { message = $"No se encontró la provincia con distritoId {distritoId}" }); - } - - // PASO 2: Obtener el estado general del recuento para la provincia. - // Como las estadísticas generales (mesas, participación) son las mismas para todas las categorías, - // simplemente tomamos la primera que encontremos para este ámbito. - var estadoGeneral = await _dbContext.EstadosRecuentosGenerales.AsNoTracking() - .FirstOrDefaultAsync(e => e.AmbitoGeograficoId == provincia.Id); - - // PASO 3: Obtener el resumen de votos por agrupación para la provincia. - // Hacemos un JOIN manual entre ResumenesVotos y AgrupacionesPoliticas para obtener los nombres. - var resultados = await _dbContext.ResumenesVotos - .AsNoTracking() - .Where(r => r.AmbitoGeograficoId == provincia.Id) - .Join( - _dbContext.AgrupacionesPoliticas.AsNoTracking(), - resumen => resumen.AgrupacionPoliticaId, - agrupacion => agrupacion.Id, - (resumen, agrupacion) => new AgrupacionResultadoDto - { - Nombre = agrupacion.Nombre, - Votos = resumen.Votos, - Porcentaje = resumen.VotosPorcentaje - }) - .OrderByDescending(r => r.Votos) + var todosLosResumenes = await _dbContext.ResumenesVotos.AsNoTracking() + .Include(r => r.AgrupacionPolitica) .ToListAsync(); - // PASO 4: Construir el objeto de respuesta (DTO). - // Si no hay datos de recuento aún, usamos valores por defecto para evitar errores en el frontend. - var respuestaDto = new ResumenProvincialDto - { - ProvinciaNombre = provincia.Nombre, - UltimaActualizacion = estadoGeneral?.FechaTotalizacion ?? DateTime.UtcNow, - PorcentajeEscrutado = estadoGeneral?.MesasTotalizadasPorcentaje ?? 0, - PorcentajeParticipacion = estadoGeneral?.ParticipacionPorcentaje ?? 0, - Resultados = resultados, - // NOTA: Los votos adicionales (nulos, en blanco) no están en la tabla de resumen provincial. - // Esto es una mejora pendiente en el Worker. Por ahora, devolvemos 0. - VotosAdicionales = new VotosAdicionalesDto { EnBlanco = 0, Nulos = 0, Recurridos = 0 } - }; + // OBTENER TODOS LOS LOGOS EN UNA SOLA CONSULTA + var logosLookup = (await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().ToListAsync()) + .ToLookup(l => $"{l.AgrupacionPoliticaId}-{l.CategoriaId}"); - _logger.LogInformation("Devolviendo {NumResultados} resultados de agrupaciones para la provincia.", respuestaDto.Resultados.Count); + if (provincia == null) return NotFound($"No se encontró la provincia con distritoId {distritoId}"); - return Ok(respuestaDto); + var estadosPorCategoria = await _dbContext.EstadosRecuentosGenerales.AsNoTracking() + .Include(e => e.CategoriaElectoral) + .Where(e => e.AmbitoGeograficoId == provincia.Id) + .ToDictionaryAsync(e => e.CategoriaId); + + var resultadosPorMunicipio = await _dbContext.ResultadosVotos + .AsNoTracking() + .Include(r => r.AgrupacionPolitica) + .Where(r => r.AmbitoGeografico.NivelId == 30) + .ToListAsync(); + + var resultadosAgrupados = resultadosPorMunicipio + .GroupBy(r => r.CategoriaId) + .Select(g => new + { + CategoriaId = g.Key, + TotalVotosCategoria = g.Sum(r => r.CantidadVotos), + // Agrupamos por el ID de la agrupación, no por el objeto + Resultados = g.GroupBy(r => r.AgrupacionPoliticaId) + .Select(partidoGroup => new + { + // El objeto Agrupacion lo tomamos del primer elemento del grupo + Agrupacion = partidoGroup.First().AgrupacionPolitica, + Votos = partidoGroup.Sum(r => r.CantidadVotos) + }) + .ToList() + }) + .Select(g => new + { + g.CategoriaId, + CategoriaNombre = estadosPorCategoria.ContainsKey(g.CategoriaId) ? estadosPorCategoria[g.CategoriaId].CategoriaElectoral.Nombre : "Desconocido", + EstadoRecuento = estadosPorCategoria.GetValueOrDefault(g.CategoriaId), + Resultados = g.Resultados + .Select(r => new + { + r.Agrupacion.Id, + r.Agrupacion.Nombre, + r.Agrupacion.NombreCorto, + r.Agrupacion.Color, + LogoUrl = logosLookup[$"{r.Agrupacion.Id}-{g.CategoriaId}"].FirstOrDefault()?.LogoUrl, + r.Votos, + VotosPorcentaje = g.TotalVotosCategoria > 0 ? ((decimal)r.Votos * 100 / g.TotalVotosCategoria) : 0 + }) + .OrderByDescending(r => r.Votos) + .ToList() + }) + .OrderBy(c => c.CategoriaId) + .ToList(); + + return Ok(resultadosAgrupados); } @@ -503,4 +513,77 @@ public class ResultadosController : ControllerBase return Ok(bancadasConOcupantes); } + [HttpGet("configuracion-publica")] + public async Task GetConfiguracionPublica() + { + // Definimos una lista de las claves de configuración que son seguras para el público. + // De esta manera, si en el futuro añadimos claves sensibles (como contraseñas de API, etc.), + // nunca se expondrán accidentalmente. + var clavesPublicas = new List + { + "TickerResultadosCantidad", + "ConcejalesResultadosCantidad" + // "OtraClavePublica" + }; + + var configuracionPublica = await _dbContext.Configuraciones + .AsNoTracking() + .Where(c => clavesPublicas.Contains(c.Clave)) + .ToDictionaryAsync(c => c.Clave, c => c.Valor); + + return Ok(configuracionPublica); + } + + [HttpGet("concejales/{seccionId}")] + public async Task GetResultadosConcejalesPorSeccion(string seccionId) + { + // 1. Encontrar todos los municipios (Nivel 30) que pertenecen a la sección dada (Nivel 20) + var municipiosDeLaSeccion = await _dbContext.AmbitosGeograficos + .AsNoTracking() + .Where(a => a.NivelId == 30 && a.SeccionProvincialId == seccionId) + .Select(a => a.Id) // Solo necesitamos sus IDs + .ToListAsync(); + + if (!municipiosDeLaSeccion.Any()) + { + return Ok(new List()); + } + + // 2. Obtener todos los resultados de la categoría Concejales (ID 7) para esos municipios + var resultadosMunicipales = await _dbContext.ResultadosVotos + .AsNoTracking() + .Include(r => r.AgrupacionPolitica) + .Where(r => r.CategoriaId == 7 && municipiosDeLaSeccion.Contains(r.AmbitoGeograficoId)) + .ToListAsync(); + + var logosConcejales = await _dbContext.LogosAgrupacionesCategorias + .AsNoTracking() + .Where(l => l.CategoriaId == 7) + .ToDictionaryAsync(l => l.AgrupacionPoliticaId); + + // 3. Agrupar y sumar en memoria para obtener el total por partido para la sección + var totalVotosSeccion = resultadosMunicipales.Sum(r => r.CantidadVotos); + + var resultadosFinales = resultadosMunicipales + .GroupBy(r => r.AgrupacionPolitica) + .Select(g => new + { + Agrupacion = g.Key, + Votos = g.Sum(r => r.CantidadVotos) + }) + .OrderByDescending(r => r.Votos) + .Select(r => new + { + r.Agrupacion.Id, + r.Agrupacion.Nombre, + r.Agrupacion.NombreCorto, + r.Agrupacion.Color, + LogoUrl = logosConcejales.GetValueOrDefault(r.Agrupacion.Id)?.LogoUrl, + r.Votos, + votosPorcentaje = totalVotosSeccion > 0 ? ((decimal)r.Votos * 100 / totalVotosSeccion) : 0 + }) + .ToList(); + + return Ok(resultadosFinales); + } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Api/Program.cs b/Elecciones-Web/src/Elecciones.Api/Program.cs index 3c40891..107b70b 100644 --- a/Elecciones-Web/src/Elecciones.Api/Program.cs +++ b/Elecciones-Web/src/Elecciones.Api/Program.cs @@ -141,6 +141,13 @@ using (var scope = app.Services.CreateScope()) context.SaveChanges(); Console.WriteLine("--> Seeded default configuration 'MostrarOcupantes'."); } + if (!context.Configuraciones.Any(c => c.Clave == "TickerResultadosCantidad")) + { + context.Configuraciones.Add(new Configuracion { Clave = "TickerResultadosCantidad", Valor = "3" }); + context.Configuraciones.Add(new Configuracion { Clave = "ConcejalesResultadosCantidad", Valor = "5" }); + context.SaveChanges(); + Console.WriteLine("--> Seeded default configuration 'TickerResultadosCantidad'."); + } } // Configurar el pipeline de peticiones HTTP. diff --git a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs index 88cecb0..a25c2cb 100644 --- a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3b8c6bf754cff6ace486ae8fe850ed4d69233280")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+608ae655bedf6c59be5fec1e14fc308871d2fd62")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json index 82c36f1..3f5f3bb 100644 --- a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json +++ b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","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=","BlOQCaw/bt9UsCnDEIqO6LwzwEh4i0OxBfeIZgKDR4U=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","2x9HRdaMF3CjEHo\u002BFx\u002BfhG7CTomq/ExTkOKw2bUeHms="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","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=","khGrM2Rl22MsVh9N6\u002B7todRrMuJ6o3ljuHxZF/aubqE=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","6xYke/2SzNspypSwIgizeNUH7b\u002Bfoz3wYfKk6z1tMsw="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json index 999061c..4a985b0 100644 --- a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json +++ b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","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=","BlOQCaw/bt9UsCnDEIqO6LwzwEh4i0OxBfeIZgKDR4U=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","2x9HRdaMF3CjEHo\u002BFx\u002BfhG7CTomq/ExTkOKw2bUeHms="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","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=","khGrM2Rl22MsVh9N6\u002B7todRrMuJ6o3ljuHxZF/aubqE=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","6xYke/2SzNspypSwIgizeNUH7b\u002Bfoz3wYfKk6z1tMsw="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs index e05bc04..2345ed0 100644 --- a/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3b8c6bf754cff6ace486ae8fe850ed4d69233280")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+608ae655bedf6c59be5fec1e14fc308871d2fd62")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs b/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs index f011b9f..8708534 100644 --- a/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs +++ b/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs @@ -19,6 +19,7 @@ public class EleccionesDbContext(DbContextOptions options) public DbSet Configuraciones { get; set; } public DbSet Bancadas { get; set; } public DbSet OcupantesBancas { get; set; } + public DbSet LogosAgrupacionesCategorias { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -75,5 +76,9 @@ public class EleccionesDbContext(DbContextOptions options) // Opcional: puede definir un índice entity.HasIndex(o => o.BancadaId).IsUnique(); }); + modelBuilder.Entity(entity => + { + entity.HasIndex(l => new { l.AgrupacionPoliticaId, l.CategoriaId }).IsUnique(); + }); } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Database/Entities/AgrupacionPolitica.cs b/Elecciones-Web/src/Elecciones.Database/Entities/AgrupacionPolitica.cs index eec34e5..b0db2dc 100644 --- a/Elecciones-Web/src/Elecciones.Database/Entities/AgrupacionPolitica.cs +++ b/Elecciones-Web/src/Elecciones.Database/Entities/AgrupacionPolitica.cs @@ -12,8 +12,6 @@ public class AgrupacionPolitica public string Nombre { get; set; } = null!; public string? NombreCorto { get; set; } // Para leyendas y gráficos public string? Color { get; set; } // Código hexadecimal, ej: "#1f77b4" - public string? LogoUrl { get; set; } // URL a la imagen del logo - // Puede ser nulo si una agrupación no tiene una posición definida. public int? OrdenDiputados { get; set; } public int? OrdenSenadores { get; set; } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Database/Entities/LogoAgrupacionCategoria.cs b/Elecciones-Web/src/Elecciones.Database/Entities/LogoAgrupacionCategoria.cs new file mode 100644 index 0000000..309a2a0 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Entities/LogoAgrupacionCategoria.cs @@ -0,0 +1,18 @@ +// src/Elecciones.Database/Entities/LogoAgrupacionCategoria.cs +using System.ComponentModel.DataAnnotations; + +namespace Elecciones.Database.Entities; + +public class LogoAgrupacionCategoria +{ + [Key] + public int Id { get; set; } + + [Required] + public string AgrupacionPoliticaId { get; set; } = null!; + + [Required] + public int CategoriaId { get; set; } + + public string? LogoUrl { get; set; } +} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Database/Entities/ResumenVoto.cs b/Elecciones-Web/src/Elecciones.Database/Entities/ResumenVoto.cs index 0186574..1dcd438 100644 --- a/Elecciones-Web/src/Elecciones.Database/Entities/ResumenVoto.cs +++ b/Elecciones-Web/src/Elecciones.Database/Entities/ResumenVoto.cs @@ -1,3 +1,4 @@ +// src/Elecciones.Database/Entities/ResumenVoto.cs using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -9,11 +10,18 @@ public class ResumenVoto [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - // El ámbito donde se resume (siempre provincial en este caso) + [Required] public int AmbitoGeograficoId { get; set; } + [Required] public string AgrupacionPoliticaId { get; set; } = null!; + + [ForeignKey("AgrupacionPoliticaId")] + public AgrupacionPolitica AgrupacionPolitica { get; set; } = null!; + [Required] public long Votos { get; set; } + + [Required] public decimal VotosPorcentaje { get; set; } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163255_AddLogoAgrupacionCategoriaTable.Designer.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163255_AddLogoAgrupacionCategoriaTable.Designer.cs new file mode 100644 index 0000000..a3a4c36 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163255_AddLogoAgrupacionCategoriaTable.Designer.cs @@ -0,0 +1,555 @@ +// +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("20250901163255_AddLogoAgrupacionCategoriaTable")] + partial class AddLogoAgrupacionCategoriaTable + { + /// + 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("Elecciones.Database.Entities.AdminUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("AdminUsers"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Color") + .HasColumnType("nvarchar(max)"); + + b.Property("IdTelegrama") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NombreCorto") + .HasColumnType("nvarchar(max)"); + + b.Property("OrdenDiputados") + .HasColumnType("int"); + + b.Property("OrdenSenadores") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AgrupacionesPoliticas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.AmbitoGeografico", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CircuitoId") + .HasColumnType("nvarchar(max)"); + + b.Property("DistritoId") + .HasColumnType("nvarchar(max)"); + + b.Property("EstablecimientoId") + .HasColumnType("nvarchar(max)"); + + b.Property("MesaId") + .HasColumnType("nvarchar(max)"); + + b.Property("MunicipioId") + .HasColumnType("nvarchar(max)"); + + b.Property("NivelId") + .HasColumnType("int"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SeccionId") + .HasColumnType("nvarchar(max)"); + + b.Property("SeccionProvincialId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AmbitosGeograficos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .HasColumnType("nvarchar(450)"); + + b.Property("Camara") + .HasColumnType("int"); + + b.Property("NumeroBanca") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId"); + + b.ToTable("Bancadas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Orden") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CategoriasElectorales"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Configuracion", b => + { + b.Property("Clave") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Valor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Clave"); + + b.ToTable("Configuraciones"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b => + { + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("CantidadElectores") + .HasColumnType("int"); + + b.Property("CantidadVotantes") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("MesasEsperadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadasPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("ParticipacionPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("VotosEnBlanco") + .HasColumnType("bigint"); + + b.Property("VotosEnBlancoPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VotosNulos") + .HasColumnType("bigint"); + + b.Property("VotosNulosPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VotosRecurridos") + .HasColumnType("bigint"); + + b.Property("VotosRecurridosPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("AmbitoGeograficoId", "CategoriaId"); + + b.ToTable("EstadosRecuentos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b => + { + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("CantidadElectores") + .HasColumnType("int"); + + b.Property("CantidadVotantes") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("MesasEsperadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadasPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId", "CategoriaId") + .IsUnique(); + + b.ToTable("LogosAgrupacionesCategorias"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BancadaId") + .HasColumnType("int"); + + b.Property("FotoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("NombreOcupante") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Periodo") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("BancadaId") + .IsUnique(); + + b.ToTable("OcupantesBancas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CantidadVotos") + .HasColumnType("bigint"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("Votos") + .HasColumnType("bigint"); + + b.Property("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("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("ContenidoBase64") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FechaEscaneo") + .HasColumnType("datetime2"); + + b.Property("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.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 + } + } +} diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163255_AddLogoAgrupacionCategoriaTable.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163255_AddLogoAgrupacionCategoriaTable.cs new file mode 100644 index 0000000..c8dce72 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163255_AddLogoAgrupacionCategoriaTable.cs @@ -0,0 +1,79 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elecciones.Database.Migrations +{ + /// + public partial class AddLogoAgrupacionCategoriaTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "AgrupacionPoliticaId", + table: "ResumenesVotos", + type: "nvarchar(450)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.CreateTable( + name: "LogosAgrupacionesCategorias", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + AgrupacionPoliticaId = table.Column(type: "nvarchar(450)", nullable: false), + CategoriaId = table.Column(type: "int", nullable: false), + LogoUrl = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LogosAgrupacionesCategorias", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_ResumenesVotos_AgrupacionPoliticaId", + table: "ResumenesVotos", + column: "AgrupacionPoliticaId"); + + migrationBuilder.CreateIndex( + name: "IX_LogosAgrupacionesCategorias_AgrupacionPoliticaId_CategoriaId", + table: "LogosAgrupacionesCategorias", + columns: new[] { "AgrupacionPoliticaId", "CategoriaId" }, + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_ResumenesVotos_AgrupacionesPoliticas_AgrupacionPoliticaId", + table: "ResumenesVotos", + column: "AgrupacionPoliticaId", + principalTable: "AgrupacionesPoliticas", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ResumenesVotos_AgrupacionesPoliticas_AgrupacionPoliticaId", + table: "ResumenesVotos"); + + migrationBuilder.DropTable( + name: "LogosAgrupacionesCategorias"); + + migrationBuilder.DropIndex( + name: "IX_ResumenesVotos_AgrupacionPoliticaId", + table: "ResumenesVotos"); + + migrationBuilder.AlterColumn( + name: "AgrupacionPoliticaId", + table: "ResumenesVotos", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(450)"); + } + } +} diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163521_RemoveLogoUrlFromAgrupacionPolitica.Designer.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163521_RemoveLogoUrlFromAgrupacionPolitica.Designer.cs new file mode 100644 index 0000000..e99a9ab --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163521_RemoveLogoUrlFromAgrupacionPolitica.Designer.cs @@ -0,0 +1,552 @@ +// +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("20250901163521_RemoveLogoUrlFromAgrupacionPolitica")] + partial class RemoveLogoUrlFromAgrupacionPolitica + { + /// + 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("Elecciones.Database.Entities.AdminUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("AdminUsers"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Color") + .HasColumnType("nvarchar(max)"); + + b.Property("IdTelegrama") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NombreCorto") + .HasColumnType("nvarchar(max)"); + + b.Property("OrdenDiputados") + .HasColumnType("int"); + + b.Property("OrdenSenadores") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AgrupacionesPoliticas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.AmbitoGeografico", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CircuitoId") + .HasColumnType("nvarchar(max)"); + + b.Property("DistritoId") + .HasColumnType("nvarchar(max)"); + + b.Property("EstablecimientoId") + .HasColumnType("nvarchar(max)"); + + b.Property("MesaId") + .HasColumnType("nvarchar(max)"); + + b.Property("MunicipioId") + .HasColumnType("nvarchar(max)"); + + b.Property("NivelId") + .HasColumnType("int"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SeccionId") + .HasColumnType("nvarchar(max)"); + + b.Property("SeccionProvincialId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AmbitosGeograficos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .HasColumnType("nvarchar(450)"); + + b.Property("Camara") + .HasColumnType("int"); + + b.Property("NumeroBanca") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId"); + + b.ToTable("Bancadas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Orden") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CategoriasElectorales"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Configuracion", b => + { + b.Property("Clave") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Valor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Clave"); + + b.ToTable("Configuraciones"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b => + { + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("CantidadElectores") + .HasColumnType("int"); + + b.Property("CantidadVotantes") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("MesasEsperadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadasPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("ParticipacionPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("VotosEnBlanco") + .HasColumnType("bigint"); + + b.Property("VotosEnBlancoPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VotosNulos") + .HasColumnType("bigint"); + + b.Property("VotosNulosPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VotosRecurridos") + .HasColumnType("bigint"); + + b.Property("VotosRecurridosPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("AmbitoGeograficoId", "CategoriaId"); + + b.ToTable("EstadosRecuentos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b => + { + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("CantidadElectores") + .HasColumnType("int"); + + b.Property("CantidadVotantes") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("MesasEsperadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadasPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId", "CategoriaId") + .IsUnique(); + + b.ToTable("LogosAgrupacionesCategorias"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BancadaId") + .HasColumnType("int"); + + b.Property("FotoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("NombreOcupante") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Periodo") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("BancadaId") + .IsUnique(); + + b.ToTable("OcupantesBancas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CantidadVotos") + .HasColumnType("bigint"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("Votos") + .HasColumnType("bigint"); + + b.Property("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("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("ContenidoBase64") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FechaEscaneo") + .HasColumnType("datetime2"); + + b.Property("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.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 + } + } +} diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163521_RemoveLogoUrlFromAgrupacionPolitica.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163521_RemoveLogoUrlFromAgrupacionPolitica.cs new file mode 100644 index 0000000..35f4704 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/20250901163521_RemoveLogoUrlFromAgrupacionPolitica.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elecciones.Database.Migrations +{ + /// + public partial class RemoveLogoUrlFromAgrupacionPolitica : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LogoUrl", + table: "AgrupacionesPoliticas"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LogoUrl", + table: "AgrupacionesPoliticas", + type: "nvarchar(max)", + nullable: true); + } + } +} diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs index de2bcb4..9d2aee4 100644 --- a/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs @@ -61,9 +61,6 @@ namespace Elecciones.Database.Migrations .IsRequired() .HasColumnType("nvarchar(max)"); - b.Property("LogoUrl") - .HasColumnType("nvarchar(max)"); - b.Property("Nombre") .IsRequired() .HasColumnType("nvarchar(max)"); @@ -275,6 +272,32 @@ namespace Elecciones.Database.Migrations b.ToTable("EstadosRecuentosGenerales"); }); + modelBuilder.Entity("Elecciones.Database.Entities.LogoAgrupacionCategoria", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId", "CategoriaId") + .IsUnique(); + + b.ToTable("LogosAgrupacionesCategorias"); + }); + modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b => { b.Property("Id") @@ -383,7 +406,7 @@ namespace Elecciones.Database.Migrations b.Property("AgrupacionPoliticaId") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasColumnType("nvarchar(450)"); b.Property("AmbitoGeograficoId") .HasColumnType("int"); @@ -397,6 +420,8 @@ namespace Elecciones.Database.Migrations b.HasKey("Id"); + b.HasIndex("AgrupacionPoliticaId"); + b.ToTable("ResumenesVotos"); }); @@ -503,6 +528,17 @@ namespace Elecciones.Database.Migrations 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"); diff --git a/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs index 0ab162e..1bbbb55 100644 --- a/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3b8c6bf754cff6ace486ae8fe850ed4d69233280")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+608ae655bedf6c59be5fec1e14fc308871d2fd62")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]