120 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| // src/components/CandidatoOverridesManager.tsx
 | |
| import { useState, useMemo, useEffect } from 'react';
 | |
| import { useQuery, useQueryClient } from '@tanstack/react-query';
 | |
| import Select from 'react-select';
 | |
| 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';
 | |
| 
 | |
| const ELECCION_OPTIONS = [
 | |
|     { value: 0, label: 'General (Todas las elecciones)' },
 | |
|     { value: 2, label: 'Elecciones Nacionales' },
 | |
|     { value: 1, label: 'Elecciones Provinciales' }
 | |
| ];
 | |
| 
 | |
| const AMBITO_LEVEL_OPTIONS = [
 | |
|     { value: 'general', label: 'General (Toda la elección)' },
 | |
|     { value: 'provincia', label: 'Por Provincia' },
 | |
|     { value: 'municipio', label: 'Por Municipio' }
 | |
| ];
 | |
| 
 | |
| export const CandidatoOverridesManager = () => {
 | |
|     const queryClient = useQueryClient();
 | |
| 
 | |
|     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);
 | |
|     const [selectedCategoria, setSelectedCategoria] = useState<{ value: number; label: string } | null>(null);
 | |
|     const [selectedAgrupacion, setSelectedAgrupacion] = useState<AgrupacionPolitica | null>(null);
 | |
|     const [nombreCandidato, setNombreCandidato] = useState('');
 | |
| 
 | |
|     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 });
 | |
| 
 | |
|     const { data: candidatos = [] } = useQuery<CandidatoOverride[]>({
 | |
|         queryKey: ['allCandidatos'],
 | |
|         queryFn: () => Promise.all([getCandidatos(0), getCandidatos(1), getCandidatos(2)]).then(res => res.flat()),
 | |
|     });
 | |
| 
 | |
|     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]);
 | |
| 
 | |
|     const getAmbitoId = () => {
 | |
|         if (selectedAmbitoLevel.value === 'municipio' && selectedMunicipio) return parseInt(selectedMunicipio.id);
 | |
|         if (selectedAmbitoLevel.value === 'provincia' && selectedProvincia) return parseInt(selectedProvincia.id);
 | |
|         return null;
 | |
|     };
 | |
| 
 | |
|     const currentCandidato = useMemo(() => {
 | |
|         if (!selectedAgrupacion || !selectedCategoria) return '';
 | |
|         const ambitoId = getAmbitoId();
 | |
|         return candidatos.find(c =>
 | |
|             c.eleccionId === selectedEleccion.value &&
 | |
|             c.ambitoGeograficoId === ambitoId &&
 | |
|             c.agrupacionPoliticaId === selectedAgrupacion.id &&
 | |
|             c.categoriaId === selectedCategoria.value
 | |
|         )?.nombreCandidato || '';
 | |
|     }, [candidatos, selectedEleccion, selectedAmbitoLevel, selectedProvincia, selectedMunicipio, selectedAgrupacion, selectedCategoria]);
 | |
| 
 | |
|     useEffect(() => { setNombreCandidato(currentCandidato || ''); }, [currentCandidato]);
 | |
| 
 | |
|     const handleSave = async () => {
 | |
|         if (!selectedAgrupacion || !selectedCategoria) return;
 | |
|         const newCandidatoEntry: CandidatoOverride = {
 | |
|             id: 0,
 | |
|             eleccionId: selectedEleccion.value,
 | |
|             agrupacionPoliticaId: selectedAgrupacion.id,
 | |
|             categoriaId: selectedCategoria.value,
 | |
|             ambitoGeograficoId: getAmbitoId(),
 | |
|             nombreCandidato: nombreCandidato.trim() || null
 | |
|         };
 | |
|         try {
 | |
|             await updateCandidatos([newCandidatoEntry]);
 | |
|             queryClient.invalidateQueries({ queryKey: ['allCandidatos'] });
 | |
|             alert('Override de candidato guardado.');
 | |
|         } catch (error) {
 | |
|             console.error(error);
 | |
|             alert('Error al guardar el override del candidato.');
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     return (
 | |
|         <div className="admin-module">
 | |
|             <h3>Overrides de Nombres de Candidatos</h3>
 | |
|             <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); }} />
 | |
|                 <Select options={categoriaOptions} value={selectedCategoria} onChange={setSelectedCategoria} placeholder="Seleccione Categoría..." />
 | |
|                 <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..."
 | |
|                 />
 | |
|                 <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' }}>
 | |
|                 <div style={{ flex: 1 }}>
 | |
|                     <label>Nombre del Candidato</label>
 | |
|                     <input type="text" value={nombreCandidato} onChange={e => setNombreCandidato(e.target.value)} style={{ width: '100%' }} disabled={!selectedAgrupacion || !selectedCategoria} />
 | |
|                 </div>
 | |
|                 <button onClick={handleSave} disabled={!selectedAgrupacion || !selectedCategoria}>Guardar</button>
 | |
|             </div>
 | |
|         </div>
 | |
|     );
 | |
| }; |