Feat Widgets Cards y Optimización de Consultas
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
// src/apiService.ts
|
||||
import axios from 'axios';
|
||||
import type { ApiResponseRankingMunicipio, ApiResponseRankingSeccion, ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple, TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker, ApiResponseResultadosPorSeccion, PanelElectoralDto } from './types/types';
|
||||
import type { ApiResponseRankingMunicipio, ApiResponseRankingSeccion,
|
||||
ApiResponseTablaDetallada, ProyeccionBancas, MunicipioSimple,
|
||||
TelegramaData, CatalogoItem, CategoriaResumen, ResultadoTicker,
|
||||
ApiResponseResultadosPorSeccion, PanelElectoralDto, ResumenProvincia } from './types/types';
|
||||
|
||||
/**
|
||||
* URL base para las llamadas a la API.
|
||||
@@ -84,6 +87,32 @@ export interface ResultadoDetalleSeccion {
|
||||
color: string | null;
|
||||
}
|
||||
|
||||
export interface PartidoComposicionNacional {
|
||||
id: string;
|
||||
nombre: string;
|
||||
nombreCorto: string | null;
|
||||
color: string | null;
|
||||
bancasFijos: number;
|
||||
bancasGanadas: number;
|
||||
bancasTotales: number;
|
||||
ordenDiputadosNacionales: number | null;
|
||||
ordenSenadoresNacionales: number | null;
|
||||
}
|
||||
|
||||
export interface CamaraComposicionNacional {
|
||||
camaraNombre: string;
|
||||
totalBancas: number;
|
||||
bancasEnJuego: number;
|
||||
partidos: PartidoComposicionNacional[];
|
||||
presidenteBancada: { color: string | null; tipoBanca: 'ganada' | 'previa' | null } | null;
|
||||
ultimaActualizacion: string;
|
||||
}
|
||||
|
||||
export interface ComposicionNacionalData {
|
||||
diputados: CamaraComposicionNacional;
|
||||
senadores: CamaraComposicionNacional;
|
||||
}
|
||||
|
||||
export const getResumenProvincial = async (eleccionId: number): Promise<CategoriaResumen[]> => {
|
||||
const response = await apiClient.get(`/elecciones/${eleccionId}/provincia/02`);
|
||||
return response.data;
|
||||
@@ -221,4 +250,16 @@ export const getPanelElectoral = async (eleccionId: number, ambitoId: string | n
|
||||
|
||||
const { data } = await apiClient.get(url);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getComposicionNacional = async (eleccionId: number): Promise<ComposicionNacionalData> => {
|
||||
const { data } = await apiClient.get(`/elecciones/${eleccionId}/composicion-nacional`);
|
||||
return data;
|
||||
};
|
||||
|
||||
// 11. Endpoint para el widget de tarjetas nacionales
|
||||
export const getResumenPorProvincia = async (eleccionId: number): Promise<ResumenProvincia[]> => {
|
||||
// Usamos el cliente público ya que son datos de resultados
|
||||
const { data } = await apiClient.get(`/elecciones/${eleccionId}/resumen-por-provincia`);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,339 @@
|
||||
// src/components/common/DiputadosNacionalesLayout.tsx
|
||||
import React from 'react';
|
||||
import type { PartidoComposicionNacional } from '../../apiService';
|
||||
|
||||
// --- Interfaces Actualizadas ---
|
||||
interface DiputadosNacionalesLayoutProps {
|
||||
partyData: PartidoComposicionNacional[];
|
||||
size?: number;
|
||||
presidenteBancada?: { color: string | null } | null; // <-- Nueva Prop
|
||||
}
|
||||
|
||||
const PRESIDENTE_SEAT_INDEX = 0; // El escaño 'seat-0' es el del presidente
|
||||
|
||||
export const DiputadosNacionalesLayout: React.FC<DiputadosNacionalesLayoutProps> = ({
|
||||
partyData,
|
||||
size = 800,
|
||||
presidenteBancada, // <-- Recibimos la nueva prop
|
||||
}) => {
|
||||
// --- ARRAY DE 257 ELEMENTOS <circle> ORDENADOS POR ID DE "seat-X" ---
|
||||
const seatElements = [
|
||||
<circle key="seat-0" id="seat-0" r="15.7" cy="639.5" cx="595.3" />,
|
||||
<circle key="seat-1" id="seat-1" r="15.7" cy="673.1" cx="109.3" />,
|
||||
<circle key="seat-2" id="seat-2" r="15.7" cy="673.1" cx="161.7" />,
|
||||
<circle key="seat-3" id="seat-3" r="15.7" cy="673.5" cx="214.3" />,
|
||||
<circle key="seat-4" id="seat-4" r="15.7" cy="673.2" cx="266.5" />,
|
||||
<circle key="seat-5" id="seat-5" r="15.7" cy="669.5" cx="319.4" />,
|
||||
<circle key="seat-6" id="seat-6" r="15.7" cy="660" cx="370.8" />,
|
||||
<circle key="seat-7" id="seat-7" transform="rotate(-88.1)" r="15.7" cy="77.69" cx="-634.1" />,
|
||||
<circle key="seat-8" id="seat-8" r="15.7" cy="639" cx="109.3" />,
|
||||
<circle key="seat-9" id="seat-9" r="15.7" cy="639" cx="161.7" />,
|
||||
<circle key="seat-10" id="seat-10" r="15.7" cy="639.2" cx="214.3" />,
|
||||
<circle key="seat-11" id="seat-11" r="15.7" cy="638.8" cx="266.7" />,
|
||||
<circle key="seat-12" id="seat-12" r="15.7" cy="635.1" cx="319.4" />,
|
||||
<circle key="seat-13" id="seat-13" r="15.7" cy="625.7" cx="371.7" />,
|
||||
<circle key="seat-14" id="seat-14" r="15.7" cy="639" cx="424.2" />,
|
||||
<circle key="seat-15" id="seat-15" transform="rotate(-88.1)" r="15.7" cy="77" cx="-600.18" />,
|
||||
<circle key="seat-16" id="seat-16" r="15.7" cy="600.9" cx="109.5" />,
|
||||
<circle key="seat-17" id="seat-17" r="15.7" cy="603.7" cx="162.1" />,
|
||||
<circle key="seat-18" id="seat-18" r="15.7" cy="598.6" cx="215" />,
|
||||
<circle key="seat-19" id="seat-19" r="15.7" cy="602.6" cx="267.1" />,
|
||||
<circle key="seat-20" id="seat-20" transform="rotate(-88.1)" r="15.7" cy="76.57" cx="-562.57" />,
|
||||
<circle key="seat-21" id="seat-21" r="15.7" cy="566.7" cx="112.2" />,
|
||||
<circle key="seat-22" id="seat-22" r="15.7" cy="570" cx="164.7" />,
|
||||
<circle key="seat-23" id="seat-23" r="15.7" cy="564.5" cx="218.2" />,
|
||||
<circle key="seat-24" id="seat-24" r="15.7" cy="568.6" cx="270.9" />,
|
||||
<circle key="seat-25" id="seat-25" r="15.7" cy="588" cx="321.1" />,
|
||||
<circle key="seat-26" id="seat-26" transform="rotate(-88.1)" r="15.7" cy="79.88" cx="-524.51" />,
|
||||
<circle key="seat-27" id="seat-27" transform="rotate(-5.7)" r="15.7" cy="539.19" cx="65.05" />,
|
||||
<circle key="seat-28" id="seat-28" r="15.7" cy="535.9" cx="170" />,
|
||||
<circle key="seat-29" id="seat-29" transform="rotate(-88.1)" r="15.7" cy="86.87" cx="-488.2" />,
|
||||
<circle key="seat-30" id="seat-30" r="15.7" cy="497.2" cx="125.2" />,
|
||||
<circle key="seat-31" id="seat-31" r="15.7" cy="502.8" cx="178.2" />,
|
||||
<circle key="seat-32" id="seat-32" r="15.7" cy="525.1" cx="226.3" />,
|
||||
<circle key="seat-33" id="seat-33" r="15.7" cy="533.1" cx="278.4" />,
|
||||
<circle key="seat-34" id="seat-34" r="15.7" cy="554.6" cx="327.1" />,
|
||||
<circle key="seat-35" id="seat-35" r="15.7" cy="567.9" cx="377.9" />,
|
||||
<circle key="seat-36" id="seat-36" r="15.7" cy="596.7" cx="426" />,
|
||||
<circle key="seat-37" id="seat-37" r="15.7" cy="453.8" cx="79.7" />,
|
||||
<circle key="seat-38" id="seat-38" r="15.7" cy="462" cx="135.7" />,
|
||||
<circle key="seat-39" id="seat-39" r="15.7" cy="469.3" cx="188.9" />,
|
||||
<circle key="seat-40" id="seat-40" r="15.7" cy="492.6" cx="236.4" />,
|
||||
<circle key="seat-41" id="seat-41" r="15.7" cy="500.6" cx="289.8" />,
|
||||
<circle key="seat-42" id="seat-42" r="15.7" cy="511.6" cx="341.5" />,
|
||||
<circle key="seat-43" id="seat-43" r="15.7" cy="535" cx="388.9" />,
|
||||
<circle key="seat-44" id="seat-44" r="15.7" cy="555" cx="437.3" />,
|
||||
<circle key="seat-45" id="seat-45" r="15.7" cy="419.3" cx="92.8" />,
|
||||
<circle key="seat-46" id="seat-46" r="15.7" cy="429.8" cx="148.1" />,
|
||||
<circle key="seat-47" id="seat-47" r="15.7" cy="387.4" cx="106.8" />,
|
||||
<circle key="seat-48" id="seat-48" transform="rotate(-5.7)" r="15.7" cy="364.72" cx="89.86" />,
|
||||
<circle key="seat-49" id="seat-49" r="15.7" cy="395.5" cx="164.4" />,
|
||||
<circle key="seat-50" id="seat-50" r="15.7" cy="437.3" cx="202.4" />,
|
||||
<circle key="seat-51" id="seat-51" r="15.7" cy="455.4" cx="252.1" />,
|
||||
<circle key="seat-52" id="seat-52" r="15.7" cy="325.1" cx="144.9" />,
|
||||
<circle key="seat-53" id="seat-53" r="15.7" cy="365.7" cx="181.3" />,
|
||||
<circle key="seat-54" id="seat-54" r="15.7" cy="405.1" cx="218.8" />,
|
||||
<circle key="seat-55" id="seat-55" r="15.7" cy="425.6" cx="267.7" />,
|
||||
<circle key="seat-56" id="seat-56" r="15.7" cy="464.9" cx="306.5" />,
|
||||
<circle key="seat-57" id="seat-57" r="15.7" cy="292.1" cx="168.7" />,
|
||||
<circle key="seat-58" id="seat-58" r="15.7" cy="334.6" cx="202.3" />,
|
||||
<circle key="seat-59" id="seat-59" r="15.7" cy="376.9" cx="236.7" />,
|
||||
<circle key="seat-60" id="seat-60" r="15.7" cy="265.1" cx="190.8" />,
|
||||
<circle key="seat-61" id="seat-61" r="15.7" cy="307.2" cx="224" />,
|
||||
<circle key="seat-62" id="seat-62" r="15.7" cy="346.9" cx="259.3" />,
|
||||
<circle key="seat-63" id="seat-63" r="15.7" cy="393" cx="289.6" />,
|
||||
<circle key="seat-64" id="seat-64" r="15.7" cy="435.9" cx="323.7" />,
|
||||
<circle key="seat-65" id="seat-65" r="15.7" cy="480.8" cx="357.3" />,
|
||||
<circle key="seat-66" id="seat-66" r="15.7" cy="236.2" cx="218.1" />,
|
||||
<circle key="seat-67" id="seat-67" r="15.7" cy="278.6" cx="250" />,
|
||||
<circle key="seat-68" id="seat-68" r="15.7" cy="320.2" cx="283" />,
|
||||
<circle key="seat-69" id="seat-69" r="15.7" cy="362" cx="315.5" />,
|
||||
<circle key="seat-70" id="seat-70" r="15.7" cy="403.8" cx="348.7" />,
|
||||
<circle key="seat-71" id="seat-71" r="15.7" cy="445.9" cx="381.6" />,
|
||||
<circle key="seat-72" id="seat-72" r="15.7" cy="489" cx="415.1" />,
|
||||
<circle key="seat-73" id="seat-73" r="15.7" cy="515.6" cx="460.7" />,
|
||||
<circle key="seat-74" id="seat-74" r="15.7" cy="485.2" cx="491" />,
|
||||
<circle key="seat-75" id="seat-75" r="15.7" cy="213.6" cx="243.2" />,
|
||||
<circle key="seat-76" id="seat-76" r="15.7" cy="254.9" cx="275.3" />,
|
||||
<circle key="seat-77" id="seat-77" r="15.7" cy="296.4" cx="307.8" />,
|
||||
<circle key="seat-78" id="seat-78" r="15.7" cy="337.6" cx="339.9" />,
|
||||
<circle key="seat-79" id="seat-79" r="15.7" cy="379" cx="372.5" />,
|
||||
<circle key="seat-80" id="seat-80" r="15.7" cy="420.8" cx="405.1" />,
|
||||
<circle key="seat-81" id="seat-81" r="15.7" cy="462.7" cx="437.2" />,
|
||||
<circle key="seat-82" id="seat-82" r="15.5" cy="181.8" cx="283.1" />,
|
||||
<circle key="seat-83" id="seat-83" r="15.5" cy="223.6" cx="315.4" />,
|
||||
<circle key="seat-84" id="seat-84" r="15.7" cy="262.6" cx="351" />,
|
||||
<circle key="seat-85" id="seat-85" r="15.5" cy="304.5" cx="382.7" />,
|
||||
<circle key="seat-86" id="seat-86" r="15.7" cy="339.1" cx="425.3" />,
|
||||
<circle key="seat-87" id="seat-87" r="15.7" cy="379" cx="461" />,
|
||||
<circle key="seat-88" id="seat-88" r="15.7" cy="420.4" cx="495.9" />,
|
||||
<circle key="seat-89" id="seat-89" r="15.7" cy="463.5" cx="528.1" />,
|
||||
<circle key="seat-90" id="seat-90" r="15.5" cy="160.4" cx="315.7" />,
|
||||
<circle key="seat-91" id="seat-91" r="15.5" cy="206.2" cx="342.9" />,
|
||||
<circle key="seat-92" id="seat-92" r="15.7" cy="245.1" cx="379" />,
|
||||
<circle key="seat-93" id="seat-93" r="15.5" cy="287.4" cx="410.5" />,
|
||||
<circle key="seat-94" id="seat-94" r="15.7" cy="323.4" cx="455.9" />,
|
||||
<circle key="seat-95" id="seat-95" transform="rotate(-80.8)" r="15.7" cy="555.93" cx="-274.27" />,
|
||||
<circle key="seat-96" id="seat-96" r="15.7" cy="407.6" cx="527.7" />,
|
||||
<circle key="seat-97" id="seat-97" r="15.5" cy="142.7" cx="345.9" />,
|
||||
<circle key="seat-98" id="seat-98" r="15.5" cy="186.8" cx="375.8" />,
|
||||
<circle key="seat-99" id="seat-99" r="15.5" cy="125.9" cx="377.8" />,
|
||||
<circle key="seat-100" id="seat-100" r="15.5" cy="173.7" cx="405.1" />,
|
||||
<circle key="seat-101" id="seat-101" r="15.7" cy="223" cx="422.9" />,
|
||||
<circle key="seat-102" id="seat-102" r="15.5" cy="270.9" cx="444.3" />,
|
||||
<circle key="seat-103" id="seat-103" r="15.5" cy="112" cx="409.4" />,
|
||||
<circle key="seat-104" id="seat-104" r="15.5" cy="157.7" cx="438.1" />,
|
||||
<circle key="seat-105" id="seat-105" r="15.7" cy="209" cx="453.9" />,
|
||||
<circle key="seat-106" id="seat-106" r="15.5" cy="259.6" cx="474.2" />,
|
||||
<circle key="seat-107" id="seat-107" r="15.7" cy="306.3" cx="499.3" />,
|
||||
<circle key="seat-108" id="seat-108" r="15.5" cy="100.1" cx="443.4" />,
|
||||
<circle key="seat-109" id="seat-109" r="15.5" cy="146.7" cx="472.7" />,
|
||||
<circle key="seat-110" id="seat-110" r="15.7" cy="197.9" cx="497" />,
|
||||
<circle key="seat-111" id="seat-111" r="15.5" cy="249" cx="508.8" />,
|
||||
<circle key="seat-112" id="seat-112" r="15.7" cy="298.4" cx="532.7" />,
|
||||
<circle key="seat-113" id="seat-113" r="15.7" cy="350.8" cx="538.1" />,
|
||||
<circle key="seat-114" id="seat-114" r="15.5" cy="92.2" cx="477" />,
|
||||
<circle key="seat-115" id="seat-115" r="15.5" cy="84.4" cx="510" />,
|
||||
<circle key="seat-116" id="seat-116" transform="rotate(-80.8)" r="15.5" cy="523.04" cx="-55.62" />,
|
||||
<circle key="seat-117" id="seat-117" r="15.7" cy="190.1" cx="531.6" />,
|
||||
<circle key="seat-118" id="seat-118" r="15.5" cy="243.4" cx="542.3" />,
|
||||
<circle key="seat-119" id="seat-119" r="15.5" cy="80.7" cx="544.3" />,
|
||||
<circle key="seat-120" id="seat-120" r="15.5" cy="136.1" cx="541.9" />,
|
||||
<circle key="seat-121" id="seat-121" r="15.5" cy="78.5" cx="579" />,
|
||||
<circle key="seat-122" id="seat-122" r="15.5" cy="135" cx="578.2" />,
|
||||
<circle key="seat-123" id="seat-123" r="15.7" cy="187.6" cx="577.9" />,
|
||||
<circle key="seat-124" id="seat-124" r="15.5" cy="240" cx="579" />,
|
||||
<circle key="seat-125" id="seat-125" r="15.7" cy="292.6" cx="578" />,
|
||||
<circle key="seat-126" id="seat-126" r="15.7" cy="345.3" cx="578" />,
|
||||
<circle key="seat-127" id="seat-127" r="15.7" cy="398" cx="577.8" />,
|
||||
<circle key="seat-128" id="seat-128" r="15.7" cy="451.2" cx="572.2" />,
|
||||
<circle key="seat-129" id="seat-129" r="15.5" cy="78.5" cx="613.5" />,
|
||||
<circle key="seat-130" id="seat-130" r="15.5" cy="135" cx="612.3" />,
|
||||
<circle key="seat-131" id="seat-131" r="15.7" cy="187.6" cx="612.6" />,
|
||||
<circle key="seat-132" id="seat-132" r="15.5" cy="240" cx="611.5" />,
|
||||
<circle key="seat-133" id="seat-133" r="15.7" cy="292.6" cx="612.5" />,
|
||||
<circle key="seat-134" id="seat-134" r="15.7" cy="345.3" cx="612.5" />,
|
||||
<circle key="seat-135" id="seat-135" r="15.7" cy="398" cx="612.7" />,
|
||||
<circle key="seat-136" id="seat-136" r="15.7" cy="451.2" cx="618.3" />,
|
||||
<circle key="seat-137" id="seat-137" r="15.5" cy="82.6" cx="646.3" />,
|
||||
<circle key="seat-138" id="seat-138" r="15.5" cy="86.4" cx="680.5" />,
|
||||
<circle key="seat-139" id="seat-139" r="15.5" cy="138.4" cx="650.6" />,
|
||||
<circle key="seat-140" id="seat-140" r="15.5" cy="94.2" cx="715.6" />,
|
||||
<circle key="seat-141" id="seat-141" r="15.5" cy="142.6" cx="685.4" />,
|
||||
<circle key="seat-142" id="seat-142" r="15.7" cy="190.1" cx="657" />,
|
||||
<circle key="seat-143" id="seat-143" r="15.5" cy="243.4" cx="648.3" />,
|
||||
<circle key="seat-144" id="seat-144" r="15.5" cy="104.1" cx="747.1" />,
|
||||
<circle key="seat-145" id="seat-145" r="15.5" cy="150.7" cx="719.9" />,
|
||||
<circle key="seat-146" id="seat-146" r="15.7" cy="197.9" cx="691.5" />,
|
||||
<circle key="seat-147" id="seat-147" r="15.5" cy="248.5" cx="679.8" />,
|
||||
<circle key="seat-148" id="seat-148" r="15.7" cy="298.4" cx="657.8" />,
|
||||
<circle key="seat-149" id="seat-149" r="15.7" cy="350.8" cx="652.4" />,
|
||||
<circle key="seat-150" id="seat-150" r="15.5" cy="116" cx="783.1" />,
|
||||
<circle key="seat-151" id="seat-151" r="15.5" cy="159.7" cx="750.4" />,
|
||||
<circle key="seat-152" id="seat-152" r="15.7" cy="211" cx="736.6" />,
|
||||
<circle key="seat-153" id="seat-153" r="15.5" cy="259.6" cx="716.4" />,
|
||||
<circle key="seat-154" id="seat-154" r="15.7" cy="306.3" cx="691.2" />,
|
||||
<circle key="seat-155" id="seat-155" r="15.5" cy="127.9" cx="812.8" />,
|
||||
<circle key="seat-156" id="seat-156" r="15.5" cy="173.7" cx="785.5" />,
|
||||
<circle key="seat-157" id="seat-157" r="15.7" cy="223" cx="767.7" />,
|
||||
<circle key="seat-158" id="seat-158" r="15.5" cy="270.9" cx="746.3" />,
|
||||
<circle key="seat-159" id="seat-159" r="15.5" cy="144.7" cx="846.6" />,
|
||||
<circle key="seat-160" id="seat-160" r="15.5" cy="186.8" cx="814.8" />,
|
||||
<circle key="seat-161" id="seat-161" r="15.5" cy="160.4" cx="874.8" />,
|
||||
<circle key="seat-162" id="seat-162" r="15.5" cy="206.2" cx="847.6" />,
|
||||
<circle key="seat-163" id="seat-163" r="15.7" cy="245.1" cx="811.5" />,
|
||||
<circle key="seat-164" id="seat-164" r="15.5" cy="287.4" cx="780.1" />,
|
||||
<circle key="seat-165" id="seat-165" r="15.7" cy="323.4" cx="734.6" />,
|
||||
<circle key="seat-166" id="seat-166" r="15.7" cy="357.8" cx="687.4" />,
|
||||
<circle key="seat-167" id="seat-167" r="15.7" cy="407.6" cx="662.8" />,
|
||||
<circle key="seat-168" id="seat-168" r="15.5" cy="181.8" cx="907.5" />,
|
||||
<circle key="seat-169" id="seat-169" r="15.5" cy="223.6" cx="875.2" />,
|
||||
<circle key="seat-170" id="seat-170" r="15.7" cy="262.6" cx="839.5" />,
|
||||
<circle key="seat-171" id="seat-171" r="15.5" cy="304.3" cx="807.8" />,
|
||||
<circle key="seat-172" id="seat-172" r="15.7" cy="339.1" cx="765.3" />,
|
||||
<circle key="seat-173" id="seat-173" r="15.7" cy="379" cx="729.6" />,
|
||||
<circle key="seat-174" id="seat-174" r="15.7" cy="420.4" cx="694.6" />,
|
||||
<circle key="seat-175" id="seat-175" r="15.7" cy="463.5" cx="662.5" />,
|
||||
<circle key="seat-176" id="seat-176" r="15.7" cy="485.4" cx="699.5" />,
|
||||
<circle key="seat-177" id="seat-177" r="15.7" cy="213.6" cx="947.4" />,
|
||||
<circle key="seat-178" id="seat-178" r="15.7" cy="254.9" cx="915.2" />,
|
||||
<circle key="seat-179" id="seat-179" r="15.7" cy="296.4" cx="882.7" />,
|
||||
<circle key="seat-180" id="seat-180" r="15.7" cy="337.6" cx="850.7" />,
|
||||
<circle key="seat-181" id="seat-181" r="15.7" cy="379" cx="818.1" />,
|
||||
<circle key="seat-182" id="seat-182" r="15.7" cy="420.8" cx="785.4" />,
|
||||
<circle key="seat-183" id="seat-183" r="15.7" cy="462.7" cx="753.4" />,
|
||||
<circle key="seat-184" id="seat-184" r="15.7" cy="515.4" cx="730.1" />,
|
||||
<circle key="seat-185" id="seat-185" r="15.7" cy="236.2" cx="972.4" />,
|
||||
<circle key="seat-186" id="seat-186" r="15.7" cy="278.6" cx="940.5" />,
|
||||
<circle key="seat-187" id="seat-187" r="15.7" cy="320.2" cx="907.5" />,
|
||||
<circle key="seat-188" id="seat-188" r="15.7" cy="362" cx="875.1" />,
|
||||
<circle key="seat-189" id="seat-189" r="15.7" cy="403.8" cx="841.8" />,
|
||||
<circle key="seat-190" id="seat-190" r="15.7" cy="445.9" cx="808.9" />,
|
||||
<circle key="seat-191" id="seat-191" r="15.7" cy="489" cx="775.5" />,
|
||||
<circle key="seat-192" id="seat-192" r="15.7" cy="265.1" cx="999.7" />,
|
||||
<circle key="seat-193" id="seat-193" r="15.7" cy="307.2" cx="966.6" />,
|
||||
<circle key="seat-194" id="seat-194" r="15.7" cy="346.9" cx="931.2" />,
|
||||
<circle key="seat-195" id="seat-195" r="15.7" cy="393" cx="901" />,
|
||||
<circle key="seat-196" id="seat-196" r="15.7" cy="435.9" cx="866.9" />,
|
||||
<circle key="seat-197" id="seat-197" r="15.7" cy="480.8" cx="833.2" />,
|
||||
<circle key="seat-198" id="seat-198" transform="rotate(-80.8)" r="15.7" cy="1055.16" cx="-124.85" />,
|
||||
<circle key="seat-199" id="seat-199" r="15.7" cy="334.6" cx="988.2" />,
|
||||
<circle key="seat-200" id="seat-200" r="15.7" cy="376.9" cx="953.8" />,
|
||||
<circle key="seat-201" id="seat-201" r="15.7" cy="425.6" cx="922.8" />,
|
||||
<circle key="seat-202" id="seat-202" r="15.7" cy="464.9" cx="884" />,
|
||||
<circle key="seat-203" id="seat-203" r="15.7" cy="325.1" cx="1045.7" />,
|
||||
<circle key="seat-204" id="seat-204" r="15.7" cy="365.7" cx="1009.2" />,
|
||||
<circle key="seat-205" id="seat-205" r="15.7" cy="405.1" cx="971.7" />,
|
||||
<circle key="seat-206" id="seat-206" r="15.7" cy="354.1" cx="1063.2" />,
|
||||
<circle key="seat-207" id="seat-207" transform="rotate(-80.8)" r="15.7" cy="1075.78" cx="-226.25" />,
|
||||
<circle key="seat-208" id="seat-208" r="15.7" cy="387.4" cx="1081.8" />,
|
||||
<circle key="seat-209" id="seat-209" r="15.7" cy="421.3" cx="1095.7" />,
|
||||
<circle key="seat-210" id="seat-210" r="15.7" cy="429.8" cx="1042.5" />,
|
||||
<circle key="seat-211" id="seat-211" r="15.7" cy="437.3" cx="988.2" />,
|
||||
<circle key="seat-212" id="seat-212" r="15.7" cy="455.4" cx="938.5" />,
|
||||
<circle key="seat-213" id="seat-213" r="15.7" cy="455.8" cx="1108.8" />,
|
||||
<circle key="seat-214" id="seat-214" r="15.7" cy="462" cx="1054.9" />,
|
||||
<circle key="seat-215" id="seat-215" r="15.7" cy="469.3" cx="1001.6" />,
|
||||
<circle key="seat-216" id="seat-216" r="15.7" cy="492.6" cx="954.1" />,
|
||||
<circle key="seat-217" id="seat-217" r="15.7" cy="500.6" cx="900.8" />,
|
||||
<circle key="seat-218" id="seat-218" r="15.7" cy="511.6" cx="849" />,
|
||||
<circle key="seat-219" id="seat-219" r="15.7" cy="535" cx="801.6" />,
|
||||
<circle key="seat-220" id="seat-220" r="15.7" cy="554.8" cx="753.3" />,
|
||||
<circle key="seat-221" id="seat-221" r="15.7" cy="490.9" cx="1118" />,
|
||||
<circle key="seat-222" id="seat-222" r="15.7" cy="497.2" cx="1065.3" />,
|
||||
<circle key="seat-223" id="seat-223" r="15.7" cy="502.8" cx="1012.3" />,
|
||||
<circle key="seat-224" id="seat-224" r="15.7" cy="525.1" cx="964.2" />,
|
||||
<circle key="seat-225" id="seat-225" r="15.7" cy="533.1" cx="912.2" />,
|
||||
<circle key="seat-226" id="seat-226" r="15.7" cy="554.6" cx="863.4" />,
|
||||
<circle key="seat-227" id="seat-227" r="15.7" cy="567.9" cx="812.7" />,
|
||||
<circle key="seat-228" id="seat-228" r="15.7" cy="596.7" cx="764.8" />,
|
||||
<circle key="seat-229" id="seat-229" r="15.7" cy="528.9" cx="1126.1" />,
|
||||
<circle key="seat-230" id="seat-230" r="15.7" cy="530.2" cx="1072.7" />,
|
||||
<circle key="seat-231" id="seat-231" transform="rotate(-80.8)" r="15.7" cy="1092.81" cx="-365.69" />,
|
||||
<circle key="seat-232" id="seat-232" r="15.7" cy="562.9" cx="1130.6" />,
|
||||
<circle key="seat-233" id="seat-233" r="15.7" cy="566.7" cx="1078.3" />,
|
||||
<circle key="seat-234" id="seat-234" transform="rotate(-80.8)" r="15.7" cy="1103.39" cx="-398.54" />,
|
||||
<circle key="seat-235" id="seat-235" r="15.7" cy="564.5" cx="972.4" />,
|
||||
<circle key="seat-236" id="seat-236" r="15.7" cy="568.6" cx="919.7" />,
|
||||
<circle key="seat-237" id="seat-237" r="15.7" cy="588" cx="869.4" />,
|
||||
<circle key="seat-238" id="seat-238" r="15.7" cy="602.5" cx="1133.5" />,
|
||||
<circle key="seat-239" id="seat-239" r="15.7" cy="600.9" cx="1081" />,
|
||||
<circle key="seat-240" id="seat-240" transform="rotate(-80.8)" r="15.7" cy="1111.41" cx="-431.3" />,
|
||||
<circle key="seat-241" id="seat-241" r="15.7" cy="598.6" cx="975.6" />,
|
||||
<circle key="seat-242" id="seat-242" r="15.7" cy="602.6" cx="923.4" />,
|
||||
<circle key="seat-243" id="seat-243" r="15.7" cy="636.4" cx="1133.9" />,
|
||||
<circle key="seat-244" id="seat-244" r="15.7" cy="639" cx="1081.3" />,
|
||||
<circle key="seat-245" id="seat-245" transform="rotate(-80.8)" r="15.7" cy="1117.48" cx="-466.13" />,
|
||||
<circle key="seat-246" id="seat-246" r="15.7" cy="639.2" cx="976.3" />,
|
||||
<circle key="seat-247" id="seat-247" r="15.7" cy="638.8" cx="923.9" />,
|
||||
<circle key="seat-248" id="seat-248" r="15.7" cy="635.1" cx="871.2" />,
|
||||
<circle key="seat-249" id="seat-249" r="15.7" cy="625.7" cx="818.8" />,
|
||||
<circle key="seat-250" id="seat-250" r="15.7" cy="639" cx="766.3" />,
|
||||
<circle key="seat-251" id="seat-251" r="15.7" cy="673.1" cx="1081.3" />,
|
||||
<circle key="seat-252" id="seat-252" transform="rotate(-80.8)" r="15.7" cy="1122.99" cx="-499.74" />,
|
||||
<circle key="seat-253" id="seat-253" r="15.7" cy="673.5" cx="976.3" />,
|
||||
<circle key="seat-254" id="seat-254" r="15.7" cy="673.2" cx="924" />,
|
||||
<circle key="seat-255" id="seat-255" r="15.7" cy="669.5" cx="871.2" />,
|
||||
<circle key="seat-256" id="seat-256" r="15.7" cy="660" cx="819.7" />,
|
||||
];
|
||||
|
||||
let seatIndex = 1; // Empezamos a contar desde 1, ya que el 0 es presidencial
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 1190.6 772.2" width={size} height={size * (772.2 / 1190.6)} style={{ display: 'block', margin: 'auto' }}>
|
||||
<g>
|
||||
{/* Renderizamos el escaño presidencial primero y por separado */}
|
||||
{presidenteBancada && React.cloneElement(seatElements[PRESIDENTE_SEAT_INDEX], {
|
||||
fill: presidenteBancada.color || '#A9A9A9',
|
||||
strokeWidth: 0.5,
|
||||
})}
|
||||
{partyData.map(partido => {
|
||||
// Por cada partido, creamos un array combinado de sus escaños
|
||||
const partySeats = [
|
||||
...Array(partido.bancasFijos).fill({ isNew: false }),
|
||||
...Array(partido.bancasGanadas).fill({ isNew: true })
|
||||
];
|
||||
|
||||
return (
|
||||
// Envolvemos todos los escaños de un partido en un <g>
|
||||
<g
|
||||
key={partido.id}
|
||||
className="party-block"
|
||||
data-tooltip-id="party-tooltip"
|
||||
data-tooltip-content={`${partido.nombreCorto || partido.nombre}: ${partido.bancasTotales} bancas`}
|
||||
>
|
||||
{partySeats.map((seatInfo, i) => {
|
||||
// Si ya no hay más plantillas de escaños, no renderizamos nada
|
||||
if (seatIndex >= seatElements.length) return null;
|
||||
|
||||
const template = seatElements[seatIndex];
|
||||
seatIndex++; // Incrementamos el contador para el siguiente escaño
|
||||
|
||||
// Clonamos la plantilla con el estilo apropiado
|
||||
return React.cloneElement(template, {
|
||||
key: `${partido.id}-${i}`,
|
||||
className: 'seat-circle',
|
||||
fill: partido.color || '#808080',
|
||||
fillOpacity: seatInfo.isNew ? 1 : 0.3, // Opacidad para bancas previas
|
||||
stroke: partido.color || '#808080',
|
||||
strokeWidth: 0.5,
|
||||
});
|
||||
})}
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
{/* Renderizamos los escaños vacíos sobrantes */}
|
||||
{seatIndex < seatElements.length &&
|
||||
seatElements.slice(seatIndex).map((template, i) =>
|
||||
React.cloneElement(template, {
|
||||
key: `empty-${i}`,
|
||||
fill: '#E0E0E0',
|
||||
stroke: '#ffffff',
|
||||
strokeWidth: 0.5
|
||||
})
|
||||
)
|
||||
}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
// src/components/common/SenadoresNacionalesLayout.tsx
|
||||
import React from 'react';
|
||||
import type { PartidoComposicionNacional } from '../../apiService';
|
||||
|
||||
// Interfaces
|
||||
interface SenadoresNacionalesLayoutProps {
|
||||
partyData: PartidoComposicionNacional[];
|
||||
size?: number;
|
||||
presidenteBancada?: { color: string | null } | null;
|
||||
}
|
||||
|
||||
const PRESIDENTE_SEAT_INDEX = 0;
|
||||
|
||||
export const SenadoresNacionalesLayout: React.FC<SenadoresNacionalesLayoutProps> = ({
|
||||
partyData,
|
||||
size = 800,
|
||||
presidenteBancada,
|
||||
}) => {
|
||||
// --- ARRAY DE 73 ELEMENTOS <circle> ORDENADOS POR ID DE "seat-X" ---
|
||||
// El asiento 0 es el presidencial, los 72 restantes son los senadores.
|
||||
const seatElements = [
|
||||
<circle key="seat-0" id="seat-0" r="7.1" cy="187" cx="168.6" />,
|
||||
<circle key="seat-1" id="seat-1" r="7.1" cy="166" cx="21.8" />,
|
||||
<circle key="seat-2" id="seat-2" r="7.1" cy="172" cx="51.5" />,
|
||||
<circle key="seat-3" id="seat-3" r="7.1" cy="174.5" cx="82.7" />,
|
||||
<circle key="seat-4" id="seat-4" r="7.1" cy="147.4" cx="21.5" />,
|
||||
<circle key="seat-5" id="seat-5" r="7.1" cy="155.2" cx="51.8" />,
|
||||
<circle key="seat-6" id="seat-6" r="7.1" cy="156.3" cx="83.4" />,
|
||||
<circle key="seat-7" id="seat-7" r="7.1" cy="169.9" cx="120.9" />,
|
||||
<circle key="seat-8" id="seat-8" r="7.1" cy="128.4" cx="22.8" />,
|
||||
<circle key="seat-9" id="seat-9" r="7.1" cy="137.9" cx="53.2" />,
|
||||
<circle key="seat-10" id="seat-10" r="7.1" cy="138.8" cx="85.5" />,
|
||||
<circle key="seat-11" id="seat-11" r="7.1" cy="151.9" cx="120.9" />,
|
||||
<circle key="seat-12" id="seat-12" r="7.1" cy="109" cx="25.6" />,
|
||||
<circle key="seat-13" id="seat-13" r="7.1" cy="121.3" cx="57.2" />,
|
||||
<circle key="seat-14" id="seat-14" r="7.1" cy="91.5" cx="34.2" />,
|
||||
<circle key="seat-15" id="seat-15" r="7.1" cy="105.7" cx="64.8" />,
|
||||
<circle key="seat-16" id="seat-16" r="7.1" cy="122.5" cx="92.9" />,
|
||||
<circle key="seat-17" id="seat-17" r="7.1" cy="136.2" cx="128.2" />,
|
||||
<circle key="seat-18" id="seat-18" r="7.1" cy="75.5" cx="45.3" />,
|
||||
<circle key="seat-19" id="seat-19" r="7.1" cy="91.3" cx="75.7" />,
|
||||
<circle key="seat-20" id="seat-20" r="7.1" cy="106.5" cx="106.3" />,
|
||||
<circle key="seat-21" id="seat-21" r="7.1" cy="59.8" cx="57.9" />,
|
||||
<circle key="seat-22" id="seat-22" r="7.1" cy="78.6" cx="89.5" />,
|
||||
<circle key="seat-23" id="seat-23" r="7.1" cy="45.3" cx="73.2" />,
|
||||
<circle key="seat-24" id="seat-24" r="7.1" cy="67.2" cx="104.6" />,
|
||||
<circle key="seat-25" id="seat-25" r="7.1" cy="94.3" cx="121.6" />,
|
||||
<circle key="seat-26" id="seat-26" r="7.1" cy="124.3" cx="141.1" />,
|
||||
<circle key="seat-27" id="seat-27" r="7.1" cy="32.7" cx="90.8" />,
|
||||
<circle key="seat-28" id="seat-28" r="7.1" cy="58.3" cx="120.9" />,
|
||||
<circle key="seat-29" id="seat-29" r="7.1" cy="84.9" cx="139.1" />,
|
||||
<circle key="seat-30" id="seat-30" r="7.1" cy="116.4" cx="157.2" />,
|
||||
<circle key="seat-31" id="seat-31" r="7.1" cy="24.6" cx="109.5" />,
|
||||
<circle key="seat-32" id="seat-32" r="7.1" cy="52.2" cx="138.6" />,
|
||||
<circle key="seat-33" id="seat-33" r="7.1" cy="79.5" cx="157.8" />,
|
||||
<circle key="seat-34" id="seat-34" r="7.1" cy="17.9" cx="128.8" />,
|
||||
<circle key="seat-35" id="seat-35" r="7.1" cy="15.2" cx="147.7" />,
|
||||
<circle key="seat-36" id="seat-36" r="7.1" cy="48.3" cx="156.9" />,
|
||||
<circle key="seat-37" id="seat-37" r="7.1" cy="15.2" cx="192.5" />,
|
||||
<circle key="seat-38" id="seat-38" r="7.1" cy="48.3" cx="183.3" />,
|
||||
<circle key="seat-39" id="seat-39" r="7.1" cy="79.5" cx="182.4" />,
|
||||
<circle key="seat-40" id="seat-40" r="7.1" cy="115.8" cx="182.2" />,
|
||||
<circle key="seat-41" id="seat-41" r="7.1" cy="17.9" cx="211.4" />,
|
||||
<circle key="seat-42" id="seat-42" r="7.1" cy="52.2" cx="201.6" />,
|
||||
<circle key="seat-43" id="seat-43" r="7.1" cy="24.6" cx="230.7" />,
|
||||
<circle key="seat-44" id="seat-44" r="7.1" cy="58.3" cx="219.3" />,
|
||||
<circle key="seat-45" id="seat-45" r="7.1" cy="84.9" cx="201.1" />,
|
||||
<circle key="seat-46" id="seat-46" r="7.1" cy="32.7" cx="249.4" />,
|
||||
<circle key="seat-47" id="seat-47" r="7.1" cy="67.2" cx="235.6" />,
|
||||
<circle key="seat-48" id="seat-48" r="7.1" cy="94.3" cx="218.6" />,
|
||||
<circle key="seat-49" id="seat-49" r="7.1" cy="124.3" cx="199.1" />,
|
||||
<circle key="seat-50" id="seat-50" r="7.1" cy="45.3" cx="267" />,
|
||||
<circle key="seat-51" id="seat-51" r="7.1" cy="59.8" cx="282.3" />,
|
||||
<circle key="seat-52" id="seat-52" r="7.1" cy="78.6" cx="250.7" />,
|
||||
<circle key="seat-53" id="seat-53" r="7.1" cy="106.5" cx="234" />,
|
||||
<circle key="seat-54" id="seat-54" r="7.1" cy="136.2" cx="212" />,
|
||||
<circle key="seat-55" id="seat-55" r="7.1" cy="75.5" cx="294.9" />,
|
||||
<circle key="seat-56" id="seat-56" r="7.1" cy="91.3" cx="264.5" />,
|
||||
<circle key="seat-57" id="seat-57" r="7.1" cy="91.5" cx="306" />,
|
||||
<circle key="seat-58" id="seat-58" r="7.1" cy="105.7" cx="275.4" />,
|
||||
<circle key="seat-59" id="seat-59" r="7.1" cy="122.5" cx="247.3" />,
|
||||
<circle key="seat-60" id="seat-60" r="7.1" cy="109" cx="313.5" />,
|
||||
<circle key="seat-61" id="seat-61" r="7.1" cy="121.3" cx="283" />,
|
||||
<circle key="seat-62" id="seat-62" r="7.1" cy="138.8" cx="254.7" />,
|
||||
<circle key="seat-63" id="seat-63" r="7.1" cy="151.9" cx="219.3" />,
|
||||
<circle key="seat-64" id="seat-64" r="7.1" cy="128.4" cx="317.4" />,
|
||||
<circle key="seat-65" id="seat-65" r="7.1" cy="137.9" cx="287" />,
|
||||
<circle key="seat-66" id="seat-66" r="7.1" cy="156.3" cx="256.8" />,
|
||||
<circle key="seat-67" id="seat-67" r="7.1" cy="169.9" cx="219.3" />,
|
||||
<circle key="seat-68" id="seat-68" r="7.1" cy="147.4" cx="318.7" />,
|
||||
<circle key="seat-69" id="seat-69" r="7.1" cy="155.2" cx="288.4" />,
|
||||
<circle key="seat-70" id="seat-70" r="7.1" cy="166" cx="318.4" />,
|
||||
<circle key="seat-71" id="seat-71" r="7.1" cy="172" cx="288.7" />,
|
||||
<circle key="seat-72" id="seat-72" r="7.1" cy="174.5" cx="257.5" />,
|
||||
];
|
||||
|
||||
let seatIndex = 1; // Empezamos desde 1 porque el 0 es para el presidente
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 340.2 220.5" width={size} height={size * (220.5 / 340.2)} style={{ display: 'block', margin: 'auto' }}>
|
||||
<g>
|
||||
{/* Renderizamos primero el escaño del presidente por separado */}
|
||||
{presidenteBancada && React.cloneElement(seatElements[PRESIDENTE_SEAT_INDEX], {
|
||||
fill: presidenteBancada.color || '#A9A9A9',
|
||||
strokeWidth: 0.5,
|
||||
})}
|
||||
|
||||
{/* Mapeamos los partidos para crear los bloques */}
|
||||
{partyData.map(partido => {
|
||||
const partySeats = [
|
||||
...Array(partido.bancasFijos).fill({ isNew: false }),
|
||||
...Array(partido.bancasGanadas).fill({ isNew: true })
|
||||
];
|
||||
|
||||
return (
|
||||
<g
|
||||
key={partido.id}
|
||||
className="party-block"
|
||||
data-tooltip-id="party-tooltip"
|
||||
data-tooltip-content={`${partido.nombreCorto || partido.nombre}: ${partido.bancasTotales} bancas`}
|
||||
>
|
||||
{partySeats.map((seatInfo, i) => {
|
||||
if (seatIndex >= seatElements.length) return null;
|
||||
|
||||
const template = seatElements[seatIndex];
|
||||
seatIndex++;
|
||||
|
||||
return React.cloneElement(template, {
|
||||
key: `${partido.id}-${i}`,
|
||||
className: 'seat-circle',
|
||||
fill: partido.color || '#808080',
|
||||
fillOpacity: seatInfo.isNew ? 1 : 0.3,
|
||||
stroke: partido.color || '#808080',
|
||||
strokeWidth: 0.5,
|
||||
});
|
||||
})}
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
{/* Renderizamos escaños vacíos si sobran */}
|
||||
{seatIndex < seatElements.length &&
|
||||
seatElements.slice(seatIndex).map((template, i) =>
|
||||
React.cloneElement(template, {
|
||||
key: `empty-${i}`,
|
||||
fill: '#E0E0E0',
|
||||
stroke: '#ffffff',
|
||||
strokeWidth: 0.5
|
||||
})
|
||||
)
|
||||
}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,6 @@
|
||||
// src/features/legislativas/rovinciales/DevAppLegislativas.tsx
|
||||
import { ResultadosNacionalesCardsWidget } from './nacionales/ResultadosNacionalesCardsWidget';
|
||||
import { CongresoNacionalWidget } from './nacionales/CongresoNacionalWidget';
|
||||
import { PanelNacionalWidget } from './nacionales/PanelNacionalWidget';
|
||||
import './DevAppStyle.css'
|
||||
|
||||
@@ -6,9 +8,8 @@ export const DevAppLegislativas = () => {
|
||||
return (
|
||||
<div className="container">
|
||||
<h1>Visor de Widgets</h1>
|
||||
|
||||
{/* Le pasamos el ID de la elección que queremos visualizar.
|
||||
Para tus datos de prueba provinciales, este ID es 1. */}
|
||||
<ResultadosNacionalesCardsWidget eleccionId={2} />
|
||||
<CongresoNacionalWidget eleccionId={2} />
|
||||
<PanelNacionalWidget eleccionId={2} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
// src/features/legislativas/nacionales/CongresoNacionalWidget.tsx
|
||||
import { useState, Suspense, useMemo } from 'react';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { DiputadosNacionalesLayout } from '../../../components/common/DiputadosNacionalesLayout';
|
||||
import { SenadoresNacionalesLayout } from '../../../components/common/SenadoresNacionalesLayout';
|
||||
import { getComposicionNacional, type ComposicionNacionalData, type PartidoComposicionNacional } from '../../../apiService';
|
||||
import '../provinciales/CongresoWidget.css';
|
||||
|
||||
interface CongresoNacionalWidgetProps {
|
||||
eleccionId: number;
|
||||
}
|
||||
|
||||
const formatTimestamp = (dateString: string) => {
|
||||
if (!dateString) return '...';
|
||||
const date = new Date(dateString);
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${day}/${month}/${year} ${hours}:${minutes}`;
|
||||
};
|
||||
|
||||
const WidgetContent = ({ eleccionId }: CongresoNacionalWidgetProps) => {
|
||||
const [camaraActiva, setCamaraActiva] = useState<'diputados' | 'senadores'>('diputados');
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
|
||||
const { data } = useSuspenseQuery<ComposicionNacionalData>({
|
||||
queryKey: ['composicionNacional', eleccionId],
|
||||
queryFn: () => getComposicionNacional(eleccionId),
|
||||
refetchInterval: 30000,
|
||||
});
|
||||
|
||||
const datosCamaraActual = data[camaraActiva];
|
||||
|
||||
const partidosOrdenados = useMemo(() => {
|
||||
if (!datosCamaraActual?.partidos) return [];
|
||||
const partidosACopiar = [...datosCamaraActual.partidos];
|
||||
partidosACopiar.sort((a, b) => {
|
||||
const ordenA = camaraActiva === 'diputados' ? a.ordenDiputadosNacionales : a.ordenSenadoresNacionales;
|
||||
const ordenB = camaraActiva === 'diputados' ? b.ordenDiputadosNacionales : b.ordenSenadoresNacionales;
|
||||
return (ordenA ?? 999) - (ordenB ?? 999);
|
||||
});
|
||||
return partidosACopiar;
|
||||
}, [datosCamaraActual, camaraActiva]);
|
||||
|
||||
const partyDataParaLayout = useMemo(() => {
|
||||
if (camaraActiva === 'senadores') return partidosOrdenados;
|
||||
if (!partidosOrdenados || !datosCamaraActual.presidenteBancada?.color) return partidosOrdenados;
|
||||
const partidoPresidente = partidosOrdenados.find(p => p.color === datosCamaraActual.presidenteBancada!.color);
|
||||
if (!partidoPresidente) return partidosOrdenados;
|
||||
|
||||
const adjustedPartyData = JSON.parse(JSON.stringify(partidosOrdenados));
|
||||
const partidoAjustar = adjustedPartyData.find((p: PartidoComposicionNacional) => p.id === partidoPresidente.id);
|
||||
|
||||
if (partidoAjustar) {
|
||||
const tipoBanca = datosCamaraActual.presidenteBancada.tipoBanca;
|
||||
if (tipoBanca === 'ganada' && partidoAjustar.bancasGanadas > 0) {
|
||||
partidoAjustar.bancasGanadas -= 1;
|
||||
} else if (tipoBanca === 'previa' && partidoAjustar.bancasFijos > 0) {
|
||||
partidoAjustar.bancasFijos -= 1;
|
||||
} else {
|
||||
if (partidoAjustar.bancasGanadas > 0) {
|
||||
partidoAjustar.bancasGanadas -= 1;
|
||||
} else if (partidoAjustar.bancasFijos > 0) {
|
||||
partidoAjustar.bancasFijos -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return adjustedPartyData;
|
||||
}, [partidosOrdenados, datosCamaraActual.presidenteBancada, camaraActiva]);
|
||||
|
||||
return (
|
||||
<div className="congreso-container">
|
||||
<div className="congreso-grafico">
|
||||
<div
|
||||
className={`congreso-hemiciclo-wrapper ${isHovering ? 'is-hovering' : ''}`}
|
||||
onMouseEnter={() => setIsHovering(true)}
|
||||
onMouseLeave={() => setIsHovering(false)}
|
||||
>
|
||||
{camaraActiva === 'diputados' ?
|
||||
<DiputadosNacionalesLayout
|
||||
partyData={partyDataParaLayout}
|
||||
presidenteBancada={datosCamaraActual.presidenteBancada || null}
|
||||
size={700}
|
||||
/> :
|
||||
<SenadoresNacionalesLayout
|
||||
partyData={partyDataParaLayout}
|
||||
presidenteBancada={datosCamaraActual.presidenteBancada || null}
|
||||
size={700}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div className="congreso-footer">
|
||||
<div className="footer-legend">
|
||||
<div className="footer-legend-item">
|
||||
{/* Usamos la nueva clase CSS para el círculo sólido */}
|
||||
<span className="legend-icon legend-icon--solid"></span>
|
||||
<span>Bancas en juego</span>
|
||||
</div>
|
||||
<div className="footer-legend-item">
|
||||
{/* Reemplazamos el SVG por un span con la nueva clase para el anillo */}
|
||||
<span className="legend-icon legend-icon--ring"></span>
|
||||
<span>Bancas previas</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer-timestamp">
|
||||
Última Actualización: {formatTimestamp(datosCamaraActual.ultimaActualizacion)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="congreso-summary">
|
||||
<div className="chamber-tabs">
|
||||
<button className={camaraActiva === 'diputados' ? 'active' : ''} onClick={() => setCamaraActiva('diputados')}>
|
||||
Diputados
|
||||
</button>
|
||||
<button className={camaraActiva === 'senadores' ? 'active' : ''} onClick={() => setCamaraActiva('senadores')}>
|
||||
Senadores
|
||||
</button>
|
||||
</div>
|
||||
<h3>{datosCamaraActual.camaraNombre}</h3>
|
||||
<div className="summary-metric">
|
||||
<span>Total de Bancas</span>
|
||||
<strong>{datosCamaraActual.totalBancas}</strong>
|
||||
</div>
|
||||
<div className="summary-metric">
|
||||
<span>Bancas en Juego</span>
|
||||
<strong>{datosCamaraActual.bancasEnJuego}</strong>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="partido-lista-container">
|
||||
<ul className="partido-lista">
|
||||
{partidosOrdenados
|
||||
.filter(p => p.bancasTotales > 0)
|
||||
.map((partido: PartidoComposicionNacional) => (
|
||||
<li key={partido.id}>
|
||||
<span className="partido-color-box" style={{ backgroundColor: partido.color || '#808080' }}></span>
|
||||
<span className="partido-nombre">{partido.nombreCorto || partido.nombre}</span>
|
||||
<strong
|
||||
className="partido-bancas"
|
||||
title={`${partido.bancasFijos} bancas previas + ${partido.bancasGanadas} ganadas`}
|
||||
>
|
||||
{partido.bancasTotales}
|
||||
</strong>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip id="party-tooltip" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const CongresoNacionalWidget = ({ eleccionId }: CongresoNacionalWidgetProps) => {
|
||||
return (
|
||||
<Suspense fallback={<div className="congreso-container loading" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '400px' }}>Cargando composición del congreso...</div>}>
|
||||
<WidgetContent eleccionId={eleccionId} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
/* src/features/legislativas/nacionales/PanelNaciona.css */
|
||||
/* src/features/legislativas/nacionales/PanelNacional.css */
|
||||
.panel-nacional-container {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
@@ -491,13 +492,11 @@
|
||||
/* --- NUEVOS ESTILOS PARA EL TOGGLE MÓVIL --- */
|
||||
.mobile-view-toggle {
|
||||
display: none;
|
||||
/* Oculto por defecto */
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
position: absolute; /* <-- CAMBIO: De 'fixed' a 'absolute' */
|
||||
bottom: 10px; /* <-- AJUSTE: Menos espacio desde abajo */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 100;
|
||||
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 30px;
|
||||
padding: 5px;
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
/* src/features/legislativas/nacionales/ResultadosNacionalesCardsWidget.css */
|
||||
|
||||
/* --- Variables de Diseño --- */
|
||||
:root {
|
||||
--card-border-color: #e0e0e0;
|
||||
--card-bg-color: #ffffff;
|
||||
--card-header-bg-color: #f8f9fa;
|
||||
--card-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
--text-primary: #212529;
|
||||
--text-secondary: #6c757d;
|
||||
--font-family: "Public Sans", system-ui, sans-serif;
|
||||
--primary-accent-color: #007bff;
|
||||
}
|
||||
|
||||
/* --- Contenedor Principal del Widget --- */
|
||||
.cards-widget-container {
|
||||
font-family: var(--font-family);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.cards-widget-container h2 {
|
||||
font-size: 1.75rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
/* --- Grilla de Tarjetas --- */
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
/* Crea columnas flexibles que se ajustan al espacio disponible */
|
||||
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* --- Tarjeta Individual --- */
|
||||
.provincia-card {
|
||||
background-color: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--card-shadow);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden; /* Asegura que los bordes redondeados se apliquen al contenido */
|
||||
}
|
||||
|
||||
/* --- Cabecera de la Tarjeta --- */
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: var(--card-header-bg-color);
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
.header-info h3 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.header-info span {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.header-map {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-color: #e9ecef;
|
||||
padding: 0.25rem;
|
||||
box-sizing: border-box; /* Para que el padding no aumente el tamaño total */
|
||||
}
|
||||
|
||||
/* Contenedor del SVG para asegurar que se ajuste al espacio */
|
||||
.map-svg-container, .map-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Estilo para el SVG renderizado */
|
||||
.map-svg-container svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain; /* Asegura que el mapa no se deforme */
|
||||
}
|
||||
|
||||
/* Placeholder para cuando el mapa no carga */
|
||||
.map-placeholder.error {
|
||||
background-color: #f8d7da; /* Un color de fondo rojizo para indicar un error */
|
||||
}
|
||||
|
||||
/* --- Cuerpo de la Tarjeta --- */
|
||||
.card-body {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.candidato-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.candidato-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.candidato-foto {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.candidato-data {
|
||||
flex-grow: 1;
|
||||
min-width: 0; /* Permite que el texto se trunque si es necesario */
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.candidato-nombre {
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-primary);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.candidato-partido {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
height: 6px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
transition: width 0.5s ease-out;
|
||||
}
|
||||
|
||||
.candidato-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
.stats-percent {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.stats-votos {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.stats-bancas {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 6px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin-left: 0.75rem;
|
||||
font-weight: 700;
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-accent-color);
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.stats-bancas span {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
|
||||
/* --- Pie de la Tarjeta --- */
|
||||
.card-footer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
background-color: var(--card-header-bg-color);
|
||||
border-top: 1px solid var(--card-border-color);
|
||||
padding: 0.75rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-footer div {
|
||||
border-right: 1px solid var(--card-border-color);
|
||||
}
|
||||
|
||||
.card-footer div:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.card-footer span {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.card-footer strong {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* --- Media Query para Móvil --- */
|
||||
@media (max-width: 480px) {
|
||||
.cards-grid {
|
||||
/* En pantallas muy pequeñas, forzamos una sola columna */
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.header-info h3 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- NUEVOS ESTILOS PARA EL NOMBRE DEL PARTIDO CUANDO ES EL TÍTULO PRINCIPAL --- */
|
||||
.candidato-partido.main-title {
|
||||
font-size: 0.95rem; /* Hacemos la fuente más grande */
|
||||
font-weight: 700; /* La ponemos en negrita, como el nombre del candidato */
|
||||
color: var(--text-primary); /* Usamos el color de texto principal */
|
||||
text-transform: none; /* Quitamos el 'uppercase' para que se lea mejor */
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// src/features/legislativas/nacionales/ResultadosNacionalesCardsWidget.tsx
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getResumenPorProvincia } from '../../../apiService';
|
||||
import { ProvinciaCard } from './components/ProvinciaCard';
|
||||
import './ResultadosNacionalesCardsWidget.css';
|
||||
|
||||
interface Props {
|
||||
eleccionId: number;
|
||||
}
|
||||
|
||||
export const ResultadosNacionalesCardsWidget = ({ eleccionId }: Props) => {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['resumenPorProvincia', eleccionId],
|
||||
queryFn: () => getResumenPorProvincia(eleccionId),
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Cargando resultados por provincia...</div>;
|
||||
if (error) return <div>Error al cargar los datos.</div>;
|
||||
|
||||
return (
|
||||
<section className="cards-widget-container">
|
||||
<h2>Resultados elecciones nacionales 2025</h2>
|
||||
<div className="cards-grid">
|
||||
{data?.map(provinciaData => (
|
||||
<ProvinciaCard key={provinciaData.provinciaId} data={provinciaData} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
// src/features/legislativas/nacionales/components/MiniMapaSvg.tsx
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import axios from 'axios';
|
||||
import { useMemo } from 'react';
|
||||
import { assetBaseUrl } from '../../../../apiService';
|
||||
|
||||
interface MiniMapaSvgProps {
|
||||
provinciaNombre: string;
|
||||
fillColor: string;
|
||||
}
|
||||
|
||||
// Función para normalizar el nombre de la provincia y que coincida con el nombre del archivo SVG
|
||||
const normalizarNombreParaUrl = (nombre: string) =>
|
||||
nombre
|
||||
.toLowerCase()
|
||||
.replace(/ /g, '_') // Reemplaza espacios con guiones bajos
|
||||
.normalize("NFD") // Descompone acentos para eliminarlos en el siguiente paso
|
||||
.replace(/[\u0300-\u036f]/g, ""); // Elimina los acentos
|
||||
|
||||
export const MiniMapaSvg = ({ provinciaNombre, fillColor }: MiniMapaSvgProps) => {
|
||||
const nombreNormalizado = normalizarNombreParaUrl(provinciaNombre);
|
||||
// Asumimos que los SVGs están en /public/maps/provincias-svg/
|
||||
const mapFileUrl = `${assetBaseUrl}/maps/provincias-svg/${nombreNormalizado}.svg`;
|
||||
|
||||
// Usamos React Query para fetchear el contenido del SVG como texto
|
||||
const { data: svgContent, isLoading, isError } = useQuery<string>({
|
||||
queryKey: ['svgMapa', nombreNormalizado],
|
||||
queryFn: async () => {
|
||||
const response = await axios.get(mapFileUrl, { responseType: 'text' });
|
||||
return response.data;
|
||||
},
|
||||
staleTime: Infinity, // Estos archivos son estáticos y no cambian
|
||||
gcTime: Infinity,
|
||||
retry: false, // No reintentar si el archivo no existe
|
||||
});
|
||||
|
||||
// Usamos useMemo para modificar el SVG solo cuando el contenido o el color cambian
|
||||
const modifiedSvg = useMemo(() => {
|
||||
if (!svgContent) return '';
|
||||
|
||||
// Usamos una expresión regular para encontrar todas las etiquetas <path>
|
||||
// y añadirles el atributo de relleno con el color del ganador.
|
||||
// Esto sobrescribirá cualquier 'fill' que ya exista en la etiqueta.
|
||||
return svgContent.replace(/<path/g, `<path fill="${fillColor}"`);
|
||||
}, [svgContent, fillColor]);
|
||||
|
||||
if (isLoading) {
|
||||
return <div className="map-placeholder" />;
|
||||
}
|
||||
|
||||
if (isError || !modifiedSvg) {
|
||||
// Muestra un placeholder si el SVG no se encontró o está vacío
|
||||
return <div className="map-placeholder error" />;
|
||||
}
|
||||
|
||||
// Renderizamos el SVG modificado. dangerouslySetInnerHTML es seguro aquí
|
||||
// porque el contenido proviene de nuestros propios archivos SVG estáticos.
|
||||
return (
|
||||
<div
|
||||
className="map-svg-container"
|
||||
dangerouslySetInnerHTML={{ __html: modifiedSvg }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// src/features/legislativas/nacionales/components/ProvinciaCard.tsx
|
||||
import type { ResumenProvincia } from '../../../../types/types';
|
||||
import { MiniMapaSvg } from './MiniMapaSvg';
|
||||
import { ImageWithFallback } from '../../../../components/common/ImageWithFallback';
|
||||
import { assetBaseUrl } from '../../../../apiService';
|
||||
|
||||
interface ProvinciaCardProps {
|
||||
data: ResumenProvincia;
|
||||
}
|
||||
|
||||
const formatNumber = (num: number) => num.toLocaleString('es-AR');
|
||||
const formatPercent = (num: number) => `${num.toFixed(2).replace('.', ',')}%`;
|
||||
|
||||
export const ProvinciaCard = ({ data }: ProvinciaCardProps) => {
|
||||
// Determinamos el color del ganador para pasárselo al mapa.
|
||||
// Si no hay ganador, usamos un color gris por defecto.
|
||||
const colorGanador = data.resultados[0]?.color || '#d1d1d1';
|
||||
|
||||
return (
|
||||
<div className="provincia-card">
|
||||
<header className="card-header">
|
||||
<div className="header-info">
|
||||
<h3 style={{ whiteSpace: 'normal' }}>{data.provinciaNombre}</h3>
|
||||
<span>DIPUTADOS NACIONALES</span>
|
||||
</div>
|
||||
<div className="header-map">
|
||||
<MiniMapaSvg provinciaNombre={data.provinciaNombre} fillColor={colorGanador} />
|
||||
</div>
|
||||
</header>
|
||||
<div className="card-body">
|
||||
{data.resultados.map(res => (
|
||||
<div key={res.agrupacionId} className="candidato-row">
|
||||
<ImageWithFallback src={res.fotoUrl ?? undefined} fallbackSrc={`${assetBaseUrl}/default-avatar.png`} alt={res.nombreCandidato ?? res.nombreAgrupacion} className="candidato-foto" />
|
||||
|
||||
<div className="candidato-data">
|
||||
{res.nombreCandidato && (
|
||||
<span className="candidato-nombre">{res.nombreCandidato}</span>
|
||||
)}
|
||||
|
||||
<span className={`candidato-partido ${!res.nombreCandidato ? 'main-title' : ''}`}>
|
||||
{res.nombreAgrupacion}
|
||||
</span>
|
||||
|
||||
<div className="progress-bar-container">
|
||||
<div className="progress-bar" style={{ width: `${res.porcentaje}%`, backgroundColor: res.color || '#ccc' }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="candidato-stats">
|
||||
<span className="stats-percent">{formatPercent(res.porcentaje)}</span>
|
||||
<span className="stats-votos">{formatNumber(res.votos)} votos</span>
|
||||
</div>
|
||||
<div className="stats-bancas">
|
||||
+{res.bancasObtenidas}
|
||||
<span>Bancas</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<footer className="card-footer">
|
||||
<div>
|
||||
<span>Participación</span>
|
||||
{/* Usamos los datos reales del estado de recuento */}
|
||||
<strong>{formatPercent(data.estadoRecuento?.participacionPorcentaje ?? 0)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Mesas escrutadas</span>
|
||||
<strong>{formatPercent(data.estadoRecuento?.mesasTotalizadasPorcentaje ?? 0)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Votos totales</span>
|
||||
{/* Usamos el nuevo campo cantidadVotantes */}
|
||||
<strong>{formatNumber(data.estadoRecuento?.cantidadVotantes ?? 0)}</strong>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,27 +1,35 @@
|
||||
/* src/features/legislativas/provinciales/CongresoWidget.css */
|
||||
.congreso-container {
|
||||
display: flex;
|
||||
/* Se reduce ligeramente el espacio entre el gráfico y el panel */
|
||||
gap: 1rem;
|
||||
gap: 1.5rem;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e0e0e0;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
max-width: 800px;
|
||||
max-width: 900px;
|
||||
margin: 20px auto;
|
||||
font-family: "Public Sans", system-ui, sans-serif;
|
||||
color: #333333;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.congreso-grafico {
|
||||
/* --- CAMBIO PRINCIPAL: Se aumenta la proporción del gráfico --- */
|
||||
flex: 1 1 65%;
|
||||
flex: 2;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.congreso-hemiciclo-wrapper {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.congreso-hemiciclo-wrapper.is-hovering .party-block:not(:hover) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.congreso-grafico svg {
|
||||
@@ -30,35 +38,139 @@
|
||||
animation: fadeIn 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
/* --- NUEVOS ESTILOS PARA EL FOOTER DEL GRÁFICO --- */
|
||||
.congreso-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem 0 1rem;
|
||||
margin-top: auto; /* Empuja el footer a la parte inferior del contenedor flex */
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
.footer-legend {
|
||||
display: flex;
|
||||
gap: 1.5rem; /* Espacio entre los items de la leyenda */
|
||||
}
|
||||
|
||||
.footer-legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem; /* Espacio entre el icono y el texto */
|
||||
}
|
||||
|
||||
.footer-timestamp {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* --- ESTILOS PARA HOVER --- */
|
||||
|
||||
/* Estilo base para cada círculo de escaño */
|
||||
.seat-circle {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.party-block {
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.party-block:hover .seat-circle {
|
||||
stroke: #333 !important; /* Borde oscuro para resaltar */
|
||||
stroke-width: 1.5px !important;
|
||||
stroke-opacity: 1;
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
/* CORRECCIÓN: El selector ahora apunta al wrapper correcto */
|
||||
.congreso-hemiciclo-wrapper.is-hovering .party-block:not(:hover) {
|
||||
opacity: 0.3; /* Hacemos el desvanecimiento más pronunciado */
|
||||
}
|
||||
.congreso-grafico svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
animation: fadeIn 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: scale(0.9); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
/* --- INICIO DE NUEVOS ESTILOS PARA EL FOOTER DEL GRÁFICO --- */
|
||||
.congreso-footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0.5rem 0 0.5rem;
|
||||
margin-top: auto;
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.footer-legend {
|
||||
display: flex;
|
||||
gap: 1.25rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/* Creamos una clase base para ambos iconos para compartir tamaño */
|
||||
.legend-icon {
|
||||
display: inline-block;
|
||||
width: 14px; /* Tamaño base para ambos iconos */
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Estilo para el icono de "Bancas en juego" (círculo sólido) */
|
||||
.legend-icon--solid {
|
||||
background-color: #888;
|
||||
border: 1px solid #777;
|
||||
}
|
||||
|
||||
/* Estilo para el icono de "Bancas previas" (anillo translúcido) */
|
||||
.legend-icon--ring {
|
||||
background-color: rgba(136, 136, 136, 0.3); /* #888 con opacidad */
|
||||
border: 1px solid #888; /* Borde sólido del mismo color */
|
||||
}
|
||||
|
||||
.footer-timestamp {
|
||||
font-weight: 500;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.congreso-summary {
|
||||
/* --- CAMBIO PRINCIPAL: Se reduce la proporción del panel de datos --- */
|
||||
flex: 1 1 35%;
|
||||
flex: 1;
|
||||
border-left: 1px solid #e0e0e0;
|
||||
/* Se reduce el padding para dar aún más espacio al gráfico */
|
||||
padding-left: 1rem;
|
||||
padding-left: 1.25rem; /* Un poco más de padding */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.congreso-summary h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.75rem; /* Margen inferior reducido */
|
||||
font-size: 1.4em;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.chamber-tabs {
|
||||
display: flex;
|
||||
margin-bottom: 1.5rem;
|
||||
margin-bottom: 1rem; /* Margen inferior reducido */
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
@@ -66,7 +178,7 @@
|
||||
|
||||
.chamber-tabs button {
|
||||
flex: 1;
|
||||
padding: 0.75rem 0.5rem;
|
||||
padding: 0.5rem 0.5rem;
|
||||
border: none;
|
||||
background-color: #f8f9fa;
|
||||
color: #6c757d;
|
||||
@@ -94,7 +206,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 0.25rem; /* Margen inferior muy reducido */
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
@@ -107,7 +219,15 @@
|
||||
.congreso-summary hr {
|
||||
border: none;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin: 1.5rem 0;
|
||||
margin: 1rem 0; /* Margen vertical reducido */
|
||||
}
|
||||
|
||||
/* Contenedor de la lista de partidos para aplicar el scroll */
|
||||
.partido-lista-container {
|
||||
flex-grow: 1; /* Ocupa el espacio vertical disponible */
|
||||
overflow-y: auto; /* Muestra el scrollbar si es necesario */
|
||||
min-height: 0; /* Truco de Flexbox para que el scroll funcione */
|
||||
padding-right: 8px; /* Espacio para el scrollbar */
|
||||
}
|
||||
|
||||
.partido-lista {
|
||||
@@ -119,14 +239,14 @@
|
||||
.partido-lista li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-bottom: 0.85rem; /* Un poco más de espacio entre items */
|
||||
}
|
||||
|
||||
.partido-color-box {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 3px;
|
||||
margin-right: 10px;
|
||||
width: 16px; /* Cuadro de color más grande */
|
||||
height: 16px;
|
||||
border-radius: 4px; /* Un poco más cuadrado */
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -139,19 +259,54 @@
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/* --- Media Query para Responsividad Móvil --- */
|
||||
/* --- Media Query para Responsividad Móvil (HASTA 768px) --- */
|
||||
@media (max-width: 768px) {
|
||||
.congreso-container {
|
||||
flex-direction: column;
|
||||
padding: 1.5rem;
|
||||
padding: 0.5rem;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.congreso-summary {
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
margin-top: 2rem;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.partido-lista-container {
|
||||
overflow-y: visible;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.congreso-footer {
|
||||
flex-direction: column; /* Apila la leyenda y el timestamp verticalmente */
|
||||
align-items: flex-start; /* Alinea todo a la izquierda */
|
||||
gap: 0.5rem; /* Añade un pequeño espacio entre la leyenda y el timestamp */
|
||||
padding: 0.75rem 0rem; /* Ajusta el padding para móvil */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-legend {
|
||||
gap: 0.75rem; /* Reduce el espacio entre los items de la leyenda */
|
||||
}
|
||||
|
||||
.footer-legend-item{
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.footer-timestamp {
|
||||
font-size: 0.75em; /* Reduce el tamaño de la fuente para que quepa mejor */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* --- Media Query para Escritorio (DESDE 769px en adelante) --- */
|
||||
@media (min-width: 769px) {
|
||||
.congreso-container {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
height: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,4 +245,29 @@ export interface PanelElectoralDto {
|
||||
mapaData: ResultadoMapaDto[];
|
||||
resultadosPanel: ResultadoTicker[]; // Reutilizamos el tipo que ya tienes
|
||||
estadoRecuento: EstadoRecuentoTicker; // Reutilizamos el tipo que ya tienes
|
||||
}
|
||||
|
||||
// --- TIPOS PARA EL WIDGET DE TARJETAS ---
|
||||
export interface EstadoRecuentoDto {
|
||||
participacionPorcentaje: number;
|
||||
mesasTotalizadasPorcentaje: number;
|
||||
cantidadVotantes: number;
|
||||
}
|
||||
|
||||
export interface ResultadoCandidato {
|
||||
agrupacionId: string;
|
||||
nombreCandidato: string | null;
|
||||
nombreAgrupacion: string;
|
||||
fotoUrl: string | null;
|
||||
color: string | null;
|
||||
porcentaje: number;
|
||||
votos: number;
|
||||
bancasObtenidas: number;
|
||||
}
|
||||
|
||||
export interface ResumenProvincia {
|
||||
provinciaId: string;
|
||||
provinciaNombre: string;
|
||||
estadoRecuento: EstadoRecuentoDto | null;
|
||||
resultados: ResultadoCandidato[];
|
||||
}
|
||||
Reference in New Issue
Block a user