'use client'; import { useState, useRef, useEffect } from 'react'; import Link from 'next/link'; import Image from 'next/image'; import { Star } from 'lucide-react'; import type { DriverDTO } from '@core/racing/application/dto/DriverDTO'; import type { LeagueDriverSeasonStatsDTO } from '@core/racing/application/dto/LeagueDriverSeasonStatsDTO'; import type { LeagueMembership, MembershipRole } from '@/lib/leagueMembership'; import { getLeagueRoleDisplay } from '@/lib/leagueRoles'; import CountryFlag from '@/components/ui/CountryFlag'; // Position background colors const getPositionBgColor = (position: number): string => { switch (position) { case 1: return 'bg-yellow-500/10 border-l-4 border-l-yellow-500'; case 2: return 'bg-gray-300/10 border-l-4 border-l-gray-400'; case 3: return 'bg-amber-600/10 border-l-4 border-l-amber-600'; default: return 'border-l-4 border-l-transparent'; } }; interface StandingsTableProps { standings: LeagueDriverSeasonStatsDTO[]; drivers: DriverDTO[]; leagueId: string; memberships?: LeagueMembership[]; currentDriverId?: string; isAdmin?: boolean; onRemoveMember?: (driverId: string) => void; onUpdateRole?: (driverId: string, role: MembershipRole) => void; } export default function StandingsTable({ standings, drivers, leagueId, memberships = [], currentDriverId, isAdmin = false, onRemoveMember, onUpdateRole }: StandingsTableProps) { const [hoveredRow, setHoveredRow] = useState(null); const [activeMenu, setActiveMenu] = useState<{ driverId: string; type: 'member' | 'points' } | null>(null); const menuRef = useRef(null); // Close menu when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(event.target as Node)) { setActiveMenu(null); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const getDriver = (driverId: string): DriverDTO | undefined => { return drivers.find((d) => d.id === driverId); }; const getMembership = (driverId: string): LeagueMembership | undefined => { return memberships.find((m) => m.driverId === driverId); }; const canModifyMember = (driverId: string): boolean => { if (!isAdmin) return false; if (driverId === currentDriverId) return false; const membership = getMembership(driverId); // Allow managing drivers even without formal membership (they have standings = they're participating) // But don't allow modifying the owner if (membership && membership.role === 'owner') return false; return true; }; const isCurrentUser = (driverId: string): boolean => { return driverId === currentDriverId; }; const handleRoleChange = (driverId: string, newRole: MembershipRole) => { if (!onUpdateRole) return; const membership = getMembership(driverId); if (!membership) return; const confirmationMessages: Record = { owner: 'Cannot promote to owner', admin: 'Promote this member to Admin? They will have full management permissions.', steward: 'Assign Steward role? They will be able to manage protests and penalties.', member: 'Demote to regular Member? They will lose elevated permissions.' }; if (newRole === 'owner') { alert(confirmationMessages.owner); return; } if (newRole !== membership.role && confirm(confirmationMessages[newRole])) { onUpdateRole(driverId, newRole); setActiveMenu(null); } }; const handleRemove = (driverId: string) => { if (!onRemoveMember) return; const driver = getDriver(driverId); const driverName = driver?.name || 'this member'; if (confirm(`Remove ${driverName} from the league? This action cannot be undone.`)) { onRemoveMember(driverId); setActiveMenu(null); } }; const MemberActionMenu = ({ driverId }: { driverId: string }) => { const membership = getMembership(driverId); // For drivers without membership, show limited options (add as member, remove from standings) const hasMembership = !!membership; return (
e.stopPropagation()} >
Member Management
{hasMembership ? ( <> {/* Role Management for existing members */} {membership!.role !== 'admin' && membership!.role !== 'owner' && ( )} {membership!.role === 'admin' && ( )} {membership!.role === 'member' && ( )} {membership!.role === 'steward' && ( )}
) : ( <> {/* Options for drivers without membership (participating but not formal members) */}
Driver not a formal member
)}
); }; const PointsActionMenu = ({ driverId }: { driverId: string }) => { return (
e.stopPropagation()} >
Score Actions
); }; if (standings.length === 0) { return (
No standings available
); } return (
{standings.map((row) => { const driver = getDriver(row.driverId); const membership = getMembership(row.driverId); const roleDisplay = membership ? getLeagueRoleDisplay(membership.role) : null; const canModify = canModifyMember(row.driverId); const driverStatsData = getDriverStats(row.driverId); const isRowHovered = hoveredRow === row.driverId; const isMemberMenuOpen = activeMenu?.driverId === row.driverId && activeMenu?.type === 'member'; const isPointsMenuOpen = activeMenu?.driverId === row.driverId && activeMenu?.type === 'points'; const isMe = isCurrentUser(row.driverId); return ( setHoveredRow(row.driverId)} onMouseLeave={() => { setHoveredRow(null); if (!isMemberMenuOpen && !isPointsMenuOpen) { setActiveMenu(null); } }} > {/* Position */} {/* Driver with Rating and Nationality */} {/* Team */} {/* Total Points with Hover Action */} {/* Races (Finished/Started) */} {/* Avg Finish */} {/* Penalty */} {/* Bonus */} ); })}
Pos Driver Team Points Races Avg Finish Penalty Bonus
{row.position}
{/* Avatar */}
{driver && ( {driver.name} )}
{/* Nationality flag */} {driver && driver.country && (
)}
{/* Name and Rating */}
{driver?.name || 'Unknown Driver'} {isMe && ( You )} {roleDisplay && roleDisplay.text !== 'Member' && ( {roleDisplay.text} )}
{driverStatsData && ( {driverStatsData.rating} )}
{/* Hover Actions for Member Management */} {isAdmin && canModify && (
)}
{isMemberMenuOpen && }
{row.teamName ?? '—'}
{row.totalPoints} {isAdmin && canModify && ( )}
{isPointsMenuOpen && }
{row.racesFinished} /{row.racesStarted} {row.avgFinish !== null ? row.avgFinish.toFixed(1) : '—'} 0 ? 'text-red-400 font-medium' : 'text-gray-500'}> {row.penaltyPoints > 0 ? `-${row.penaltyPoints}` : '—'} {row.bonusPoints !== 0 ? `+${row.bonusPoints}` : '—'}
); }