Feat Widgets Carousel Selector de Porv. Fix Tablas Movil
This commit is contained in:
@@ -354,3 +354,8 @@ export const getResumenNacionalPorProvincia = async (eleccionId: number, categor
|
||||
const response = await apiClient.get(`/elecciones/${eleccionId}/resumen-nacional-por-provincia?categoriaId=${categoriaId}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getProvincias = async (): Promise<CatalogoItem[]> => {
|
||||
const response = await apiClient.get('/catalogos/provincias');
|
||||
return response.data;
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { HomeCarouselNacionalWidget } from './nacionales/HomeCarouselNacionalWid
|
||||
import { TablaConurbanoWidget } from './nacionales/TablaConurbanoWidget';
|
||||
import { TablaSeccionesWidget } from './nacionales/TablaSeccionesWidget';
|
||||
import { ResumenNacionalWidget } from './nacionales/ResumenNacionalWidget';
|
||||
import { HomeCarouselProvincialWidget } from './nacionales/HomeCarouselProvincialWidget';
|
||||
|
||||
// --- NUEVO COMPONENTE REUTILIZABLE PARA CONTENIDO COLAPSABLE ---
|
||||
const CollapsibleWidgetWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
@@ -90,7 +91,34 @@ export const DevAppLegislativas = () => {
|
||||
<HomeCarouselNacionalWidget
|
||||
eleccionId={2}
|
||||
categoriaId={2} // 3 para Diputados, 2 para Senadores
|
||||
titulo="Senadores - Total País" mapLinkUrl={''} />
|
||||
titulo="Senadores - Total País" mapLinkUrl={''} />
|
||||
</div>
|
||||
|
||||
<div style={sectionStyle}>
|
||||
<h2>Widget: Carrusel Provincial con Selector (Home)</h2>
|
||||
<p style={descriptionStyle}>
|
||||
Categoría Diputados
|
||||
</p>
|
||||
<p style={descriptionStyle}>
|
||||
Uso: <code style={codeStyle}><HomeCarouselProvincialWidget eleccionId={2} categoriaId={3} titulo="Diputados" /></code>
|
||||
</p>
|
||||
<HomeCarouselProvincialWidget
|
||||
eleccionId={2}
|
||||
categoriaId={3} // 3 para Diputados, 2 para Senadores
|
||||
titulo="Diputados"
|
||||
/>
|
||||
|
||||
<p style={descriptionStyle}>
|
||||
Categoría Senadores
|
||||
</p>
|
||||
<p style={descriptionStyle}>
|
||||
Uso: <code style={codeStyle}><HomeCarouselProvincialWidget eleccionId={2} categoriaId={2} titulo="Senadores" /></code>
|
||||
</p>
|
||||
<HomeCarouselProvincialWidget
|
||||
eleccionId={2}
|
||||
categoriaId={2} // 3 para Diputados, 2 para Senadores
|
||||
titulo="Senadores"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* --- SECCIÓN PARA EL WIDGET DE TARJETAS CON EJEMPLOS --- */}
|
||||
|
||||
@@ -57,7 +57,7 @@ export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo, ma
|
||||
|
||||
return (
|
||||
<div className={styles.homeCarouselWidget}>
|
||||
<div className={styles.widgetHeader}>
|
||||
<div className={`${styles.widgetHeader} ${styles.headerSingleLine}`}>
|
||||
<h2 className={styles.widgetTitle}>{titulo}</h2>
|
||||
<a href={mapLinkUrl} className={styles.mapLinkButton}>
|
||||
<TfiMapAlt />
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// src/features/legislativas/nacionales/HomeCarouselProvincialWidget.tsx
|
||||
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Select, { type SingleValue } from 'react-select';
|
||||
import { getHomeResumen, getProvincias } from '../../../apiService';
|
||||
import type { CatalogoItem } from '../../../types/types';
|
||||
import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
|
||||
import { assetBaseUrl } from '../../../apiService';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import { Navigation, A11y } from 'swiper/modules';
|
||||
|
||||
// @ts-ignore
|
||||
import 'swiper/css';
|
||||
// @ts-ignore
|
||||
import 'swiper/css/navigation';
|
||||
import styles from './HomeCarouselWidget.module.css';
|
||||
|
||||
interface Props {
|
||||
eleccionId: number;
|
||||
categoriaId: number;
|
||||
titulo: string;
|
||||
}
|
||||
|
||||
interface OptionType {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
// --- LÓGICA DE FILTRADO ---
|
||||
// 1. Definimos los IDs de distrito de las provincias que renuevan senadores.
|
||||
const PROVINCIAS_QUE_RENUEVAN_SENADORES = new Set(['01', '06', '08', '15', '16', '17', '22', '24']);
|
||||
const CATEGORIA_SENADORES = 2;
|
||||
// --- FIN LÓGICA DE FILTRADO ---
|
||||
|
||||
const formatPercent = (num: number | null | undefined) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
|
||||
const formatNumber = (num: number) => num.toLocaleString('es-AR');
|
||||
const formatDateTime = (dateString: string | undefined | null) => {
|
||||
if (!dateString) return '...';
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${day}/${month}/${year}, ${hours}:${minutes} hs.`;
|
||||
} catch (e) {
|
||||
return dateString;
|
||||
}
|
||||
};
|
||||
|
||||
export const HomeCarouselProvincialWidget = ({ eleccionId, categoriaId, titulo }: Props) => {
|
||||
// 2. Estado inicial nulo para que el useEffect lo establezca dinámicamente.
|
||||
const [selectedProvince, setSelectedProvince] = useState<OptionType | null>(null);
|
||||
|
||||
const { data: provincias = [], isLoading: isLoadingProvincias } = useQuery<CatalogoItem[]>({
|
||||
queryKey: ['provincias'],
|
||||
queryFn: getProvincias,
|
||||
});
|
||||
|
||||
// 3. Usamos useMemo para filtrar las opciones solo cuando sea necesario.
|
||||
const provinceOptions: OptionType[] = useMemo(() => {
|
||||
const allOptions = provincias.map(p => ({ value: p.id, label: p.nombre }));
|
||||
if (categoriaId === CATEGORIA_SENADORES) {
|
||||
return allOptions.filter(opt => PROVINCIAS_QUE_RENUEVAN_SENADORES.has(opt.value));
|
||||
}
|
||||
return allOptions;
|
||||
}, [provincias, categoriaId]);
|
||||
|
||||
// 4. useEffect para establecer y validar la provincia por defecto.
|
||||
useEffect(() => {
|
||||
if (provinceOptions.length > 0) {
|
||||
// Si no hay nada seleccionado, establece el default.
|
||||
if (!selectedProvince) {
|
||||
const defaultOption = provinceOptions.find(opt => opt.value === '01'); // CABA
|
||||
setSelectedProvince(defaultOption || provinceOptions[0]); // Si CABA no está, usa la primera opción.
|
||||
} else {
|
||||
// Si ya hay algo seleccionado, verifica que siga siendo válido. Si no, lo resetea.
|
||||
const isSelectedStillValid = provinceOptions.some(opt => opt.value === selectedProvince.value);
|
||||
if (!isSelectedStillValid) {
|
||||
setSelectedProvince(provinceOptions[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [provinceOptions, selectedProvince]);
|
||||
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['homeResumen', eleccionId, selectedProvince?.value, categoriaId],
|
||||
queryFn: () => getHomeResumen(eleccionId, selectedProvince!.value, categoriaId),
|
||||
enabled: !!selectedProvince, // La consulta solo se ejecuta si hay una provincia seleccionada
|
||||
refetchInterval: 30000,
|
||||
});
|
||||
|
||||
const uniqueId = `swiper-${Math.random().toString(36).substring(2, 9)}`;
|
||||
const prevButtonClass = `prev-${uniqueId}`;
|
||||
const nextButtonClass = `next-${uniqueId}`;
|
||||
|
||||
return (
|
||||
<div className={styles.homeCarouselWidget}>
|
||||
<div className={styles.widgetHeader}>
|
||||
<h2 className={styles.widgetTitle}>{`${titulo} - ${selectedProvince?.label || '...'}`}</h2>
|
||||
<div className={styles.provinceSelector}>
|
||||
<Select
|
||||
value={selectedProvince}
|
||||
options={provinceOptions}
|
||||
onChange={(option: SingleValue<OptionType>) => option && setSelectedProvince(option)}
|
||||
isLoading={isLoadingProvincias}
|
||||
isSearchable={true}
|
||||
placeholder="Seleccionar provincia..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(isLoading || !selectedProvince) && <div>Cargando resultados...</div>}
|
||||
{error && <div>No se pudieron cargar los datos.</div>}
|
||||
{data && selectedProvince && (
|
||||
<>
|
||||
<div className={styles.carouselContainer}>
|
||||
<Swiper
|
||||
modules={[Navigation, A11y]}
|
||||
spaceBetween={16}
|
||||
slidesPerView={1.3}
|
||||
navigation={{
|
||||
prevEl: `.${prevButtonClass}`,
|
||||
nextEl: `.${nextButtonClass}`,
|
||||
}}
|
||||
breakpoints={{
|
||||
320: { slidesPerView: 1.25, spaceBetween: 10 },
|
||||
430: { slidesPerView: 1.4, spaceBetween: 12 },
|
||||
640: { slidesPerView: 2.5 },
|
||||
1024: { slidesPerView: 3 },
|
||||
1200: { slidesPerView: 3.5 }
|
||||
}}
|
||||
>
|
||||
{data.resultados.map(candidato => (
|
||||
<SwiperSlide key={candidato.agrupacionId}>
|
||||
<div className={styles.candidateCard} style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}>
|
||||
<div className={styles.candidatePhotoWrapper}>
|
||||
<ImageWithFallback
|
||||
src={candidato.fotoUrl ?? undefined}
|
||||
fallbackSrc={`${assetBaseUrl}/default-avatar.png`}
|
||||
alt={candidato.nombreCandidato ?? ''}
|
||||
className={styles.candidatePhoto}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.candidateDetails}>
|
||||
<div className={styles.candidateInfo}>
|
||||
{candidato.nombreCandidato ? (
|
||||
<>
|
||||
<span className={styles.candidateName}>{candidato.nombreCandidato}</span>
|
||||
<span className={styles.partyName}>{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.candidateName}>{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.candidateResults}>
|
||||
<span className={styles.percentage}>{formatPercent(candidato.porcentaje)}</span>
|
||||
<span className={styles.votes}>{formatNumber(candidato.votos)} votos</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
|
||||
<div className={`${styles.navButton} ${styles.navButtonPrev} ${prevButtonClass}`}></div>
|
||||
<div className={`${styles.navButton} ${styles.navButtonNext} ${nextButtonClass}`}></div>
|
||||
</div>
|
||||
|
||||
<div className={styles.topStatsBar}>
|
||||
<div>
|
||||
<span>Participación</span>
|
||||
<strong>{formatPercent(data.estadoRecuento?.participacionPorcentaje)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.longText}>Mesas escrutadas</span>
|
||||
<span className={styles.shortText}>Escrutado</span>
|
||||
<strong>{formatPercent(data.estadoRecuento?.mesasTotalizadasPorcentaje)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.longText}>Votos en blanco</span>
|
||||
<span className={styles.shortText}>En blanco</span>
|
||||
<strong>{formatPercent(data.votosEnBlancoPorcentaje)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span className={styles.longText}>Votos totales</span>
|
||||
<span className={styles.shortText}>Votos</span>
|
||||
<strong>{formatNumber(data.votosTotales)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.widgetFooter}>
|
||||
Última actualización: {formatDateTime(data.ultimaActualizacion)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -49,6 +49,15 @@
|
||||
border: none;
|
||||
text-align: left;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.provinceSelector {
|
||||
min-width: 180px;
|
||||
flex-shrink: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.mapLinkButton {
|
||||
@@ -57,14 +66,15 @@
|
||||
gap: 0.4rem;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
padding: 0.4rem 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 700;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.2s ease, box-shadow 0.2s ease;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mapLinkButton svg {
|
||||
@@ -94,7 +104,7 @@
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.topStatsBar > div {
|
||||
.topStatsBar>div {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5rem;
|
||||
@@ -103,9 +113,21 @@
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
.topStatsBar > div:last-child { border-right: none; }
|
||||
.topStatsBar span { font-size: 0.9rem; color: var(--secondary-text); }
|
||||
.topStatsBar strong { font-size: 0.9rem; font-weight: 600; color: var(--primary-text); }
|
||||
|
||||
.topStatsBar>div:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.topStatsBar span {
|
||||
font-size: 0.9rem;
|
||||
color: var(--secondary-text);
|
||||
}
|
||||
|
||||
.topStatsBar strong {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-text);
|
||||
}
|
||||
|
||||
.candidateCard {
|
||||
display: flex;
|
||||
@@ -149,13 +171,14 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items:flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 0.1rem;
|
||||
min-width: 0;
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.candidateName, .partyName {
|
||||
.candidateName,
|
||||
.partyName {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -165,17 +188,23 @@
|
||||
color: var(--primary-text);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.candidateName {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.partyName {
|
||||
font-size: 0.8rem;
|
||||
color: var(--secondary-text);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.candidateResults { text-align: right; flex-shrink: 0; }
|
||||
.candidateResults {
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.percentage {
|
||||
display: block;
|
||||
font-size: 1.2rem;
|
||||
@@ -183,6 +212,7 @@
|
||||
color: var(--primary-text);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.votes {
|
||||
font-size: 0.75rem;
|
||||
color: var(--secondary-text);
|
||||
@@ -208,7 +238,8 @@
|
||||
|
||||
/* Usamos el pseudo-elemento ::after para mostrar el icono SVG como fondo */
|
||||
.navButton::after {
|
||||
content: ''; /* Es necesario para que el pseudo-elemento se muestre */
|
||||
content: '';
|
||||
/* Es necesario para que el pseudo-elemento se muestre */
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -222,6 +253,7 @@
|
||||
.navButtonPrev {
|
||||
left: -10px;
|
||||
}
|
||||
|
||||
.navButtonPrev::after {
|
||||
/* SVG de flecha izquierda (chevron) codificado en Base64 */
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSIxNSA2IDkgMTIgMTUgMTgiPjwvcG9seWxpbmU+PC9zdmc+");
|
||||
@@ -230,6 +262,7 @@
|
||||
.navButtonNext {
|
||||
right: -10px;
|
||||
}
|
||||
|
||||
.navButtonNext::after {
|
||||
/* SVG de flecha derecha (chevron) codificado en Base64 */
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSI5IDYgMTUgMTIgOSAxOCI+PC9wb2x5bGluZT48L3N2Zz4=");
|
||||
@@ -285,8 +318,13 @@
|
||||
color: var(--primary-text) !important;
|
||||
}
|
||||
|
||||
.homeCarouselWidget :global(.swiper-button-prev) { left: 10px !important; }
|
||||
.homeCarouselWidget :global(.swiper-button-next) { right: 10px !important; }
|
||||
.homeCarouselWidget :global(.swiper-button-prev) {
|
||||
left: 10px !important;
|
||||
}
|
||||
|
||||
.homeCarouselWidget :global(.swiper-button-next) {
|
||||
right: 10px !important;
|
||||
}
|
||||
|
||||
.homeCarouselWidget :global(.swiper-button-disabled) {
|
||||
opacity: 0 !important;
|
||||
@@ -294,10 +332,10 @@
|
||||
}
|
||||
|
||||
.widgetFooter {
|
||||
text-align: right;
|
||||
font-size: 0.75rem;
|
||||
color: var(--secondary-text);
|
||||
margin-top: 0.5rem;
|
||||
text-align: right;
|
||||
font-size: 0.75rem;
|
||||
color: var(--secondary-text);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.shortText {
|
||||
@@ -306,15 +344,32 @@
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.homeCarouselWidget .widgetHeader {
|
||||
/* Comportamiento por defecto en móvil: apilado y centrado */
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* NUEVA CLASE MODIFICADORA para los widgets con botón */
|
||||
.homeCarouselWidget .headerSingleLine {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .widgetTitle {
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Ajuste para que el título vuelva a la izquierda en la vista de una línea */
|
||||
.headerSingleLine .widgetTitle {
|
||||
text-align: left;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.provinceSelector {
|
||||
min-width: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.mapLinkButton {
|
||||
@@ -325,31 +380,83 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .topStatsBar { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.2rem; padding: 0.3rem; }
|
||||
.homeCarouselWidget .topStatsBar > div { padding: 0.25rem 0.5rem; border-right: none; }
|
||||
.homeCarouselWidget .topStatsBar > div:nth-child(odd) { border-right: 1px solid var(--border-color); }
|
||||
.homeCarouselWidget .longText { display: none; }
|
||||
.homeCarouselWidget .shortText { display:inline; }
|
||||
.homeCarouselWidget .topStatsBar span { font-size: 0.8rem; text-align: left; }
|
||||
.homeCarouselWidget .topStatsBar strong { font-size: 0.85rem; text-align: right; }
|
||||
.homeCarouselWidget .topStatsBar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.2rem;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .topStatsBar>div {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .topStatsBar>div:nth-child(odd) {
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.homeCarouselWidget .longText {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .shortText {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .topStatsBar span {
|
||||
font-size: 0.8rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .topStatsBar strong {
|
||||
font-size: 0.85rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Ajustamos los botones custom en mobile */
|
||||
.navButton {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.navButton::after {
|
||||
line-height: 32px;
|
||||
}
|
||||
.navButtonPrev { left: -10px; }
|
||||
.navButtonNext { right: -10px; }
|
||||
|
||||
.homeCarouselWidget .candidateCard { gap: 0.5rem; padding: 0.5rem; }
|
||||
.homeCarouselWidget .candidatePhotoWrapper { width: 50px; height: 50px; }
|
||||
.homeCarouselWidget .candidateName { font-size: 0.9rem; }
|
||||
.homeCarouselWidget .percentage { font-size: 1.1rem; }
|
||||
.homeCarouselWidget .votes { font-size: 0.7rem; }
|
||||
.homeCarouselWidget .widgetFooter { text-align: center; }
|
||||
.navButtonPrev {
|
||||
left: -10px;
|
||||
}
|
||||
|
||||
.navButtonNext {
|
||||
right: -10px;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .candidateCard {
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .candidatePhotoWrapper {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .candidateName {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .percentage {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .votes {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.homeCarouselWidget .widgetFooter {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mantenemos estos estilos globales por si acaso */
|
||||
|
||||
@@ -58,7 +58,7 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo
|
||||
|
||||
return (
|
||||
<div className={styles.homeCarouselWidget}>
|
||||
<div className={styles.widgetHeader}>
|
||||
<div className={`${styles.widgetHeader} ${styles.headerSingleLine}`}>
|
||||
<h2 className={styles.widgetTitle}>{titulo}</h2>
|
||||
<a href={mapLinkUrl} className={styles.mapLinkButton}>
|
||||
<TfiMapAlt />
|
||||
|
||||
@@ -1,81 +1,107 @@
|
||||
/* src/components/widgets/ResumenNacionalWidget.module.css */
|
||||
.widgetContainer {
|
||||
font-family: sans-serif;
|
||||
border: 1px solid #ccc;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
max-width: 1000px;
|
||||
max-width: 500px;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.subHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.subHeader h4 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.categoriaSelector {
|
||||
min-width: 280px;
|
||||
min-width: 230px;
|
||||
}
|
||||
|
||||
.listaProvincias {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.resultsTable {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.provinciaItem {
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
.resultsTable thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.provinciaItem:last-child {
|
||||
border-bottom: none;
|
||||
.resultsTable td {
|
||||
padding: 3px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.provinciaHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 0.5rem;
|
||||
.provinciaBlock {
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.provinciaBlock:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.provinciaNombre {
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
text-transform: uppercase;
|
||||
color: #333;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.provinciaEscrutado {
|
||||
font-size: 0.8rem;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.resultadosLista {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.resultadoItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.25rem 0;
|
||||
color: #666;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
padding-top: 1rem;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.partidoNombre {
|
||||
color: #333;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.partidoPorcentaje {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
/* --- INICIO DE ESTILOS PARA MÓVILES --- */
|
||||
@media (max-width: 768px) {
|
||||
.subHeader {
|
||||
flex-direction: column; /* Apila el título y el selector */
|
||||
align-items: center; /* Centra los elementos */
|
||||
gap: 0.75rem; /* Añade espacio entre ellos */
|
||||
}
|
||||
|
||||
.subHeader h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.categoriaSelector {
|
||||
width: 100%; /* Hace que el selector ocupe todo el ancho */
|
||||
min-width: unset; /* Elimina el ancho mínimo que interfiere */
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/components/widgets/ResumenNacionalWidget.tsx
|
||||
import { useState } from 'react';
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Select from 'react-select';
|
||||
import { getResumenNacionalPorProvincia } from '../../../apiService';
|
||||
@@ -11,6 +11,35 @@ const CATEGORIAS_NACIONALES = [
|
||||
{ value: 2, label: 'Senadores Nacionales' },
|
||||
];
|
||||
|
||||
// 1. Mapa para definir el orden y número de cada provincia según el PDF
|
||||
const PROVINCE_ORDER_MAP: Record<string, number> = {
|
||||
'02': 1, // Buenos Aires
|
||||
'03': 2, // Catamarca
|
||||
'06': 3, // Chaco
|
||||
'07': 4, // Chubut
|
||||
'04': 5, // Córdoba
|
||||
'05': 6, // Corrientes
|
||||
'08': 7, // Entre Ríos
|
||||
'09': 8, // Formosa
|
||||
'10': 9, // Jujuy
|
||||
'11': 10, // La Pampa
|
||||
'12': 11, // La Rioja
|
||||
'13': 12, // Mendoza
|
||||
'14': 13, // Misiones
|
||||
'15': 14, // Neuquén
|
||||
'16': 15, // Río Negro
|
||||
'17': 16, // Salta
|
||||
'18': 17, // San Juan
|
||||
'19': 18, // San Luis
|
||||
'20': 19, // Santa Cruz
|
||||
'21': 20, // Santa Fe
|
||||
'22': 21, // Santiago del Estero
|
||||
'23': 22, // Tierra del Fuego
|
||||
'24': 23, // Tucumán
|
||||
'01': 24, // CABA
|
||||
};
|
||||
|
||||
|
||||
export const ResumenNacionalWidget = () => {
|
||||
const [categoria, setCategoria] = useState(CATEGORIAS_NACIONALES[0]);
|
||||
|
||||
@@ -20,12 +49,22 @@ export const ResumenNacionalWidget = () => {
|
||||
refetchInterval: 60000,
|
||||
});
|
||||
|
||||
const formatPercent = (num: number) => `${num.toFixed(2)}%`;
|
||||
// 2. Ordenar los datos de la API usando el mapa de ordenamiento
|
||||
const sortedData = useMemo(() => {
|
||||
if (!data) return [];
|
||||
return [...data].sort((a, b) => {
|
||||
const orderA = PROVINCE_ORDER_MAP[a.provinciaId] ?? 99;
|
||||
const orderB = PROVINCE_ORDER_MAP[b.provinciaId] ?? 99;
|
||||
return orderA - orderB;
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
const formatPercent = (num: number) => `${num.toFixed(2).replace('.', ',')}%`;
|
||||
|
||||
return (
|
||||
<div className={styles.widgetContainer}>
|
||||
<div className={styles.header}>
|
||||
<h3>{categoria.label}</h3>
|
||||
<div className={styles.subHeader}>
|
||||
<h4>{categoria.label}</h4>
|
||||
<Select
|
||||
className={styles.categoriaSelector}
|
||||
options={CATEGORIAS_NACIONALES}
|
||||
@@ -36,25 +75,30 @@ export const ResumenNacionalWidget = () => {
|
||||
</div>
|
||||
{isLoading && <p>Cargando resumen nacional...</p>}
|
||||
{error && <p style={{ color: 'red' }}>Error al cargar los datos.</p>}
|
||||
{data && (
|
||||
<ul className={styles.listaProvincias}>
|
||||
{data.map((provincia) => (
|
||||
<li key={provincia.provinciaId} className={styles.provinciaItem}>
|
||||
<div className={styles.provinciaHeader}>
|
||||
<span className={styles.provinciaNombre}>{provincia.provinciaNombre}</span>
|
||||
<span className={styles.provinciaEscrutado}>ESCR. {formatPercent(provincia.porcentajeEscrutado)}</span>
|
||||
</div>
|
||||
<ul className={styles.resultadosLista}>
|
||||
{provincia.resultados.map((partido, index) => (
|
||||
<li key={index} className={styles.resultadoItem}>
|
||||
<span className={styles.partidoNombre}>{partido.nombre}</span>
|
||||
<span className={styles.partidoPorcentaje}>{formatPercent(partido.porcentaje)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
{sortedData && (
|
||||
<table className={styles.resultsTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Concepto</th>
|
||||
<th>Valor</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{sortedData.map((provincia) => (
|
||||
<tbody key={provincia.provinciaId} className={styles.provinciaBlock}>
|
||||
<tr>
|
||||
{/* 3. Añadir el número antes del nombre */}
|
||||
<td className={styles.provinciaNombre}>{`${PROVINCE_ORDER_MAP[provincia.provinciaId]}- ${provincia.provinciaNombre}`}</td>
|
||||
<td className={styles.provinciaEscrutado}>ESCR. {formatPercent(provincia.porcentajeEscrutado)}</td>
|
||||
</tr>
|
||||
{provincia.resultados.map((partido, index) => (
|
||||
<tr key={index}>
|
||||
<td className={styles.partidoNombre}>{partido.nombre}</td>
|
||||
<td className={styles.partidoPorcentaje}>{formatPercent(partido.porcentaje)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
))}
|
||||
</ul>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,13 +4,10 @@ import { getTablaConurbano } from '../../../apiService';
|
||||
import styles from './TablaResultadosWidget.module.css';
|
||||
|
||||
export const TablaConurbanoWidget = () => {
|
||||
// CORRECCIÓN: Se elimina el estado y el selector de categoría.
|
||||
const ELECCION_ID = 2; // Exclusivo para elecciones nacionales
|
||||
const ELECCION_ID = 2;
|
||||
|
||||
const { data, isLoading, error } = useQuery({
|
||||
// La queryKey ya no necesita la categoría
|
||||
queryKey: ['tablaConurbano', ELECCION_ID],
|
||||
// La llamada a la API ya no necesita la categoría
|
||||
queryFn: () => getTablaConurbano(ELECCION_ID),
|
||||
refetchInterval: 60000,
|
||||
});
|
||||
@@ -20,7 +17,7 @@ export const TablaConurbanoWidget = () => {
|
||||
return (
|
||||
<div className={styles.widgetContainer}>
|
||||
<div className={styles.header}>
|
||||
<h3>Resultados Conurbano - Diputados Nacionales</h3>
|
||||
<h3>Diputados Nacionales</h3>
|
||||
</div>
|
||||
{isLoading && <p>Cargando resultados...</p>}
|
||||
{error && <p>Error al cargar los datos.</p>}
|
||||
@@ -38,7 +35,9 @@ export const TablaConurbanoWidget = () => {
|
||||
<tbody>
|
||||
{data.map((fila, index) => (
|
||||
<tr key={fila.ambitoId}>
|
||||
<td className={styles.distritoCell}><span className={styles.distritoIndex}>{index + 1}.</span>{fila.nombre}</td>
|
||||
<td className={styles.distritoCell}>
|
||||
<span className={styles.distritoIndex}>{index + 1}.</span>{fila.nombre}
|
||||
</td>
|
||||
<td className={styles.fuerzaCell}>{fila.fuerza1Display}</td>
|
||||
<td className={styles.porcentajeCell}>{formatPercent(fila.fuerza1Porcentaje)}</td>
|
||||
<td className={styles.fuerzaCell}>{fila.fuerza2Display}</td>
|
||||
|
||||
@@ -74,3 +74,103 @@
|
||||
color: #6c757d;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* --- INICIO DE ESTILOS PARA MÓVILES --- */
|
||||
@media (max-width: 768px) {
|
||||
.widgetContainer {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.resultsTable thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.resultsTable,
|
||||
.resultsTable tbody {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 1. Cada TR es una grilla */
|
||||
.resultsTable tr {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
/* Columna para nombres, columna para % */
|
||||
grid-template-rows: auto auto auto;
|
||||
/* Fila para distrito, 1ra fuerza, 2da fuerza */
|
||||
gap: 4px 1rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.resultsTable tr:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.resultsTable td {
|
||||
padding: 0;
|
||||
border-bottom: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 2. Posicionamos cada celda en la grilla */
|
||||
.distritoCell {
|
||||
grid-column: 1 / -1;
|
||||
/* Ocupa toda la primera fila */
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.fuerzaCell:nth-of-type(2) {
|
||||
grid-row: 2;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.porcentajeCell:nth-of-type(3) {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.fuerzaCell:nth-of-type(4) {
|
||||
grid-row: 3;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.porcentajeCell:nth-of-type(5) {
|
||||
grid-row: 3;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
/* 3. Añadimos los labels "1ra:" y "2da:" con pseudo-elementos */
|
||||
.fuerzaCell::before {
|
||||
font-weight: 500;
|
||||
color: #6c757d;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.fuerzaCell:nth-of-type(2)::before {
|
||||
content: '1ra:';
|
||||
}
|
||||
|
||||
.fuerzaCell:nth-of-type(4)::before {
|
||||
content: '2da:';
|
||||
}
|
||||
|
||||
/* Ajustes de alineación */
|
||||
.fuerzaCell {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.porcentajeCell {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.seccionHeader td {
|
||||
display: block;
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/features/legislativas/nacionales/TablaSeccionesWidget.tsx
|
||||
import React from 'react'; // Importar React para React.Fragment
|
||||
import React from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getTablaSecciones } from '../../../apiService';
|
||||
import styles from './TablaResultadosWidget.module.css';
|
||||
@@ -18,7 +18,7 @@ export const TablaSeccionesWidget = () => {
|
||||
return (
|
||||
<div className={styles.widgetContainer}>
|
||||
<div className={styles.header}>
|
||||
<h3>Resultados por Sección - Diputados Nacionales</h3>
|
||||
<h3>Diputados Nacionales</h3>
|
||||
</div>
|
||||
{isLoading && <p>Cargando resultados...</p>}
|
||||
{error && <p>Error al cargar los datos.</p>}
|
||||
@@ -41,7 +41,9 @@ export const TablaSeccionesWidget = () => {
|
||||
</tr>
|
||||
{seccion.municipios.map((fila, index) => (
|
||||
<tr key={fila.ambitoId}>
|
||||
<td className={styles.distritoCell}><span className={styles.distritoIndex}>{index + 1}.</span>{fila.nombre}</td>
|
||||
<td className={styles.distritoCell}>
|
||||
<span className={styles.distritoIndex}>{index + 1}.</span>{fila.nombre}
|
||||
</td>
|
||||
<td className={styles.fuerzaCell}>{fila.fuerza1Display}</td>
|
||||
<td className={styles.porcentajeCell}>{formatPercent(fila.fuerza1Porcentaje)}</td>
|
||||
<td className={styles.fuerzaCell}>{fila.fuerza2Display}</td>
|
||||
|
||||
@@ -32,6 +32,7 @@ import { HomeCarouselNacionalWidget } from './features/legislativas/nacionales/H
|
||||
import { TablaConurbanoWidget } from './features/legislativas/nacionales/TablaConurbanoWidget';
|
||||
import { TablaSeccionesWidget } from './features/legislativas/nacionales/TablaSeccionesWidget';
|
||||
import { ResumenNacionalWidget } from './features/legislativas/nacionales/ResumenNacionalWidget';
|
||||
import { HomeCarouselProvincialWidget } from './features/legislativas/nacionales/HomeCarouselProvincialWidget';
|
||||
|
||||
import { DevAppLegislativas } from './features/legislativas/DevAppLegislativas';
|
||||
|
||||
@@ -68,6 +69,7 @@ const WIDGET_MAP: Record<string, React.ElementType> = {
|
||||
'tabla-conurbano': TablaConurbanoWidget,
|
||||
'tabla-secciones': TablaSeccionesWidget,
|
||||
'resumen-nacional': ResumenNacionalWidget,
|
||||
'home-carousel-provincial': HomeCarouselProvincialWidget,
|
||||
};
|
||||
|
||||
// Vite establece `import.meta.env.DEV` a `true` cuando ejecutamos 'npm run dev'
|
||||
|
||||
@@ -16,6 +16,23 @@ public class CatalogosController : ControllerBase
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene la lista de todas las provincias (distritos).
|
||||
/// </summary>
|
||||
[HttpGet("provincias")]
|
||||
public async Task<IActionResult> GetProvincias()
|
||||
{
|
||||
var provincias = await _dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.Where(a => a.NivelId == 10 && !string.IsNullOrEmpty(a.DistritoId)) // Nivel 10 = Provincia
|
||||
.OrderBy(a => a.Nombre)
|
||||
.Select(a => new { Id = a.DistritoId, a.Nombre })
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(provincias);
|
||||
}
|
||||
|
||||
[HttpGet("municipios")]
|
||||
public async Task<IActionResult> GetMunicipios([FromQuery] int? categoriaId)
|
||||
{
|
||||
|
||||
@@ -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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")]
|
||||
[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":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","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=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","UIntj4QoiyGr7bnJN8KK5PGrhQd89m\u002BLfh4T8VKPxAk=","J\u002Bfv/j3QyIW9bxolc46wDka8641F622/QgIllt0Re80=","Y/o0rakw9VYzEfz9M659qW77P9kvz\u002B2gTe1Lv3zgUDE=","8QWUReqP8upfOnmA5lMNgBxAfYJ1z3zv/WYBUXBEiog=","1L7p1HQI/Uoosqm7RyBuYjKbRFTycFgJEtHPSdlXWhU=","ZxPpBx5gkHuilHLcg/vcjvaXswvTqiUM0YaAEwbNSLI=","zSbNtRd32h6wCMWjU5ecl5a3ECd\u002BVBstFC3etkdk4s0=","urIQ/RlknPjR8\u002BeAcCsDIPiRjQGFfUdIC\u002BoT3wYB2dU=","QgvJ\u002BjH2t7prbQ/Cu9eYOIBqysMeDcsXR6lggWr0auI=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","xGsYFCt4z/3oybSKe/TfOuJ3mQW6fLJVS9hZfmpKuPY="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","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=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","UIntj4QoiyGr7bnJN8KK5PGrhQd89m\u002BLfh4T8VKPxAk=","J\u002Bfv/j3QyIW9bxolc46wDka8641F622/QgIllt0Re80=","Y/o0rakw9VYzEfz9M659qW77P9kvz\u002B2gTe1Lv3zgUDE=","8QWUReqP8upfOnmA5lMNgBxAfYJ1z3zv/WYBUXBEiog=","1L7p1HQI/Uoosqm7RyBuYjKbRFTycFgJEtHPSdlXWhU=","ZxPpBx5gkHuilHLcg/vcjvaXswvTqiUM0YaAEwbNSLI=","zSbNtRd32h6wCMWjU5ecl5a3ECd\u002BVBstFC3etkdk4s0=","urIQ/RlknPjR8\u002BeAcCsDIPiRjQGFfUdIC\u002BoT3wYB2dU=","ytyPPQGU70eGo9tCrHq5\u002BwXF3yVuqv9Z\u002Br1Zdf0XUCI=","scnW1D7e2F059zWPpwmOsIw6KIyloYSDqXXW70WAZpQ=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","7Yl3qA5xr\u002BXUmuY\u002Bshj87a0l8dEYVlvjk253M66DWfo="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","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=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","UIntj4QoiyGr7bnJN8KK5PGrhQd89m\u002BLfh4T8VKPxAk=","J\u002Bfv/j3QyIW9bxolc46wDka8641F622/QgIllt0Re80=","Y/o0rakw9VYzEfz9M659qW77P9kvz\u002B2gTe1Lv3zgUDE=","8QWUReqP8upfOnmA5lMNgBxAfYJ1z3zv/WYBUXBEiog=","1L7p1HQI/Uoosqm7RyBuYjKbRFTycFgJEtHPSdlXWhU=","ZxPpBx5gkHuilHLcg/vcjvaXswvTqiUM0YaAEwbNSLI=","zSbNtRd32h6wCMWjU5ecl5a3ECd\u002BVBstFC3etkdk4s0=","urIQ/RlknPjR8\u002BeAcCsDIPiRjQGFfUdIC\u002BoT3wYB2dU=","QgvJ\u002BjH2t7prbQ/Cu9eYOIBqysMeDcsXR6lggWr0auI=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","xGsYFCt4z/3oybSKe/TfOuJ3mQW6fLJVS9hZfmpKuPY="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","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=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","UIntj4QoiyGr7bnJN8KK5PGrhQd89m\u002BLfh4T8VKPxAk=","J\u002Bfv/j3QyIW9bxolc46wDka8641F622/QgIllt0Re80=","Y/o0rakw9VYzEfz9M659qW77P9kvz\u002B2gTe1Lv3zgUDE=","8QWUReqP8upfOnmA5lMNgBxAfYJ1z3zv/WYBUXBEiog=","1L7p1HQI/Uoosqm7RyBuYjKbRFTycFgJEtHPSdlXWhU=","ZxPpBx5gkHuilHLcg/vcjvaXswvTqiUM0YaAEwbNSLI=","zSbNtRd32h6wCMWjU5ecl5a3ECd\u002BVBstFC3etkdk4s0=","urIQ/RlknPjR8\u002BeAcCsDIPiRjQGFfUdIC\u002BoT3wYB2dU=","ytyPPQGU70eGo9tCrHq5\u002BwXF3yVuqv9Z\u002Br1Zdf0XUCI=","scnW1D7e2F059zWPpwmOsIw6KIyloYSDqXXW70WAZpQ=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","7Yl3qA5xr\u002BXUmuY\u002Bshj87a0l8dEYVlvjk253M66DWfo="],"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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")]
|
||||
[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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")]
|
||||
[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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")]
|
||||
[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