Fix Overrides Candidatos
This commit is contained in:
		| @@ -9,6 +9,14 @@ const SENADORES_ID = 5; | ||||
| const DIPUTADOS_ID = 6; | ||||
| const CONCEJALES_ID = 7; | ||||
|  | ||||
| // Esta función limpia cualquier carácter no válido de un string de color. | ||||
| const sanitizeColor = (color: string | null | undefined): string => { | ||||
|     if (!color) return '#000000'; // Devuelve un color válido por defecto si es nulo | ||||
|     // Usa una expresión regular para eliminar todo lo que no sea un '#' o un carácter hexadecimal | ||||
|     const sanitized = color.replace(/[^#0-9a-fA-F]/g, ''); | ||||
|     return sanitized.startsWith('#') ? sanitized : `#${sanitized}`; | ||||
| }; | ||||
|  | ||||
| export const AgrupacionesManager = () => { | ||||
|     const queryClient = useQueryClient(); | ||||
|  | ||||
| @@ -27,20 +35,33 @@ export const AgrupacionesManager = () => { | ||||
|         queryFn: getLogos, | ||||
|     }); | ||||
|  | ||||
|     // Usamos useEffect para reaccionar cuando los datos de 'logos' se cargan o cambian. | ||||
|     useEffect(() => { | ||||
|         if (logos) { | ||||
|             setEditedLogos(logos); | ||||
|         // Solo procedemos si los datos de agrupaciones están disponibles | ||||
|         if (agrupaciones && agrupaciones.length > 0) { | ||||
|             // Inicializamos el estado de 'editedAgrupaciones' una sola vez. | ||||
|             // Usamos una función en setState para asegurarnos de que solo se ejecute | ||||
|             // si el estado está vacío, evitando sobreescribir ediciones del usuario. | ||||
|             setEditedAgrupaciones(prev => { | ||||
|                 if (Object.keys(prev).length === 0) { | ||||
|                     return Object.fromEntries(agrupaciones.map(a => [a.id, {}])); | ||||
|                 } | ||||
|                 return prev; | ||||
|             }); | ||||
|         } | ||||
|     }, [logos]); | ||||
|  | ||||
|     // Usamos otro useEffect para reaccionar a los datos de 'agrupaciones'. | ||||
|     useEffect(() => { | ||||
|         if (agrupaciones) { | ||||
|             const initialEdits = Object.fromEntries(agrupaciones.map(a => [a.id, {}])); | ||||
|             setEditedAgrupaciones(initialEdits); | ||||
|         // Hacemos lo mismo para los logos | ||||
|         if (logos && logos.length > 0) { | ||||
|             setEditedLogos(prev => { | ||||
|                 if (prev.length === 0) { | ||||
|                     // Creamos una copia profunda para evitar mutaciones accidentales | ||||
|                     return JSON.parse(JSON.stringify(logos)); | ||||
|                 } | ||||
|                 return prev; | ||||
|             }); | ||||
|         } | ||||
|     }, [agrupaciones]); | ||||
|         // La dependencia ahora es el estado de carga. El hook se ejecutará cuando | ||||
|         // isLoadingAgrupaciones o isLoadingLogos cambien de true a false. | ||||
|     }, [agrupaciones, logos]); | ||||
|  | ||||
|     const handleInputChange = (id: string, field: 'nombreCorto' | 'color', value: string) => { | ||||
|         setEditedAgrupaciones(prev => ({ | ||||
| @@ -130,7 +151,14 @@ export const AgrupacionesManager = () => { | ||||
|                                 <tr key={agrupacion.id}> | ||||
|                                     <td>{agrupacion.nombre}</td> | ||||
|                                     <td><input type="text" value={editedAgrupaciones[agrupacion.id]?.nombreCorto ?? agrupacion.nombreCorto ?? ''} onChange={(e) => handleInputChange(agrupacion.id, 'nombreCorto', e.target.value)} /></td> | ||||
|                                     <td><input type="color" value={editedAgrupaciones[agrupacion.id]?.color ?? agrupacion.color ?? '#000000'} onChange={(e) => handleInputChange(agrupacion.id, 'color', e.target.value)} /></td> | ||||
|                                     <td> | ||||
|                                         <input  | ||||
|                                             type="color"  | ||||
|                                             // Usamos la función sanitizeColor para asegurarnos de que el valor sea siempre válido | ||||
|                                             value={sanitizeColor(editedAgrupaciones[agrupacion.id]?.color ?? agrupacion.color)}  | ||||
|                                             onChange={(e) => handleInputChange(agrupacion.id, 'color', e.target.value)}  | ||||
|                                         /> | ||||
|                                     </td> | ||||
|                                     <td><input type="text" placeholder="URL de la imagen" value={getLogoUrl(agrupacion.id, SENADORES_ID)} onChange={(e) => handleLogoChange(agrupacion.id, SENADORES_ID, e.target.value)} /></td> | ||||
|                                     <td><input type="text" placeholder="URL de la imagen" value={getLogoUrl(agrupacion.id, DIPUTADOS_ID)} onChange={(e) => handleLogoChange(agrupacion.id, DIPUTADOS_ID, e.target.value)} /></td> | ||||
|                                     <td><input type="text" placeholder="URL de la imagen" value={getLogoUrl(agrupacion.id, CONCEJALES_ID)} onChange={(e) => handleLogoChange(agrupacion.id, CONCEJALES_ID, e.target.value)} /></td> | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| // src/components/CandidatoOverridesManager.tsx | ||||
|  | ||||
| import { useState, useMemo, useEffect } from 'react'; | ||||
| import { useQuery, useQueryClient } from '@tanstack/react-query'; | ||||
| import Select from 'react-select'; | ||||
| import { getMunicipiosForAdmin, getAgrupaciones, getCandidatos, updateCandidatos } from '../services/apiService'; | ||||
| import type { MunicipioSimple, AgrupacionPolitica, CandidatoOverride } from '../types'; | ||||
|  | ||||
| // Las categorías son las mismas que para los logos | ||||
| const CATEGORIAS_OPTIONS = [ | ||||
|     { value: 5, label: 'Senadores' }, | ||||
|     { value: 6, label: 'Diputados' }, | ||||
| @@ -15,23 +16,31 @@ export const CandidatoOverridesManager = () => { | ||||
|     const queryClient = useQueryClient(); | ||||
|     const { data: municipios = [] } = useQuery<MunicipioSimple[]>({ queryKey: ['municipiosForAdmin'], queryFn: getMunicipiosForAdmin }); | ||||
|     const { data: agrupaciones = [] } = useQuery<AgrupacionPolitica[]>({ queryKey: ['agrupaciones'], queryFn: getAgrupaciones }); | ||||
|     // --- Usar la query para candidatos --- | ||||
|     const { data: candidatos = [] } = useQuery<CandidatoOverride[]>({ queryKey: ['candidatos'], queryFn: getCandidatos }); | ||||
|  | ||||
|     const [selectedCategoria, setSelectedCategoria] = useState<{ value: number; label: string } | null>(null); | ||||
|     const [selectedMunicipio, setSelectedMunicipio] = useState<{ value: string; label: string } | null>(null); | ||||
|     const [selectedAgrupacion, setSelectedAgrupacion] = useState<{ value: string; label: string } | null>(null); | ||||
|     // --- El estado es para el nombre del candidato --- | ||||
|     const [nombreCandidato, setNombreCandidato] = useState(''); | ||||
|  | ||||
|     const municipioOptions = useMemo(() => municipios.map(m => ({ value: m.id, label: m.nombre })), [municipios]); | ||||
|     const municipioOptions = useMemo(() =>  | ||||
|         // Añadimos la opción "General" que representará un ámbito nulo | ||||
|         [{ value: 'general', label: 'General (Todos los Municipios)' }, ...municipios.map(m => ({ value: m.id, label: m.nombre }))] | ||||
|     , [municipios]); | ||||
|      | ||||
|     const agrupacionOptions = useMemo(() => agrupaciones.map(a => ({ value: a.id, label: a.nombre })), [agrupaciones]); | ||||
|  | ||||
|     // --- Lógica para encontrar el nombre del candidato actual --- | ||||
|     const currentCandidato = useMemo(() => { | ||||
|         if (!selectedMunicipio || !selectedAgrupacion || !selectedCategoria) return ''; | ||||
|         if (!selectedAgrupacion || !selectedCategoria) return ''; | ||||
|          | ||||
|         // Determina si estamos buscando un override general (null) o específico (ID numérico) | ||||
|         const ambitoIdBuscado = selectedMunicipio?.value === 'general' ? null : (selectedMunicipio ? parseInt(selectedMunicipio.value) : undefined); | ||||
|  | ||||
|         // Si no se ha seleccionado un municipio, no buscamos nada | ||||
|         if (ambitoIdBuscado === undefined) return ''; | ||||
|  | ||||
|         return candidatos.find(c =>  | ||||
|             c.ambitoGeograficoId === parseInt(selectedMunicipio.value) &&  | ||||
|             c.ambitoGeograficoId === ambitoIdBuscado &&  | ||||
|             c.agrupacionPoliticaId === selectedAgrupacion.value && | ||||
|             c.categoriaId === selectedCategoria.value | ||||
|         )?.nombreCandidato || ''; | ||||
| @@ -42,34 +51,40 @@ export const CandidatoOverridesManager = () => { | ||||
|     const handleSave = async () => { | ||||
|         if (!selectedMunicipio || !selectedAgrupacion || !selectedCategoria) return; | ||||
|  | ||||
|         // --- Construir el objeto CandidatoOverride --- | ||||
|         const ambitoIdParaEnviar = selectedMunicipio.value === 'general'  | ||||
|             ? null  | ||||
|             : parseInt(selectedMunicipio.value); | ||||
|  | ||||
|         const newCandidatoEntry: CandidatoOverride = { | ||||
|             id: 0, // El backend no necesita el ID para un upsert | ||||
|             id: 0, // El backend no lo necesita para el upsert | ||||
|             agrupacionPoliticaId: selectedAgrupacion.value, | ||||
|             categoriaId: selectedCategoria.value, | ||||
|             ambitoGeograficoId: parseInt(selectedMunicipio.value), | ||||
|             nombreCandidato: nombreCandidato | ||||
|             ambitoGeograficoId: ambitoIdParaEnviar, | ||||
|             nombreCandidato: nombreCandidato || null | ||||
|         }; | ||||
|  | ||||
|         try { | ||||
|             await updateCandidatos([newCandidatoEntry]); | ||||
|             queryClient.invalidateQueries({ queryKey: ['candidatos'] }); | ||||
|             alert('Override de candidato guardado.'); | ||||
|         } catch { alert('Error al guardar.'); } | ||||
|         } 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 municipio y categoría determinados.</p> | ||||
|             <div style={{ display: 'flex', gap: '1rem', alignItems: 'flex-end' }}> | ||||
|             <p>Configure un nombre de candidato específico para un partido, categoría y municipio (o general).</p> | ||||
|             <div style={{ display: 'flex', gap: '1rem', alignItems: 'flex-end', flexWrap: 'wrap' }}> | ||||
|                 <div style={{ flex: 1 }}> | ||||
|                     <label>Categoría</label> | ||||
|                     <Select options={CATEGORIAS_OPTIONS} value={selectedCategoria} onChange={setSelectedCategoria} isClearable placeholder="Seleccione..."/> | ||||
|                 </div> | ||||
|                 <div style={{ flex: 1 }}> | ||||
|                     <label>Municipio</label> | ||||
|                     <Select options={municipioOptions} value={selectedMunicipio} onChange={setSelectedMunicipio} isClearable placeholder="Seleccione..."/> | ||||
|                     <label>Municipio (Opcional)</label> | ||||
|                     <Select options={municipioOptions} value={selectedMunicipio} onChange={setSelectedMunicipio} isClearable placeholder="General..."/> | ||||
|                 </div> | ||||
|                 <div style={{ flex: 1 }}> | ||||
|                     <label>Agrupación</label> | ||||
| @@ -77,9 +92,9 @@ export const CandidatoOverridesManager = () => { | ||||
|                 </div> | ||||
|                 <div style={{ flex: 2 }}> | ||||
|                     <label>Nombre del Candidato</label> | ||||
|                     <input type="text" value={nombreCandidato} onChange={e => setNombreCandidato(e.target.value)} style={{ width: '100%' }} disabled={!selectedMunicipio || !selectedAgrupacion || !selectedCategoria} /> | ||||
|                     <input type="text" value={nombreCandidato} onChange={e => setNombreCandidato(e.target.value)} style={{ width: '100%' }} disabled={!selectedAgrupacion || !selectedCategoria} /> | ||||
|                 </div> | ||||
|                 <button onClick={handleSave} disabled={!selectedMunicipio || !selectedAgrupacion || !selectedCategoria}>Guardar</button> | ||||
|                 <button onClick={handleSave} disabled={!selectedAgrupacion || !selectedCategoria}>Guardar</button> | ||||
|             </div> | ||||
|         </div> | ||||
|     ); | ||||
|   | ||||
| @@ -24,7 +24,9 @@ export const LogoOverridesManager = () => { | ||||
|     const [selectedAgrupacion, setSelectedAgrupacion] = useState<{ value: string; label: string } | null>(null); | ||||
|     const [logoUrl, setLogoUrl] = useState(''); | ||||
|  | ||||
|     const municipioOptions = useMemo(() => municipios.map(m => ({ value: m.id, label: m.nombre })), [municipios]); | ||||
|     const municipioOptions = useMemo(() =>  | ||||
|         [{ value: 'general', label: 'General (Todas las secciones)' }, ...municipios.map(m => ({ value: m.id, label: m.nombre }))] | ||||
|     , [municipios]); | ||||
|     const agrupacionOptions = useMemo(() => agrupaciones.map(a => ({ value: a.id, label: a.nombre })), [agrupaciones]); | ||||
|  | ||||
|     const currentLogo = useMemo(() => { | ||||
|   | ||||
| @@ -55,5 +55,5 @@ export interface CandidatoOverride { | ||||
|   agrupacionPoliticaId: string; | ||||
|   categoriaId: number; | ||||
|   ambitoGeograficoId: number | null; | ||||
|   nombreCandidato: string; | ||||
|   nombreCandidato: string | null; | ||||
| } | ||||
| @@ -252,11 +252,10 @@ public class AdminController : ControllerBase | ||||
|   /// Guarda (actualiza o crea) una lista de overrides de candidatos. | ||||
|   /// </summary> | ||||
|   [HttpPut("candidatos")] | ||||
|   public async Task<IActionResult> UpdateCandidatos([FromBody] List<CandidatoOverride> candidatos) | ||||
|   public async Task<IActionResult> UpdateCandidatos([FromBody] List<UpdateCandidatoDto> candidatos) | ||||
|   { | ||||
|     foreach (var candidatoDto in candidatos) | ||||
|     { | ||||
|       // Buscamos un override existente basado en la combinación única | ||||
|       var candidatoExistente = await _dbContext.CandidatosOverrides | ||||
|           .FirstOrDefaultAsync(c => | ||||
|               c.AgrupacionPoliticaId == candidatoDto.AgrupacionPoliticaId && | ||||
| @@ -265,18 +264,24 @@ public class AdminController : ControllerBase | ||||
|  | ||||
|       if (candidatoExistente != null) | ||||
|       { | ||||
|         // Si existe y el nombre es diferente, lo actualizamos. | ||||
|         if (candidatoExistente.NombreCandidato != candidatoDto.NombreCandidato) | ||||
|         // El registro ya existe | ||||
|         if (string.IsNullOrWhiteSpace(candidatoDto.NombreCandidato)) | ||||
|         { | ||||
|           // El usuario envió un nombre vacío -> Eliminar el registro | ||||
|           _dbContext.CandidatosOverrides.Remove(candidatoExistente); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|           // El usuario envió un nombre válido -> Actualizar | ||||
|           candidatoExistente.NombreCandidato = candidatoDto.NombreCandidato; | ||||
|         } | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         // Si no se encontró un registro exacto, lo añadimos a la base de datos, | ||||
|         // pero solo si el nombre del candidato no está vacío. | ||||
|         // El registro no existe | ||||
|         if (!string.IsNullOrWhiteSpace(candidatoDto.NombreCandidato)) | ||||
|         { | ||||
|           // El usuario envió un nombre válido -> Crear nuevo registro | ||||
|           _dbContext.CandidatosOverrides.Add(new CandidatoOverride | ||||
|           { | ||||
|             AgrupacionPoliticaId = candidatoDto.AgrupacionPoliticaId, | ||||
| @@ -285,27 +290,11 @@ public class AdminController : ControllerBase | ||||
|             NombreCandidato = candidatoDto.NombreCandidato | ||||
|           }); | ||||
|         } | ||||
|         // Si no existe y el nombre está vacío, no hacemos nada. | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // También necesitamos manejar los casos donde se borra un nombre (se envía un string vacío) | ||||
|     var overridesAEliminar = await _dbContext.CandidatosOverrides | ||||
|         .Where(c => candidatos.Any(dto => | ||||
|             dto.AgrupacionPoliticaId == c.AgrupacionPoliticaId && | ||||
|             dto.CategoriaId == c.CategoriaId && | ||||
|             dto.AmbitoGeograficoId == c.AmbitoGeograficoId && | ||||
|             string.IsNullOrWhiteSpace(dto.NombreCandidato) | ||||
|         )) | ||||
|         .ToListAsync(); | ||||
|  | ||||
|     if (overridesAEliminar.Any()) | ||||
|     { | ||||
|       _dbContext.CandidatosOverrides.RemoveRange(overridesAEliminar); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     await _dbContext.SaveChangesAsync(); | ||||
|     _logger.LogInformation("Se procesaron {Count} overrides de candidatos.", candidatos.Count); | ||||
|     return NoContent(); // Respuesta estándar para un PUT exitoso | ||||
|     return NoContent(); | ||||
|   } | ||||
| } | ||||
| @@ -14,7 +14,7 @@ using System.Reflection; | ||||
| [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] | ||||
| [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | ||||
| [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")] | ||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] | ||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] | ||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| {"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","/hrHm\u002B3v8DuHSlyFsxHPCFUvDW\u002BZsLR4kaMxNwUHl2M=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v\u002BEjGaN1m59e9gwl3kXTpjNw\u002B3kwhJD2SLKx38/opjM="],"CachedAssets":{},"CachedCopyCandidates":{}} | ||||
| {"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Kt4ImnGs0wklEJp/6NxrhrTvGLQxPfYUAB5LMWAnz10=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v1SBeIVg8rE3EddYwnvF/EsPYr2F5GAppt/Egvdtr/0="],"CachedAssets":{},"CachedCopyCandidates":{}} | ||||
| @@ -1 +1 @@ | ||||
| {"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","/hrHm\u002B3v8DuHSlyFsxHPCFUvDW\u002BZsLR4kaMxNwUHl2M=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v\u002BEjGaN1m59e9gwl3kXTpjNw\u002B3kwhJD2SLKx38/opjM="],"CachedAssets":{},"CachedCopyCandidates":{}} | ||||
| {"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Kt4ImnGs0wklEJp/6NxrhrTvGLQxPfYUAB5LMWAnz10=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v1SBeIVg8rE3EddYwnvF/EsPYr2F5GAppt/Egvdtr/0="],"CachedAssets":{},"CachedCopyCandidates":{}} | ||||
| @@ -0,0 +1,10 @@ | ||||
| namespace Elecciones.Core.DTOs.ApiRequests; | ||||
|  | ||||
| public class UpdateCandidatoDto | ||||
| { | ||||
|     // Esta clase solo contiene los IDs y el valor, igual que lo que envía el frontend. | ||||
|     public string AgrupacionPoliticaId { get; set; } = null!; | ||||
|     public int CategoriaId { get; set; } | ||||
|     public int? AmbitoGeograficoId { get; set; } | ||||
|     public string? NombreCandidato { get; set; } | ||||
| } | ||||
| @@ -13,7 +13,7 @@ using System.Reflection; | ||||
| [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")] | ||||
| [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | ||||
| [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")] | ||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")] | ||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")] | ||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Reflection; | ||||
| [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")] | ||||
| [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | ||||
| [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")] | ||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")] | ||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")] | ||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||
|   | ||||
| @@ -13,7 +13,7 @@ using System.Reflection; | ||||
| [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] | ||||
| [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] | ||||
| [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] | ||||
| [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")] | ||||
| [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] | ||||
| [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] | ||||
| [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user