This commit is contained in:
2025-12-11 00:57:32 +01:00
parent 1303a14493
commit 6a427eab57
112 changed files with 6148 additions and 2272 deletions

View File

@@ -1,32 +1,58 @@
'use client';
import Link from 'next/link';
import { Result } from '@gridpilot/racing/domain/entities/Result';
import { Driver } from '@gridpilot/racing/domain/entities/Driver';
import type { PenaltyType } from '@gridpilot/racing/domain/entities/Penalty';
import { AlertTriangle, ExternalLink } from 'lucide-react';
/**
* Penalty data for display (can be domain Penalty or RacePenaltyDTO)
*/
type PenaltyTypeDTO =
| 'time_penalty'
| 'grid_penalty'
| 'points_deduction'
| 'disqualification'
| 'warning'
| 'license_points'
| string;
interface ResultDTO {
id: string;
raceId: string;
driverId: string;
position: number;
fastestLap: number;
incidents: number;
startPosition: number;
getPositionChange(): number;
}
interface DriverDTO {
id: string;
name: string;
}
interface PenaltyData {
driverId: string;
type: PenaltyType;
type: PenaltyTypeDTO;
value?: number;
}
interface ResultsTableProps {
results: Result[];
drivers: Driver[];
results: ResultDTO[];
drivers: DriverDTO[];
pointsSystem: Record<number, number>;
fastestLapTime?: number;
penalties?: PenaltyData[];
currentDriverId?: string;
}
export default function ResultsTable({ results, drivers, pointsSystem, fastestLapTime, penalties = [], currentDriverId }: ResultsTableProps) {
const getDriver = (driverId: string): Driver | undefined => {
return drivers.find(d => d.id === driverId);
export default function ResultsTable({
results,
drivers,
pointsSystem,
fastestLapTime,
penalties = [],
currentDriverId,
}: ResultsTableProps) {
const getDriver = (driverId: string): DriverDTO | undefined => {
return drivers.find((d) => d.id === driverId);
};
const getDriverName = (driverId: string): string => {
@@ -35,7 +61,7 @@ export default function ResultsTable({ results, drivers, pointsSystem, fastestLa
};
const getDriverPenalties = (driverId: string): PenaltyData[] => {
return penalties.filter(p => p.driverId === driverId);
return penalties.filter((p) => p.driverId === driverId);
};
const getPenaltyDescription = (penalty: PenaltyData): string => {
@@ -97,30 +123,39 @@ export default function ResultsTable({ results, drivers, pointsSystem, fastestLa
<tbody>
{results.map((result) => {
const positionChange = result.getPositionChange();
const isFastestLap = fastestLapTime && result.fastestLap === fastestLapTime;
const isFastestLap =
typeof fastestLapTime === 'number' && result.fastestLap === fastestLapTime;
const driverPenalties = getDriverPenalties(result.driverId);
const driver = getDriver(result.driverId);
const isCurrentUser = currentDriverId === result.driverId;
const isPodium = result.position <= 3;
return (
<tr
key={result.id}
className={`
border-b border-charcoal-outline/50 transition-colors
${isCurrentUser
? 'bg-gradient-to-r from-primary-blue/20 via-primary-blue/10 to-transparent hover:from-primary-blue/30'
: 'hover:bg-iron-gray/20'}
${
isCurrentUser
? 'bg-gradient-to-r from-primary-blue/20 via-primary-blue/10 to-transparent hover:from-primary-blue/30'
: 'hover:bg-iron-gray/20'
}
`}
>
<td className="py-3 px-4">
<div className={`
<div
className={`
inline-flex items-center justify-center w-8 h-8 rounded-lg font-bold text-sm
${result.position === 1 ? 'bg-yellow-500/20 text-yellow-400' :
result.position === 2 ? 'bg-gray-400/20 text-gray-300' :
result.position === 3 ? 'bg-amber-600/20 text-amber-500' :
'text-white'}
`}>
${
result.position === 1
? 'bg-yellow-500/20 text-yellow-400'
: result.position === 2
? 'bg-gray-400/20 text-gray-300'
: result.position === 3
? 'bg-amber-600/20 text-amber-500'
: 'text-white'
}
`}
>
{result.position}
</div>
</td>
@@ -128,17 +163,27 @@ export default function ResultsTable({ results, drivers, pointsSystem, fastestLa
<div className="flex items-center gap-3">
{driver ? (
<>
<div className={`
<div
className={`
w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold flex-shrink-0
${isCurrentUser ? 'bg-primary-blue/30 text-primary-blue ring-2 ring-primary-blue/50' : 'bg-iron-gray text-gray-400'}
`}>
${
isCurrentUser
? 'bg-primary-blue/30 text-primary-blue ring-2 ring-primary-blue/50'
: 'bg-iron-gray text-gray-400'
}
`}
>
{driver.name.charAt(0)}
</div>
<Link
href={`/drivers/${driver.id}`}
className={`
flex items-center gap-1.5 group
${isCurrentUser ? 'text-primary-blue font-semibold' : 'text-white hover:text-primary-blue'}
${
isCurrentUser
? 'text-primary-blue font-semibold'
: 'text-white hover:text-primary-blue'
}
transition-colors
`}
>
@@ -157,20 +202,30 @@ export default function ResultsTable({ results, drivers, pointsSystem, fastestLa
</div>
</td>
<td className="py-3 px-4">
<span className={isFastestLap ? 'text-performance-green font-medium' : 'text-white'}>
<span
className={
isFastestLap ? 'text-performance-green font-medium' : 'text-white'
}
>
{formatLapTime(result.fastestLap)}
</span>
</td>
<td className="py-3 px-4">
<span className={result.incidents > 0 ? 'text-warning-amber' : 'text-white'}>
<span
className={result.incidents > 0 ? 'text-warning-amber' : 'text-white'}
>
{result.incidents}×
</span>
</td>
<td className="py-3 px-4">
<span className="text-white font-medium">{getPoints(result.position)}</span>
<span className="text-white font-medium">
{getPoints(result.position)}
</span>
</td>
<td className="py-3 px-4">
<span className={`font-medium ${getPositionChangeColor(positionChange)}`}>
<span
className={`font-medium ${getPositionChangeColor(positionChange)}`}
>
{getPositionChangeText(positionChange)}
</span>
</td>