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

@@ -1,7 +1,9 @@
'use client';
import Link from 'next/link';
import { League } from '@gridpilot/racing/domain/entities/League';
import Card from '../ui/Card';
import { getLeagueCoverClasses } from '@/lib/leagueCovers';
interface LeagueCardProps {
league: League;
@@ -15,27 +17,35 @@ export default function LeagueCard({ league, onClick }: LeagueCardProps) {
onClick={onClick}
>
<Card>
<div className="space-y-3">
<div className="flex items-start justify-between">
<h3 className="text-xl font-semibold text-white">{league.name}</h3>
<span className="text-xs text-gray-500">
{new Date(league.createdAt).toLocaleDateString()}
</span>
</div>
<p className="text-gray-400 text-sm line-clamp-2">
{league.description}
</p>
<div className="flex items-center justify-between pt-2 border-t border-charcoal-outline">
<div className="text-xs text-gray-500">
Owner ID: {league.ownerId.slice(0, 8)}...
<div className="space-y-3">
<div className={getLeagueCoverClasses(league.id)} aria-hidden="true" />
<div className="flex items-start justify-between">
<h3 className="text-xl font-semibold text-white">{league.name}</h3>
<span className="text-xs text-gray-500">
{new Date(league.createdAt).toLocaleDateString()}
</span>
</div>
<div className="text-xs text-primary-blue font-medium">
{league.settings.pointsSystem.toUpperCase()}
<p className="text-gray-400 text-sm line-clamp-2">
{league.description}
</p>
<div className="flex items-center justify-between pt-2 border-t border-charcoal-outline">
<div className="text-xs text-gray-500">
Owner:{' '}
<Link
href={`/drivers/${league.ownerId}?from=league&leagueId=${league.id}`}
className="text-primary-blue hover:underline"
>
{league.ownerId.slice(0, 8)}...
</Link>
</div>
<div className="text-xs text-primary-blue font-medium">
{league.settings.pointsSystem.toUpperCase()}
</div>
</div>
</div>
</div>
</Card>
</div>
);

View File

@@ -0,0 +1,70 @@
'use client';
import React from 'react';
import Link from 'next/link';
import Image from 'next/image';
import MembershipStatus from '@/components/leagues/MembershipStatus';
import FeatureLimitationTooltip from '@/components/alpha/FeatureLimitationTooltip';
import { getLeagueCoverClasses } from '@/lib/leagueCovers';
interface LeagueHeaderProps {
leagueId: string;
leagueName: string;
description?: string | null;
ownerId: string;
ownerName: string;
}
export default function LeagueHeader({
leagueId,
leagueName,
description,
ownerId,
ownerName,
}: LeagueHeaderProps) {
const coverUrl = `https://picsum.photos/seed/${leagueId}/1200/280?blur=2`;
return (
<div className="mb-8">
<div className="mb-4">
<div className={getLeagueCoverClasses(leagueId)} aria-hidden="true">
<div className="relative w-full h-full">
<Image
src={coverUrl}
alt="League cover placeholder"
fill
className="object-cover opacity-80"
sizes="100vw"
/>
</div>
</div>
</div>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<h1 className="text-3xl font-bold text-white">{leagueName}</h1>
<MembershipStatus leagueId={leagueId} />
</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>
{description && (
<p className="text-gray-400 mb-2">{description}</p>
)}
<div className="text-sm text-gray-400 mb-6">
<span className="mr-2">Owner:</span>
<Link
href={`/drivers/${ownerId}?from=league&leagueId=${leagueId}`}
className="text-primary-blue hover:underline"
>
{ownerName}
</Link>
</div>
</div>
);
}

View File

@@ -1,6 +1,7 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import Link from 'next/link';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import { getDriverRepository, getDriverStats } from '@/lib/di-container';
import {
@@ -167,9 +168,12 @@ export default function LeagueMembers({
>
<td className="py-3 px-4">
<div className="flex items-center gap-2">
<span className="text-white font-medium">
<Link
href={`/drivers/${member.driverId}?from=league&leagueId=${leagueId}`}
className="text-white font-medium hover:text-primary-blue transition-colors"
>
{getDriverName(member.driverId)}
</span>
</Link>
{isCurrentUser && (
<span className="text-xs text-gray-500">(You)</span>
)}

View File

@@ -182,7 +182,7 @@ export default function LeagueSchedule({ leagueId }: LeagueScheduleProps) {
? 'bg-iron-gray/50 border-charcoal-outline/50 opacity-75'
: 'bg-deep-graphite border-charcoal-outline hover:border-primary-blue'
}`}
onClick={() => router.push(`/races/${race.id}`)}
onClick={() => router.push(`/leagues/${leagueId}/races/${race.id}`)}
>
<div className="flex items-center justify-between gap-4">
<div className="flex-1">

View File

@@ -1,16 +1,18 @@
'use client';
import Link from 'next/link';
import { Standing } from '@gridpilot/racing/domain/entities/Standing';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
interface StandingsTableProps {
standings: Standing[];
drivers: Driver[];
leagueId: string;
}
export default function StandingsTable({ standings, drivers }: StandingsTableProps) {
export default function StandingsTable({ standings, drivers, leagueId }: StandingsTableProps) {
const getDriverName = (driverId: string): string => {
const driver = drivers.find(d => d.id === driverId);
const driver = drivers.find((d) => d.id === driverId);
return driver?.name || 'Unknown Driver';
};
@@ -37,9 +39,9 @@ export default function StandingsTable({ standings, drivers }: StandingsTablePro
<tbody>
{standings.map((standing) => {
const isLeader = standing.position === 1;
return (
<tr
<tr
key={`${standing.leagueId}-${standing.driverId}`}
className="border-b border-charcoal-outline/50 hover:bg-iron-gray/20 transition-colors"
>
@@ -49,9 +51,16 @@ export default function StandingsTable({ standings, drivers }: StandingsTablePro
</span>
</td>
<td className="py-3 px-4">
<span className={isLeader ? 'text-white font-semibold' : 'text-white'}>
<Link
href={`/drivers/${standing.driverId}?from=league&leagueId=${leagueId}`}
className={
isLeader
? 'text-white font-semibold hover:text-primary-blue transition-colors'
: 'text-white hover:text-primary-blue transition-colors'
}
>
{getDriverName(standing.driverId)}
</span>
</Link>
</td>
<td className="py-3 px-4">
<span className="text-white font-medium">{standing.points}</span>