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-10-01 10:03:01 -03:00
|
|
|
|
import { Toaster } from 'react-hot-toast';
|
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">
|
2025-10-01 10:03:01 -03:00
|
|
|
|
<Toaster containerClassName="widget-toaster-container" />
|
2025-09-17 11:31:17 -03:00
|
|
|
|
<header className="panel-header">
|
|
|
|
|
|
<div className="header-top-row">
|
|
|
|
|
|
<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
|
|
|
|
/>
|
2025-10-01 10:03:01 -03:00
|
|
|
|
<Breadcrumbs
|
|
|
|
|
|
nivel={ambitoActual.nivel}
|
|
|
|
|
|
nombreAmbito={ambitoActual.nombre}
|
|
|
|
|
|
nombreProvincia={ambitoActual.provinciaNombre}
|
|
|
|
|
|
onReset={handleResetToPais}
|
|
|
|
|
|
onVolverProvincia={handleVolverAProvincia}
|
|
|
|
|
|
/>
|
2025-09-17 11:31:17 -03:00
|
|
|
|
</div>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|