// src/features/legislativas/nacionales/PanelNacionalWidget.tsx import { useMemo, useState, Suspense, useEffect } from 'react'; import { useSuspenseQuery } from '@tanstack/react-query'; import { getPanelElectoral } from '../../../apiService'; import { MapaNacional } from './components/MapaNacional'; import { PanelResultados } from './components/PanelResultados'; import { Breadcrumbs } from './components/Breadcrumbs'; import { MunicipioSearch } from './components/MunicipioSearch'; // 1. La importación de CSS ahora se hace como un módulo import styles from './PanelNacional.module.css'; import Select from 'react-select'; import type { PanelElectoralDto, ResultadoTicker } from '../../../types/types'; import { FiMap, FiList, FiChevronDown, FiChevronUp } from 'react-icons/fi'; import { useMediaQuery } from './hooks/useMediaQuery'; import toast, { Toaster } from 'react-hot-toast'; import { ImageWithFallback } from '../../../components/common/ImageWithFallback'; import { assetBaseUrl } from '../../../apiService'; import { useQueryClient } from '@tanstack/react-query'; // --- COMPONENTE INTERNO PARA LA TARJETA DE RESULTADOS EN MÓVIL --- interface MobileResultsCardProps { eleccionId: number; ambitoId: string | null; categoriaId: number; ambitoNombre: string; ambitoNivel: 'pais' | 'provincia' | 'municipio'; } const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`; // --- SUB-COMPONENTE PARA UNA FILA DE RESULTADO --- // 2. Todas las props 'className' ahora usan el objeto 'styles' const ResultRow = ({ partido }: { partido: ResultadoTicker }) => (
{partido.nombreCandidato ? ( <> {partido.nombreCandidato} {partido.nombreCorto || partido.nombre} ) : ( {partido.nombreCorto || partido.nombre} )}
{formatPercent(partido.porcentaje)} {partido.votos.toLocaleString('es-AR')}
); // --- COMPONENTE REFACTORIZADO PARA LA TARJETA MÓVIL --- interface MobileResultsCardProps { eleccionId: number; ambitoId: string | null; categoriaId: number; ambitoNombre: string; ambitoNivel: 'pais' | 'provincia' | 'municipio'; mobileView: 'mapa' | 'resultados'; setMobileView: (view: 'mapa' | 'resultados') => void; } const MobileResultsCard = ({ eleccionId, ambitoId, categoriaId, ambitoNombre, ambitoNivel, mobileView, setMobileView }: MobileResultsCardProps) => { const [isExpanded, setIsExpanded] = useState(false); const { data } = useSuspenseQuery({ queryKey: ['panelElectoral', eleccionId, ambitoId, categoriaId], queryFn: () => getPanelElectoral(eleccionId, ambitoId, categoriaId), refetchInterval: 30000, }); useEffect(() => { setIsExpanded(ambitoNivel === 'municipio'); }, [ambitoNivel]); const topResults = data.resultadosPanel.slice(0, 3); if (topResults.length === 0 && ambitoNivel === 'pais') { return null; } // 3. Clases condicionales también se construyen con el objeto 'styles' const cardClasses = [ styles.mobileResultsCardContainer, isExpanded ? styles.expanded : '', styles[`view-${mobileView}`] ].join(' '); return (
{/* Sección Colapsable con Resultados */}
setIsExpanded(!isExpanded)}>

{ambitoNombre}

{isExpanded ? 'Ocultar resultados' : 'Ver top 3'}
{isExpanded ? : }
{topResults.length > 0 ? ( topResults.map(partido => ) ) : (

No hay resultados para esta selección.

)}
{/* Footer Fijo con Botones de Navegación */}
); }; // --- WIDGET PRINCIPAL --- interface PanelNacionalWidgetProps { eleccionId: number; } type AmbitoState = { id: string | null; nivel: 'pais' | 'provincia' | 'municipio'; nombre: string; provinciaNombre?: string; provinciaDistritoId?: string | null; }; const CATEGORIAS_NACIONALES = [ { value: 3, label: 'Diputados Nacionales' }, { value: 2, label: 'Senadores Nacionales' }, ]; const PanelContenido = ({ eleccionId, ambitoActual, categoriaId }: { eleccionId: number, ambitoActual: AmbitoState, categoriaId: number }) => { const { data } = useSuspenseQuery({ queryKey: ['panelElectoral', eleccionId, ambitoActual.id, categoriaId], queryFn: () => getPanelElectoral(eleccionId, ambitoActual.id, categoriaId), refetchInterval: 30000, }); // Si la API devolvió la bandera 'sinDatos', mostramos un mensaje. if (data.sinDatos) { return (

Sin Resultados Detallados

Aún no hay datos disponibles para esta selección.

Por favor, intente de nuevo más tarde.

); } // Si no, renderizamos los resultados. return ; }; export const PanelNacionalWidget = ({ eleccionId }: PanelNacionalWidgetProps) => { const queryClient = useQueryClient(); const [ambitoActual, setAmbitoActual] = useState({ id: null, nivel: 'pais', nombre: 'Argentina', provinciaDistritoId: null }); const [categoriaId, setCategoriaId] = useState(3); const [isPanelOpen, setIsPanelOpen] = useState(true); const [mobileView, setMobileView] = useState<'mapa' | 'resultados'>('mapa'); const isMobile = useMediaQuery('(max-width: 800px)'); const handleAmbitoSelect = (nuevoAmbitoId: string, nuevoNivel: 'provincia' | 'municipio', nuevoNombre: string) => { if (nuevoNivel === 'municipio') { toast.promise( queryClient.invalidateQueries({ queryKey: ['panelElectoral', eleccionId, nuevoAmbitoId, categoriaId] }), { loading: `Cargando datos de ${nuevoNombre}...`, error: No se pudieron cargar los datos., } ); } setAmbitoActual(prev => ({ id: nuevoAmbitoId, nivel: nuevoNivel, nombre: nuevoNombre, provinciaNombre: nuevoNivel === 'municipio' ? prev.provinciaNombre : (nuevoNivel === 'provincia' ? nuevoNombre : undefined), 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, provinciaDistritoId: ambitoActual.provinciaDistritoId, provinciaNombre: ambitoActual.provinciaNombre, }); } else { handleResetToPais(); } }; const selectedCategoria = useMemo(() => CATEGORIAS_NACIONALES.find(c => c.value === categoriaId), [categoriaId] ); const mainContentClasses = [ styles.panelMainContent, !isPanelOpen ? styles.panelCollapsed : '', isMobile ? styles[`mobile-view-${mobileView}`] : '' ].join(' '); return (