/**
* SeatCountDataPanel Component
* Right panel showing seat distribution data table for desktop view
*/
'use client';
import React, { useMemo } from 'react';
import { useLocale } from 'next-intl';
import { useSeatDataContext } from '@/contexts/SeatDataContext';
import { useVisualizerStore } from '@/hooks/useVisualizerStore';
import { provinceCodes, getProvinceName } from '@/lib/visualizer/provinceData';
import { getPartyColor, getPartyTextColor } from '@/lib/visualizer/partyColors';
// Total ridings in Canada (as of 2024 redistribution)
const TOTAL_RIDINGS = 343;
export function SeatCountDataPanel() {
const locale = useLocale() as 'en' | 'fr';
const { getProvinceSeatData, getDominantParty, totalSeats, nationalTotals } = useSeatDataContext();
const { selectedProvince, setSelectedProvince, setHoveredProvince, selectedParty, setSelectedParty } = useVisualizerStore();
// Sort provinces by total seats
const sortedProvinces = useMemo(() => {
return [...provinceCodes].sort((a, b) => {
const dataA = getProvinceSeatData(a);
const dataB = getProvinceSeatData(b);
const seatsA = dataA?.seats ? Object.values(dataA.seats).reduce((sum, n) => sum + n, 0) : 0;
const seatsB = dataB?.seats ? Object.values(dataB.seats).reduce((sum, n) => sum + n, 0) : 0;
return seatsB - seatsA;
});
}, [getProvinceSeatData]);
// Sort parties by total seats
const sortedParties = useMemo(() => {
if (!nationalTotals) return [];
return Object.entries(nationalTotals)
.filter(([, seats]) => seats > 0)
.sort(([, a], [, b]) => b - a);
}, [nationalTotals]);
return (
<div className="flex flex-col h-full">
{/* Party Summary - fixed height */}
<div className="flex-shrink-0 mb-4">
<h3 className="text-sm font-semibold text-text-primary mb-3">
{locale === 'en' ? 'Party Standings' : 'Situation des partis'}
</h3>
<div className="space-y-2">
{sortedParties.map(([party, seats]) => {
const isSelected = selectedParty === party;
return (
<button
key={party}
onClick={() => setSelectedParty(isSelected ? null : party)}
className={`
w-full flex items-center justify-between p-2 rounded-lg transition-all
${isSelected
? 'ring-2 ring-offset-1 ring-offset-bg-secondary'
: 'bg-bg-elevated hover:bg-bg-primary'
}
`}
style={isSelected ? {
backgroundColor: getPartyColor(party),
// @ts-expect-error -- ring color is set via CSS variable
'--tw-ring-color': getPartyColor(party),
} : undefined}
>
<div className="flex items-center gap-2">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: isSelected ? '#FFFFFF' : getPartyColor(party) }}
/>
<span className={`text-sm ${isSelected ? 'text-white font-medium' : 'text-text-primary'}`}>
{party}
</span>
</div>
<div className="text-right">
<span className={`text-sm font-semibold ${isSelected ? 'text-white' : 'text-text-primary'}`}>
{seats}
</span>
<span className={`text-xs ml-1 ${isSelected ? 'text-white/80' : 'text-text-tertiary'}`}>
({((seats / TOTAL_RIDINGS) * 100).toFixed(1)}%)
</span>
</div>
</button>
);
})}
<div className="flex items-center justify-between p-2 rounded-lg bg-bg-primary border border-border-subtle">
<span className="text-sm font-medium text-text-secondary">
{locale === 'en' ? 'Total' : 'Total'}
</span>
<span className="text-sm font-bold text-text-primary">{totalSeats} / {TOTAL_RIDINGS}</span>
</div>
</div>
</div>
{/* Province Table - fills remaining space and scrolls */}
<div className="flex-1 min-h-0 flex flex-col">
<h3 className="flex-shrink-0 text-sm font-semibold text-text-primary mb-3">
{locale === 'en' ? 'By Province/Territory' : 'Par province/territoire'}
</h3>
<div className="flex-1 min-h-0 border border-border-subtle rounded-lg overflow-hidden">
<div className="h-full overflow-y-auto">
<table className="w-full text-xs">
<thead className="bg-bg-elevated sticky top-0">
<tr>
<th className="text-left p-2 font-medium text-text-secondary">
{locale === 'en' ? 'Region' : 'Région'}
</th>
<th className="text-right p-2 font-medium text-text-secondary">
{locale === 'en' ? 'Seats' : 'Sièges'}
</th>
<th className="text-center p-2 font-medium text-text-secondary">
{locale === 'en' ? 'Leading' : 'En tête'}
</th>
</tr>
</thead>
<tbody>
{sortedProvinces.map((code) => {
const provinceData = getProvinceSeatData(code);
const total = provinceData?.seats ? Object.values(provinceData.seats).reduce((sum, n) => sum + n, 0) : 0;
const dominant = getDominantParty(code);
const isSelected = selectedProvince === code;
return (
<tr
key={code}
onClick={() => setSelectedProvince(isSelected ? null : code)}
onMouseEnter={() => setHoveredProvince(code)}
onMouseLeave={() => setHoveredProvince(null)}
className={`
cursor-pointer transition-colors border-t border-border-subtle
${isSelected
? 'bg-accent-red/10'
: 'hover:bg-bg-elevated'
}
`}
>
<td className="p-2">
<div className="flex items-center gap-1.5">
<div
className="w-2 h-2 rounded-full flex-shrink-0"
style={{ backgroundColor: getPartyColor(dominant) }}
/>
<span className={`${isSelected ? 'font-medium text-text-primary' : 'text-text-secondary'}`}>
{getProvinceName(code, locale)}
</span>
</div>
</td>
<td className="p-2 text-right font-medium text-text-primary">
{total}
</td>
<td className="p-2 text-center">
<span
className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium"
style={{
backgroundColor: getPartyColor(dominant),
color: getPartyTextColor(dominant),
}}
>
{dominant || '-'}
</span>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}