This commit is contained in:
2025-12-04 18:05:46 +01:00
parent 88c6befc7c
commit 9fa21a488a
21 changed files with 1156 additions and 388 deletions

View File

@@ -4,17 +4,13 @@ import { useState, useEffect } from 'react';
import { useRouter, useParams } from 'next/navigation';
import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';
import FeatureLimitationTooltip from '@/components/alpha/FeatureLimitationTooltip';
import JoinLeagueButton from '@/components/leagues/JoinLeagueButton';
import MembershipStatus from '@/components/leagues/MembershipStatus';
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 Breadcrumbs from '@/components/layout/Breadcrumbs';
import { League } from '@gridpilot/racing/domain/entities/League';
import { Standing } from '@gridpilot/racing/domain/entities/Standing';
import { Race } from '@gridpilot/racing/domain/entities/Race';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import { getLeagueRepository, getRaceRepository, getDriverRepository, getStandingRepository } from '@/lib/di-container';
import { getMembership, isOwnerOrAdmin, getCurrentDriverId } from '@/lib/racingLegacyFacade';
@@ -80,31 +76,23 @@ export default function LeagueDetailPage() {
if (loading) {
return (
<div className="min-h-screen bg-deep-graphite py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
<div className="text-center text-gray-400">Loading league...</div>
</div>
</div>
<div className="text-center text-gray-400">Loading league...</div>
);
}
if (error || !league) {
return (
<div className="min-h-screen bg-deep-graphite py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
<Card className="text-center py-12">
<div className="text-warning-amber mb-4">
{error || 'League not found'}
</div>
<Button
variant="secondary"
onClick={() => router.push('/leagues')}
>
Back to Leagues
</Button>
</Card>
<Card className="text-center py-12">
<div className="text-warning-amber mb-4">
{error || 'League not found'}
</div>
</div>
<Button
variant="secondary"
onClick={() => router.push('/leagues')}
>
Back to Leagues
</Button>
</Card>
);
}
@@ -114,221 +102,194 @@ export default function LeagueDetailPage() {
};
return (
<div className="min-h-screen bg-deep-graphite py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
{/* Breadcrumb */}
<Breadcrumbs
items={[
{ label: 'Home', href: '/' },
{ label: 'Leagues', href: '/leagues' },
{ label: league.name }
]}
/>
{/* League Header */}
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<h1 className="text-3xl font-bold text-white">{league.name}</h1>
<MembershipStatus leagueId={leagueId} />
<>
{/* Action Card */}
{!membership && (
<Card className="mb-6">
<div className="flex items-center justify-between">
<div>
<h3 className="text-lg font-semibold text-white mb-2">Join This League</h3>
<p className="text-gray-400 text-sm">Become a member to participate in races and track your progress</p>
</div>
<div className="w-48">
<JoinLeagueButton
leagueId={leagueId}
onMembershipChange={handleMembershipChange}
/>
</div>
<FeatureLimitationTooltip message="Multi-league memberships coming in production">
<span className="px-2 py-1 text-xs font-medium bg-primary-blue/10 text-primary-blue rounded border border-primary-blue/30">
Alpha: Single League
</span>
</FeatureLimitationTooltip>
</div>
<p className="text-gray-400">{league.description}</p>
</div>
</Card>
)}
{/* Action Card */}
{!membership && (
<Card className="mb-6">
<div className="flex items-center justify-between">
{/* Overview section switcher (in-page, not primary tabs) */}
<div className="mb-6">
<div className="inline-flex flex-wrap gap-2 rounded-full bg-iron-gray/60 px-2 py-1">
<button
onClick={() => setActiveTab('overview')}
className={`px-3 py-1 text-xs font-medium rounded-full transition-colors ${
activeTab === 'overview'
? 'bg-primary-blue text-white'
: 'text-gray-300 hover:text-white hover:bg-charcoal-outline/80'
}`}
>
Overview
</button>
<button
onClick={() => setActiveTab('schedule')}
className={`px-3 py-1 text-xs font-medium rounded-full transition-colors ${
activeTab === 'schedule'
? 'bg-primary-blue text-white'
: 'text-gray-300 hover:text-white hover:bg-charcoal-outline/80'
}`}
>
Schedule
</button>
<button
onClick={() => setActiveTab('standings')}
className={`px-3 py-1 text-xs font-medium rounded-full transition-colors ${
activeTab === 'standings'
? 'bg-primary-blue text-white'
: 'text-gray-300 hover:text-white hover:bg-charcoal-outline/80'
}`}
>
Standings
</button>
<button
onClick={() => setActiveTab('members')}
className={`px-3 py-1 text-xs font-medium rounded-full transition-colors ${
activeTab === 'members'
? 'bg-primary-blue text-white'
: 'text-gray-300 hover:text-white hover:bg-charcoal-outline/80'
}`}
>
Members
</button>
{isAdmin && (
<button
onClick={() => setActiveTab('admin')}
className={`px-3 py-1 text-xs font-medium rounded-full transition-colors ${
activeTab === 'admin'
? 'bg-primary-blue text-white'
: 'text-gray-300 hover:text-white hover:bg-charcoal-outline/80'
}`}
>
Admin
</button>
)}
</div>
</div>
{/* Tab Content */}
{activeTab === 'overview' && (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* League Info */}
<Card className="lg:col-span-2">
<h2 className="text-xl font-semibold text-white mb-4">League Information</h2>
<div className="space-y-4">
<div>
<h3 className="text-lg font-semibold text-white mb-2">Join This League</h3>
<p className="text-gray-400 text-sm">Become a member to participate in races and track your progress</p>
<label className="text-sm text-gray-500">Owner</label>
<p className="text-white">{owner ? owner.name : `ID: ${league.ownerId.slice(0, 8)}...`}</p>
</div>
<div className="w-48">
<JoinLeagueButton
leagueId={leagueId}
onMembershipChange={handleMembershipChange}
/>
<div>
<label className="text-sm text-gray-500">Created</label>
<p className="text-white">
{new Date(league.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
</div>
</div>
</Card>
)}
{/* Tabs Navigation */}
<div className="mb-6 border-b border-charcoal-outline">
<div className="flex gap-4 overflow-x-auto">
<button
onClick={() => setActiveTab('overview')}
className={`pb-3 px-1 font-medium whitespace-nowrap transition-colors ${
activeTab === 'overview'
? 'text-primary-blue border-b-2 border-primary-blue'
: 'text-gray-400 hover:text-white'
}`}
>
Overview
</button>
<button
onClick={() => setActiveTab('schedule')}
className={`pb-3 px-1 font-medium whitespace-nowrap transition-colors ${
activeTab === 'schedule'
? 'text-primary-blue border-b-2 border-primary-blue'
: 'text-gray-400 hover:text-white'
}`}
>
Schedule
</button>
<button
onClick={() => setActiveTab('standings')}
className={`pb-3 px-1 font-medium whitespace-nowrap transition-colors ${
activeTab === 'standings'
? 'text-primary-blue border-b-2 border-primary-blue'
: 'text-gray-400 hover:text-white'
}`}
>
Standings
</button>
<button
onClick={() => setActiveTab('members')}
className={`pb-3 px-1 font-medium whitespace-nowrap transition-colors ${
activeTab === 'members'
? 'text-primary-blue border-b-2 border-primary-blue'
: 'text-gray-400 hover:text-white'
}`}
>
Members
</button>
{isAdmin && (
<button
onClick={() => setActiveTab('admin')}
className={`pb-3 px-1 font-medium whitespace-nowrap transition-colors ${
activeTab === 'admin'
? 'text-primary-blue border-b-2 border-primary-blue'
: 'text-gray-400 hover:text-white'
}`}
>
Admin
</button>
)}
</div>
</div>
<div className="pt-4 border-t border-charcoal-outline">
<h3 className="text-white font-medium mb-3">League Settings</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm text-gray-500">Points System</label>
<p className="text-white">{league.settings.pointsSystem.toUpperCase()}</p>
</div>
{/* Tab Content */}
{activeTab === 'overview' && (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* League Info */}
<Card className="lg:col-span-2">
<h2 className="text-xl font-semibold text-white mb-4">League Information</h2>
<div className="space-y-4">
<div>
<label className="text-sm text-gray-500">Owner</label>
<p className="text-white">{owner ? owner.name : `ID: ${league.ownerId.slice(0, 8)}...`}</p>
</div>
<div>
<label className="text-sm text-gray-500">Session Duration</label>
<p className="text-white">{league.settings.sessionDuration} minutes</p>
</div>
<div>
<label className="text-sm text-gray-500">Created</label>
<p className="text-white">
{new Date(league.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</p>
</div>
<div className="pt-4 border-t border-charcoal-outline">
<h3 className="text-white font-medium mb-3">League Settings</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm text-gray-500">Points System</label>
<p className="text-white">{league.settings.pointsSystem.toUpperCase()}</p>
</div>
<div>
<label className="text-sm text-gray-500">Session Duration</label>
<p className="text-white">{league.settings.sessionDuration} minutes</p>
</div>
<div>
<label className="text-sm text-gray-500">Qualifying Format</label>
<p className="text-white capitalize">{league.settings.qualifyingFormat}</p>
</div>
<div>
<label className="text-sm text-gray-500">Qualifying Format</label>
<p className="text-white capitalize">{league.settings.qualifyingFormat}</p>
</div>
</div>
</div>
</Card>
</div>
</Card>
{/* Quick Actions */}
<Card>
<h2 className="text-xl font-semibold text-white mb-4">Quick Actions</h2>
<div className="space-y-3">
{membership ? (
<>
<Button
variant="primary"
className="w-full"
onClick={() => setActiveTab('schedule')}
>
View Schedule
</Button>
<Button
variant="secondary"
className="w-full"
onClick={() => setActiveTab('standings')}
>
View Standings
</Button>
<JoinLeagueButton
leagueId={leagueId}
onMembershipChange={handleMembershipChange}
/>
</>
) : (
{/* Quick Actions */}
<Card>
<h2 className="text-xl font-semibold text-white mb-4">Quick Actions</h2>
<div className="space-y-3">
{membership ? (
<>
<Button
variant="primary"
className="w-full"
onClick={() => setActiveTab('schedule')}
>
View Schedule
</Button>
<Button
variant="secondary"
className="w-full"
onClick={() => setActiveTab('standings')}
>
View Standings
</Button>
<JoinLeagueButton
leagueId={leagueId}
onMembershipChange={handleMembershipChange}
/>
)}
</div>
</Card>
</div>
)}
{activeTab === 'schedule' && (
<Card>
<LeagueSchedule leagueId={leagueId} key={refreshKey} />
</>
) : (
<JoinLeagueButton
leagueId={leagueId}
onMembershipChange={handleMembershipChange}
/>
)}
</div>
</Card>
)}
</div>
)}
{activeTab === 'standings' && (
<Card>
<h2 className="text-xl font-semibold text-white mb-4">Standings</h2>
<StandingsTable standings={standings} drivers={drivers} />
</Card>
)}
{activeTab === 'schedule' && (
<Card>
<LeagueSchedule leagueId={leagueId} key={refreshKey} />
</Card>
)}
{activeTab === 'members' && (
<Card>
<h2 className="text-xl font-semibold text-white mb-4">League Members</h2>
<LeagueMembers leagueId={leagueId} key={refreshKey} />
</Card>
)}
{activeTab === 'standings' && (
<Card>
<h2 className="text-xl font-semibold text-white mb-4">Standings</h2>
<StandingsTable standings={standings} drivers={drivers} leagueId={leagueId} />
</Card>
)}
{activeTab === 'admin' && isAdmin && (
<LeagueAdmin
league={league}
onLeagueUpdate={handleMembershipChange}
key={refreshKey}
/>
)}
</div>
</div>
{activeTab === 'members' && (
<Card>
<h2 className="text-xl font-semibold text-white mb-4">League Members</h2>
<LeagueMembers leagueId={leagueId} key={refreshKey} />
</Card>
)}
{activeTab === 'admin' && isAdmin && (
<LeagueAdmin
league={league}
onLeagueUpdate={handleMembershipChange}
key={refreshKey}
/>
)}
</>
);
}