Feat Widgets Carousel Selector de Porv. Fix Tablas Movil
This commit is contained in:
		| @@ -353,4 +353,9 @@ export const getTablaSecciones = async (eleccionId: number): Promise<ResultadoSe | |||||||
| export const getResumenNacionalPorProvincia = async (eleccionId: number, categoriaId: number): Promise<ProvinciaResumen[]> => { | export const getResumenNacionalPorProvincia = async (eleccionId: number, categoriaId: number): Promise<ProvinciaResumen[]> => { | ||||||
|   const response = await apiClient.get(`/elecciones/${eleccionId}/resumen-nacional-por-provincia?categoriaId=${categoriaId}`); |   const response = await apiClient.get(`/elecciones/${eleccionId}/resumen-nacional-por-provincia?categoriaId=${categoriaId}`); | ||||||
|   return response.data; |   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 { TablaConurbanoWidget } from './nacionales/TablaConurbanoWidget'; | ||||||
| import { TablaSeccionesWidget } from './nacionales/TablaSeccionesWidget'; | import { TablaSeccionesWidget } from './nacionales/TablaSeccionesWidget'; | ||||||
| import { ResumenNacionalWidget } from './nacionales/ResumenNacionalWidget'; | import { ResumenNacionalWidget } from './nacionales/ResumenNacionalWidget'; | ||||||
|  | import { HomeCarouselProvincialWidget } from './nacionales/HomeCarouselProvincialWidget'; | ||||||
|  |  | ||||||
| // --- NUEVO COMPONENTE REUTILIZABLE PARA CONTENIDO COLAPSABLE --- | // --- NUEVO COMPONENTE REUTILIZABLE PARA CONTENIDO COLAPSABLE --- | ||||||
| const CollapsibleWidgetWrapper = ({ children }: { children: React.ReactNode }) => { | const CollapsibleWidgetWrapper = ({ children }: { children: React.ReactNode }) => { | ||||||
| @@ -90,7 +91,34 @@ export const DevAppLegislativas = () => { | |||||||
|                 <HomeCarouselNacionalWidget |                 <HomeCarouselNacionalWidget | ||||||
|                     eleccionId={2} |                     eleccionId={2} | ||||||
|                     categoriaId={2} // 3 para Diputados, 2 para Senadores |                     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> |             </div> | ||||||
|  |  | ||||||
|             {/* --- SECCIÓN PARA EL WIDGET DE TARJETAS CON EJEMPLOS --- */} |             {/* --- SECCIÓN PARA EL WIDGET DE TARJETAS CON EJEMPLOS --- */} | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo, ma | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className={styles.homeCarouselWidget}> |     <div className={styles.homeCarouselWidget}> | ||||||
|       <div className={styles.widgetHeader}> |       <div className={`${styles.widgetHeader} ${styles.headerSingleLine}`}> | ||||||
|         <h2 className={styles.widgetTitle}>{titulo}</h2> |         <h2 className={styles.widgetTitle}>{titulo}</h2> | ||||||
|         <a href={mapLinkUrl} className={styles.mapLinkButton}> |         <a href={mapLinkUrl} className={styles.mapLinkButton}> | ||||||
|           <TfiMapAlt /> |           <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> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @@ -23,7 +23,7 @@ | |||||||
|     padding: 0.75rem; |     padding: 0.75rem; | ||||||
|     max-width: 1200px; |     max-width: 1200px; | ||||||
|     margin: 2rem auto; |     margin: 2rem auto; | ||||||
|     position: relative;  |     position: relative; | ||||||
| } | } | ||||||
|  |  | ||||||
| .carouselContainer { | .carouselContainer { | ||||||
| @@ -49,6 +49,15 @@ | |||||||
|     border: none; |     border: none; | ||||||
|     text-align: left; |     text-align: left; | ||||||
|     flex-grow: 1; |     flex-grow: 1; | ||||||
|  |     white-space: nowrap; | ||||||
|  |     overflow: hidden; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .provinceSelector { | ||||||
|  |     min-width: 180px; | ||||||
|  |     flex-shrink: 0; | ||||||
|  |     z-index: 9999; | ||||||
| } | } | ||||||
|  |  | ||||||
| .mapLinkButton { | .mapLinkButton { | ||||||
| @@ -57,14 +66,15 @@ | |||||||
|     gap: 0.4rem; |     gap: 0.4rem; | ||||||
|     background-color: #007bff; |     background-color: #007bff; | ||||||
|     color: white; |     color: white; | ||||||
|     padding: 0.4rem 1rem; |     padding: 0.5rem 1rem; | ||||||
|     border-radius: 9999px; |     border-radius: 9999px; | ||||||
|     font-size: 0.9rem; |     font-size: 0.8rem; | ||||||
|     font-weight: 700; |     font-weight: 600; | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|     white-space: nowrap; |     white-space: nowrap; | ||||||
|     transition: background-color 0.2s ease, box-shadow 0.2s ease; |     transition: background-color 0.2s ease, box-shadow 0.2s ease; | ||||||
|     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); | ||||||
|  |     flex-shrink: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .mapLinkButton svg { | .mapLinkButton svg { | ||||||
| @@ -94,18 +104,30 @@ | |||||||
|     margin-top: 0.5rem; |     margin-top: 0.5rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .topStatsBar > div {  | .topStatsBar>div { | ||||||
|     display: flex; |     display: flex; | ||||||
|     align-items: baseline; |     align-items: baseline; | ||||||
|     gap: 0.5rem; |     gap: 0.5rem; | ||||||
|     border-right: 1px solid var(--border-color);  |     border-right: 1px solid var(--border-color); | ||||||
|     padding: 0 0.5rem; |     padding: 0 0.5rem; | ||||||
|     flex-grow: 1; |     flex-grow: 1; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
| } | } | ||||||
| .topStatsBar > div:last-child { border-right: none; } |  | ||||||
| .topStatsBar span { font-size: 0.9rem; color: var(--secondary-text); } | .topStatsBar>div:last-child { | ||||||
| .topStatsBar strong { font-size: 0.9rem; font-weight: 600; color: var(--primary-text); } |     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 { | .candidateCard { | ||||||
|     display: flex; |     display: flex; | ||||||
| @@ -149,33 +171,40 @@ | |||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     align-items:flex-start; |     align-items: flex-start; | ||||||
|     gap: 0.1rem; |     gap: 0.1rem; | ||||||
|     min-width: 0; |     min-width: 0; | ||||||
|     margin-right: 0.75rem; |     margin-right: 0.75rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .candidateName, .partyName { | .candidateName, | ||||||
|  | .partyName { | ||||||
|     white-space: nowrap; |     white-space: nowrap; | ||||||
|     overflow: hidden; |     overflow: hidden; | ||||||
|     text-overflow: ellipsis; |     text-overflow: ellipsis; | ||||||
|     display: block; |     display: block; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     margin: 0;  |     margin: 0; | ||||||
|     color: var(--primary-text); |     color: var(--primary-text); | ||||||
|     text-align: left; |     text-align: left; | ||||||
| } | } | ||||||
|  |  | ||||||
| .candidateName { | .candidateName { | ||||||
|     font-size: 0.95rem; |     font-size: 0.95rem; | ||||||
|     font-weight: 700; |     font-weight: 700; | ||||||
| } | } | ||||||
|  |  | ||||||
| .partyName { | .partyName { | ||||||
|     font-size: 0.8rem; |     font-size: 0.8rem; | ||||||
|     color: var(--secondary-text); |     color: var(--secondary-text); | ||||||
|     font-weight: 400; |     font-weight: 400; | ||||||
| } | } | ||||||
|  |  | ||||||
| .candidateResults { text-align: right; flex-shrink: 0; } | .candidateResults { | ||||||
|  |     text-align: right; | ||||||
|  |     flex-shrink: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| .percentage { | .percentage { | ||||||
|     display: block; |     display: block; | ||||||
|     font-size: 1.2rem; |     font-size: 1.2rem; | ||||||
| @@ -183,6 +212,7 @@ | |||||||
|     color: var(--primary-text); |     color: var(--primary-text); | ||||||
|     line-height: 1.1; |     line-height: 1.1; | ||||||
| } | } | ||||||
|  |  | ||||||
| .votes { | .votes { | ||||||
|     font-size: 0.75rem; |     font-size: 0.75rem; | ||||||
|     color: var(--secondary-text); |     color: var(--secondary-text); | ||||||
| @@ -208,20 +238,22 @@ | |||||||
|  |  | ||||||
| /* Usamos el pseudo-elemento ::after para mostrar el icono SVG como fondo */ | /* Usamos el pseudo-elemento ::after para mostrar el icono SVG como fondo */ | ||||||
| .navButton::after { | .navButton::after { | ||||||
|     content: ''; /* Es necesario para que el pseudo-elemento se muestre */ |     content: ''; | ||||||
|  |     /* Es necesario para que el pseudo-elemento se muestre */ | ||||||
|     display: block; |     display: block; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     height: 100%; |     height: 100%; | ||||||
|     background-repeat: no-repeat; |     background-repeat: no-repeat; | ||||||
|     background-position: center; |     background-position: center; | ||||||
|     /* Ajustamos el tamaño del icono dentro del botón */ |     /* Ajustamos el tamaño del icono dentro del botón */ | ||||||
|     background-size: 75%;  |     background-size: 75%; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Posición y contenido específico para cada botón */ | /* Posición y contenido específico para cada botón */ | ||||||
| .navButtonPrev { | .navButtonPrev { | ||||||
|     left: -10px; |     left: -10px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .navButtonPrev::after { | .navButtonPrev::after { | ||||||
|     /* SVG de flecha izquierda (chevron) codificado en Base64 */ |     /* SVG de flecha izquierda (chevron) codificado en Base64 */ | ||||||
|     background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSIxNSA2IDkgMTIgMTUgMTgiPjwvcG9seWxpbmU+PC9zdmc+"); |     background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSIxNSA2IDkgMTIgMTUgMTgiPjwvcG9seWxpbmU+PC9zdmc+"); | ||||||
| @@ -230,6 +262,7 @@ | |||||||
| .navButtonNext { | .navButtonNext { | ||||||
|     right: -10px; |     right: -10px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .navButtonNext::after { | .navButtonNext::after { | ||||||
|     /* SVG de flecha derecha (chevron) codificado en Base64 */ |     /* SVG de flecha derecha (chevron) codificado en Base64 */ | ||||||
|     background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSI5IDYgMTUgMTIgOSAxOCI+PC9wb2x5bGluZT48L3N2Zz4="); |     background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTI1MjkiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cG9seWxpbmUgcG9pbnRzPSI5IDYgMTUgMTIgOSAxOCI+PC9wb2x5bGluZT48L3N2Zz4="); | ||||||
| @@ -285,8 +318,13 @@ | |||||||
|     color: var(--primary-text) !important; |     color: var(--primary-text) !important; | ||||||
| } | } | ||||||
|  |  | ||||||
| .homeCarouselWidget :global(.swiper-button-prev) { left: 10px !important; } | .homeCarouselWidget :global(.swiper-button-prev) { | ||||||
| .homeCarouselWidget :global(.swiper-button-next) { right: 10px !important; } |     left: 10px !important; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .homeCarouselWidget :global(.swiper-button-next) { | ||||||
|  |     right: 10px !important; | ||||||
|  | } | ||||||
|  |  | ||||||
| .homeCarouselWidget :global(.swiper-button-disabled) { | .homeCarouselWidget :global(.swiper-button-disabled) { | ||||||
|     opacity: 0 !important; |     opacity: 0 !important; | ||||||
| @@ -294,10 +332,10 @@ | |||||||
| } | } | ||||||
|  |  | ||||||
| .widgetFooter { | .widgetFooter { | ||||||
|   text-align: right; |     text-align: right; | ||||||
|   font-size: 0.75rem; |     font-size: 0.75rem; | ||||||
|   color: var(--secondary-text); |     color: var(--secondary-text); | ||||||
|   margin-top: 0.5rem;  |     margin-top: 0.5rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .shortText { | .shortText { | ||||||
| @@ -306,17 +344,34 @@ | |||||||
|  |  | ||||||
| @media (max-width: 768px) { | @media (max-width: 768px) { | ||||||
|     .homeCarouselWidget .widgetHeader { |     .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; |         flex-direction: row; | ||||||
|         justify-content: space-between; |         justify-content: space-between; | ||||||
|         align-items: center; |         align-items: center; | ||||||
|         gap: 0.5rem; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     .homeCarouselWidget .widgetTitle {  |     .homeCarouselWidget .widgetTitle { | ||||||
|         text-align: left;  |         text-align: center; | ||||||
|         font-size: 1rem;  |         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; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .provinceSelector { | ||||||
|  |         min-width: 100%; | ||||||
|  |         z-index: 9999; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     .mapLinkButton { |     .mapLinkButton { | ||||||
|         padding: 0.5rem; |         padding: 0.5rem; | ||||||
|     } |     } | ||||||
| @@ -325,31 +380,83 @@ | |||||||
|         display: none; |         display: none; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     .homeCarouselWidget .topStatsBar { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.2rem; padding: 0.3rem; } |     .homeCarouselWidget .topStatsBar { | ||||||
|     .homeCarouselWidget .topStatsBar > div { padding: 0.25rem 0.5rem; border-right: none; } |         display: grid; | ||||||
|     .homeCarouselWidget .topStatsBar > div:nth-child(odd) { border-right: 1px solid var(--border-color); } |         grid-template-columns: repeat(2, 1fr); | ||||||
|     .homeCarouselWidget .longText { display: none; } |         gap: 0.2rem; | ||||||
|     .homeCarouselWidget .shortText { display:inline; } |         padding: 0.3rem; | ||||||
|     .homeCarouselWidget .topStatsBar span { font-size: 0.8rem; text-align: left; } |     } | ||||||
|     .homeCarouselWidget .topStatsBar strong { font-size: 0.85rem; text-align: right; } |  | ||||||
|      |     .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 */ |     /* Ajustamos los botones custom en mobile */ | ||||||
|     .navButton { |     .navButton { | ||||||
|         width: 32px; |         width: 32px; | ||||||
|         height: 32px; |         height: 32px; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     .navButton::after { |     .navButton::after { | ||||||
|         line-height: 32px; |         line-height: 32px; | ||||||
|     } |     } | ||||||
|     .navButtonPrev { left: -10px; } |  | ||||||
|     .navButtonNext { right: -10px; } |  | ||||||
|  |  | ||||||
|     .homeCarouselWidget .candidateCard { gap: 0.5rem; padding: 0.5rem; } |     .navButtonPrev { | ||||||
|     .homeCarouselWidget .candidatePhotoWrapper { width: 50px; height: 50px; } |         left: -10px; | ||||||
|     .homeCarouselWidget .candidateName { font-size: 0.9rem; } |     } | ||||||
|     .homeCarouselWidget .percentage { font-size: 1.1rem; } |  | ||||||
|     .homeCarouselWidget .votes { font-size: 0.7rem; } |     .navButtonNext { | ||||||
|     .homeCarouselWidget .widgetFooter { text-align: center; } |         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 */ | /* Mantenemos estos estilos globales por si acaso */ | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|         <div className={styles.homeCarouselWidget}> |         <div className={styles.homeCarouselWidget}> | ||||||
|             <div className={styles.widgetHeader}> |             <div className={`${styles.widgetHeader} ${styles.headerSingleLine}`}> | ||||||
|                 <h2 className={styles.widgetTitle}>{titulo}</h2> |                 <h2 className={styles.widgetTitle}>{titulo}</h2> | ||||||
|                 <a href={mapLinkUrl} className={styles.mapLinkButton}> |                 <a href={mapLinkUrl} className={styles.mapLinkButton}> | ||||||
|                     <TfiMapAlt /> |                     <TfiMapAlt /> | ||||||
|   | |||||||
| @@ -1,81 +1,107 @@ | |||||||
| /* src/components/widgets/ResumenNacionalWidget.module.css */ | /* src/components/widgets/ResumenNacionalWidget.module.css */ | ||||||
| .widgetContainer { | .widgetContainer { | ||||||
|   font-family: sans-serif; |   font-family: 'Roboto', sans-serif; | ||||||
|   border: 1px solid #ccc; |   border: 1px solid #e0e0e0; | ||||||
|   border-radius: 8px; |   border-radius: 8px; | ||||||
|   padding: 1.5rem; |   padding: 1.5rem; | ||||||
|   max-width: 1000px; |   max-width: 500px; | ||||||
|   margin: 2rem auto; |   margin: 2rem auto; | ||||||
| } | } | ||||||
|  |  | ||||||
| .header { | .header { | ||||||
|   display: flex; |   text-align: center; | ||||||
|   justify-content: space-between; |  | ||||||
|   align-items: center; |  | ||||||
|   padding-bottom: 1rem; |   padding-bottom: 1rem; | ||||||
|   margin-bottom: 1rem; |   margin-bottom: 1rem; | ||||||
|   border-bottom: 1px solid #eee; |   border-bottom: 1px solid #eee; | ||||||
| } | } | ||||||
|  |  | ||||||
| .header h3 { | .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; |   margin: 0; | ||||||
|   font-size: 1.5rem; |   font-size: 1.5rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .categoriaSelector { | .categoriaSelector { | ||||||
|   min-width: 280px; |   min-width: 230px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .listaProvincias { | .resultsTable { | ||||||
|   list-style: none; |   width: 100%; | ||||||
|   padding: 0; |   border-collapse: collapse; | ||||||
|   margin: 0; |   border-spacing: 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| .provinciaItem { | .resultsTable thead { | ||||||
|   padding: 1rem 0; |   display: none; | ||||||
|   border-bottom: 1px solid #eee; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .provinciaItem:last-child { | .resultsTable td { | ||||||
|   border-bottom: none; |   padding: 3px 8px; | ||||||
|  |   text-align: left; | ||||||
| } | } | ||||||
|  |  | ||||||
| .provinciaHeader { | .provinciaBlock { | ||||||
|   display: flex; |   border-top: 1px solid #e0e0e0; | ||||||
|   justify-content: space-between; | } | ||||||
|   align-items: baseline; |  | ||||||
|   margin-bottom: 0.5rem; | .provinciaBlock:first-child { | ||||||
|  |   border-top: none; | ||||||
| } | } | ||||||
|  |  | ||||||
| .provinciaNombre { | .provinciaNombre { | ||||||
|   font-weight: bold; |   font-weight: 700; | ||||||
|   font-size: 1.1rem; |   font-size: 1rem; | ||||||
|   text-transform: uppercase; |   text-transform: uppercase; | ||||||
|  |   color: #333; | ||||||
|  |   padding-top: 1rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .provinciaEscrutado { | .provinciaEscrutado { | ||||||
|   font-size: 0.8rem; |   font-size: 0.8rem; | ||||||
|   color: #555; |   color: #666; | ||||||
|   font-weight: 500; |   text-align: right; | ||||||
| } |   white-space: nowrap; | ||||||
|  |   padding-top: 1rem; | ||||||
| .resultadosLista { |   width: 1%; | ||||||
|   list-style: none; |  | ||||||
|   padding: 0; |  | ||||||
|   margin: 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .resultadoItem { |  | ||||||
|   display: flex; |  | ||||||
|   justify-content: space-between; |  | ||||||
|   padding: 0.25rem 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .partidoNombre { | .partidoNombre { | ||||||
|   color: #333; |   font-size: 0.9rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| .partidoPorcentaje { | .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 | // src/components/widgets/ResumenNacionalWidget.tsx | ||||||
| import { useState } from 'react'; | import { useState, useMemo } from 'react'; | ||||||
| import { useQuery } from '@tanstack/react-query'; | import { useQuery } from '@tanstack/react-query'; | ||||||
| import Select from 'react-select'; | import Select from 'react-select'; | ||||||
| import { getResumenNacionalPorProvincia } from '../../../apiService'; | import { getResumenNacionalPorProvincia } from '../../../apiService'; | ||||||
| @@ -11,6 +11,35 @@ const CATEGORIAS_NACIONALES = [ | |||||||
|   { value: 2, label: 'Senadores 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 = () => { | export const ResumenNacionalWidget = () => { | ||||||
|   const [categoria, setCategoria] = useState(CATEGORIAS_NACIONALES[0]); |   const [categoria, setCategoria] = useState(CATEGORIAS_NACIONALES[0]); | ||||||
|  |  | ||||||
| @@ -20,12 +49,22 @@ export const ResumenNacionalWidget = () => { | |||||||
|     refetchInterval: 60000, |     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 ( |   return ( | ||||||
|     <div className={styles.widgetContainer}> |     <div className={styles.widgetContainer}> | ||||||
|       <div className={styles.header}> |       <div className={styles.subHeader}> | ||||||
|         <h3>{categoria.label}</h3> |         <h4>{categoria.label}</h4> | ||||||
|         <Select |         <Select | ||||||
|           className={styles.categoriaSelector} |           className={styles.categoriaSelector} | ||||||
|           options={CATEGORIAS_NACIONALES} |           options={CATEGORIAS_NACIONALES} | ||||||
| @@ -36,25 +75,30 @@ export const ResumenNacionalWidget = () => { | |||||||
|       </div> |       </div> | ||||||
|       {isLoading && <p>Cargando resumen nacional...</p>} |       {isLoading && <p>Cargando resumen nacional...</p>} | ||||||
|       {error && <p style={{ color: 'red' }}>Error al cargar los datos.</p>} |       {error && <p style={{ color: 'red' }}>Error al cargar los datos.</p>} | ||||||
|       {data && ( |       {sortedData && ( | ||||||
|         <ul className={styles.listaProvincias}> |         <table className={styles.resultsTable}> | ||||||
|           {data.map((provincia) => ( |           <thead> | ||||||
|             <li key={provincia.provinciaId} className={styles.provinciaItem}> |             <tr> | ||||||
|               <div className={styles.provinciaHeader}> |               <th>Concepto</th> | ||||||
|                 <span className={styles.provinciaNombre}>{provincia.provinciaNombre}</span> |               <th>Valor</th> | ||||||
|                 <span className={styles.provinciaEscrutado}>ESCR. {formatPercent(provincia.porcentajeEscrutado)}</span> |             </tr> | ||||||
|               </div> |           </thead> | ||||||
|               <ul className={styles.resultadosLista}> |           {sortedData.map((provincia) => ( | ||||||
|                 {provincia.resultados.map((partido, index) => ( |             <tbody key={provincia.provinciaId} className={styles.provinciaBlock}> | ||||||
|                   <li key={index} className={styles.resultadoItem}> |               <tr> | ||||||
|                     <span className={styles.partidoNombre}>{partido.nombre}</span> |                 {/* 3. Añadir el número antes del nombre */} | ||||||
|                     <span className={styles.partidoPorcentaje}>{formatPercent(partido.porcentaje)}</span> |                 <td className={styles.provinciaNombre}>{`${PROVINCE_ORDER_MAP[provincia.provinciaId]}- ${provincia.provinciaNombre}`}</td> | ||||||
|                   </li> |                 <td className={styles.provinciaEscrutado}>ESCR. {formatPercent(provincia.porcentajeEscrutado)}</td> | ||||||
|                 ))} |               </tr> | ||||||
|               </ul> |               {provincia.resultados.map((partido, index) => ( | ||||||
|             </li> |                 <tr key={index}> | ||||||
|  |                   <td className={styles.partidoNombre}>{partido.nombre}</td> | ||||||
|  |                   <td className={styles.partidoPorcentaje}>{formatPercent(partido.porcentaje)}</td> | ||||||
|  |                 </tr> | ||||||
|  |               ))} | ||||||
|  |             </tbody> | ||||||
|           ))} |           ))} | ||||||
|         </ul> |         </table> | ||||||
|       )} |       )} | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -4,13 +4,10 @@ import { getTablaConurbano } from '../../../apiService'; | |||||||
| import styles from './TablaResultadosWidget.module.css'; | import styles from './TablaResultadosWidget.module.css'; | ||||||
|  |  | ||||||
| export const TablaConurbanoWidget = () => { | export const TablaConurbanoWidget = () => { | ||||||
|   // CORRECCIÓN: Se elimina el estado y el selector de categoría. |   const ELECCION_ID = 2; | ||||||
|   const ELECCION_ID = 2; // Exclusivo para elecciones nacionales |  | ||||||
|  |  | ||||||
|   const { data, isLoading, error } = useQuery({ |   const { data, isLoading, error } = useQuery({ | ||||||
|     // La queryKey ya no necesita la categoría |  | ||||||
|     queryKey: ['tablaConurbano', ELECCION_ID], |     queryKey: ['tablaConurbano', ELECCION_ID], | ||||||
|     // La llamada a la API ya no necesita la categoría |  | ||||||
|     queryFn: () => getTablaConurbano(ELECCION_ID), |     queryFn: () => getTablaConurbano(ELECCION_ID), | ||||||
|     refetchInterval: 60000, |     refetchInterval: 60000, | ||||||
|   }); |   }); | ||||||
| @@ -20,7 +17,7 @@ export const TablaConurbanoWidget = () => { | |||||||
|   return ( |   return ( | ||||||
|     <div className={styles.widgetContainer}> |     <div className={styles.widgetContainer}> | ||||||
|       <div className={styles.header}> |       <div className={styles.header}> | ||||||
|         <h3>Resultados Conurbano - Diputados Nacionales</h3> |         <h3>Diputados Nacionales</h3> | ||||||
|       </div> |       </div> | ||||||
|       {isLoading && <p>Cargando resultados...</p>} |       {isLoading && <p>Cargando resultados...</p>} | ||||||
|       {error && <p>Error al cargar los datos.</p>} |       {error && <p>Error al cargar los datos.</p>} | ||||||
| @@ -38,7 +35,9 @@ export const TablaConurbanoWidget = () => { | |||||||
|           <tbody> |           <tbody> | ||||||
|             {data.map((fila, index) => ( |             {data.map((fila, index) => ( | ||||||
|               <tr key={fila.ambitoId}> |               <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.fuerzaCell}>{fila.fuerza1Display}</td> | ||||||
|                 <td className={styles.porcentajeCell}>{formatPercent(fila.fuerza1Porcentaje)}</td> |                 <td className={styles.porcentajeCell}>{formatPercent(fila.fuerza1Porcentaje)}</td> | ||||||
|                 <td className={styles.fuerzaCell}>{fila.fuerza2Display}</td> |                 <td className={styles.fuerzaCell}>{fila.fuerza2Display}</td> | ||||||
|   | |||||||
| @@ -73,4 +73,104 @@ | |||||||
|   font-weight: 400; |   font-weight: 400; | ||||||
|   color: #6c757d; |   color: #6c757d; | ||||||
|   padding-right: 0.5rem; |   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 | // 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 { useQuery } from '@tanstack/react-query'; | ||||||
| import { getTablaSecciones } from '../../../apiService'; | import { getTablaSecciones } from '../../../apiService'; | ||||||
| import styles from './TablaResultadosWidget.module.css'; | import styles from './TablaResultadosWidget.module.css'; | ||||||
| @@ -18,7 +18,7 @@ export const TablaSeccionesWidget = () => { | |||||||
|   return ( |   return ( | ||||||
|     <div className={styles.widgetContainer}> |     <div className={styles.widgetContainer}> | ||||||
|       <div className={styles.header}> |       <div className={styles.header}> | ||||||
|         <h3>Resultados por Sección - Diputados Nacionales</h3> |         <h3>Diputados Nacionales</h3> | ||||||
|       </div> |       </div> | ||||||
|       {isLoading && <p>Cargando resultados...</p>} |       {isLoading && <p>Cargando resultados...</p>} | ||||||
|       {error && <p>Error al cargar los datos.</p>} |       {error && <p>Error al cargar los datos.</p>} | ||||||
| @@ -41,7 +41,9 @@ export const TablaSeccionesWidget = () => { | |||||||
|                 </tr> |                 </tr> | ||||||
|                 {seccion.municipios.map((fila, index) => ( |                 {seccion.municipios.map((fila, index) => ( | ||||||
|                   <tr key={fila.ambitoId}> |                   <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.fuerzaCell}>{fila.fuerza1Display}</td> | ||||||
|                     <td className={styles.porcentajeCell}>{formatPercent(fila.fuerza1Porcentaje)}</td> |                     <td className={styles.porcentajeCell}>{formatPercent(fila.fuerza1Porcentaje)}</td> | ||||||
|                     <td className={styles.fuerzaCell}>{fila.fuerza2Display}</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 { TablaConurbanoWidget } from './features/legislativas/nacionales/TablaConurbanoWidget'; | ||||||
| import { TablaSeccionesWidget } from './features/legislativas/nacionales/TablaSeccionesWidget'; | import { TablaSeccionesWidget } from './features/legislativas/nacionales/TablaSeccionesWidget'; | ||||||
| import { ResumenNacionalWidget } from './features/legislativas/nacionales/ResumenNacionalWidget'; | import { ResumenNacionalWidget } from './features/legislativas/nacionales/ResumenNacionalWidget'; | ||||||
|  | import { HomeCarouselProvincialWidget } from './features/legislativas/nacionales/HomeCarouselProvincialWidget'; | ||||||
|  |  | ||||||
| import { DevAppLegislativas } from './features/legislativas/DevAppLegislativas'; | import { DevAppLegislativas } from './features/legislativas/DevAppLegislativas'; | ||||||
|  |  | ||||||
| @@ -68,6 +69,7 @@ const WIDGET_MAP: Record<string, React.ElementType> = { | |||||||
|     'tabla-conurbano': TablaConurbanoWidget, |     'tabla-conurbano': TablaConurbanoWidget, | ||||||
|     'tabla-secciones': TablaSeccionesWidget, |     'tabla-secciones': TablaSeccionesWidget, | ||||||
|     'resumen-nacional': ResumenNacionalWidget, |     'resumen-nacional': ResumenNacionalWidget, | ||||||
|  |     'home-carousel-provincial': HomeCarouselProvincialWidget, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Vite establece `import.meta.env.DEV` a `true` cuando ejecutamos 'npm run dev' | // Vite establece `import.meta.env.DEV` a `true` cuando ejecutamos 'npm run dev' | ||||||
|   | |||||||
| @@ -16,6 +16,23 @@ public class CatalogosController : ControllerBase | |||||||
|         _dbContext = dbContext; |         _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")] |     [HttpGet("municipios")] | ||||||
|     public async Task<IActionResult> GetMunicipios([FromQuery] int? categoriaId) |     public async Task<IActionResult> GetMunicipios([FromQuery] int? categoriaId) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")] | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")] | ||||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] | [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] | ||||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] | [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] | ||||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| {"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["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.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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")] | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")] | ||||||
| [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")] | ||||||
|   | |||||||
| @@ -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+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")] | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")] | ||||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")] | [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")] | ||||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")] | [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")] | ||||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ using System.Reflection; | |||||||
| [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] | [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] | ||||||
| [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | ||||||
| [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | ||||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+248171146d637f758fd8b14a6a2ef9fcb0bcd3b5")] | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1335b54d759c402b859b6d8338cd0c944cc70171")] | ||||||
| [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")] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user