91 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			91 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | // src/components/BancasWidget.tsx
 | ||
|  | import { useState, useEffect } from 'react'; | ||
|  | import { ResponsiveWaffle } from '@nivo/waffle'; | ||
|  | import { getBancasPorSeccion } from '../apiService'; | ||
|  | import type { ProyeccionBancas } from '../types'; | ||
|  | import './BancasWidget.css'; | ||
|  | 
 | ||
|  | // Paleta de colores consistente
 | ||
|  | const NIVO_COLORS = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]; | ||
|  | 
 | ||
|  | // Las Secciones Electorales de la Provincia (esto podría venir de la API en el futuro)
 | ||
|  | const secciones = [ | ||
|  |     { id: '1', nombre: 'Primera Sección' }, | ||
|  |     { id: '2', nombre: 'Segunda Sección' }, | ||
|  |     { id: '3', nombre: 'Tercera Sección' }, | ||
|  |     { id: '4', nombre: 'Cuarta Sección' }, | ||
|  |     { id: '5', nombre: 'Quinta Sección' }, | ||
|  |     { id: '6', nombre: 'Sexta Sección' }, | ||
|  |     { id: '7', nombre: 'Séptima Sección' }, | ||
|  |     { id: '8', nombre: 'Octava Sección (Capital)' }, | ||
|  | ]; | ||
|  | 
 | ||
|  | export const BancasWidget = () => { | ||
|  |     const [seccionActual, setSeccionActual] = useState('1'); // Empezamos con la Primera Sección
 | ||
|  |     const [data, setData] = useState<ProyeccionBancas | null>(null); | ||
|  |     const [loading, setLoading] = useState(true); | ||
|  | 
 | ||
|  |     useEffect(() => { | ||
|  |         const fetchData = async () => { | ||
|  |             setLoading(true); | ||
|  |             try { | ||
|  |                 const result = await getBancasPorSeccion(seccionActual); | ||
|  |                 setData(result); | ||
|  |             } catch (error) { | ||
|  |                 console.error(`Error cargando datos de bancas para sección ${seccionActual}:`, error); | ||
|  |                 setData(null); // Limpiar datos en caso de error
 | ||
|  |             } finally { | ||
|  |                 setLoading(false); | ||
|  |             } | ||
|  |         }; | ||
|  |         fetchData(); | ||
|  |     }, [seccionActual]); // Se ejecuta cada vez que cambia la sección
 | ||
|  | 
 | ||
|  |     const waffleData = data?.proyeccion.map(p => ({ | ||
|  |         id: p.agrupacionNombre, | ||
|  |         label: p.agrupacionNombre, | ||
|  |         value: p.bancas, | ||
|  |     })) || []; | ||
|  | 
 | ||
|  |     const totalBancas = waffleData.reduce((sum, current) => sum + current.value, 0); | ||
|  | 
 | ||
|  |     return ( | ||
|  |         <div className="bancas-widget-container"> | ||
|  |             <div className="bancas-header"> | ||
|  |                 <h4>Distribución de Bancas</h4> | ||
|  |                 <select value={seccionActual} onChange={e => setSeccionActual(e.target.value)}> | ||
|  |                     {secciones.map(s => <option key={s.id} value={s.id}>{s.nombre}</option>)} | ||
|  |                 </select> | ||
|  |             </div> | ||
|  |             <div className="waffle-chart-container"> | ||
|  |                 {loading ? <p>Cargando...</p> : !data ? <p>No hay datos disponibles para esta sección.</p> : | ||
|  |                 <ResponsiveWaffle | ||
|  |                     data={waffleData} | ||
|  |                     total={totalBancas} | ||
|  |                     rows={8} | ||
|  |                     columns={10} | ||
|  |                     fillDirection="bottom" | ||
|  |                     padding={3} | ||
|  |                     colors={NIVO_COLORS} | ||
|  |                     borderColor={{ from: 'color', modifiers: [['darker', 0.3]] }} | ||
|  |                     animate={true} | ||
|  |                     legends={[ | ||
|  |                         { | ||
|  |                             anchor: 'bottom', | ||
|  |                             direction: 'row', | ||
|  |                             justify: false, | ||
|  |                             translateX: 0, | ||
|  |                             translateY: 40, | ||
|  |                             itemsSpacing: 4, | ||
|  |                             itemWidth: 100, | ||
|  |                             itemHeight: 20, | ||
|  |                             itemTextColor: '#999', | ||
|  |                             itemDirection: 'left-to-right', | ||
|  |                             symbolSize: 20, | ||
|  |                         }, | ||
|  |                     ]} | ||
|  |                 />} | ||
|  |             </div> | ||
|  |         </div> | ||
|  |     ); | ||
|  | }; |