| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  | // src/components/CandidatoOverridesManager.tsx
 | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | import { useState, useMemo, useEffect } from 'react'; | 
					
						
							|  |  |  | import { useQuery, useQueryClient } from '@tanstack/react-query'; | 
					
						
							|  |  |  | import Select from 'react-select'; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | import { getProvinciasForAdmin, getMunicipiosForAdmin, getAgrupaciones, getCandidatos, updateCandidatos } from '../services/apiService'; | 
					
						
							|  |  |  | import type { MunicipioSimple, AgrupacionPolitica, CandidatoOverride, ProvinciaSimple } from '../types'; | 
					
						
							|  |  |  | import { CATEGORIAS_NACIONALES_OPTIONS, CATEGORIAS_PROVINCIALES_OPTIONS } from '../constants/categorias'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-20 11:03:19 -03:00
										 |  |  | const ELECCION_OPTIONS = [ | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |     { value: 0, label: 'General (Todas las elecciones)' }, | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     { value: 2, label: 'Elecciones Nacionales' }, | 
					
						
							|  |  |  |     { value: 1, label: 'Elecciones Provinciales' } | 
					
						
							|  |  |  | ]; | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | const AMBITO_LEVEL_OPTIONS = [ | 
					
						
							|  |  |  |     { value: 'general', label: 'General (Toda la elección)' }, | 
					
						
							|  |  |  |     { value: 'provincia', label: 'Por Provincia' }, | 
					
						
							|  |  |  |     { value: 'municipio', label: 'Por Municipio' } | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const CandidatoOverridesManager = () => { | 
					
						
							|  |  |  |     const queryClient = useQueryClient(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const [selectedEleccion, setSelectedEleccion] = useState(ELECCION_OPTIONS[0]); | 
					
						
							|  |  |  |     const [selectedAmbitoLevel, setSelectedAmbitoLevel] = useState(AMBITO_LEVEL_OPTIONS[0]); | 
					
						
							|  |  |  |     const [selectedProvincia, setSelectedProvincia] = useState<ProvinciaSimple | null>(null); | 
					
						
							|  |  |  |     const [selectedMunicipio, setSelectedMunicipio] = useState<MunicipioSimple | null>(null); | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |     const [selectedCategoria, setSelectedCategoria] = useState<{ value: number; label: string } | null>(null); | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const [selectedAgrupacion, setSelectedAgrupacion] = useState<AgrupacionPolitica | null>(null); | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |     const [nombreCandidato, setNombreCandidato] = useState(''); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const { data: provincias = [] } = useQuery<ProvinciaSimple[]>({ queryKey: ['provinciasForAdmin'], queryFn: getProvinciasForAdmin }); | 
					
						
							|  |  |  |     const { data: municipios = [] } = useQuery<MunicipioSimple[]>({ queryKey: ['municipiosForAdmin'], queryFn: getMunicipiosForAdmin }); | 
					
						
							|  |  |  |     const { data: agrupaciones = [] } = useQuery<AgrupacionPolitica[]>({ queryKey: ['agrupaciones'], queryFn: getAgrupaciones }); | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const { data: candidatos = [] } = useQuery<CandidatoOverride[]>({ | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |         queryKey: ['allCandidatos'], | 
					
						
							|  |  |  |         queryFn: () => Promise.all([getCandidatos(0), getCandidatos(1), getCandidatos(2)]).then(res => res.flat()), | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |     const categoriaOptions = useMemo(() => { | 
					
						
							|  |  |  |         if (selectedEleccion.value === 2) return CATEGORIAS_NACIONALES_OPTIONS; | 
					
						
							|  |  |  |         if (selectedEleccion.value === 1) return CATEGORIAS_PROVINCIALES_OPTIONS; | 
					
						
							|  |  |  |         return [...CATEGORIAS_NACIONALES_OPTIONS, ...CATEGORIAS_PROVINCIALES_OPTIONS]; | 
					
						
							|  |  |  |     }, [selectedEleccion]); | 
					
						
							| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const getAmbitoId = () => { | 
					
						
							|  |  |  |         if (selectedAmbitoLevel.value === 'municipio' && selectedMunicipio) return parseInt(selectedMunicipio.id); | 
					
						
							|  |  |  |         if (selectedAmbitoLevel.value === 'provincia' && selectedProvincia) return parseInt(selectedProvincia.id); | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const currentCandidato = useMemo(() => { | 
					
						
							|  |  |  |         if (!selectedAgrupacion || !selectedCategoria) return ''; | 
					
						
							|  |  |  |         const ambitoId = getAmbitoId(); | 
					
						
							|  |  |  |         return candidatos.find(c => | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |             c.eleccionId === selectedEleccion.value && | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |             c.ambitoGeograficoId === ambitoId && | 
					
						
							|  |  |  |             c.agrupacionPoliticaId === selectedAgrupacion.id && | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |             c.categoriaId === selectedCategoria.value | 
					
						
							|  |  |  |         )?.nombreCandidato || ''; | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |     }, [candidatos, selectedEleccion, selectedAmbitoLevel, selectedProvincia, selectedMunicipio, selectedAgrupacion, selectedCategoria]); | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     useEffect(() => { setNombreCandidato(currentCandidato || ''); }, [currentCandidato]); | 
					
						
							| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |     const handleSave = async () => { | 
					
						
							|  |  |  |         if (!selectedAgrupacion || !selectedCategoria) return; | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |         const newCandidatoEntry: CandidatoOverride = { | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |             id: 0, | 
					
						
							|  |  |  |             eleccionId: selectedEleccion.value, | 
					
						
							|  |  |  |             agrupacionPoliticaId: selectedAgrupacion.id, | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |             categoriaId: selectedCategoria.value, | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |             ambitoGeograficoId: getAmbitoId(), | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |             nombreCandidato: nombreCandidato.trim() || null | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |         }; | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             await updateCandidatos([newCandidatoEntry]); | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |             queryClient.invalidateQueries({ queryKey: ['allCandidatos'] }); | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |             alert('Override de candidato guardado.'); | 
					
						
							| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  |         } catch (error) { | 
					
						
							|  |  |  |             console.error(error); | 
					
						
							|  |  |  |             alert('Error al guardar el override del candidato.'); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |     return ( | 
					
						
							|  |  |  |         <div className="admin-module"> | 
					
						
							|  |  |  |             <h3>Overrides de Nombres de Candidatos</h3> | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |             <p>Configure un nombre de candidato específico para un partido en un contexto determinado.</p> | 
					
						
							|  |  |  |             <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem', alignItems: 'flex-end' }}> | 
					
						
							|  |  |  |                 <Select options={ELECCION_OPTIONS} value={selectedEleccion} onChange={(opt) => { setSelectedEleccion(opt!); setSelectedCategoria(null); }} /> | 
					
						
							| 
									
										
										
										
											2025-10-22 15:53:40 -03:00
										 |  |  |                 <Select options={categoriaOptions} value={selectedCategoria} onChange={setSelectedCategoria} placeholder="Seleccione Categoría..." /> | 
					
						
							| 
									
										
										
										
											2025-10-20 11:03:19 -03:00
										 |  |  |                 <Select | 
					
						
							|  |  |  |                     options={agrupaciones.map(a => ({ value: a.id, label: a.nombre, ...a }))} | 
					
						
							|  |  |  |                     getOptionValue={opt => opt.id} | 
					
						
							|  |  |  |                     getOptionLabel={opt => `(${opt.id}) ${opt.nombre}`} | 
					
						
							|  |  |  |                     value={selectedAgrupacion} | 
					
						
							|  |  |  |                     onChange={setSelectedAgrupacion} | 
					
						
							|  |  |  |                     placeholder="Seleccione Agrupación..." | 
					
						
							|  |  |  |                 /> | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |                 <Select options={AMBITO_LEVEL_OPTIONS} value={selectedAmbitoLevel} onChange={(opt) => { setSelectedAmbitoLevel(opt!); setSelectedProvincia(null); setSelectedMunicipio(null); }} /> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 {selectedAmbitoLevel.value === 'provincia' || selectedAmbitoLevel.value === 'municipio' ? ( | 
					
						
							|  |  |  |                     <Select options={provincias.map(p => ({ value: p.id, label: p.nombre, ...p }))} getOptionValue={opt => opt.id} getOptionLabel={opt => opt.nombre} value={selectedProvincia} onChange={setSelectedProvincia} placeholder="Seleccione Provincia..." /> | 
					
						
							|  |  |  |                 ) : <div />} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 {selectedAmbitoLevel.value === 'municipio' ? ( | 
					
						
							|  |  |  |                     <Select options={municipios.map(m => ({ value: m.id, label: m.nombre, ...m }))} getOptionValue={opt => opt.id} getOptionLabel={opt => opt.nombre} value={selectedMunicipio} onChange={setSelectedMunicipio} placeholder="Seleccione Municipio..." isDisabled={!selectedProvincia} /> | 
					
						
							|  |  |  |                 ) : <div />} | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |             <div style={{ display: 'flex', gap: '1rem', alignItems: 'flex-end', marginTop: '1rem' }}> | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |                 <div style={{ flex: 1 }}> | 
					
						
							|  |  |  |                     <label>Nombre del Candidato</label> | 
					
						
							| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  |                     <input type="text" value={nombreCandidato} onChange={e => setNombreCandidato(e.target.value)} style={{ width: '100%' }} disabled={!selectedAgrupacion || !selectedCategoria} /> | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |                 </div> | 
					
						
							| 
									
										
										
										
											2025-09-05 12:58:52 -03:00
										 |  |  |                 <button onClick={handleSave} disabled={!selectedAgrupacion || !selectedCategoria}>Guardar</button> | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  |             </div> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | }; |