| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  | // src/features/legislativas/nacionales/PanelNacionalWidget.tsx
 | 
					
						
							|  |  |  |  | import { useMemo, useState, Suspense } from 'react'; | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |  | import { useSuspenseQuery } from '@tanstack/react-query'; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  | import { getPanelElectoral } from '../../../apiService'; | 
					
						
							|  |  |  |  | import { MapaNacional } from './components/MapaNacional'; | 
					
						
							|  |  |  |  | import { PanelResultados } from './components/PanelResultados'; | 
					
						
							|  |  |  |  | import { Breadcrumbs } from './components/Breadcrumbs'; | 
					
						
							|  |  |  |  | import './PanelNacional.css'; | 
					
						
							|  |  |  |  | import Select from 'react-select'; | 
					
						
							|  |  |  |  | import type { PanelElectoralDto } from '../../../types/types'; | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  | import { FiMap, FiList } from 'react-icons/fi'; | 
					
						
							|  |  |  |  | import { useMediaQuery } from './hooks/useMediaQuery'; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | interface PanelNacionalWidgetProps { | 
					
						
							|  |  |  |  |   eleccionId: number; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | type AmbitoState = { | 
					
						
							|  |  |  |  |   id: string | null; | 
					
						
							|  |  |  |  |   nivel: 'pais' | 'provincia' | 'municipio'; | 
					
						
							|  |  |  |  |   nombre: string; | 
					
						
							|  |  |  |  |   provinciaNombre?: string; | 
					
						
							|  |  |  |  |   provinciaDistritoId?: string | null; | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const CATEGORIAS_NACIONALES = [ | 
					
						
							|  |  |  |  |   { value: 2, label: 'Diputados Nacionales' }, | 
					
						
							|  |  |  |  |   { value: 1, label: 'Senadores Nacionales' }, | 
					
						
							|  |  |  |  | ]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const PanelContenido = ({ eleccionId, ambitoActual, categoriaId }: { eleccionId: number, ambitoActual: AmbitoState, categoriaId: number }) => { | 
					
						
							|  |  |  |  |   const { data } = useSuspenseQuery<PanelElectoralDto>({ | 
					
						
							|  |  |  |  |     queryKey: ['panelElectoral', eleccionId, ambitoActual.id, categoriaId], | 
					
						
							|  |  |  |  |     queryFn: () => getPanelElectoral(eleccionId, ambitoActual.id, categoriaId), | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |  |   return <PanelResultados resultados={data.resultadosPanel} estadoRecuento={data.estadoRecuento} />; | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export const PanelNacionalWidget = ({ eleccionId }: PanelNacionalWidgetProps) => { | 
					
						
							|  |  |  |  |   const [ambitoActual, setAmbitoActual] = useState<AmbitoState>({ id: null, nivel: 'pais', nombre: 'Argentina', provinciaDistritoId: null }); | 
					
						
							|  |  |  |  |   const [categoriaId, setCategoriaId] = useState<number>(2); | 
					
						
							|  |  |  |  |   const [isPanelOpen, setIsPanelOpen] = useState(true); | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  |   const [mobileView, setMobileView] = useState<'mapa' | 'resultados'>('mapa'); | 
					
						
							|  |  |  |  |   // --- DETECCIÓN DE VISTA MÓVIL ---
 | 
					
						
							|  |  |  |  |   const isMobile = useMediaQuery('(max-width: 800px)'); | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   const handleAmbitoSelect = (nuevoAmbitoId: string, nuevoNivel: 'provincia' | 'municipio', nuevoNombre: string) => { | 
					
						
							|  |  |  |  |     setAmbitoActual(prev => ({ | 
					
						
							|  |  |  |  |       id: nuevoAmbitoId, | 
					
						
							|  |  |  |  |       nivel: nuevoNivel, | 
					
						
							|  |  |  |  |       nombre: nuevoNombre, | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |  |       provinciaNombre: nuevoNivel === 'municipio' ? prev.provinciaNombre : (nuevoNivel === 'provincia' ? nuevoNombre : undefined), | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |       provinciaDistritoId: nuevoNivel === 'provincia' ? nuevoAmbitoId : prev.provinciaDistritoId | 
					
						
							|  |  |  |  |     })); | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const handleResetToPais = () => { | 
					
						
							|  |  |  |  |     setAmbitoActual({ id: null, nivel: 'pais', nombre: 'Argentina', provinciaDistritoId: null }); | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const handleVolverAProvincia = () => { | 
					
						
							|  |  |  |  |     if (ambitoActual.provinciaDistritoId && ambitoActual.provinciaNombre) { | 
					
						
							|  |  |  |  |       setAmbitoActual({ | 
					
						
							|  |  |  |  |         id: ambitoActual.provinciaDistritoId, | 
					
						
							|  |  |  |  |         nivel: 'provincia', | 
					
						
							|  |  |  |  |         nombre: ambitoActual.provinciaNombre, | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |  |         provinciaDistritoId: ambitoActual.provinciaDistritoId, | 
					
						
							|  |  |  |  |         provinciaNombre: ambitoActual.provinciaNombre, | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       handleResetToPais(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const selectedCategoria = useMemo(() => | 
					
						
							|  |  |  |  |     CATEGORIAS_NACIONALES.find(c => c.value === categoriaId), | 
					
						
							|  |  |  |  |     [categoriaId] | 
					
						
							|  |  |  |  |   ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return ( | 
					
						
							|  |  |  |  |     <div className="panel-nacional-container"> | 
					
						
							|  |  |  |  |       <header className="panel-header"> | 
					
						
							|  |  |  |  |         <div className="header-top-row"> | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  |           <h1>Legislativas Argentina 2025</h1> | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |           <Select | 
					
						
							|  |  |  |  |             options={CATEGORIAS_NACIONALES} | 
					
						
							|  |  |  |  |             value={selectedCategoria} | 
					
						
							|  |  |  |  |             onChange={(option) => option && setCategoriaId(option.value)} | 
					
						
							|  |  |  |  |             className="categoria-selector" | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  |             classNamePrefix="categoria-selector" | 
					
						
							|  |  |  |  |             isSearchable={false} | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |           /> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |         <Breadcrumbs | 
					
						
							|  |  |  |  |           nivel={ambitoActual.nivel} | 
					
						
							|  |  |  |  |           nombreAmbito={ambitoActual.nombre} | 
					
						
							|  |  |  |  |           nombreProvincia={ambitoActual.provinciaNombre} | 
					
						
							|  |  |  |  |           onReset={handleResetToPais} | 
					
						
							|  |  |  |  |           onVolverProvincia={handleVolverAProvincia} | 
					
						
							|  |  |  |  |         /> | 
					
						
							|  |  |  |  |       </header> | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  |       <main className={`panel-main-content ${!isPanelOpen ? 'panel-collapsed' : ''} ${isMobile ? `mobile-view-${mobileView}` : ''}`}> | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |         <div className="mapa-column"> | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |  |           <button className="panel-toggle-btn" onClick={() => setIsPanelOpen(!isPanelOpen)} title={isPanelOpen ? "Ocultar panel" : "Mostrar panel"}> | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |             {isPanelOpen ? '›' : '‹'} | 
					
						
							|  |  |  |  |           </button> | 
					
						
							|  |  |  |  |           <Suspense fallback={<div className="spinner" />}> | 
					
						
							|  |  |  |  |             <MapaNacional | 
					
						
							|  |  |  |  |               eleccionId={eleccionId} | 
					
						
							|  |  |  |  |               categoriaId={categoriaId} | 
					
						
							|  |  |  |  |               nivel={ambitoActual.nivel} | 
					
						
							|  |  |  |  |               nombreAmbito={ambitoActual.nombre} | 
					
						
							| 
									
										
										
										
											2025-09-19 17:19:10 -03:00
										 |  |  |  |               nombreProvinciaActiva={ambitoActual.provinciaNombre} | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |               provinciaDistritoId={ambitoActual.provinciaDistritoId ?? null} | 
					
						
							|  |  |  |  |               onAmbitoSelect={handleAmbitoSelect} | 
					
						
							|  |  |  |  |               onVolver={ambitoActual.nivel === 'municipio' ? handleVolverAProvincia : handleResetToPais} | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  |               isMobileView={isMobile} | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |             /> | 
					
						
							|  |  |  |  |           </Suspense> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |         <div className="resultados-column"> | 
					
						
							|  |  |  |  |           <Suspense fallback={<div className="spinner" />}> | 
					
						
							|  |  |  |  |             <PanelContenido | 
					
						
							|  |  |  |  |               eleccionId={eleccionId} | 
					
						
							|  |  |  |  |               ambitoActual={ambitoActual} | 
					
						
							|  |  |  |  |               categoriaId={categoriaId} | 
					
						
							|  |  |  |  |             /> | 
					
						
							|  |  |  |  |           </Suspense> | 
					
						
							|  |  |  |  |         </div> | 
					
						
							|  |  |  |  |       </main> | 
					
						
							| 
									
										
										
										
											2025-09-20 22:31:11 -03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |       {/* --- NUEVO CONTROLADOR DE VISTA PARA MÓVIL --- */} | 
					
						
							|  |  |  |  |       <div className="mobile-view-toggle"> | 
					
						
							|  |  |  |  |         <button | 
					
						
							|  |  |  |  |           className={`toggle-btn ${mobileView === 'mapa' ? 'active' : ''}`} | 
					
						
							|  |  |  |  |           onClick={() => setMobileView('mapa')} | 
					
						
							|  |  |  |  |         > | 
					
						
							|  |  |  |  |           <FiMap /> | 
					
						
							|  |  |  |  |           <span>Mapa</span> | 
					
						
							|  |  |  |  |         </button> | 
					
						
							|  |  |  |  |         <button | 
					
						
							|  |  |  |  |           className={`toggle-btn ${mobileView === 'resultados' ? 'active' : ''}`} | 
					
						
							|  |  |  |  |           onClick={() => setMobileView('resultados')} | 
					
						
							|  |  |  |  |         > | 
					
						
							|  |  |  |  |           <FiList /> | 
					
						
							|  |  |  |  |           <span>Resultados</span> | 
					
						
							|  |  |  |  |         </button> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							| 
									
										
										
										
											2025-09-17 11:31:17 -03:00
										 |  |  |  |     </div> | 
					
						
							|  |  |  |  |   ); | 
					
						
							|  |  |  |  | }; |