'use client'; import { useState, useEffect, useMemo } from 'react'; import { useRouter, useParams, useSearchParams } from 'next/navigation'; import Button from '@/components/ui/Button'; import Card from '@/components/ui/Card'; import JoinLeagueButton from '@/components/leagues/JoinLeagueButton'; import LeagueMembers from '@/components/leagues/LeagueMembers'; import LeagueSchedule from '@/components/leagues/LeagueSchedule'; import LeagueAdmin from '@/components/leagues/LeagueAdmin'; import StandingsTable from '@/components/leagues/StandingsTable'; import LeagueScoringTab from '@/components/leagues/LeagueScoringTab'; import DriverIdentity from '@/components/drivers/DriverIdentity'; import DriverSummaryPill from '@/components/profile/DriverSummaryPill'; import { League, Driver, EntityMappers, type DriverDTO, type LeagueDriverSeasonStatsDTO, type LeagueScoringConfigDTO, } from '@gridpilot/racing'; import { getLeagueRepository, getRaceRepository, getDriverRepository, getGetLeagueDriverSeasonStatsQuery, getGetLeagueScoringConfigQuery, getDriverStats, getAllDriverRankings, getGetLeagueStatsQuery, } from '@/lib/di-container'; import { Zap, Users, Trophy, Calendar } from 'lucide-react'; import { getMembership, getLeagueMembers, isOwnerOrAdmin } from '@/lib/leagueMembership'; import { useEffectiveDriverId } from '@/lib/currentDriver'; export default function LeagueDetailPage() { const router = useRouter(); const params = useParams(); const leagueId = params.id as string; const [league, setLeague] = useState(null); const [owner, setOwner] = useState(null); const [standings, setStandings] = useState([]); const [drivers, setDrivers] = useState([]); const [scoringConfig, setScoringConfig] = useState(null); const [averageSOF, setAverageSOF] = useState(null); const [completedRacesCount, setCompletedRacesCount] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState< 'overview' | 'schedule' | 'standings' | 'scoring' | 'admin' >('overview'); const [refreshKey, setRefreshKey] = useState(0); const currentDriverId = useEffectiveDriverId(); const membership = getMembership(leagueId, currentDriverId); const isAdmin = isOwnerOrAdmin(leagueId, currentDriverId); const searchParams = useSearchParams(); const loadLeagueData = async () => { try { const leagueRepo = getLeagueRepository(); const raceRepo = getRaceRepository(); const driverRepo = getDriverRepository(); const leagueStatsQuery = getGetLeagueStatsQuery(); const leagueData = await leagueRepo.findById(leagueId); if (!leagueData) { setError('League not found'); setLoading(false); return; } setLeague(leagueData); // Load owner data const ownerData = await driverRepo.findById(leagueData.ownerId); setOwner(ownerData); // Load standings via rich season stats query const getLeagueDriverSeasonStatsQuery = getGetLeagueDriverSeasonStatsQuery(); const leagueStandings = await getLeagueDriverSeasonStatsQuery.execute({ leagueId }); setStandings(leagueStandings); // Load scoring configuration for the active season const getLeagueScoringConfigQuery = getGetLeagueScoringConfigQuery(); const scoring = await getLeagueScoringConfigQuery.execute({ leagueId }); setScoringConfig(scoring); // Load all drivers for standings and map to DTOs for UI components const allDrivers = await driverRepo.findAll(); const driverDtos: DriverDTO[] = allDrivers .map((driver) => EntityMappers.toDriverDTO(driver)) .filter((dto): dto is DriverDTO => dto !== null); setDrivers(driverDtos); // Load league stats including average SOF from application query const leagueStats = await leagueStatsQuery.execute({ leagueId }); if (leagueStats) { setAverageSOF(leagueStats.averageSOF); setCompletedRacesCount(leagueStats.completedRaces); } else { // Fallback: count completed races manually const leagueRaces = await raceRepo.findByLeagueId(leagueId); const completedRaces = leagueRaces.filter(r => r.status === 'completed'); setCompletedRacesCount(completedRaces.length); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load league'); } finally { setLoading(false); } }; useEffect(() => { loadLeagueData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [leagueId]); useEffect(() => { const initialTab = searchParams?.get('tab'); if (!initialTab) { return; } if ( initialTab === 'overview' || initialTab === 'schedule' || initialTab === 'standings' || initialTab === 'scoring' || initialTab === 'admin' ) { setActiveTab(initialTab as typeof activeTab); } }, [searchParams]); const handleMembershipChange = () => { setRefreshKey(prev => prev + 1); loadLeagueData(); }; const driversById = useMemo(() => { const map: Record = {}; for (const d of drivers) { map[d.id] = d; } return map; }, [drivers]); const leagueMemberships = getLeagueMembers(leagueId); const ownerMembership = leagueMemberships.find((m) => m.role === 'owner') ?? null; const adminMemberships = leagueMemberships.filter((m) => m.role === 'admin'); const buildDriverSummary = (driverId: string) => { const driverDto = driversById[driverId]; if (!driverDto) { return null; } const stats = getDriverStats(driverDto.id); const allRankings = getAllDriverRankings(); let rating: number | null = stats?.rating ?? null; let rank: number | null = null; if (stats) { if (typeof stats.overallRank === 'number' && stats.overallRank > 0) { rank = stats.overallRank; } else { const indexInGlobal = allRankings.findIndex( (stat) => stat.driverId === stats.driverId, ); if (indexInGlobal !== -1) { rank = indexInGlobal + 1; } } if (rating === null) { const globalEntry = allRankings.find( (stat) => stat.driverId === stats.driverId, ); if (globalEntry) { rating = globalEntry.rating; } } } return { driver: driverDto, rating, rank, }; }; return loading ? (
Loading league...
) : error || !league ? (
{error || 'League not found'}
) : ( <> {/* Action Card */} {!membership && (

Join This League

Become a member to participate in races and track your progress

)} {/* Overview section switcher (in-page, not primary tabs) */}
{isAdmin && ( )}
{/* Tab Content */} {activeTab === 'overview' && (
{/* League Info */}

League Information

{new Date(league.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}

At a glance

Structure

Solo • {league.settings.maxDrivers ?? 32} drivers

Schedule

{completedRacesCount > 0 ? `${completedRacesCount} races completed` : 'Season upcoming'}

Scoring & drops

{league.settings.pointsSystem.toUpperCase()}

Avg. Strength of Field

{averageSOF ? averageSOF : '—'}

{league.socialLinks && (

Community & Social

{league.socialLinks.discordUrl && ( Discord )} {league.socialLinks.youtubeUrl && ( YouTube )} {league.socialLinks.websiteUrl && ( Website )}
)}

Management

{ownerMembership && (
{buildDriverSummary(ownerMembership.driverId) ? ( ) : (

{owner ? owner.name : `ID: ${league.ownerId.slice(0, 8)}...`}

)}
)} {adminMemberships.length > 0 && (
{adminMemberships.map((membership) => { const driverDto = driversById[membership.driverId]; const summary = buildDriverSummary(membership.driverId); const meta = summary && summary.rating !== null ? `Rating ${summary.rating}${summary.rank ? ` • Rank ${summary.rank}` : ''}` : null; return (
{driverDto ? ( ) : ( Unknown admin )}
); })}
)} {adminMemberships.length === 0 && !ownerMembership && (

Management roles have not been configured for this league yet.

)}
{/* Quick Actions */}

Quick Actions

{membership ? ( <> ) : ( )}
)} {activeTab === 'schedule' && ( )} {activeTab === 'standings' && (

Standings

)} {activeTab === 'scoring' && (

Scoring

)} {activeTab === 'admin' && isAdmin && ( )} ); }