| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | // src/services/apiService.ts
 | 
					
						
							|  |  |  | import axios from 'axios'; | 
					
						
							|  |  |  | import { triggerLogout } from '../context/authUtils'; | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  | import type { | 
					
						
							|  |  |  |   CandidatoOverride, AgrupacionPolitica, | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  |   UpdateAgrupacionData, Bancada, LogoAgrupacionCategoria, | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   MunicipioSimple, BancaPrevia, ProvinciaSimple | 
					
						
							|  |  |  | } from '../types'; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-03 18:49:55 -03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * URL base para las llamadas a la API. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const API_URL_BASE = import.meta.env.DEV | 
					
						
							|  |  |  |   ? 'http://localhost:5217/api' | 
					
						
							|  |  |  |   : 'https://elecciones2025.eldia.com/api'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * URL completa para el endpoint de autenticación. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export const AUTH_API_URL = `${API_URL_BASE}/auth`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * URL completa para los endpoints de administración. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export const ADMIN_API_URL = `${API_URL_BASE}/admin`; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // Cliente de API para endpoints de administración (requiere token)
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | const adminApiClient = axios.create({ | 
					
						
							|  |  |  |   baseURL: ADMIN_API_URL, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // Cliente de API para endpoints públicos (no envía token)
 | 
					
						
							|  |  |  | const apiClient = axios.create({ | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   baseURL: API_URL_BASE, | 
					
						
							|  |  |  |   headers: { 'Content-Type': 'application/json' }, | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // --- INTERCEPTORES (Solo para el cliente de admin) ---
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | adminApiClient.interceptors.request.use( | 
					
						
							|  |  |  |   (config) => { | 
					
						
							|  |  |  |     const token = localStorage.getItem('admin-jwt-token'); | 
					
						
							|  |  |  |     if (token) { | 
					
						
							|  |  |  |       config.headers['Authorization'] = `Bearer ${token}`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return config; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   (error) => Promise.reject(error) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | adminApiClient.interceptors.response.use( | 
					
						
							|  |  |  |   (response) => response, | 
					
						
							|  |  |  |   (error) => { | 
					
						
							|  |  |  |     if (axios.isAxiosError(error) && error.response?.status === 401) { | 
					
						
							|  |  |  |       console.log("Token expirado o inválido. Deslogueando..."); | 
					
						
							|  |  |  |       triggerLogout(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return Promise.reject(error); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // --- INTERFACES PARA COMPOSICIÓN NACIONAL (NECESARIAS PARA EL NUEVO MÉTODO) ---
 | 
					
						
							|  |  |  | export interface PartidoComposicionNacional { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   id: string; | 
					
						
							|  |  |  |   nombre: string; | 
					
						
							|  |  |  |   nombreCorto: string | null; | 
					
						
							|  |  |  |   color: string | null; | 
					
						
							|  |  |  |   bancasFijos: number; | 
					
						
							|  |  |  |   bancasGanadas: number; | 
					
						
							|  |  |  |   bancasTotales: number; | 
					
						
							|  |  |  |   ordenDiputadosNacionales: number | null; | 
					
						
							|  |  |  |   ordenSenadoresNacionales: number | null; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | export interface CamaraComposicionNacional { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   camaraNombre: string; | 
					
						
							|  |  |  |   totalBancas: number; | 
					
						
							|  |  |  |   bancasEnJuego: number; | 
					
						
							|  |  |  |   partidos: PartidoComposicionNacional[]; | 
					
						
							|  |  |  |   presidenteBancada: { color: string | null; tipoBanca: 'ganada' | 'previa' | null } | null; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | export interface ComposicionNacionalData { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   diputados: CamaraComposicionNacional; | 
					
						
							|  |  |  |   senadores: CamaraComposicionNacional; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | // --- SERVICIOS DE API ---
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 1. Autenticación
 | 
					
						
							|  |  |  | export interface LoginCredentials { username: string; password: string; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const loginUser = async (credentials: LoginCredentials): Promise<string | null> => { | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     const response = await axios.post(`${AUTH_API_URL}/login`, credentials); | 
					
						
							|  |  |  |     return response.data.token; | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     console.error("Error during login request:", error); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 2. Agrupaciones
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | export const getAgrupaciones = async (): Promise<AgrupacionPolitica[]> => { | 
					
						
							|  |  |  |   const response = await adminApiClient.get('/agrupaciones'); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const updateAgrupacion = async (id: string, data: UpdateAgrupacionData): Promise<void> => { | 
					
						
							|  |  |  |   await adminApiClient.put(`/agrupaciones/${id}`, data); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 3. Ordenamiento de Agrupaciones
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | export const updateOrden = async (camara: 'diputados' | 'senadores' | 'diputados-nacionales' | 'senadores-nacionales', ids: string[]): Promise<void> => { | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   await adminApiClient.put(`/agrupaciones/orden-${camara}`, ids); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 4. Gestión de Bancas
 | 
					
						
							|  |  |  | export const getBancadas = async (camara: 'diputados' | 'senadores', eleccionId: number): Promise<Bancada[]> => { | 
					
						
							|  |  |  |   const camaraId = (camara === 'diputados') ? 0 : 1; | 
					
						
							|  |  |  |   const response = await adminApiClient.get(`/bancadas/${camaraId}?eleccionId=${eleccionId}`); | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface UpdateBancadaData { | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   agrupacionPoliticaId: string | null; | 
					
						
							|  |  |  |   nombreOcupante: string | null; | 
					
						
							|  |  |  |   fotoUrl: string | null; | 
					
						
							|  |  |  |   periodo: string | null; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const updateBancada = async (bancadaId: number, data: UpdateBancadaData): Promise<void> => { | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   await adminApiClient.put(`/bancadas/${bancadaId}`, data); | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 5. Configuración General
 | 
					
						
							|  |  |  | export type ConfiguracionResponse = Record<string, string>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getConfiguracion = async (): Promise<ConfiguracionResponse> => { | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   const response = await adminApiClient.get('/configuracion'); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const updateConfiguracion = async (data: Record<string, string>): Promise<void> => { | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   await adminApiClient.put('/configuracion', data); | 
					
						
							| 
									
										
										
										
											2025-09-01 14:04:40 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 6. Logos y Candidatos
 | 
					
						
							|  |  |  | export const getLogos = async (eleccionId: number): Promise<LogoAgrupacionCategoria[]> => { | 
					
						
							|  |  |  |   const response = await adminApiClient.get(`/logos?eleccionId=${eleccionId}`); | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-09-01 14:04:40 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | export const updateLogos = async (data: LogoAgrupacionCategoria[]): Promise<void> => { | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  |   await adminApiClient.put('/logos', data); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | export const getCandidatos = async (eleccionId: number): Promise<CandidatoOverride[]> => { | 
					
						
							|  |  |  |   const response = await adminApiClient.get(`/candidatos?eleccionId=${eleccionId}`); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | export const updateCandidatos = async (data: CandidatoOverride[]): Promise<void> => { | 
					
						
							|  |  |  |   await adminApiClient.put('/candidatos', data); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 7. Catálogos
 | 
					
						
							| 
									
										
										
										
											2025-09-02 15:39:17 -03:00
										 |  |  | export const getMunicipiosForAdmin = async (): Promise<MunicipioSimple[]> => { | 
					
						
							| 
									
										
										
										
											2025-09-06 21:44:52 -03:00
										 |  |  |   const response = await adminApiClient.get('/catalogos/municipios'); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 8. Logging
 | 
					
						
							|  |  |  | export interface UpdateLoggingLevelData { level: string; } | 
					
						
							|  |  |  | export const updateLoggingLevel = async (data: UpdateLoggingLevelData): Promise<void> => { | 
					
						
							|  |  |  |   await adminApiClient.put(`/logging-level`, data); | 
					
						
							| 
									
										
										
										
											2025-09-05 11:38:25 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 9. Bancas Previas
 | 
					
						
							|  |  |  | export const getBancasPrevias = async (eleccionId: number): Promise<BancaPrevia[]> => { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   const response = await adminApiClient.get(`/bancas-previas/${eleccionId}`); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | export const updateBancasPrevias = async (eleccionId: number, data: BancaPrevia[]): Promise<void> => { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   await adminApiClient.put(`/bancas-previas/${eleccionId}`, data); | 
					
						
							| 
									
										
										
										
											2025-09-06 21:44:52 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // 10. Obtener Composición Nacional (Endpoint Público)
 | 
					
						
							|  |  |  | export const getComposicionNacional = async (eleccionId: number): Promise<ComposicionNacionalData> => { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   // Este es un endpoint público, por lo que usamos el cliente sin token de admin.
 | 
					
						
							|  |  |  |   const response = await apiClient.get(`/elecciones/${eleccionId}/composicion-nacional`); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2025-09-06 21:44:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-28 19:04:09 -03:00
										 |  |  | // Obtenemos las provincias para el selector de ámbito
 | 
					
						
							|  |  |  | export const getProvinciasForAdmin = async (): Promise<ProvinciaSimple[]> => { | 
					
						
							| 
									
										
										
										
											2025-10-20 14:38:10 -03:00
										 |  |  |   const response = await adminApiClient.get('/catalogos/provincias'); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface CreateAgrupacionData { | 
					
						
							|  |  |  |   nombre: string; | 
					
						
							|  |  |  |   nombreCorto: string | null; | 
					
						
							|  |  |  |   color: string | null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Servicio para crear una nueva agrupación
 | 
					
						
							|  |  |  | export const createAgrupacion = async (data: CreateAgrupacionData): Promise<AgrupacionPolitica> => { | 
					
						
							|  |  |  |   const response = await adminApiClient.post('/agrupaciones', data); | 
					
						
							|  |  |  |   return response.data; | 
					
						
							| 
									
										
										
										
											2025-08-29 09:54:22 -03:00
										 |  |  | }; |