Fix Widgets Carousel
This commit is contained in:
		| @@ -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}><HomeCarouselNacionalWidget eleccionId={2} categoriaId={2} titulo="Senadores - Argentina" /></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> | ||||||
|   | |||||||
| @@ -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}> | ||||||
|   | |||||||
| @@ -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}> | ||||||
|   | |||||||
| @@ -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")] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user