Files
Elecciones-2025/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs
dmolinari d78a02a0eb Feat Widgets
Se añade la tabla CandidatosOverrides
Se añade el Overrides de Candidatos al panel de administrador
Se Añade el nombre de los candidatos a los Widgets de categorias por municipio
2025-09-05 11:38:25 -03:00

311 lines
10 KiB
C#

// src/Elecciones.Api/Controllers/AdminController.cs
using Elecciones.Database;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Elecciones.Core.DTOs.ApiRequests;
using Elecciones.Database.Entities;
using Microsoft.AspNetCore.Authorization;
using Elecciones.Core.Enums;
namespace Elecciones.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class AdminController : ControllerBase
{
private readonly EleccionesDbContext _dbContext;
private readonly ILogger<AdminController> _logger;
public AdminController(EleccionesDbContext dbContext, ILogger<AdminController> logger)
{
_dbContext = dbContext;
_logger = logger;
}
// Endpoint para obtener todas las agrupaciones para el panel de admin
[HttpGet("agrupaciones")]
public async Task<IActionResult> GetAgrupaciones()
{
var agrupaciones = await _dbContext.AgrupacionesPoliticas
.AsNoTracking()
.OrderBy(a => a.Nombre)
.ToListAsync();
return Ok(agrupaciones);
}
[HttpPut("agrupaciones/{id}")]
public async Task<IActionResult> UpdateAgrupacion(string id, [FromBody] UpdateAgrupacionDto agrupacionDto)
{
// Buscamos la agrupación en la base de datos por su ID.
var agrupacion = await _dbContext.AgrupacionesPoliticas.FindAsync(id);
if (agrupacion == null)
{
// Si no existe, devolvemos un error 404 Not Found.
return NotFound(new { message = $"No se encontró la agrupación con ID {id}" });
}
// Actualizamos las propiedades de la entidad con los valores del DTO.
agrupacion.NombreCorto = agrupacionDto.NombreCorto;
agrupacion.Color = agrupacionDto.Color;
// Guardamos los cambios en la base de datos.
await _dbContext.SaveChangesAsync();
_logger.LogInformation("Se actualizó la agrupación: {Id}", id);
// Devolvemos una respuesta 204 No Content, que es el estándar para un PUT exitoso sin devolver datos.
return NoContent();
}
[HttpPut("agrupaciones/orden-diputados")]
public async Task<IActionResult> UpdateDiputadosOrden([FromBody] List<string> idsAgrupacionesOrdenadas)
{
// Reseteamos solo el orden de diputados
await _dbContext.AgrupacionesPoliticas.ExecuteUpdateAsync(s => s.SetProperty(a => a.OrdenDiputados, (int?)null));
for (int i = 0; i < idsAgrupacionesOrdenadas.Count; i++)
{
var agrupacion = await _dbContext.AgrupacionesPoliticas.FindAsync(idsAgrupacionesOrdenadas[i]);
if (agrupacion != null) agrupacion.OrdenDiputados = i + 1;
}
await _dbContext.SaveChangesAsync();
return Ok();
}
[HttpPut("agrupaciones/orden-senadores")]
public async Task<IActionResult> UpdateSenadoresOrden([FromBody] List<string> idsAgrupacionesOrdenadas)
{
// Reseteamos solo el orden de senadores
await _dbContext.AgrupacionesPoliticas.ExecuteUpdateAsync(s => s.SetProperty(a => a.OrdenSenadores, (int?)null));
for (int i = 0; i < idsAgrupacionesOrdenadas.Count; i++)
{
var agrupacion = await _dbContext.AgrupacionesPoliticas.FindAsync(idsAgrupacionesOrdenadas[i]);
if (agrupacion != null) agrupacion.OrdenSenadores = i + 1;
}
await _dbContext.SaveChangesAsync();
return Ok();
}
// LEER todas las configuraciones
[HttpGet("configuracion")]
public async Task<IActionResult> GetConfiguracion()
{
var configs = await _dbContext.Configuraciones.AsNoTracking().ToListAsync();
// Devolvemos un diccionario para que sea fácil de consumir en el frontend
return Ok(configs.ToDictionary(c => c.Clave, c => c.Valor));
}
// GUARDAR un conjunto de configuraciones
[HttpPut("configuracion")]
public async Task<IActionResult> UpdateConfiguracion([FromBody] Dictionary<string, string> nuevasConfiguraciones)
{
foreach (var kvp in nuevasConfiguraciones)
{
var config = await _dbContext.Configuraciones.FindAsync(kvp.Key);
if (config == null)
{
// Si no existe, la creamos
_dbContext.Configuraciones.Add(new Configuracion { Clave = kvp.Key, Valor = kvp.Value });
}
else
{
// Si existe, la actualizamos
config.Valor = kvp.Value;
}
}
await _dbContext.SaveChangesAsync();
return NoContent();
}
// LEER: Obtener todas las bancadas para una cámara, con su partido y ocupante actual
[HttpGet("bancadas/{camara}")]
public async Task<IActionResult> GetBancadas(TipoCamara camara)
{
var bancadas = await _dbContext.Bancadas
.AsNoTracking()
.Include(b => b.AgrupacionPolitica)
.Include(b => b.Ocupante)
.Where(b => b.Camara == camara)
.OrderBy(b => b.Id) // Ordenar por ID para consistencia
.ToListAsync();
return Ok(bancadas);
}
// ACTUALIZAR: Asignar un partido y/o un ocupante a una bancada específica
[HttpPut("bancadas/{bancadaId}")]
public async Task<IActionResult> UpdateBancada(int bancadaId, [FromBody] UpdateBancadaDto dto)
{
var bancada = await _dbContext.Bancadas
.Include(b => b.Ocupante)
.FirstOrDefaultAsync(b => b.Id == bancadaId);
if (bancada == null)
{
return NotFound(new { message = "La bancada especificada no existe." });
}
// 1. Actualizar la agrupación de la bancada
bancada.AgrupacionPoliticaId = dto.AgrupacionPoliticaId;
// 2. Lógica de "Upsert" (Update/Insert/Delete) para el ocupante
if (string.IsNullOrEmpty(dto.NombreOcupante))
{
// Si no se envía nombre, se elimina el ocupante existente
if (bancada.Ocupante != null)
{
_dbContext.OcupantesBancas.Remove(bancada.Ocupante);
}
}
else
{
// Si se envía un nombre, se crea o actualiza el ocupante
if (bancada.Ocupante == null)
{
bancada.Ocupante = new OcupanteBanca(); // Crea uno nuevo si no existía
}
bancada.Ocupante.NombreOcupante = dto.NombreOcupante;
bancada.Ocupante.FotoUrl = dto.FotoUrl;
bancada.Ocupante.Periodo = dto.Periodo;
}
await _dbContext.SaveChangesAsync();
_logger.LogInformation("Se actualizó la bancada con ID: {BancadaId}", bancadaId);
return NoContent();
}
[HttpGet("logos")]
public async Task<IActionResult> GetLogos()
{
return Ok(await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().ToListAsync());
}
[HttpPut("logos")]
public async Task<IActionResult> UpdateLogos([FromBody] List<LogoAgrupacionCategoria> logos)
{
foreach (var logo in logos)
{
var logoExistente = await _dbContext.LogosAgrupacionesCategorias
.FirstOrDefaultAsync(l =>
l.AgrupacionPoliticaId == logo.AgrupacionPoliticaId &&
l.CategoriaId == logo.CategoriaId &&
l.AmbitoGeograficoId == logo.AmbitoGeograficoId);
if (logoExistente != null)
{
// Si encontramos el registro exacto, solo actualizamos su URL.
logoExistente.LogoUrl = logo.LogoUrl;
}
else if (!string.IsNullOrEmpty(logo.LogoUrl))
{
// Si no se encontró un registro exacto (es un override nuevo),
// lo añadimos a la base de datos.
_dbContext.LogosAgrupacionesCategorias.Add(new LogoAgrupacionCategoria
{
AgrupacionPoliticaId = logo.AgrupacionPoliticaId,
CategoriaId = logo.CategoriaId,
AmbitoGeograficoId = logo.AmbitoGeograficoId,
LogoUrl = logo.LogoUrl
});
}
}
await _dbContext.SaveChangesAsync();
return NoContent();
}
[HttpGet("catalogos/municipios")]
public async Task<IActionResult> GetMunicipiosForAdmin()
{
var municipios = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 30) // Nivel 30 = Municipio
.OrderBy(a => a.Nombre)
.Select(a => new
{
// Devolvemos el ID de la base de datos como un string,
// que es lo que el componente Select espera.
Id = a.Id.ToString(),
Nombre = a.Nombre
})
.ToListAsync();
return Ok(municipios);
}
/// <summary>
/// Obtiene todos los overrides de candidatos configurados.
/// </summary>
[HttpGet("candidatos")]
public async Task<IActionResult> GetCandidatos()
{
var candidatos = await _dbContext.CandidatosOverrides
.AsNoTracking()
.ToListAsync();
return Ok(candidatos);
}
/// <summary>
/// Guarda (actualiza o crea) una lista de overrides de candidatos.
/// </summary>
[HttpPut("candidatos")]
public async Task<IActionResult> UpdateCandidatos([FromBody] List<CandidatoOverride> 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 &&
c.CategoriaId == candidatoDto.CategoriaId &&
c.AmbitoGeograficoId == candidatoDto.AmbitoGeograficoId);
if (candidatoExistente != null)
{
// Si existe y el nombre es diferente, lo actualizamos.
if (candidatoExistente.NombreCandidato != candidatoDto.NombreCandidato)
{
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.
if (!string.IsNullOrWhiteSpace(candidatoDto.NombreCandidato))
{
_dbContext.CandidatosOverrides.Add(new CandidatoOverride
{
AgrupacionPoliticaId = candidatoDto.AgrupacionPoliticaId,
CategoriaId = candidatoDto.CategoriaId,
AmbitoGeograficoId = candidatoDto.AmbitoGeograficoId,
NombreCandidato = candidatoDto.NombreCandidato
});
}
}
}
// 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
}
}