Fix bancas widget
This commit is contained in:
@@ -26,8 +26,8 @@ export interface ComposicionData {
|
||||
export interface OcupanteBanca {
|
||||
id: number;
|
||||
nombreOcupante: string;
|
||||
fotoUrl: string;
|
||||
periodo: string;
|
||||
fotoUrl: string | null;
|
||||
periodo: string | null;
|
||||
}
|
||||
|
||||
interface PartidoData {
|
||||
@@ -35,7 +35,6 @@ interface PartidoData {
|
||||
nombre: string;
|
||||
nombreCorto: string | null;
|
||||
bancasTotales: number;
|
||||
bancasEnJuego: number;
|
||||
color: string | null;
|
||||
ocupantes: OcupanteBanca[];
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ export const CongresoWidget = () => {
|
||||
const { data: composicionData, isLoading: isLoadingComposicion, error: errorComposicion } = useQuery<ComposicionData>({
|
||||
queryKey: ['composicionCongreso'],
|
||||
queryFn: getComposicionCongreso,
|
||||
// Vuelve a buscar los datos cada 20 segundos
|
||||
refetchInterval: 20000,
|
||||
});
|
||||
|
||||
const { data: bancadasDetalle = [] } = useQuery<BancadaDetalle[]>({
|
||||
@@ -29,28 +31,41 @@ export const CongresoWidget = () => {
|
||||
|
||||
// --- LÓGICA DE SEATFILLDATA ---
|
||||
const seatFillData = useMemo(() => {
|
||||
if (!datosCamaraActual || !bancadasDetalle.length) return [];
|
||||
if (!datosCamaraActual) return [];
|
||||
|
||||
const camaraId = camaraActiva === 'diputados' ? 0 : 1;
|
||||
const bancadasDeCamara = bancadasDetalle.filter(b => b.camara === camaraId);
|
||||
// --- LÓGICA DEL INTERRUPTOR ---
|
||||
// Verificamos si la respuesta de la API contiene datos de ocupantes.
|
||||
// Si bancadasDetalle tiene elementos, significa que el modo "Oficial" está activo en el backend.
|
||||
const modoOficialActivo = bancadasDetalle.length > 0;
|
||||
|
||||
// Creamos un mapa de AgrupacionId -> Color para un acceso rápido
|
||||
const colorMap = new Map<string, string>();
|
||||
datosCamaraActual.partidos.forEach(p => {
|
||||
if (p.id && p.color) {
|
||||
colorMap.set(p.id, p.color);
|
||||
}
|
||||
});
|
||||
if (modoOficialActivo) {
|
||||
// --- MODO OFICIAL: Construir desde las bancas físicas ---
|
||||
const camaraId = camaraActiva === 'diputados' ? 0 : 1;
|
||||
const bancadasDeCamara = bancadasDetalle.filter(b => b.camara === camaraId);
|
||||
|
||||
// 1. Aseguramos que la lista de bancas esté en orden físico (por su ID).
|
||||
const bancadasOrdenadas = bancadasDeCamara.sort((a, b) => a.id - b.id);
|
||||
const colorMap = new Map<string, string>();
|
||||
datosCamaraActual.partidos.forEach(p => {
|
||||
if (p.id && p.color) {
|
||||
colorMap.set(p.id, p.color);
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Mapeamos cada banca física a un objeto SeatFillData.
|
||||
// El índice del array corresponderá al asiento visual.
|
||||
return bancadasOrdenadas.map(bancada => ({
|
||||
color: bancada.agrupacionPoliticaId ? colorMap.get(bancada.agrupacionPoliticaId) || DEFAULT_COLOR : DEFAULT_COLOR,
|
||||
ocupante: bancada.ocupante
|
||||
}));
|
||||
const bancadasOrdenadas = bancadasDeCamara.sort((a, b) => a.id - b.id);
|
||||
|
||||
return bancadasOrdenadas.map(bancada => ({
|
||||
color: bancada.agrupacionPoliticaId ? colorMap.get(bancada.agrupacionPoliticaId) || DEFAULT_COLOR : DEFAULT_COLOR,
|
||||
ocupante: bancada.ocupante
|
||||
}));
|
||||
|
||||
} else {
|
||||
// --- MODO PROYECCIÓN: Construir desde los totales de los partidos ---
|
||||
// Esta es la lógica original que teníamos para el modo proyección.
|
||||
return datosCamaraActual.partidos.flatMap(party => {
|
||||
const seatColor = party.color || DEFAULT_COLOR;
|
||||
// En modo proyección, no hay ocupantes individuales.
|
||||
return Array(party.bancasTotales).fill({ color: seatColor, ocupante: null });
|
||||
});
|
||||
}
|
||||
|
||||
}, [datosCamaraActual, bancadasDetalle, camaraActiva]);
|
||||
|
||||
|
||||
@@ -127,43 +127,44 @@ export const ParliamentLayout: React.FC<ParliamentLayoutProps> = ({
|
||||
|
||||
// Si hay un presidente y su partido tiene bancas, le quitamos una para asignarla.
|
||||
if (presidenteBancada && presidenteBancada.color) {
|
||||
// Encontramos el índice del último asiento que pertenece al partido del presidente.
|
||||
const lastSeatIndex = finalSeatData.map(s => s.color).lastIndexOf(presidenteBancada.color);
|
||||
|
||||
if (lastSeatIndex !== -1) {
|
||||
// Eliminamos ese asiento de la lista de asientos electorales.
|
||||
finalSeatData.splice(lastSeatIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const renderedElements = seatElements.map((child, index) => {
|
||||
// El asiento presidencial sigue siendo un caso especial
|
||||
if (index === PRESIDENTE_SEAT_INDEX) {
|
||||
// --- CASO ESPECIAL: ASIENTO PRESIDENCIAL ---
|
||||
if (index === PRESIDENTE_SEAT_INDEX) {
|
||||
return React.cloneElement(child, {
|
||||
fill: presidenteBancada?.color || '#A9A9A9',
|
||||
stroke: '#000000',
|
||||
strokeWidth: 2,
|
||||
// Le damos un tooltip genérico al presidente
|
||||
'data-tooltip-id': 'seat-tooltip',
|
||||
'data-tooltip-html': `<div class="seat-tooltip"><p>Presidencia de la Cámara</p></div>`
|
||||
});
|
||||
}
|
||||
|
||||
// --- LÓGICA NORMAL PARA EL RESTO DE ASIENTOS ---
|
||||
// Usamos la copia modificada 'finalSeatData'. Como este array es más corto,
|
||||
// el último asiento electoral no encontrará datos y quedará gris, lo cual es correcto.
|
||||
const seat = finalSeatData[index];
|
||||
|
||||
if (!seat) {
|
||||
return React.cloneElement(child, { fill: '#E0E0E0', stroke: '#ffffff', strokeWidth: 1.5 });
|
||||
}
|
||||
|
||||
return React.cloneElement(child, {
|
||||
fill: presidenteBancada?.color || '#A9A9A9',
|
||||
stroke: '#000000',
|
||||
strokeWidth: 2,
|
||||
fill: seat.color,
|
||||
stroke: '#ffffff',
|
||||
strokeWidth: 1.5,
|
||||
'data-tooltip-id': seat.ocupante ? 'seat-tooltip' : undefined,
|
||||
'data-tooltip-html': seat.ocupante
|
||||
? `<div class="seat-tooltip"><img src="${seat.ocupante.fotoUrl || '/default-avatar.png'}" alt="${seat.ocupante.nombreOcupante}" /><p>${seat.ocupante.nombreOcupante}</p></div>`
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// La lógica ahora es simple: el asiento en el índice X del SVG
|
||||
// corresponde al asiento en el índice X de los datos.
|
||||
const seat = seatData[index];
|
||||
|
||||
if (!seat) {
|
||||
return React.cloneElement(child, { fill: '#E0E0E0', stroke: '#ffffff', strokeWidth: 1.5 });
|
||||
}
|
||||
|
||||
return React.cloneElement(child, {
|
||||
fill: seat.color,
|
||||
stroke: '#ffffff',
|
||||
strokeWidth: 1.5,
|
||||
'data-tooltip-id': seat.ocupante ? 'seat-tooltip' : undefined,
|
||||
'data-tooltip-html': seat.ocupante
|
||||
? `<div class="seat-tooltip"><img src="${seat.ocupante.fotoUrl || '/default-avatar.png'}" alt="${seat.ocupante.nombreOcupante}" /><p>${seat.ocupante.nombreOcupante}</p></div>`
|
||||
: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 550 375" width={size} height={size * (375 / 550)} style={{ display: 'block', margin: 'auto' }}>
|
||||
|
||||
@@ -81,28 +81,29 @@ export const SenateLayout: React.FC<SenateLayoutProps> = ({
|
||||
|
||||
// Si hay un presidente y su partido tiene bancas, le quitamos una para asignarla.
|
||||
if (presidenteBancada && presidenteBancada.color) {
|
||||
// Encontramos el índice del último asiento que pertenece al partido del presidente.
|
||||
const lastSeatIndex = finalSeatData.map(s => s.color).lastIndexOf(presidenteBancada.color);
|
||||
|
||||
if (lastSeatIndex !== -1) {
|
||||
// Eliminamos ese asiento de la lista de asientos electorales.
|
||||
finalSeatData.splice(lastSeatIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const renderedElements = seatElements.map((child, index) => {
|
||||
// El asiento presidencial sigue siendo un caso especial
|
||||
// --- CASO ESPECIAL: ASIENTO PRESIDENCIAL ---
|
||||
if (index === PRESIDENTE_SEAT_INDEX) {
|
||||
return React.cloneElement(child, {
|
||||
fill: presidenteBancada?.color || '#A9A9A9',
|
||||
stroke: '#000000',
|
||||
strokeWidth: 2,
|
||||
// Le damos un tooltip genérico al presidente
|
||||
'data-tooltip-id': 'seat-tooltip',
|
||||
'data-tooltip-html': `<div class="seat-tooltip"><p>Presidencia de la Cámara</p></div>`
|
||||
});
|
||||
}
|
||||
|
||||
// La lógica ahora es simple: el asiento en el índice X del SVG
|
||||
// corresponde al asiento en el índice X de los datos.
|
||||
const seat = seatData[index];
|
||||
// --- LÓGICA NORMAL PARA EL RESTO DE ASIENTOS ---
|
||||
// Usamos la copia modificada 'finalSeatData'. Como este array es más corto,
|
||||
// el último asiento electoral no encontrará datos y quedará gris, lo cual es correcto.
|
||||
const seat = finalSeatData[index];
|
||||
|
||||
if (!seat) {
|
||||
return React.cloneElement(child, { fill: '#E0E0E0', stroke: '#ffffff', strokeWidth: 1.5 });
|
||||
|
||||
Reference in New Issue
Block a user