Feat Widgets 1930
This commit is contained in:
@@ -140,8 +140,8 @@ export const getResultadosConcejales = async (seccionId: string): Promise<Result
|
||||
};
|
||||
|
||||
export const getDetalleSeccion = async (seccionId: string, categoriaId: number): Promise<ResultadoDetalleSeccion[]> => {
|
||||
const response = await apiClient.get(`/resultados/seccion/${seccionId}?categoriaId=${categoriaId}`);
|
||||
return response.data;
|
||||
const response = await apiClient.get(`/resultados/seccion/${seccionId}?categoriaId=${categoriaId}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getResultadosPorMunicipioYCategoria = async (municipioId: string, categoriaId: number): Promise<ResultadoTicker[]> => {
|
||||
@@ -149,7 +149,18 @@ export const getResultadosPorMunicipioYCategoria = async (municipioId: string, c
|
||||
return response.data.resultados;
|
||||
};
|
||||
|
||||
export const getMunicipios = async (): Promise<MunicipioSimple[]> => {
|
||||
const response = await apiClient.get('/catalogos/municipios');
|
||||
return response.data;
|
||||
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;
|
||||
};
|
||||
|
||||
export const getMunicipios = async (categoriaId?: number): Promise<MunicipioSimple[]> => {
|
||||
let url = '/catalogos/municipios';
|
||||
if (categoriaId) {
|
||||
url += `?categoriaId=${categoriaId}`;
|
||||
}
|
||||
const response = await apiClient.get(url);
|
||||
// La API ahora devuelve { seccionId, nombre }, lo mapeamos a { id, nombre }
|
||||
return response.data.map((m: any) => ({ id: m.seccionId, nombre: m.nombre }));
|
||||
};
|
||||
@@ -1,80 +1,75 @@
|
||||
// src/components/BancasWidget.tsx
|
||||
import { useState, useEffect } from 'react';
|
||||
// Se cambia la importación: de ResponsiveWaffle a ResponsiveBar
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { ResponsiveBar } from '@nivo/bar';
|
||||
import { getBancasPorSeccion, getSeccionesElectorales } from '../apiService';
|
||||
import type { ProyeccionBancas, MunicipioSimple } from '../types/types';
|
||||
import './BancasWidget.css';
|
||||
|
||||
// La paleta de colores se mantiene para consistencia visual
|
||||
const NIVO_COLORS = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"];
|
||||
|
||||
export const BancasWidget = () => {
|
||||
const [secciones, setSecciones] = useState<MunicipioSimple[]>([]);
|
||||
const [seccionActual, setSeccionActual] = useState<string>('');
|
||||
const [data, setData] = useState<ProyeccionBancas | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// useEffect para cargar la lista de secciones una sola vez
|
||||
// useEffect para cargar la lista de secciones una sola vez (esto está bien)
|
||||
useEffect(() => {
|
||||
const fetchSecciones = async () => {
|
||||
try {
|
||||
const seccionesData = await getSeccionesElectorales();
|
||||
if (seccionesData && seccionesData.length > 0) {
|
||||
|
||||
// --- INICIO DE LA LÓGICA DE ORDENAMIENTO ---
|
||||
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));
|
||||
// --- FIN DE LA LÓGICA DE ORDENAMIENTO ---
|
||||
|
||||
setSecciones(seccionesData);
|
||||
setSeccionActual(seccionesData[0].id);
|
||||
} else {
|
||||
setError("No se encontraron secciones electorales.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error cargando secciones electorales:", err);
|
||||
setError("No se pudo cargar la lista de secciones.");
|
||||
// Opcional: manejar el error en la UI si la carga de secciones falla
|
||||
}
|
||||
};
|
||||
fetchSecciones();
|
||||
}, []);
|
||||
|
||||
// useEffect para cargar los datos de bancas cuando cambia la sección
|
||||
useEffect(() => {
|
||||
if (!seccionActual) return;
|
||||
// --- CÓDIGO REFACTORIZADO ---
|
||||
// Eliminamos los useState para data, loading y error
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const result = await getBancasPorSeccion(seccionActual);
|
||||
setData(result);
|
||||
} catch (err) {
|
||||
console.error(`Error cargando datos de bancas para sección ${seccionActual}:`, err);
|
||||
setData(null);
|
||||
setError("No hay datos de bancas disponibles para esta sección.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [seccionActual]);
|
||||
// useQuery ahora es la única fuente de verdad para los datos de bancas
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
error
|
||||
} = useQuery<ProyeccionBancas, Error>({
|
||||
queryKey: ['bancasPorSeccion', seccionActual],
|
||||
queryFn: () => getBancasPorSeccion(seccionActual),
|
||||
enabled: !!seccionActual,
|
||||
retry: (failureCount, error: any) => {
|
||||
if (error.response?.status === 404) return false;
|
||||
return failureCount < 3;
|
||||
},
|
||||
});
|
||||
|
||||
// Se preparan los datos para el gráfico de barras.
|
||||
// Se invierte el array para que el partido con más bancas aparezca arriba.
|
||||
const barData = data ? [...data.proyeccion].reverse() : [];
|
||||
|
||||
const getErrorMessage = () => {
|
||||
if (error) {
|
||||
if ((error as any).response?.status === 404) {
|
||||
return "La proyección de bancas para esta sección aún no está disponible.";
|
||||
}
|
||||
return "No se pudo conectar para obtener los datos de bancas.";
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const errorMessage = getErrorMessage();
|
||||
|
||||
return (
|
||||
<div className="bancas-widget-container">
|
||||
<div className="bancas-header">
|
||||
@@ -84,8 +79,7 @@ export const BancasWidget = () => {
|
||||
</select>
|
||||
</div>
|
||||
<div className="waffle-chart-container">
|
||||
{loading ? <p>Cargando...</p> : error ? <p>{error}</p> :
|
||||
// --- SE REEMPLAZA EL GRÁFICO WAFFLE POR EL GRÁFICO DE BARRAS ---
|
||||
{isLoading ? <p>Cargando...</p> : errorMessage ? <p>{errorMessage}</p> :
|
||||
<ResponsiveBar
|
||||
data={barData}
|
||||
keys={['bancas']}
|
||||
@@ -106,7 +100,6 @@ export const BancasWidget = () => {
|
||||
legend: 'Cantidad de Bancas',
|
||||
legendPosition: 'middle',
|
||||
legendOffset: 20,
|
||||
// Asegura que los ticks del eje sean números enteros
|
||||
format: (value) => Math.floor(value) === value ? value : ''
|
||||
}}
|
||||
axisLeft={{
|
||||
@@ -118,7 +111,6 @@ export const BancasWidget = () => {
|
||||
labelSkipHeight={12}
|
||||
labelTextColor={{ from: 'color', modifiers: [['darker', 3]] }}
|
||||
animate={true}
|
||||
// Se elimina la leyenda, ya que las etiquetas en el eje son suficientes
|
||||
legends={[]}
|
||||
/>}
|
||||
</div>
|
||||
|
||||
@@ -1,68 +1,62 @@
|
||||
// src/components/ConcejalesWidget.tsx
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Select from 'react-select'; // <-- 1. Importar react-select
|
||||
import { getMunicipios, getResultadosPorMunicipioYCategoria, getConfiguracionPublica } from '../apiService';
|
||||
import Select from 'react-select';
|
||||
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica } from '../apiService';
|
||||
import type { MunicipioSimple, ResultadoTicker } from '../types/types';
|
||||
import { ImageWithFallback } from './ImageWithFallback';
|
||||
import './TickerWidget.css';
|
||||
|
||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||
|
||||
// Estilos personalizados para que el selector se vea bien
|
||||
const customSelectStyles = {
|
||||
control: (base: any) => ({ ...base, minWidth: '220px', border: '1px solid #ced4da' }),
|
||||
menu: (base: any) => ({ ...base, zIndex: 10 }), // Para que el menú se superponga
|
||||
menu: (base: any) => ({ ...base, zIndex: 10 }),
|
||||
};
|
||||
|
||||
const CATEGORIA_ID = 7; // ID para Concejales
|
||||
|
||||
export const ConcejalesWidget = () => {
|
||||
// 2. Cambiamos el estado para que se adapte a react-select
|
||||
const [selectedMunicipio, setSelectedMunicipio] = useState<{ value: string; label: string } | null>(null);
|
||||
|
||||
// 1. Query para la configuración (se había eliminado por error)
|
||||
const { data: configData } = useQuery({
|
||||
queryKey: ['configuracionPublica'],
|
||||
queryFn: getConfiguracionPublica,
|
||||
staleTime: 0,
|
||||
});
|
||||
|
||||
// Usamos la clave de configuración correcta
|
||||
// 2. Query para la lista de municipios
|
||||
const { data: municipios = [], isLoading: isLoadingMunicipios } = useQuery<MunicipioSimple[]>({
|
||||
// Usamos una clave genérica porque siempre pedimos la lista completa.
|
||||
queryKey: ['municipios'],
|
||||
// Llamamos a la función sin argumentos para obtener todos los municipios.
|
||||
queryFn: () => getMunicipios(),
|
||||
});
|
||||
|
||||
const cantidadAMostrar = parseInt(configData?.ConcejalesResultadosCantidad || '5', 10);
|
||||
|
||||
// 3. Query para obtener la lista de MUNICIPIOS
|
||||
const { data: municipios = [], isLoading: isLoadingMunicipios } = useQuery<MunicipioSimple[]>({
|
||||
queryKey: ['municipios'],
|
||||
queryFn: getMunicipios,
|
||||
});
|
||||
|
||||
// Este useEffect se encarga de establecer el valor por defecto
|
||||
useEffect(() => {
|
||||
// Se ejecuta solo si tenemos la lista de municipios y aún no hemos seleccionado nada
|
||||
if (municipios.length > 0 && !selectedMunicipio) {
|
||||
// Buscamos "LA PLATA" en la lista (insensible a mayúsculas/minúsculas)
|
||||
const laPlata = municipios.find(m => m.nombre.toUpperCase() === 'LA PLATA');
|
||||
|
||||
// Si lo encontramos, lo establecemos como el municipio seleccionado
|
||||
if (laPlata) {
|
||||
setSelectedMunicipio({ value: laPlata.id, label: laPlata.nombre });
|
||||
}
|
||||
}
|
||||
}, [municipios, selectedMunicipio]); // Se ejecuta cuando 'municipios' o 'selectedMunicipio' cambian
|
||||
}, [municipios, selectedMunicipio]);
|
||||
|
||||
// 4. Transformamos los datos para react-select
|
||||
const municipioOptions = useMemo(() =>
|
||||
municipios
|
||||
.map(m => ({ value: m.id, label: m.nombre }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)), // Orden alfabético
|
||||
[municipios]);
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
[municipios]);
|
||||
|
||||
// 5. Query para obtener los resultados del MUNICIPIO seleccionado
|
||||
const { data: resultados, isLoading: isLoadingResultados } = useQuery<ResultadoTicker[]>({
|
||||
queryKey: ['resultadosConcejalesPorMunicipio', selectedMunicipio?.value],
|
||||
queryFn: () => getResultadosPorMunicipioYCategoria(selectedMunicipio!.value, 7),
|
||||
queryKey: ['resultadosPorMunicipio', selectedMunicipio?.value, CATEGORIA_ID],
|
||||
queryFn: () => getResultadosPorMunicipio(selectedMunicipio!.value, CATEGORIA_ID),
|
||||
enabled: !!selectedMunicipio,
|
||||
});
|
||||
|
||||
// 6. Lógica para "Otros" (sin cambios funcionales)
|
||||
// Lógica para "Otros"
|
||||
let displayResults: ResultadoTicker[] = resultados || [];
|
||||
if (resultados && resultados.length > cantidadAMostrar) {
|
||||
const topParties = resultados.slice(0, cantidadAMostrar - 1);
|
||||
@@ -92,6 +86,7 @@ export const ConcejalesWidget = () => {
|
||||
onChange={(option) => setSelectedMunicipio(option)}
|
||||
isLoading={isLoadingMunicipios}
|
||||
placeholder="Buscar y seleccionar un municipio..."
|
||||
isClearable
|
||||
styles={customSelectStyles}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
// src/components/DiputadosWidget.tsx
|
||||
import { useMemo } from 'react';
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getResumenProvincial, getConfiguracionPublica } from '../apiService';
|
||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
||||
import Select from 'react-select';
|
||||
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica } from '../apiService';
|
||||
import type { MunicipioSimple, ResultadoTicker } from '../types/types';
|
||||
import { ImageWithFallback } from './ImageWithFallback';
|
||||
import './TickerWidget.css';
|
||||
|
||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||
const customSelectStyles = {
|
||||
control: (base: any) => ({ ...base, minWidth: '220px', border: '1px solid #ced4da' }),
|
||||
menu: (base: any) => ({ ...base, zIndex: 10 }),
|
||||
};
|
||||
|
||||
// Constante para la categoría de este widget
|
||||
const CATEGORIA_ID = 6; // Diputados
|
||||
|
||||
export const DiputadosWidget = () => {
|
||||
const { data: categorias, isLoading, error } = useQuery<CategoriaResumen[]>({
|
||||
queryKey: ['resumenProvincial'],
|
||||
queryFn: getResumenProvincial,
|
||||
refetchInterval: 30000,
|
||||
});
|
||||
const [selectedMunicipio, setSelectedMunicipio] = useState<{ value: string; label: string } | null>(null);
|
||||
|
||||
const { data: configData } = useQuery({
|
||||
queryKey: ['configuracionPublica'],
|
||||
@@ -21,24 +25,47 @@ export const DiputadosWidget = () => {
|
||||
staleTime: 0,
|
||||
});
|
||||
|
||||
// Usamos la clave de configuración del Ticker, ya que es para Senadores/Diputados
|
||||
const cantidadAMostrar = parseInt(configData?.TickerResultadosCantidad || '5', 10);
|
||||
|
||||
// Usamos useMemo para encontrar los datos específicos de Diputados (ID 6)
|
||||
const diputadosData = useMemo(() => {
|
||||
return categorias?.find(c => c.categoriaId === 6);
|
||||
}, [categorias]);
|
||||
const { data: municipios = [], isLoading: isLoadingMunicipios } = useQuery<MunicipioSimple[]>({
|
||||
queryKey: ['municipios', CATEGORIA_ID], // Key única para la lista de municipios de diputados
|
||||
queryFn: () => getMunicipios(CATEGORIA_ID), // Pide solo los municipios que votan diputados
|
||||
});
|
||||
|
||||
if (isLoading) return <div className="ticker-card loading">Cargando...</div>;
|
||||
if (error || !diputadosData) return <div className="ticker-card error">Datos de Diputados no disponibles.</div>;
|
||||
// useEffect para establecer "LA PLATA" por defecto
|
||||
useEffect(() => {
|
||||
if (municipios.length > 0 && !selectedMunicipio) {
|
||||
const laPlata = municipios.find(m => m.nombre.toUpperCase() === 'LA PLATA');
|
||||
if (laPlata) {
|
||||
setSelectedMunicipio({ value: laPlata.id, label: laPlata.nombre });
|
||||
} else if (municipios.length > 0) {
|
||||
// Si no está La Plata, seleccionamos el primero de la lista
|
||||
setSelectedMunicipio({ value: municipios[0].id, label: municipios[0].nombre });
|
||||
}
|
||||
}
|
||||
}, [municipios, selectedMunicipio]);
|
||||
|
||||
// Lógica para "Otros" aplicada solo a los resultados de Diputados
|
||||
let displayResults: ResultadoTicker[] = diputadosData.resultados;
|
||||
if (diputadosData.resultados.length > cantidadAMostrar) {
|
||||
const topParties = diputadosData.resultados.slice(0, cantidadAMostrar - 1);
|
||||
const otherParties = diputadosData.resultados.slice(cantidadAMostrar - 1);
|
||||
const otrosPorcentaje = otherParties.reduce((sum, party) => sum + party.porcentaje, 0);
|
||||
const municipioOptions = useMemo(() =>
|
||||
municipios
|
||||
.map(m => ({ value: m.id, label: m.nombre }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
[municipios]);
|
||||
|
||||
const { data: resultados, isLoading: isLoadingResultados } = useQuery<ResultadoTicker[]>({
|
||||
queryKey: ['resultadosMunicipio', selectedMunicipio?.value, CATEGORIA_ID],
|
||||
queryFn: () => getResultadosPorMunicipio(selectedMunicipio!.value, CATEGORIA_ID),
|
||||
enabled: !!selectedMunicipio,
|
||||
});
|
||||
|
||||
// Lógica para "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.porcentaje || 0), 0);
|
||||
const otrosEntry: ResultadoTicker = {
|
||||
id: `otros-diputados`,
|
||||
id: `otros-diputados-${selectedMunicipio?.value}`,
|
||||
nombre: 'Otros',
|
||||
nombreCorto: 'Otros',
|
||||
color: '#888888',
|
||||
@@ -47,20 +74,27 @@ export const DiputadosWidget = () => {
|
||||
porcentaje: otrosPorcentaje,
|
||||
};
|
||||
displayResults = [...topParties, otrosEntry];
|
||||
} else {
|
||||
displayResults = diputadosData.resultados.slice(0, cantidadAMostrar);
|
||||
} else if (resultados) {
|
||||
displayResults = resultados.slice(0, cantidadAMostrar);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ticker-card">
|
||||
<div className="ticker-header">
|
||||
<h3>{diputadosData.categoriaNombre.replace(' PROVINCIALES', '')}</h3>
|
||||
<div className="ticker-stats">
|
||||
<span>Mesas: <strong>{formatPercent(diputadosData.estadoRecuento?.mesasTotalizadasPorcentaje || 0)}</strong></span>
|
||||
<span>Part: <strong>{formatPercent(diputadosData.estadoRecuento?.participacionPorcentaje || 0)}</strong></span>
|
||||
</div>
|
||||
<h3>DIPUTADOS POR MUNICIPIO</h3>
|
||||
<Select
|
||||
options={municipioOptions}
|
||||
value={selectedMunicipio}
|
||||
onChange={(option) => setSelectedMunicipio(option)}
|
||||
isLoading={isLoadingMunicipios}
|
||||
placeholder="Buscar municipio..."
|
||||
isClearable
|
||||
styles={customSelectStyles}
|
||||
/>
|
||||
</div>
|
||||
<div className="ticker-results">
|
||||
{(isLoadingMunicipios || (isLoadingResultados && selectedMunicipio)) && <p>Cargando...</p>}
|
||||
{!selectedMunicipio && !isLoadingMunicipios && <p style={{textAlign: 'center', color: '#666'}}>Seleccione un municipio.</p>}
|
||||
{displayResults.map(partido => (
|
||||
<div key={partido.id} className="ticker-party">
|
||||
<div className="party-logo">
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
// src/components/SenadoresWidget.tsx
|
||||
import { useMemo } from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getResumenProvincial, getConfiguracionPublica } from '../apiService';
|
||||
import type { CategoriaResumen, ResultadoTicker } from '../types/types';
|
||||
import Select from 'react-select'; // Importamos react-select
|
||||
import { getMunicipios, getResultadosPorMunicipio, getConfiguracionPublica } from '../apiService'; // Usamos las funciones genéricas
|
||||
import type { MunicipioSimple, ResultadoTicker } from '../types/types';
|
||||
import { ImageWithFallback } from './ImageWithFallback';
|
||||
import './TickerWidget.css';
|
||||
|
||||
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||
|
||||
// Estilos para el selector, podemos moverlos a un archivo común más adelante
|
||||
const customSelectStyles = {
|
||||
control: (base: any) => ({ ...base, minWidth: '220px', border: '1px solid #ced4da' }),
|
||||
menu: (base: any) => ({ ...base, zIndex: 10 }),
|
||||
};
|
||||
|
||||
// Constante para la categoría de este widget
|
||||
const CATEGORIA_ID = 5; // Senadores
|
||||
|
||||
export const SenadoresWidget = () => {
|
||||
const { data: categorias, isLoading, error } = useQuery<CategoriaResumen[]>({
|
||||
queryKey: ['resumenProvincial'],
|
||||
queryFn: getResumenProvincial,
|
||||
refetchInterval: 30000,
|
||||
});
|
||||
const [selectedMunicipio, setSelectedMunicipio] = useState<{ value: string; label: string } | null>(null);
|
||||
|
||||
const { data: configData } = useQuery({
|
||||
queryKey: ['configuracionPublica'],
|
||||
@@ -21,24 +27,44 @@ export const SenadoresWidget = () => {
|
||||
staleTime: 0,
|
||||
});
|
||||
|
||||
// Usamos la clave de configuración del Ticker, ya que es para Senadores/Diputados
|
||||
const cantidadAMostrar = parseInt(configData?.TickerResultadosCantidad || '5', 10);
|
||||
|
||||
// Usamos useMemo para encontrar los datos específicos de Senadores (ID 5)
|
||||
const senadoresData = useMemo(() => {
|
||||
return categorias?.find(c => c.categoriaId === 5);
|
||||
}, [categorias]);
|
||||
const { data: municipios = [], isLoading: isLoadingMunicipios } = useQuery<MunicipioSimple[]>({
|
||||
queryKey: ['municipios', CATEGORIA_ID], // Key única para la caché
|
||||
queryFn: () => getMunicipios(CATEGORIA_ID), // Pasamos el ID de la categoría
|
||||
});
|
||||
|
||||
if (isLoading) return <div className="ticker-card loading">Cargando...</div>;
|
||||
if (error || !senadoresData) return <div className="ticker-card error">Datos de Senadores no disponibles.</div>;
|
||||
// useEffect para establecer "LA PLATA" por defecto
|
||||
useEffect(() => {
|
||||
if (municipios.length > 0 && !selectedMunicipio) {
|
||||
const laPlata = municipios.find(m => m.nombre.toUpperCase() === 'LA PLATA');
|
||||
if (laPlata) {
|
||||
setSelectedMunicipio({ value: laPlata.id, label: laPlata.nombre });
|
||||
}
|
||||
}
|
||||
}, [municipios, selectedMunicipio]);
|
||||
|
||||
// Lógica para "Otros" aplicada solo a los resultados de Senadores
|
||||
let displayResults: ResultadoTicker[] = senadoresData.resultados;
|
||||
if (senadoresData.resultados.length > cantidadAMostrar) {
|
||||
const topParties = senadoresData.resultados.slice(0, cantidadAMostrar - 1);
|
||||
const otherParties = senadoresData.resultados.slice(cantidadAMostrar - 1);
|
||||
const otrosPorcentaje = otherParties.reduce((sum, party) => sum + party.porcentaje, 0);
|
||||
const municipioOptions = useMemo(() =>
|
||||
municipios
|
||||
.map(m => ({ value: m.id, label: m.nombre }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
[municipios]);
|
||||
|
||||
const { data: resultados, isLoading: isLoadingResultados } = useQuery<ResultadoTicker[]>({
|
||||
queryKey: ['resultadosMunicipio', selectedMunicipio?.value, CATEGORIA_ID],
|
||||
queryFn: () => getResultadosPorMunicipio(selectedMunicipio!.value, CATEGORIA_ID),
|
||||
enabled: !!selectedMunicipio,
|
||||
});
|
||||
|
||||
// Lógica para "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.porcentaje || 0), 0);
|
||||
const otrosEntry: ResultadoTicker = {
|
||||
id: `otros-senadores`,
|
||||
id: `otros-senadores-${selectedMunicipio?.value}`,
|
||||
nombre: 'Otros',
|
||||
nombreCorto: 'Otros',
|
||||
color: '#888888',
|
||||
@@ -47,20 +73,27 @@ export const SenadoresWidget = () => {
|
||||
porcentaje: otrosPorcentaje,
|
||||
};
|
||||
displayResults = [...topParties, otrosEntry];
|
||||
} else {
|
||||
displayResults = senadoresData.resultados.slice(0, cantidadAMostrar);
|
||||
} else if (resultados) {
|
||||
displayResults = resultados.slice(0, cantidadAMostrar);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ticker-card">
|
||||
<div className="ticker-header">
|
||||
<h3>{senadoresData.categoriaNombre.replace(' PROVINCIALES', '')}</h3>
|
||||
<div className="ticker-stats">
|
||||
<span>Mesas: <strong>{formatPercent(senadoresData.estadoRecuento?.mesasTotalizadasPorcentaje || 0)}</strong></span>
|
||||
<span>Part: <strong>{formatPercent(senadoresData.estadoRecuento?.participacionPorcentaje || 0)}</strong></span>
|
||||
</div>
|
||||
<h3>SENADORES POR MUNICIPIO</h3>
|
||||
<Select
|
||||
options={municipioOptions}
|
||||
value={selectedMunicipio}
|
||||
onChange={(option) => setSelectedMunicipio(option)}
|
||||
isLoading={isLoadingMunicipios}
|
||||
placeholder="Buscar municipio..."
|
||||
isClearable
|
||||
styles={customSelectStyles}
|
||||
/>
|
||||
</div>
|
||||
<div className="ticker-results">
|
||||
{(isLoadingMunicipios || (isLoadingResultados && selectedMunicipio)) && <p>Cargando...</p>}
|
||||
{!selectedMunicipio && !isLoadingMunicipios && <p style={{ textAlign: 'center', color: '#666' }}>Seleccione un municipio.</p>}
|
||||
{displayResults.map(partido => (
|
||||
<div key={partido.id} className="ticker-party">
|
||||
<div className="party-logo">
|
||||
|
||||
@@ -17,20 +17,39 @@ public class CatalogosController : ControllerBase
|
||||
}
|
||||
|
||||
[HttpGet("municipios")]
|
||||
public async Task<IActionResult> GetMunicipios()
|
||||
public async Task<IActionResult> GetMunicipios([FromQuery] int? categoriaId)
|
||||
{
|
||||
var municipios = await _dbContext.AmbitosGeograficos
|
||||
var municipiosQuery = _dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.Where(a => a.NivelId == 30 && a.SeccionId != null)
|
||||
.Select(a => new MunicipioSimpleDto
|
||||
{
|
||||
Id = a.SeccionId!,
|
||||
Nombre = a.Nombre
|
||||
})
|
||||
.OrderBy(m => m.Nombre)
|
||||
.Where(a => a.NivelId == 30);
|
||||
|
||||
// Si NO se proporciona una categoriaId, devolvemos todos (para Concejales).
|
||||
if (categoriaId == null)
|
||||
{
|
||||
var todosLosMunicipios = await municipiosQuery
|
||||
.OrderBy(a => a.Nombre)
|
||||
.Select(a => new { a.SeccionId, a.Nombre }) // Usamos SeccionId como el ID electoral
|
||||
.ToListAsync();
|
||||
return Ok(todosLosMunicipios);
|
||||
}
|
||||
|
||||
// --- LÓGICA DE FILTRADO PARA DIPUTADOS Y SENADORES ---
|
||||
// 1. Encontrar las Secciones Electorales que SÍ tienen resultados para la categoría.
|
||||
var seccionesActivasIds = await _dbContext.ProyeccionesBancas
|
||||
.AsNoTracking()
|
||||
.Where(p => p.CategoriaId == categoriaId)
|
||||
.Select(p => p.AmbitoGeografico.SeccionProvincialId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(municipios);
|
||||
// 2. Filtramos la lista de municipios.
|
||||
var municipiosFiltrados = await municipiosQuery
|
||||
.Where(m => seccionesActivasIds.Contains(m.SeccionProvincialId))
|
||||
.OrderBy(a => a.Nombre)
|
||||
.Select(a => new { a.SeccionId, a.Nombre })
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(municipiosFiltrados);
|
||||
}
|
||||
|
||||
[HttpGet("agrupaciones")]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=TECNICA3;Database=Elecciones2025;User Id=apielecciones2025;Password=PTP847Elecciones2025;Encrypt=False;MultipleActiveResultSets=True;TrustServerCertificate=True;"
|
||||
"DefaultConnection": "Server=192.168.5.128;Database=Elecciones2025;User Id=apielecciones2025;Password=PTP847Elecciones2025;Encrypt=False;MultipleActiveResultSets=True;TrustServerCertificate=True;"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=TECNICA3;Database=Elecciones2025;User Id=apielecciones2025;Password=PTP847Elecciones2025;Encrypt=False;MultipleActiveResultSets=True;TrustServerCertificate=True;"
|
||||
"DefaultConnection": "Server=192.168.5.128;Database=Elecciones2025;User Id=apielecciones2025;Password=PTP847Elecciones2025;Encrypt=False;MultipleActiveResultSets=True;TrustServerCertificate=True;"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
@@ -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+6ac60342554e3ad2bc0d4f6dd3a7e62c81cd79a9")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9393d2bc05f1fda0ad9e78d988aa3fc088cfc2d7")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","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=","quzFFdCjq28RUvzP4QEPqUv1aG3JErbxco/IZLvopsc=","6CAjHexjcmVc1caYyfNvMfhJRU6qtmi57Siv1ysirg0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","IXt64lxLeDS4dW1rszsenQT32xUD\u002BLsm/9zmNzXoV/c="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["h1yBBcAgq4jIQ1vINVvluRQMeuJlGA3/Zciq/j5c0AM=","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=","KaNgYB2ifCIE3p/Tay5fVAWfGAbZ/FRwD44afnqRoKI=","6CAjHexjcmVc1caYyfNvMfhJRU6qtmi57Siv1ysirg0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","iL0B6pwfSYZpOYzq7AuHcEbBAAVseMon4HovdUC\u002BTcU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","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=","quzFFdCjq28RUvzP4QEPqUv1aG3JErbxco/IZLvopsc=","6CAjHexjcmVc1caYyfNvMfhJRU6qtmi57Siv1ysirg0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","IXt64lxLeDS4dW1rszsenQT32xUD\u002BLsm/9zmNzXoV/c="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["h1yBBcAgq4jIQ1vINVvluRQMeuJlGA3/Zciq/j5c0AM=","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=","KaNgYB2ifCIE3p/Tay5fVAWfGAbZ/FRwD44afnqRoKI=","6CAjHexjcmVc1caYyfNvMfhJRU6qtmi57Siv1ysirg0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","iL0B6pwfSYZpOYzq7AuHcEbBAAVseMon4HovdUC\u002BTcU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"O7YawHw32G/Fh2bs+snZgm9O7okI0WYgTQmXM931znY=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["BNGxWTPjjFD1Fj56FltRDUvsBzgMlQvuqV\u002BraH2IhwQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"O7YawHw32G/Fh2bs+snZgm9O7okI0WYgTQmXM931znY=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["h1yBBcAgq4jIQ1vINVvluRQMeuJlGA3/Zciq/j5c0AM=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -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+6ac60342554e3ad2bc0d4f6dd3a7e62c81cd79a9")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9393d2bc05f1fda0ad9e78d988aa3fc088cfc2d7")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+6ac60342554e3ad2bc0d4f6dd3a7e62c81cd79a9")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9393d2bc05f1fda0ad9e78d988aa3fc088cfc2d7")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -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+6ac60342554e3ad2bc0d4f6dd3a7e62c81cd79a9")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9393d2bc05f1fda0ad9e78d988aa3fc088cfc2d7")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
Reference in New Issue
Block a user