Feat Widgets Controles y Estilos

This commit is contained in:
2025-10-03 13:26:20 -03:00
parent 1719e79723
commit 64d45a7a39
17 changed files with 544 additions and 278 deletions

View File

@@ -289,6 +289,11 @@ export const getResumenPorProvincia = async (eleccionId: number, params: Resumen
return data; return data;
}; };
export const getMunicipiosPorDistrito = async (distritoId: string): Promise<CatalogoItem[]> => {
const response = await apiClient.get(`/catalogos/municipios-por-distrito/${distritoId}`);
return response.data;
};
export const getHomeResumen = async (eleccionId: number, distritoId: string, categoriaId: number): Promise<CategoriaResumenHome> => { export const getHomeResumen = async (eleccionId: number, distritoId: string, categoriaId: number): Promise<CategoriaResumenHome> => {
const queryParams = new URLSearchParams({ const queryParams = new URLSearchParams({
eleccionId: eleccionId.toString(), eleccionId: eleccionId.toString(),

View File

@@ -144,7 +144,6 @@
font-size: 1.2rem; font-size: 1.2rem;
} }
.panel-main-content { .panel-main-content {
display: flex; display: flex;
height: 75vh; height: 75vh;
@@ -180,15 +179,16 @@
.partido-logo { .partido-logo {
flex-shrink: 0; flex-shrink: 0;
width: 65px; /* ANTES: 75px */ width: 65px;
height: 65px; /* ANTES: 75px */ height: 65px;
border-radius: 12px;
box-sizing: border-box;
} }
.partido-logo img { .partido-logo img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: contain; border-radius: 12px;
border-radius: 10%;
} }
.partido-main-content { .partido-main-content {
@@ -279,7 +279,6 @@
display: block; display: block;
} }
/* --- MAPA Y ELEMENTOS ASOCIADOS --- */ /* --- MAPA Y ELEMENTOS ASOCIADOS --- */
.mapa-componente-container { .mapa-componente-container {
width: 100%; width: 100%;
@@ -313,7 +312,7 @@
transition: transform 0.75s ease-in-out; transition: transform 0.75s ease-in-out;
} }
/* AÑADIDO: Desactivar la transición durante el arrastre */ /* Desactivar la transición durante el arrastre */
.rsm-zoomable-group.panning { .rsm-zoomable-group.panning {
transition: none; transition: none;
} }
@@ -475,52 +474,12 @@
margin-top: 4px; margin-top: 4px;
} }
/* --- NUEVOS ESTILOS PARA EL TOGGLE MÓVIL --- */
.mobile-view-toggle {
display: none;
position: absolute;
/* <-- CAMBIO: De 'fixed' a 'absolute' */
bottom: 10px;
/* <-- AJUSTE: Menos espacio desde abajo */
left: 50%;
transform: translateX(-50%);
z-index: 100;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 30px;
padding: 5px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
gap: 5px;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
.mobile-view-toggle .toggle-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
border: none;
background-color: transparent;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
color: #555;
transition: all 0.2s ease-in-out;
}
.mobile-view-toggle .toggle-btn.active {
background-color: #007bff;
color: white;
}
/* --- ESTILOS PARA LOS BOTONES DE ZOOM DEL MAPA --- */ /* --- ESTILOS PARA LOS BOTONES DE ZOOM DEL MAPA --- */
.zoom-controls-container { .zoom-controls-container {
position: absolute; position: absolute;
top: 5px; top: 5px;
right: 10px; right: 10px;
z-index: 30; z-index: 30;
/* Debe ser MAYOR que el z-index del header (20) */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 5px; gap: 5px;
@@ -542,15 +501,12 @@
} }
.zoom-icon-wrapper { .zoom-icon-wrapper {
/* Contenedor del icono */
display: flex; display: flex;
/* Necesario para que el SVG interno se alinee */
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.zoom-icon-wrapper svg { .zoom-icon-wrapper svg {
/* Apunta directamente al SVG del icono */
width: 20px; width: 20px;
height: 20px; height: 20px;
color: #333; color: #333;
@@ -558,9 +514,7 @@
.zoom-btn.disabled { .zoom-btn.disabled {
opacity: 0.5; opacity: 0.5;
/* Lo hace semitransparente */
cursor: not-allowed; cursor: not-allowed;
/* Muestra el cursor de "no permitido" */
} }
.zoom-btn:hover { .zoom-btn:hover {
@@ -576,22 +530,41 @@
cursor: grab; cursor: grab;
} }
/* El cursor 'grabbing' se aplica automáticamente por el navegador durante el arrastre */ .header-bottom-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1rem;
gap: 1rem;
}
.municipio-search-container {
min-width: 280px; /* Ancho mínimo para el buscador en desktop */
}
/* --- MEDIA QUERY PARA RESPONSIVE (ENFOQUE FINAL CON CAPAS) --- */ /* --- MEDIA QUERY PARA RESPONSIVE (REFACTORIZADA) --- */
@media (max-width: 800px) { @media (max-width: 800px) {
/* --- CONFIGURACIÓN GENERAL --- */ .panel-nacional-container {
html, display: flex;
body { flex-direction: column;
width: 100%; height: 100vh;
overflow-x: hidden; padding: 0;
border: none;
border-radius: 0;
} }
/* Controles de vista y header (sin cambios) */ .panel-header {
.mobile-view-toggle { flex-shrink: 0;
display: flex; padding: 1rem;
border-radius: 0;
}
.panel-main-content {
flex-grow: 1;
position: relative;
height: auto;
min-height: 0;
} }
.panel-toggle-btn { .panel-toggle-btn {
@@ -608,21 +581,10 @@
width: 100%; width: 100%;
} }
/* --- NUEVO LAYOUT DE CAPAS SUPERPUESTAS --- */
/* 1. El contenedor principal ahora es un ancla de posicionamiento */
.panel-main-content {
position: relative;
/* Clave para que los hijos se posicionen dentro de él */
height: calc(100vh - 200px);
/* Le damos una altura fija y predecible */
min-height: 450px;
}
/* 2. Ambas columnas son capas que ocupan el 100% del espacio del padre */
.mapa-column, .mapa-column,
.resultados-column { .resultados-column {
position: absolute; position: absolute;
top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -630,30 +592,20 @@
transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
} }
/* Le damos un estilo específico a la columna del mapa para subirla */
.mapa-column { .mapa-column {
top: -50px;
left: -10px;
z-index: 10; z-index: 10;
} }
/* Hacemos que la columna de resultados pueda tener su propio scroll... */
.resultados-column { .resultados-column {
top: 0;
/* Aseguramos que los resultados se queden en su sitio */
padding: 1rem; padding: 1rem;
overflow-y: auto; overflow-y: auto;
z-index: 15; z-index: 15;
} }
/* 3. Lógica de visibilidad: controlamos qué capa está "arriba" */
.panel-main-content.mobile-view-mapa .resultados-column { .panel-main-content.mobile-view-mapa .resultados-column {
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
/* Esta es la propiedad clave que ya tenías, pero es importante verificarla */
pointer-events: none; pointer-events: none;
/* Asegura que la capa oculta no bloquee el mapa */
} }
.panel-main-content.mobile-view-resultados .mapa-column { .panel-main-content.mobile-view-resultados .mapa-column {
@@ -662,85 +614,35 @@
pointer-events: none; pointer-events: none;
} }
/* Hacemos que la columna de resultados pueda tener su propio scroll si el contenido es largo */
.resultados-column { .resultados-column {
padding: 1rem; padding: 1rem;
overflow-y: auto; overflow-y: auto;
} }
/* 4. Estilos de los resultados (ya estaban bien, se mantienen) */
.partido-fila {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 0;
border-bottom: 1px solid #f0f0f0;
border-left: 5px solid;
/* Grosor del borde */
border-radius: 12px;
/* Redondeamos las esquinas */
padding-left: 1rem;
/* Espacio a la izquierda */
}
.partido-logo {
width: 60px;
height: 60px;
flex-shrink: 0;
}
.partido-main-content {
flex-grow: 1;
min-width: 0;
}
.partido-top-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.partido-info-wrapper {
min-width: 0;
}
.partido-nombre {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.partido-stats {
text-align: right;
flex-shrink: 0;
padding-left: 0.5rem;
}
/* --- AJUSTE DE TAMAÑO DEL CONTENEDOR INTERNO DEL MAPA --- */
.mapa-column .mapa-componente-container, .mapa-column .mapa-componente-container,
.mapa-column .mapa-render-area { .mapa-column .mapa-render-area {
height: 100%; height: 100%;
} }
/* Margen de seguridad para el último elemento de la lista de resultados */
.panel-partidos-container .partido-fila:last-child { .panel-partidos-container .partido-fila:last-child {
margin-bottom: 90px; margin-bottom: 90px;
} }
.zoom-controls-container { .zoom-controls-container, .mapa-volver-btn {
top: 55px; top: 15px;
} }
.mapa-volver-btn { .header-bottom-row {
top: 55px; flex-direction: column;
left: 12px; align-items: stretch; /* Para que ambos elementos ocupen el ancho completo */
gap: 1rem;
}
.municipio-search-container {
min-width: 100%; /* El buscador ocupa todo el ancho en móvil */
} }
/* --- MEDIA QUERY ADICIONAL PARA MÓVIL EN HORIZONTAL --- */
/* Se activa cuando la pantalla es ancha pero no muy alta, como un teléfono en landscape */
@media (max-width: 900px) and (orientation: landscape) { @media (max-width: 900px) and (orientation: landscape) {
/* Layout flexible de dos columnas */
.panel-main-content { .panel-main-content {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -748,11 +650,8 @@
height: 85vh; height: 85vh;
min-height: 400px; min-height: 400px;
} }
.mapa-column, .resultados-column {
.mapa-column,
.resultados-column {
position: static; position: static;
/* Desactivamos el posicionamiento absoluto */
height: auto; height: auto;
width: auto; width: auto;
opacity: 1; opacity: 1;
@@ -760,23 +659,177 @@
pointer-events: auto; pointer-events: auto;
flex: 3; flex: 3;
overflow-y: auto; overflow-y: auto;
/* Permitimos que la columna de resultados tenga su propio scroll */
} }
.resultados-column { .resultados-column {
flex: 2; flex: 2;
min-width: 300px; min-width: 300px;
/* Un mínimo para que no se comprima */
} }
.mobile-results-card-container { display: none; }
.panel-toggle-btn { display: flex; }
}
}
/* 3. Ocultamos los botones de cambio de vista móvil, ya que ambas se ven */ /* --- ESTILOS PARA LA TARJETA DE RESULTADOS EN MÓVIL (ACTUALIZADOS) --- */
.mobile-view-toggle { .mobile-results-card-container {
display: none; position: absolute;
} bottom: 10px;
left: 50%;
transform: translateX(-50%);
z-index: 40;
width: 95%;
max-width: 450px;
background-color: rgba(255, 255, 255, 0.95);
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(0, 0, 0, 0.1);
transition: all 0.3s ease-in-out;
display: flex;
flex-direction: column;
}
/* 4. Mostramos de nuevo el botón lateral para colapsar el panel de resultados */ .mobile-results-card-container.view-resultados .collapsible-section {
.panel-toggle-btn { display: none;
display: flex; }
.mobile-results-card-container.view-resultados .mobile-card-view-toggle {
border-top: none;
}
.collapsible-section {
display: flex;
flex-direction: column;
}
.mobile-results-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 18px; /* REDUCIDO */
cursor: pointer;
}
.mobile-results-header .header-info {
display: flex; /* AÑADIDO */
align-items: baseline; /* AÑADIDO */
gap: 12px; /* AÑADIDO */
}
.mobile-results-header .header-info h4 {
margin: 0;
font-size: 1.2rem;
font-weight: 700;
}
/* SELECTOR ESPECÍFICO PARA EL TEXTO DE ACCIÓN */
.mobile-results-header .header-info .header-action-text {
font-size: 0.8rem;
color: #6c757d;
font-weight: 500;
text-transform: uppercase;
}
.mobile-results-header .header-toggle-icon {
font-size: 1.5rem;
color: #007bff;
transition: transform 0.3s;
}
.mobile-results-content {
max-height: 0;
opacity: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out, padding 0.3s ease-in-out;
padding: 0 15px;
border-top: 1px solid transparent;
}
.mobile-results-card-container.expanded .mobile-results-content {
max-height: 500px;
opacity: 1;
padding: 5px 15px 15px 15px;
border-top-color: #e0e0e0;
}
.mobile-result-row {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
border-left: 4px solid;
padding-left: 8px;
}
.mobile-result-row:last-child { border-bottom: none; }
.mobile-result-logo {
flex-shrink: 0;
width: 40px;
height: 40px;
border-radius: 8px;
box-sizing: border-box;
} }
.mobile-result-logo img {
width: 100%;
height: 100%;
border-radius: 8px;
}
.mobile-result-info { flex-grow: 1; min-width: 0; }
.mobile-result-party-name { display: block; font-weight: 600; font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mobile-result-candidate-name { display: block; font-size: 0.75rem; color: #6c757d; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mobile-result-stats { display: flex; flex-direction: column; align-items: flex-end; flex-shrink: 0; }
.mobile-result-stats strong { font-size: 0.95rem; font-weight: 700; }
.mobile-result-stats span { font-size: 0.7rem; color: #6c757d; }
.no-results-text { padding: 1rem; text-align: center; color: #6c757d; font-size: 0.9rem; }
.mobile-card-view-toggle {
display: flex;
padding: 5px;
background-color: rgba(230, 230, 230, 0.6);
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.mobile-card-view-toggle .toggle-btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 15px; /* Aumentado para pantallas más grandes */
border: none;
background-color: transparent;
border-radius: 25px;
cursor: pointer;
font-size: 1rem; /* Mantenido para pantallas más grandes */
font-weight: 500;
color: #555;
transition: all 0.2s ease-in-out;
}
.mobile-card-view-toggle .toggle-btn.active {
background-color: #007bff;
color: white;
box-shadow: 0 2px 5px rgba(0, 123, 255, 0.2);
}
/* Ajustes para pantallas pequeñas como el iPhone SE */
@media (max-width: 380px) {
.mobile-results-header {
padding: 4px 10px;
}
.mobile-results-header .header-info h4 {
font-size: 0.75rem;
text-transform: uppercase; /* Se achica el título */
}
.mobile-results-header .header-info .header-action-text {
font-size: 0.7rem; /* Se achica el texto de acción */
}
.mobile-card-view-toggle .toggle-btn {
padding: 6px 10px; /* Se reduce el padding de los botones */
font-size: 0.8rem; /* Se achica la fuente de los botones */
} }
} }

View File

@@ -1,17 +1,130 @@
// src/features/legislativas/nacionales/PanelNacionalWidget.tsx import { useMemo, useState, Suspense, useEffect } from 'react';
import { useMemo, useState, Suspense } from 'react';
import { useSuspenseQuery } from '@tanstack/react-query'; import { useSuspenseQuery } from '@tanstack/react-query';
import { getPanelElectoral } from '../../../apiService'; import { getPanelElectoral } from '../../../apiService';
import { MapaNacional } from './components/MapaNacional'; import { MapaNacional } from './components/MapaNacional';
import { PanelResultados } from './components/PanelResultados'; import { PanelResultados } from './components/PanelResultados';
import { Breadcrumbs } from './components/Breadcrumbs'; import { Breadcrumbs } from './components/Breadcrumbs';
import { MunicipioSearch } from './components/MunicipioSearch';
import './PanelNacional.css'; import './PanelNacional.css';
import Select from 'react-select'; import Select from 'react-select';
import type { PanelElectoralDto } from '../../../types/types'; import type { PanelElectoralDto, ResultadoTicker } from '../../../types/types';
import { FiMap, FiList } from 'react-icons/fi'; import { FiMap, FiList, FiChevronDown, FiChevronUp } from 'react-icons/fi';
import { useMediaQuery } from './hooks/useMediaQuery'; import { useMediaQuery } from './hooks/useMediaQuery';
import { Toaster } from 'react-hot-toast'; import { Toaster } from 'react-hot-toast';
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
import { assetBaseUrl } from '../../../apiService';
// --- COMPONENTE INTERNO PARA LA TARJETA DE RESULTADOS EN MÓVIL ---
interface MobileResultsCardProps {
eleccionId: number;
ambitoId: string | null;
categoriaId: number;
ambitoNombre: string;
ambitoNivel: 'pais' | 'provincia' | 'municipio';
}
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
// --- SUB-COMPONENTE PARA UNA FILA DE RESULTADO ---
const ResultRow = ({ partido }: { partido: ResultadoTicker }) => (
<div className="mobile-result-row" style={{ borderLeftColor: partido.color || '#ccc' }}>
<div className="mobile-result-logo" style={{ backgroundColor: partido.color || '#e9ecef' }}>
<ImageWithFallback src={partido.logoUrl || undefined} fallbackSrc={`${assetBaseUrl}/default-avatar.png`} alt={partido.nombre} />
</div>
<div className="mobile-result-info">
{partido.nombreCandidato ? (
<>
<span className="mobile-result-party-name">{partido.nombreCandidato}</span>
<span className="mobile-result-candidate-name">{partido.nombreCorto || partido.nombre}</span>
</>
) : (
<span className="mobile-result-party-name">{partido.nombreCorto || partido.nombre}</span>
)}
</div>
<div className="mobile-result-stats">
<strong>{formatPercent(partido.porcentaje)}</strong>
<span>{partido.votos.toLocaleString('es-AR')}</span>
</div>
</div>
);
// --- COMPONENTE REFACTORIZADO PARA LA TARJETA MÓVIL ---
interface MobileResultsCardProps {
eleccionId: number;
ambitoId: string | null;
categoriaId: number;
ambitoNombre: string;
ambitoNivel: 'pais' | 'provincia' | 'municipio';
mobileView: 'mapa' | 'resultados';
setMobileView: (view: 'mapa' | 'resultados') => void;
}
const MobileResultsCard = ({
eleccionId, ambitoId, categoriaId, ambitoNombre, ambitoNivel, mobileView, setMobileView
}: MobileResultsCardProps) => {
const [isExpanded, setIsExpanded] = useState(false);
const { data } = useSuspenseQuery<PanelElectoralDto>({
queryKey: ['panelElectoral', eleccionId, ambitoId, categoriaId],
queryFn: () => getPanelElectoral(eleccionId, ambitoId, categoriaId),
});
useEffect(() => {
setIsExpanded(ambitoNivel === 'municipio');
}, [ambitoNivel]);
const topResults = data.resultadosPanel.slice(0, 4);
if (topResults.length === 0 && ambitoNivel === 'pais') {
return null;
}
return (
<div className={`mobile-results-card-container ${isExpanded ? 'expanded' : ''} view-${mobileView}`}>
{/* Sección Colapsable con Resultados */}
<div className="collapsible-section">
<div className="mobile-results-header" onClick={() => setIsExpanded(!isExpanded)}>
<div className="header-info">
<h4>{ambitoNombre}</h4>
{/* Se añade una clase para estilizar este texto específicamente */}
<span className="header-action-text">{isExpanded ? 'Ocultar resultados' : 'Ver top 4'}</span>
</div>
<div className="header-toggle-icon">
{isExpanded ? <FiChevronUp /> : <FiChevronDown />}
</div>
</div>
<div className="mobile-results-content">
{topResults.length > 0 ? (
topResults.map(partido => <ResultRow key={partido.id} partido={partido} />)
) : (
<p className="no-results-text">No hay resultados para esta selección.</p>
)}
</div>
</div>
{/* Footer Fijo con Botones de Navegación */}
<div className="mobile-card-view-toggle">
<button
className={`toggle-btn ${mobileView === 'mapa' ? 'active' : ''}`}
onClick={() => setMobileView('mapa')}
>
<FiMap />
<span>Mapa</span>
</button>
<button
className={`toggle-btn ${mobileView === 'resultados' ? 'active' : ''}`}
onClick={() => setMobileView('resultados')}
>
<FiList />
<span>Resultados</span>
</button>
</div>
</div>
);
};
// --- WIDGET PRINCIPAL ---
interface PanelNacionalWidgetProps { interface PanelNacionalWidgetProps {
eleccionId: number; eleccionId: number;
} }
@@ -42,7 +155,6 @@ export const PanelNacionalWidget = ({ eleccionId }: PanelNacionalWidgetProps) =>
const [categoriaId, setCategoriaId] = useState<number>(2); const [categoriaId, setCategoriaId] = useState<number>(2);
const [isPanelOpen, setIsPanelOpen] = useState(true); const [isPanelOpen, setIsPanelOpen] = useState(true);
const [mobileView, setMobileView] = useState<'mapa' | 'resultados'>('mapa'); const [mobileView, setMobileView] = useState<'mapa' | 'resultados'>('mapa');
// --- DETECCIÓN DE VISTA MÓVIL ---
const isMobile = useMediaQuery('(max-width: 800px)'); const isMobile = useMediaQuery('(max-width: 800px)');
const handleAmbitoSelect = (nuevoAmbitoId: string, nuevoNivel: 'provincia' | 'municipio', nuevoNombre: string) => { const handleAmbitoSelect = (nuevoAmbitoId: string, nuevoNivel: 'provincia' | 'municipio', nuevoNombre: string) => {
@@ -91,62 +203,55 @@ export const PanelNacionalWidget = ({ eleccionId }: PanelNacionalWidgetProps) =>
classNamePrefix="categoria-selector" classNamePrefix="categoria-selector"
isSearchable={false} isSearchable={false}
/> />
<Breadcrumbs </div>
nivel={ambitoActual.nivel} {/* --- 2. NUEVO CONTENEDOR PARA BREADCRUMBS Y BUSCADOR --- */}
nombreAmbito={ambitoActual.nombre} <div className="header-bottom-row">
nombreProvincia={ambitoActual.provinciaNombre} <Breadcrumbs
onReset={handleResetToPais} nivel={ambitoActual.nivel}
onVolverProvincia={handleVolverAProvincia} nombreAmbito={ambitoActual.nombre}
/> nombreProvincia={ambitoActual.provinciaNombre}
onReset={handleResetToPais}
onVolverProvincia={handleVolverAProvincia}
/>
{/* --- 3. RENDERIZADO CONDICIONAL DEL BUSCADOR --- */}
{ambitoActual.nivel === 'provincia' && ambitoActual.provinciaDistritoId && (
<MunicipioSearch
distritoId={ambitoActual.provinciaDistritoId}
onMunicipioSelect={(municipioId, municipioNombre) =>
handleAmbitoSelect(municipioId, 'municipio', municipioNombre)
}
/>
)}
</div> </div>
</header> </header>
<main className={`panel-main-content ${!isPanelOpen ? 'panel-collapsed' : ''} ${isMobile ? `mobile-view-${mobileView}` : ''}`}> <main className={`panel-main-content ${!isPanelOpen ? 'panel-collapsed' : ''} ${isMobile ? `mobile-view-${mobileView}` : ''}`}>
<div className="mapa-column"> <div className="mapa-column">
<button className="panel-toggle-btn" onClick={() => setIsPanelOpen(!isPanelOpen)} title={isPanelOpen ? "Ocultar panel" : "Mostrar panel"}> <button className="panel-toggle-btn" onClick={() => setIsPanelOpen(!isPanelOpen)} title={isPanelOpen ? "Ocultar panel" : "Mostrar panel"}> {isPanelOpen ? '' : ''} </button>
{isPanelOpen ? '' : ''}
</button>
<Suspense fallback={<div className="spinner" />}> <Suspense fallback={<div className="spinner" />}>
<MapaNacional <MapaNacional eleccionId={eleccionId} categoriaId={categoriaId} nivel={ambitoActual.nivel} nombreAmbito={ambitoActual.nombre} nombreProvinciaActiva={ambitoActual.provinciaNombre} provinciaDistritoId={ambitoActual.provinciaDistritoId ?? null} onAmbitoSelect={handleAmbitoSelect} onVolver={ambitoActual.nivel === 'municipio' ? handleVolverAProvincia : handleResetToPais} isMobileView={isMobile} />
eleccionId={eleccionId}
categoriaId={categoriaId}
nivel={ambitoActual.nivel}
nombreAmbito={ambitoActual.nombre}
nombreProvinciaActiva={ambitoActual.provinciaNombre}
provinciaDistritoId={ambitoActual.provinciaDistritoId ?? null}
onAmbitoSelect={handleAmbitoSelect}
onVolver={ambitoActual.nivel === 'municipio' ? handleVolverAProvincia : handleResetToPais}
isMobileView={isMobile}
/>
</Suspense> </Suspense>
</div> </div>
<div className="resultados-column"> <div className="resultados-column">
<Suspense fallback={<div className="spinner" />}> <Suspense fallback={<div className="spinner" />}>
<PanelContenido <PanelContenido eleccionId={eleccionId} ambitoActual={ambitoActual} categoriaId={categoriaId} />
eleccionId={eleccionId}
ambitoActual={ambitoActual}
categoriaId={categoriaId}
/>
</Suspense> </Suspense>
</div> </div>
</main>
{/* --- NUEVO CONTROLADOR DE VISTA PARA MÓVIL --- */} <Suspense fallback={null}>
<div className="mobile-view-toggle"> {isMobile && (
<button <MobileResultsCard
className={`toggle-btn ${mobileView === 'mapa' ? 'active' : ''}`} eleccionId={eleccionId}
onClick={() => setMobileView('mapa')} ambitoId={ambitoActual.id}
> categoriaId={categoriaId}
<FiMap /> ambitoNombre={ambitoActual.nombre}
<span>Mapa</span> ambitoNivel={ambitoActual.nivel}
</button> mobileView={mobileView}
<button setMobileView={setMobileView}
className={`toggle-btn ${mobileView === 'resultados' ? 'active' : ''}`} />
onClick={() => setMobileView('resultados')} )}
> </Suspense>
<FiList /> </main>
<span>Resultados</span>
</button>
</div>
</div> </div>
); );
}; };

View File

@@ -106,6 +106,10 @@
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
} }
.candidato-row:last-child {
border-bottom: none;
}
.candidato-row { .candidato-row {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -114,19 +118,24 @@
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
border-left: 5px solid; /* Grosor del borde */ border-left: 5px solid; /* Grosor del borde */
border-radius: 12px; /* Redondeamos las esquinas para un look más suave */ border-radius: 12px; /* Redondeamos las esquinas para un look más suave */
padding-left: 1rem; /* Añadimos un poco de espacio a la izquierda */ padding-left: 0.75rem; /* Añadimos un poco de espacio a la izquierda */
} }
.candidato-row:last-child { /* Nuevo contenedor para el logo con fondo de color */
border-bottom: none; .candidato-foto-wrapper {
}
.candidato-foto {
width: 60px; width: 60px;
height: 60px; height: 60px;
border-radius: 5%; border-radius: 12px;
object-fit: cover;
flex-shrink: 0; flex-shrink: 0;
box-sizing: border-box;
background-color: #e9ecef; /* Color de fallback */
}
/* La imagen ahora llena su nuevo contenedor */
.candidato-foto {
width: 100%;
height: 100%;
border-radius: 12px;
} }
.candidato-data { .candidato-data {

View File

@@ -1,4 +1,3 @@
// src/features/legislativas/nacionales/components/MapaNacional.tsx
import axios from 'axios'; import axios from 'axios';
import { Suspense, useState, useEffect, useCallback, useRef } from 'react'; import { Suspense, useState, useEffect, useCallback, useRef } from 'react';
import { useSuspenseQuery } from '@tanstack/react-query'; import { useSuspenseQuery } from '@tanstack/react-query';
@@ -12,6 +11,7 @@ import { MapaProvincial } from './MapaProvincial';
import { CabaLupa } from './CabaLupa'; import { CabaLupa } from './CabaLupa';
import { BiZoomIn, BiZoomOut } from "react-icons/bi"; import { BiZoomIn, BiZoomOut } from "react-icons/bi";
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { useMediaQuery } from '../hooks/useMediaQuery';
const DEFAULT_MAP_COLOR = '#E0E0E0'; const DEFAULT_MAP_COLOR = '#E0E0E0';
const FADED_BACKGROUND_COLOR = '#F0F0F0'; const FADED_BACKGROUND_COLOR = '#F0F0F0';
@@ -19,15 +19,21 @@ const normalizarTexto = (texto: string = '') => texto.trim().toUpperCase().norma
type PointTuple = [number, number]; type PointTuple = [number, number];
const PROVINCE_VIEW_CONFIG: Record<string, { center: PointTuple; zoom: number }> = { interface ViewConfig {
"BUENOS AIRES": { center: [-60.5, -37.3], zoom: 5 }, center: PointTuple;
"SANTA CRUZ": { center: [-69.5, -49.3], zoom: 5 }, zoom: number;
"CIUDAD AUTONOMA DE BUENOS AIRES": { center: [-58.45, -34.6], zoom: 85 }, }
"CHUBUT": { center: [-68.5, -44.5], zoom: 5.5 },
"SANTA FE": { center: [-61, -31.2], zoom: 6 }, const PROVINCE_VIEW_CONFIG: Record<string, { desktop: ViewConfig; mobile?: ViewConfig }> = {
"CORRIENTES": { center: [-58, -29], zoom: 7 }, "BUENOS AIRES": { desktop: { center: [-60.5, -37.3], zoom: 5 }, mobile: { center: [-60, -38], zoom: 5.5 } },
"RIO NEGRO": { center: [-67.5, -40], zoom: 5.5 }, "SANTA CRUZ": { desktop: { center: [-69.5, -49.3], zoom: 5 }, mobile: { center: [-69.5, -50], zoom: 4 } },
"TIERRA DEL FUEGO": { center: [-66.5, -54.2], zoom: 7 }, "CIUDAD AUTONOMA DE BUENOS AIRES": { desktop: { center: [-58.44, -34.65], zoom: 150 } },
"CHUBUT": { desktop: { center: [-68.5, -44.5], zoom: 5.5 }, mobile: { center: [-68, -44.5], zoom: 4.5 } },
"SANTA FE": { desktop: { center: [-61, -31.2], zoom: 6 }, mobile: { center: [-61, -31.5], zoom: 7.5 } },
"CORRIENTES": { desktop: { center: [-58, -29], zoom: 7 }, mobile: { center: [-57.5, -28.8], zoom: 9 } },
"RIO NEGRO": { desktop: { center: [-67.5, -40], zoom: 5.5 }, mobile: { center: [-67.5, -40], zoom: 4.3 } },
"SALTA": { desktop: { center: [-64.5, -24], zoom: 7 }, mobile: { center: [-65.5, -24.5], zoom: 6 } },
"TIERRA DEL FUEGO": { desktop: { center: [-66.5, -54.2], zoom: 7 }, mobile: { center: [-66, -54], zoom: 7.5 } },
}; };
const LUPA_SIZE_RATIO = 0.2; const LUPA_SIZE_RATIO = 0.2;
@@ -48,15 +54,20 @@ interface MapaNacionalProps {
// --- CONFIGURACIONES DEL MAPA --- // --- CONFIGURACIONES DEL MAPA ---
const desktopProjectionConfig = { scale: 700, center: [-65, -40] as [number, number] }; const desktopProjectionConfig = { scale: 700, center: [-65, -40] as [number, number] };
const mobileProjectionConfig = { scale: 1100, center: [-64, -41] as [number, number] }; const mobileProjectionConfig = { scale: 1100, center: [-64, -42.5] as [number, number] };
// --- LÍNEA A CALIBRAR ---
const mobileSmallProjectionConfig = { scale: 900, center: [-64, -43] as [number, number] };
export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nombreProvinciaActiva, provinciaDistritoId, onAmbitoSelect, onVolver, isMobileView }: MapaNacionalProps) => { export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nombreProvinciaActiva, provinciaDistritoId, onAmbitoSelect, onVolver, isMobileView }: MapaNacionalProps) => {
const isMobileSmall = useMediaQuery('(max-width: 380px)');
const [position, setPosition] = useState({ const [position, setPosition] = useState({
zoom: isMobileView ? 1.5 : 1.05, // 1.5 para móvil, 1.05 para desktop zoom: isMobileView ? 1.5 : 1.05,
center: [-65, -40] as PointTuple center: isMobileView ? mobileProjectionConfig.center : desktopProjectionConfig.center as PointTuple
}); });
const [isPanning, setIsPanning] = useState(false); const [isPanning, setIsPanning] = useState(false);
const initialProvincePositionRef = useRef<{ zoom: number, center: PointTuple } | null>(null); const initialProvincePositionRef = useRef<ViewConfig | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const lupaRef = useRef<HTMLDivElement | null>(null); const lupaRef = useRef<HTMLDivElement | null>(null);
@@ -82,32 +93,38 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
useEffect(() => { useEffect(() => {
if (nivel === 'pais') { if (nivel === 'pais') {
const currentMobileConfig = isMobileSmall ? mobileSmallProjectionConfig : mobileProjectionConfig;
const currentMobileZoom = isMobileSmall ? 1.4 : 1.5;
setPosition({ setPosition({
zoom: isMobileView ? 1.4 : 1.05, zoom: isMobileView ? currentMobileZoom : 1.05,
center: [-65, -40] center: isMobileView ? currentMobileConfig.center : desktopProjectionConfig.center
}); });
initialProvincePositionRef.current = null; initialProvincePositionRef.current = null;
} else if (nivel === 'provincia') { } else if (nivel === 'provincia') {
const nombreNormalizado = normalizarTexto(nombreAmbito); const nombreNormalizado = normalizarTexto(nombreAmbito);
const manualConfig = PROVINCE_VIEW_CONFIG[nombreNormalizado]; const manualConfig = PROVINCE_VIEW_CONFIG[nombreNormalizado];
let provinceConfig = { zoom: 7, center: [-65, -40] as PointTuple }; let provinceConfig: ViewConfig | undefined;
if (manualConfig) { if (manualConfig) {
provinceConfig = manualConfig; provinceConfig = (isMobileView && manualConfig.mobile) ? manualConfig.mobile : manualConfig.desktop;
} else { } else {
const provinciaGeo = geoDataNacional.objects.provincias.geometries.find((g: any) => normalizarTexto(g.properties.nombre) === nombreNormalizado); const provinciaGeo = geoDataNacional.objects.provincias.geometries.find((g: any) => normalizarTexto(g.properties.nombre) === nombreNormalizado);
if (provinciaGeo) { if (provinciaGeo) {
const provinciaFeature = feature(geoDataNacional, provinciaGeo); const provinciaFeature = feature(geoDataNacional, provinciaGeo);
const centroid = geoCentroid(provinciaFeature); const centroid = geoCentroid(provinciaFeature);
provinceConfig = { zoom: 7, center: centroid as PointTuple }; provinceConfig = { zoom: isMobileView ? 8 : 7, center: centroid as PointTuple };
} }
} }
setPosition(provinceConfig); if (provinceConfig) {
initialProvincePositionRef.current = provinceConfig; setPosition(provinceConfig);
initialProvincePositionRef.current = provinceConfig;
}
} }
}, [nivel, nombreAmbito, geoDataNacional, isMobileView]); }, [nivel, nombreAmbito, geoDataNacional, isMobileView, isMobileSmall]);
const resultadosNacionalesPorNombre = new Map<string, ResultadoMapaDto>(mapaDataNacional.map(d => [normalizarTexto(d.ambitoNombre), d])); const resultadosNacionalesPorNombre = new Map<string, ResultadoMapaDto>(mapaDataNacional.map(d => [normalizarTexto(d.ambitoNombre), d]));
const nombreMunicipioSeleccionado = nivel === 'municipio' ? nombreAmbito : null; const nombreMunicipioSeleccionado = nivel === 'municipio' ? nombreAmbito : null;
@@ -173,14 +190,9 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
position.zoom > initialProvincePositionRef.current.zoom && position.zoom > initialProvincePositionRef.current.zoom &&
!nombreMunicipioSeleccionado; !nombreMunicipioSeleccionado;
// --- INICIO DE LA CORRECCIÓN ---
const handleZoomIn = () => { const handleZoomIn = () => {
// Solo mostramos la notificación si el paneo NO está ya habilitado
if (!panEnabled && initialProvincePositionRef.current) { if (!panEnabled && initialProvincePositionRef.current) {
// Calculamos cuál será el nuevo nivel de zoom
const newZoom = position.zoom * 1.8; const newZoom = position.zoom * 1.8;
// Si el nuevo zoom supera el umbral inicial, activamos la notificación
if (newZoom > initialProvincePositionRef.current.zoom) { if (newZoom > initialProvincePositionRef.current.zoom) {
toast.success('Desplazamiento Habilitado', { toast.success('Desplazamiento Habilitado', {
icon: '🖐️', icon: '🖐️',
@@ -193,10 +205,8 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
}; };
const handleZoomOut = () => { const handleZoomOut = () => {
// Solo mostramos la notificación si el paneo SÍ está habilitado actualmente
if (panEnabled && initialProvincePositionRef.current) { if (panEnabled && initialProvincePositionRef.current) {
const newZoom = position.zoom / 1.8; const newZoom = position.zoom / 1.8;
// Si el nuevo zoom es igual o menor al umbral, desactivamos
if (newZoom <= initialProvincePositionRef.current.zoom) { if (newZoom <= initialProvincePositionRef.current.zoom) {
toast.error('Desplazamiento Deshabilitado', { toast.error('Desplazamiento Deshabilitado', {
icon: '🔒', icon: '🔒',
@@ -205,7 +215,6 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
}); });
} }
} }
// La lógica para actualizar la posición no cambia
setPosition(prev => { setPosition(prev => {
const newZoom = Math.max(prev.zoom / 1.8, 1); const newZoom = Math.max(prev.zoom / 1.8, 1);
const initialPos = initialProvincePositionRef.current; const initialPos = initialProvincePositionRef.current;
@@ -254,7 +263,7 @@ export const MapaNacional = ({ eleccionId, categoriaId, nivel, nombreAmbito, nom
<div className="mapa-render-area"> <div className="mapa-render-area">
<ComposableMap <ComposableMap
projection="geoMercator" projection="geoMercator"
projectionConfig={isMobileView ? mobileProjectionConfig : desktopProjectionConfig} projectionConfig={isMobileSmall ? mobileSmallProjectionConfig : (isMobileView ? mobileProjectionConfig : desktopProjectionConfig)}
style={{ width: "100%", height: "100%" }} style={{ width: "100%", height: "100%" }}
> >
<ZoomableGroup <ZoomableGroup

View File

@@ -0,0 +1,69 @@
// src/features/legislativas/nacionales/components/MunicipioSearch.tsx
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import Select, { type SingleValue } from 'react-select';
import { getMunicipiosPorDistrito } from '../../../../apiService';
import type { CatalogoItem } from '../../../../types/types';
interface MunicipioSearchProps {
distritoId: string;
onMunicipioSelect: (municipioId: string, municipioNombre: string) => void;
}
interface OptionType {
value: string;
label: string;
}
const customSelectStyles = {
control: (base: any) => ({
...base,
borderRadius: '8px',
borderColor: '#e0e0e0',
boxShadow: 'none',
'&:hover': { borderColor: '#007bff' }
}),
menu: (base: any) => ({
...base,
borderRadius: '8px',
zIndex: 30
})
};
export const MunicipioSearch = ({ distritoId, onMunicipioSelect }: MunicipioSearchProps) => {
const [selectedOption, setSelectedOption] = useState<SingleValue<OptionType>>(null);
const { data: municipios = [], isLoading } = useQuery<CatalogoItem[]>({
queryKey: ['municipiosPorDistrito', distritoId],
queryFn: () => getMunicipiosPorDistrito(distritoId),
enabled: !!distritoId,
});
const options: OptionType[] = municipios.map(m => ({
value: m.id,
label: m.nombre
}));
const handleChange = (selected: SingleValue<OptionType>) => {
if (selected) {
onMunicipioSelect(selected.value, selected.label);
setSelectedOption(null); // Resetea el selector para que muestre el placeholder de nuevo
}
};
return (
<div className="municipio-search-container">
<Select
options={options}
onChange={handleChange}
value={selectedOption}
isLoading={isLoading}
placeholder="Buscar municipio..."
isClearable
isSearchable
styles={customSelectStyles}
noOptionsMessage={() => 'No se encontraron municipios'}
/>
</div>
);
};

View File

@@ -12,7 +12,6 @@ const formatVotes = (num: number) => Math.round(num).toLocaleString('es-AR');
const SvgDefs = () => ( const SvgDefs = () => (
<svg style={{ height: 0, width: 0, position: 'absolute' }}> <svg style={{ height: 0, width: 0, position: 'absolute' }}>
<defs> <defs>
{/* El gradiente ahora se define para que el color oscuro se mantenga en la segunda mitad del recorrido vertical */}
<linearGradient id="participationGradient" gradientTransform="rotate(90)"> <linearGradient id="participationGradient" gradientTransform="rotate(90)">
<stop offset="0%" stopColor="#e0f3ffff" /> <stop offset="0%" stopColor="#e0f3ffff" />
<stop offset="100%" stopColor="#007bff" /> <stop offset="100%" stopColor="#007bff" />
@@ -40,13 +39,13 @@ export const PanelResultados = ({ resultados, estadoRecuento }: PanelResultadosP
value={estadoRecuento.participacionPorcentaje} value={estadoRecuento.participacionPorcentaje}
text={formatPercent(estadoRecuento.participacionPorcentaje)} text={formatPercent(estadoRecuento.participacionPorcentaje)}
strokeWidth={12} strokeWidth={12}
circleRatio={0.75} /* Se convierte en un arco de 270 grados */ circleRatio={0.75}
styles={buildStyles({ styles={buildStyles({
textColor: '#333', textColor: '#333',
pathColor: 'url(#participationGradient)', pathColor: 'url(#participationGradient)',
trailColor: '#e9ecef', trailColor: '#e9ecef',
textSize: '22px', textSize: '22px',
rotation: 0.625, /* Rota el inicio para que la apertura quede abajo */ rotation: 0.625,
})} })}
/> />
<span>Participación</span> <span>Participación</span>
@@ -56,13 +55,13 @@ export const PanelResultados = ({ resultados, estadoRecuento }: PanelResultadosP
value={estadoRecuento.mesasTotalizadasPorcentaje} value={estadoRecuento.mesasTotalizadasPorcentaje}
text={formatPercent(estadoRecuento.mesasTotalizadasPorcentaje)} text={formatPercent(estadoRecuento.mesasTotalizadasPorcentaje)}
strokeWidth={12} strokeWidth={12}
circleRatio={0.75} /* Se convierte en un arco de 270 grados */ circleRatio={0.75}
styles={buildStyles({ styles={buildStyles({
textColor: '#333', textColor: '#333',
pathColor: 'url(#scrutinizedGradient)', pathColor: 'url(#scrutinizedGradient)',
trailColor: '#e9ecef', trailColor: '#e9ecef',
textSize: '22px', textSize: '22px',
rotation: 0.625, /* Rota el inicio para que la apertura quede abajo */ rotation: 0.625,
})} })}
/> />
<span>Escrutado</span> <span>Escrutado</span>
@@ -76,7 +75,7 @@ export const PanelResultados = ({ resultados, estadoRecuento }: PanelResultadosP
className="partido-fila" className="partido-fila"
style={{ borderLeftColor: partido.color || '#ccc' }} style={{ borderLeftColor: partido.color || '#ccc' }}
> >
<div className="partido-logo"> <div className="partido-logo" style={{ backgroundColor: partido.color || '#e9ecef' }}>
<ImageWithFallback src={partido.logoUrl || undefined} fallbackSrc={`${assetBaseUrl}/default-avatar.png`} alt={partido.nombre} /> <ImageWithFallback src={partido.logoUrl || undefined} fallbackSrc={`${assetBaseUrl}/default-avatar.png`} alt={partido.nombre} />
</div> </div>
<div className="partido-main-content"> <div className="partido-main-content">

View File

@@ -4,7 +4,6 @@ import { MiniMapaSvg } from './MiniMapaSvg';
import { ImageWithFallback } from '../../../../components/common/ImageWithFallback'; import { ImageWithFallback } from '../../../../components/common/ImageWithFallback';
import { assetBaseUrl } from '../../../../apiService'; import { assetBaseUrl } from '../../../../apiService';
// --- 1. AÑADIR LA PROP A AMBAS INTERFACES ---
interface CategoriaDisplayProps { interface CategoriaDisplayProps {
categoria: CategoriaResumen; categoria: CategoriaResumen;
mostrarBancas?: boolean; mostrarBancas?: boolean;
@@ -18,7 +17,6 @@ interface ProvinciaCardProps {
const formatNumber = (num: number) => num.toLocaleString('es-AR'); const formatNumber = (num: number) => num.toLocaleString('es-AR');
const formatPercent = (num: number) => `${num.toFixed(2).replace('.', ',')}%`; const formatPercent = (num: number) => `${num.toFixed(2).replace('.', ',')}%`;
// --- 2. RECIBIR Y USAR LA PROP EN EL SUB-COMPONENTE ---
const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) => { const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) => {
return ( return (
<div className="categoria-bloque"> <div className="categoria-bloque">
@@ -30,20 +28,26 @@ const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) =
className="candidato-row" className="candidato-row"
style={{ borderLeftColor: res.color || '#ccc' }} style={{ borderLeftColor: res.color || '#ccc' }}
> >
<ImageWithFallback {/* --- INICIO DE LA MODIFICACIÓN --- */}
src={res.fotoUrl ?? undefined} <div className="candidato-foto-wrapper" style={{ backgroundColor: res.color || '#e9ecef' }}>
fallbackSrc={`${assetBaseUrl}/default-avatar.png`} <ImageWithFallback
alt={res.nombreCandidato ?? res.nombreAgrupacion} src={res.fotoUrl ?? undefined}
className="candidato-foto" fallbackSrc={`${assetBaseUrl}/default-avatar.png`}
/> alt={res.nombreCandidato ?? res.nombreAgrupacion}
className="candidato-foto"
/>
</div>
{/* --- FIN DE LA MODIFICACIÓN --- */}
<div className="candidato-data"> <div className="candidato-data">
{res.nombreCandidato && ( {res.nombreCandidato ? (
<span className="candidato-nombre">{res.nombreCandidato}</span> <>
<span className="candidato-nombre">{res.nombreCandidato}</span>
<span className="candidato-partido">{res.nombreCortoAgrupacion || res.nombreAgrupacion}</span>
</>
) : (
<span className="candidato-nombre">{res.nombreCortoAgrupacion || res.nombreAgrupacion}</span>
)} )}
<span className={`candidato-partido ${!res.nombreCandidato ? 'main-title' : ''}`}>
{res.nombreAgrupacion}
</span>
<div className="progress-bar-container"> <div className="progress-bar-container">
<div className="progress-bar" style={{ width: `${res.porcentaje}%`, backgroundColor: res.color || '#ccc' }} /> <div className="progress-bar" style={{ width: `${res.porcentaje}%`, backgroundColor: res.color || '#ccc' }} />
</div> </div>
@@ -53,8 +57,6 @@ const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) =
<span className="stats-votos">{formatNumber(res.votos)} votos</span> <span className="stats-votos">{formatNumber(res.votos)} votos</span>
</div> </div>
{/* --- 3. RENDERIZADO CONDICIONAL DEL CUADRO DE BANCAS --- */}
{/* Este div solo se renderizará si mostrarBancas es true */}
{mostrarBancas && ( {mostrarBancas && (
<div className="stats-bancas"> <div className="stats-bancas">
+{res.bancasObtenidas} +{res.bancasObtenidas}
@@ -82,7 +84,6 @@ const CategoriaDisplay = ({ categoria, mostrarBancas }: CategoriaDisplayProps) =
); );
}; };
// --- 4. RECIBIR Y PASAR LA PROP EN EL COMPONENTE PRINCIPAL ---
export const ProvinciaCard = ({ data, mostrarBancas }: ProvinciaCardProps) => { export const ProvinciaCard = ({ data, mostrarBancas }: ProvinciaCardProps) => {
const colorGanador = data.categorias[0]?.resultados[0]?.color || '#d1d1d1'; const colorGanador = data.categorias[0]?.resultados[0]?.color || '#d1d1d1';
@@ -101,7 +102,7 @@ export const ProvinciaCard = ({ data, mostrarBancas }: ProvinciaCardProps) => {
<CategoriaDisplay <CategoriaDisplay
key={categoria.categoriaId} key={categoria.categoriaId}
categoria={categoria} categoria={categoria}
mostrarBancas={mostrarBancas} // Pasar la prop hacia abajo mostrarBancas={mostrarBancas}
/> />
))} ))}
</div> </div>

View File

@@ -175,4 +175,20 @@ public class CatalogosController : ControllerBase
return Ok(establecimientos); return Ok(establecimientos);
} }
/// <summary>
/// Obtiene la lista de municipios (partidos) para un distrito (provincia) específico.
/// </summary>
[HttpGet("municipios-por-distrito/{distritoId}")]
public async Task<IActionResult> GetMunicipiosPorDistrito(string distritoId)
{
var municipios = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 30 && a.DistritoId == distritoId) // Nivel 30 = Municipio
.OrderBy(a => a.Nombre)
.Select(a => new { Id = a.Id.ToString(), a.Nombre }) // Devolvemos el ID de la BD como string
.ToListAsync();
return Ok(municipios);
}
} }

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+11d9417ef5e3645d51c0ab227a0804985db4b1fb")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1719e79723abcbe8891a67b4d447a068c41d9e57")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["VUEzgM3q00ehIicPw1uM8REVOid2zDpFbcwF9rvWytQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","s3VnfR5av25jQd9RIy\u002BMwczWKx/CJRSzFdhJAMaIN9k=","A\u002BWemDKn7UwHxqDXzVs57jXOqpea86CLYpxVWDzRnDo=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","jAqiyVzpgWGABD/mtww8iBZneRW/QcFx5ww\u002BXozPGx4="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","IlET7uqumshgFxIEvfKRskON\u002BeAKZ7OfD/kCeAwn0PM=","NN2rS\u002B89ZAITWlNODPcF/lHIh3ZNmAHvUX4EjqSkX4s=","vsmxP35\u002Bq1R\u002BcAPfM\u002BIO\u002BoslwbDP4hbd6She759q5LA=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","S9x8x/B4N6vR0ZrYr6IZmcOPJCnhEdxrhESZuZWdF1A="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["VUEzgM3q00ehIicPw1uM8REVOid2zDpFbcwF9rvWytQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","s3VnfR5av25jQd9RIy\u002BMwczWKx/CJRSzFdhJAMaIN9k=","A\u002BWemDKn7UwHxqDXzVs57jXOqpea86CLYpxVWDzRnDo=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","jAqiyVzpgWGABD/mtww8iBZneRW/QcFx5ww\u002BXozPGx4="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","IlET7uqumshgFxIEvfKRskON\u002BeAKZ7OfD/kCeAwn0PM=","NN2rS\u002B89ZAITWlNODPcF/lHIh3ZNmAHvUX4EjqSkX4s=","vsmxP35\u002Bq1R\u002BcAPfM\u002BIO\u002BoslwbDP4hbd6She759q5LA=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","S9x8x/B4N6vR0ZrYr6IZmcOPJCnhEdxrhESZuZWdF1A="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"O7YawHw32G/Fh2bs+snZgm9O7okI0WYgTQmXM931znY=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["VUEzgM3q00ehIicPw1uM8REVOid2zDpFbcwF9rvWytQ=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"O7YawHw32G/Fh2bs+snZgm9O7okI0WYgTQmXM931znY=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+11d9417ef5e3645d51c0ab227a0804985db4b1fb")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1719e79723abcbe8891a67b4d447a068c41d9e57")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+11d9417ef5e3645d51c0ab227a0804985db4b1fb")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1719e79723abcbe8891a67b4d447a068c41d9e57")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3b0eee25e6441da1831442a90b6552cc8ef59d07")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1719e79723abcbe8891a67b4d447a068c41d9e57")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3a8f64bf854b2bdf66d83e503aaacc7dca77138e")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1719e79723abcbe8891a67b4d447a068c41d9e57")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]