Files
Elecciones-2025/Elecciones-Web/frontend/src/features/legislativas/nacionales/components/PanelResultados.tsx

111 lines
4.4 KiB
TypeScript

// src/features/legislativas/nacionales/components/PanelResultados.tsx
import type { ResultadoTicker, EstadoRecuentoTicker } from '../../../../types/types';
import { ImageWithFallback } from '../../../../components/common/ImageWithFallback';
import { assetBaseUrl } from '../../../../apiService';
import { AnimatedNumber } from './AnimatedNumber';
import { CircularProgressbar, buildStyles } from 'react-circular-progressbar';
import 'react-circular-progressbar/dist/styles.css';
const formatPercent = (num: number) => `${(num || 0).toFixed(2).replace('.', ',')}%`;
const formatVotes = (num: number) => Math.round(num).toLocaleString('es-AR');
const SvgDefs = () => (
<svg style={{ height: 0, width: 0, position: 'absolute' }}>
<defs>
<linearGradient id="participationGradient" gradientTransform="rotate(90)">
<stop offset="0%" stopColor="#e0f3ffff" />
<stop offset="100%" stopColor="#007bff" />
</linearGradient>
<linearGradient id="scrutinizedGradient" gradientTransform="rotate(90)">
<stop offset="0%" stopColor="#e0f3ffff" />
<stop offset="100%" stopColor="#007bff" />
</linearGradient>
</defs>
</svg>
);
interface PanelResultadosProps {
resultados: ResultadoTicker[];
estadoRecuento: EstadoRecuentoTicker;
}
export const PanelResultados = ({ resultados, estadoRecuento }: PanelResultadosProps) => {
return (
<div className="panel-resultados">
<SvgDefs />
<div className="panel-estado-recuento">
<div className="estado-item">
<CircularProgressbar
value={estadoRecuento.participacionPorcentaje}
text={formatPercent(estadoRecuento.participacionPorcentaje)}
strokeWidth={12}
circleRatio={0.75}
styles={buildStyles({
textColor: '#333',
pathColor: 'url(#participationGradient)',
trailColor: '#e9ecef',
textSize: '22px',
rotation: 0.625,
})}
/>
<span>Participación</span>
</div>
<div className="estado-item">
<CircularProgressbar
value={estadoRecuento.mesasTotalizadasPorcentaje}
text={formatPercent(estadoRecuento.mesasTotalizadasPorcentaje)}
strokeWidth={12}
circleRatio={0.75}
styles={buildStyles({
textColor: '#333',
pathColor: 'url(#scrutinizedGradient)',
trailColor: '#e9ecef',
textSize: '22px',
rotation: 0.625,
})}
/>
<span>Escrutado</span>
</div>
</div>
<div className="panel-partidos-container">
{resultados.map(partido => (
<div
key={partido.id}
className="partido-fila"
style={{ borderLeftColor: partido.color || '#ccc' }}
>
<div className="partido-logo" style={{ backgroundColor: partido.color || '#e9ecef' }}>
<ImageWithFallback src={partido.logoUrl || undefined} fallbackSrc={`${assetBaseUrl}/default-avatar.png`} alt={partido.nombre} />
</div>
<div className="partido-main-content">
<div className="partido-top-row">
<div className="partido-info-wrapper">
{partido.nombreCandidato ? (
<>
<span className="candidato-nombre">{partido.nombreCandidato}</span>
<span className="partido-nombre">{partido.nombreCorto || partido.nombre}</span>
</>
) : (
<span className="partido-nombre">{partido.nombreCorto || partido.nombre}</span>
)}
</div>
<div className="partido-stats">
<span className="partido-porcentaje">
<AnimatedNumber value={partido.porcentaje} formatter={formatPercent} />
</span>
<span className="partido-votos">
<AnimatedNumber value={partido.votos} formatter={formatVotes} /> votos
</span>
</div>
</div>
<div className="partido-barra-background">
<div className="partido-barra-foreground" style={{ width: `${partido.porcentaje}%`, backgroundColor: partido.color || '#888' }} />
</div>
</div>
</div>
))}
</div>
</div>
);
};