Fix Widgets Carousel

This commit is contained in:
2025-10-18 10:51:51 -03:00
parent ae846f2d48
commit 17a5b333fd
4 changed files with 58 additions and 58 deletions

View File

@@ -77,6 +77,18 @@ export const DevAppLegislativas = () => {
/> />
</div> </div>
<div style={sectionStyle}>
<h2>Widget: Carrusel de Resultados Nación (Home)</h2>
<p style={descriptionStyle}>
Uso: <code style={codeStyle}>&lt;HomeCarouselNacionalWidget eleccionId={2} categoriaId={2} titulo="Senadores - Argentina" /&gt;</code>
</p>
<HomeCarouselNacionalWidget
eleccionId={2}
categoriaId={2} // 3 para Diputados, 2 para Senadores
titulo="Senadores - Total País"
/>
</div>
{/* --- SECCIÓN PARA EL WIDGET DE TARJETAS CON EJEMPLOS --- */} {/* --- SECCIÓN PARA EL WIDGET DE TARJETAS CON EJEMPLOS --- */}
<div style={sectionStyle}> <div style={sectionStyle}>
<h2>Widget: Resultados por Provincia (Tarjetas)</h2> <h2>Widget: Resultados por Provincia (Tarjetas)</h2>

View File

@@ -1,4 +1,5 @@
// src/features/legislativas/nacionales/HomeCarouselNacionalWidget.tsx // src/features/legislativas/nacionales/HomeCarouselNacionalWidget.tsx
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { getHomeResumenNacional } from '../../../apiService'; import { getHomeResumenNacional } from '../../../apiService';
import { ImageWithFallback } from '../../../components/common/ImageWithFallback'; import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
@@ -12,41 +13,42 @@ import 'swiper/css';
import 'swiper/css/navigation'; import 'swiper/css/navigation';
import styles from './HomeCarouselWidget.module.css'; import styles from './HomeCarouselWidget.module.css';
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);
if (isNaN(date.getTime())) {
return 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;
}
};
// Las props ya no incluyen distritoId
interface Props { interface Props {
eleccionId: number; eleccionId: number;
categoriaId: number; categoriaId: number;
titulo: string; titulo: string;
} }
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);
if (isNaN(date.getTime())) {
return 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 HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo }: Props) => { export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo }: Props) => {
const uniqueId = `swiper-${Math.random().toString(36).substring(2, 9)}`;
const prevButtonClass = `prev-${uniqueId}`;
const nextButtonClass = `next-${uniqueId}`;
const { data, isLoading, error } = useQuery({ const { data, isLoading, error } = useQuery({
// La queryKey ahora no necesita distritoId
queryKey: ['homeResumenNacional', eleccionId, categoriaId], queryKey: ['homeResumenNacional', eleccionId, categoriaId],
// Llama a la nueva función de la API
queryFn: () => getHomeResumenNacional(eleccionId, categoriaId), queryFn: () => getHomeResumenNacional(eleccionId, categoriaId),
}); });
if (isLoading) return <div>Cargando widget...</div>; if (isLoading) return <div>Cargando widget...</div>;
if (error || !data) return <div>No se pudieron cargar los datos.</div>; if (error || !data) return <div>No se pudieron cargar los datos.</div>;
@@ -82,8 +84,8 @@ export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo }:
spaceBetween={16} spaceBetween={16}
slidesPerView={1.3} slidesPerView={1.3}
navigation={{ navigation={{
prevEl: `.${styles.navButtonPrev}`, prevEl: `.${prevButtonClass}`,
nextEl: `.${styles.navButtonNext}`, nextEl: `.${nextButtonClass}`,
}} }}
breakpoints={{ breakpoints={{
320: { slidesPerView: 1.25, spaceBetween: 10 }, 320: { slidesPerView: 1.25, spaceBetween: 10 },
@@ -96,7 +98,6 @@ export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo }:
{data.resultados.map(candidato => ( {data.resultados.map(candidato => (
<SwiperSlide key={candidato.agrupacionId}> <SwiperSlide key={candidato.agrupacionId}>
<div className={styles.candidateCard} style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}> <div className={styles.candidateCard} style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}>
<div className={styles.candidatePhotoWrapper}> <div className={styles.candidatePhotoWrapper}>
<ImageWithFallback <ImageWithFallback
src={candidato.fotoUrl ?? undefined} src={candidato.fotoUrl ?? undefined}
@@ -105,22 +106,15 @@ export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo }:
className={styles.candidatePhoto} className={styles.candidatePhoto}
/> />
</div> </div>
<div className={styles.candidateDetails}> <div className={styles.candidateDetails}>
<div className={styles.candidateInfo}> <div className={styles.candidateInfo}>
{candidato.nombreCandidato ? ( {candidato.nombreCandidato ? (
<> <>
<span className={styles.candidateName}> <span className={styles.candidateName}>{candidato.nombreCandidato}</span>
{candidato.nombreCandidato} <span className={styles.partyName}>{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}</span>
</span>
<span className={styles.partyName}>
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
</span>
</> </>
) : ( ) : (
<span className={styles.candidateName}> <span className={styles.candidateName}>{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}</span>
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
</span>
)} )}
</div> </div>
<div className={styles.candidateResults}> <div className={styles.candidateResults}>
@@ -128,14 +122,13 @@ export const HomeCarouselNacionalWidget = ({ eleccionId, categoriaId, titulo }:
<span className={styles.votes}>{formatNumber(candidato.votos)} votos</span> <span className={styles.votes}>{formatNumber(candidato.votos)} votos</span>
</div> </div>
</div> </div>
</div> </div>
</SwiperSlide> </SwiperSlide>
))} ))}
</Swiper> </Swiper>
<div className={`${styles.navButton} ${styles.navButtonPrev}`}></div> <div className={`${styles.navButton} ${styles.navButtonPrev} ${prevButtonClass}`}></div>
<div className={`${styles.navButton} ${styles.navButtonNext}`}></div> <div className={`${styles.navButton} ${styles.navButtonNext} ${nextButtonClass}`}></div>
</div> </div>
<div className={styles.widgetFooter}> <div className={styles.widgetFooter}>

View File

@@ -1,4 +1,5 @@
// src/features/legislativas/nacionales/HomeCarouselWidget.tsx // src/features/legislativas/nacionales/HomeCarouselWidget.tsx
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { getHomeResumen } from '../../../apiService'; import { getHomeResumen } from '../../../apiService';
import { ImageWithFallback } from '../../../components/common/ImageWithFallback'; import { ImageWithFallback } from '../../../components/common/ImageWithFallback';
@@ -21,7 +22,6 @@ interface Props {
const formatPercent = (num: number | null | undefined) => `${(num || 0).toFixed(2).replace('.', ',')}%`; const formatPercent = (num: number | null | undefined) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
const formatNumber = (num: number) => num.toLocaleString('es-AR'); const formatNumber = (num: number) => num.toLocaleString('es-AR');
const formatDateTime = (dateString: string | undefined | null) => { const formatDateTime = (dateString: string | undefined | null) => {
if (!dateString) return '...'; if (!dateString) return '...';
try { try {
@@ -41,6 +41,10 @@ const formatDateTime = (dateString: string | undefined | null) => {
}; };
export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo }: Props) => { export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo }: Props) => {
const uniqueId = `swiper-${Math.random().toString(36).substring(2, 9)}`;
const prevButtonClass = `prev-${uniqueId}`;
const nextButtonClass = `next-${uniqueId}`;
const { data, isLoading, error } = useQuery({ const { data, isLoading, error } = useQuery({
queryKey: ['homeResumen', eleccionId, distritoId, categoriaId], queryKey: ['homeResumen', eleccionId, distritoId, categoriaId],
queryFn: () => getHomeResumen(eleccionId, distritoId, categoriaId), queryFn: () => getHomeResumen(eleccionId, distritoId, categoriaId),
@@ -81,8 +85,8 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo
spaceBetween={16} spaceBetween={16}
slidesPerView={1.3} slidesPerView={1.3}
navigation={{ navigation={{
prevEl: `.${styles.navButtonPrev}`, prevEl: `.${prevButtonClass}`,
nextEl: `.${styles.navButtonNext}`, nextEl: `.${nextButtonClass}`,
}} }}
breakpoints={{ breakpoints={{
320: { slidesPerView: 1.25, spaceBetween: 10 }, 320: { slidesPerView: 1.25, spaceBetween: 10 },
@@ -95,7 +99,6 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo
{data.resultados.map(candidato => ( {data.resultados.map(candidato => (
<SwiperSlide key={candidato.agrupacionId}> <SwiperSlide key={candidato.agrupacionId}>
<div className={styles.candidateCard} style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}> <div className={styles.candidateCard} style={{ '--candidate-color': candidato.color || '#ccc' } as React.CSSProperties}>
<div className={styles.candidatePhotoWrapper}> <div className={styles.candidatePhotoWrapper}>
<ImageWithFallback <ImageWithFallback
src={candidato.fotoUrl ?? undefined} src={candidato.fotoUrl ?? undefined}
@@ -104,22 +107,15 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo
className={styles.candidatePhoto} className={styles.candidatePhoto}
/> />
</div> </div>
<div className={styles.candidateDetails}> <div className={styles.candidateDetails}>
<div className={styles.candidateInfo}> <div className={styles.candidateInfo}>
{candidato.nombreCandidato ? ( {candidato.nombreCandidato ? (
<> <>
<span className={styles.candidateName}> <span className={styles.candidateName}>{candidato.nombreCandidato}</span>
{candidato.nombreCandidato} <span className={styles.partyName}>{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}</span>
</span>
<span className={styles.partyName}>
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
</span>
</> </>
) : ( ) : (
<span className={styles.candidateName}> <span className={styles.candidateName}>{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}</span>
{candidato.nombreCortoAgrupacion || candidato.nombreAgrupacion}
</span>
)} )}
</div> </div>
<div className={styles.candidateResults}> <div className={styles.candidateResults}>
@@ -127,14 +123,13 @@ export const HomeCarouselWidget = ({ eleccionId, distritoId, categoriaId, titulo
<span className={styles.votes}>{formatNumber(candidato.votos)} votos</span> <span className={styles.votes}>{formatNumber(candidato.votos)} votos</span>
</div> </div>
</div> </div>
</div> </div>
</SwiperSlide> </SwiperSlide>
))} ))}
</Swiper> </Swiper>
<div className={`${styles.navButton} ${styles.navButtonPrev}`}></div> <div className={`${styles.navButton} ${styles.navButtonPrev} ${prevButtonClass}`}></div>
<div className={`${styles.navButton} ${styles.navButtonNext}`}></div> <div className={`${styles.navButton} ${styles.navButtonNext} ${nextButtonClass}`}></div>
</div> </div>
<div className={styles.widgetFooter}> <div className={styles.widgetFooter}>

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4bc257df43f5813ec432b89b47fa078c1cfa1fc8")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ae846f2d4834f3cd03079e91a8225e9f74cd073b")]
[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")]