Files
gridpilot.gg/apps/website/components/leagues/LeagueScoringTab.tsx
2025-12-25 00:19:36 +01:00

237 lines
9.1 KiB
TypeScript

'use client';
import type { LeagueScoringConfigViewModel } from '@/lib/view-models/LeagueScoringConfigViewModel';
import { Trophy, Clock, Target, Zap, Info } from 'lucide-react';
type LeagueScoringConfigUi = LeagueScoringConfigViewModel & {
scoringPresetName?: string;
dropPolicySummary?: string;
championships?: Array<{
id: string;
name: string;
type: 'driver' | 'team' | 'nations' | 'trophy' | string;
sessionTypes: string[];
pointsPreview: Array<{ sessionType: string; position: number; points: number }>;
bonusSummary: string[];
dropPolicyDescription?: string;
}>;
};
interface LeagueScoringTabProps {
scoringConfig: LeagueScoringConfigViewModel | null;
practiceMinutes?: number;
qualifyingMinutes?: number;
sprintRaceMinutes?: number;
mainRaceMinutes?: number;
}
export default function LeagueScoringTab({
scoringConfig,
practiceMinutes,
qualifyingMinutes,
sprintRaceMinutes,
mainRaceMinutes,
}: LeagueScoringTabProps) {
if (!scoringConfig) {
return (
<div className="p-12 text-center">
<div className="w-16 h-16 mx-auto mb-4 rounded-full bg-purple-500/10 flex items-center justify-center">
<Target className="w-8 h-8 text-purple-400" />
</div>
<h3 className="text-lg font-semibold text-white mb-2">No Scoring System</h3>
<p className="text-sm text-gray-400">
Scoring configuration is not available for this league yet
</p>
</div>
);
}
const ui = scoringConfig as unknown as LeagueScoringConfigUi;
const championships = ui.championships ?? [];
const primaryChampionship =
championships.find((c) => c.type === 'driver') ??
championships[0];
const resolvedPractice = practiceMinutes ?? 20;
const resolvedQualifying = qualifyingMinutes ?? 30;
const resolvedSprint = sprintRaceMinutes;
const resolvedMain = mainRaceMinutes ?? 40;
return (
<div className="space-y-6">
<div className="border-b border-charcoal-outline pb-4 space-y-3">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-full bg-primary-blue/10 flex items-center justify-center">
<Trophy className="w-5 h-5 text-primary-blue" />
</div>
<div>
<h2 className="text-xl font-semibold text-white">
Scoring overview
</h2>
<p className="text-sm text-gray-400">
{scoringConfig.gameName}{' '}
{ui.scoringPresetName
? `${ui.scoringPresetName}`
: '• Custom scoring'}{' '}
{ui.dropPolicySummary ? `${ui.dropPolicySummary}` : ''}
</p>
</div>
</div>
{primaryChampionship && (
<div className="space-y-3 pt-3">
<div className="flex items-center gap-2">
<Clock className="w-4 h-4 text-gray-400" />
<h3 className="text-sm font-medium text-gray-200">
Weekend structure & timings
</h3>
</div>
<div className="flex flex-wrap gap-2 text-xs">
{primaryChampionship.sessionTypes.map((session: string) => (
<span
key={session}
className="px-3 py-1 rounded-full bg-primary-blue/10 text-primary-blue border border-primary-blue/20 font-medium"
>
{session}
</span>
))}
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
<div className="p-2 rounded-lg bg-iron-gray/50 border border-charcoal-outline">
<p className="text-xs text-gray-400 mb-1">Practice</p>
<p className="text-sm font-medium text-white">
{resolvedPractice ? `${resolvedPractice} min` : '—'}
</p>
</div>
<div className="p-2 rounded-lg bg-iron-gray/50 border border-charcoal-outline">
<p className="text-xs text-gray-400 mb-1">Qualifying</p>
<p className="text-sm font-medium text-white">{resolvedQualifying} min</p>
</div>
<div className="p-2 rounded-lg bg-iron-gray/50 border border-charcoal-outline">
<p className="text-xs text-gray-400 mb-1">Sprint</p>
<p className="text-sm font-medium text-white">
{resolvedSprint ? `${resolvedSprint} min` : '—'}
</p>
</div>
<div className="p-2 rounded-lg bg-iron-gray/50 border border-charcoal-outline">
<p className="text-xs text-gray-400 mb-1">Main race</p>
<p className="text-sm font-medium text-white">{resolvedMain} min</p>
</div>
</div>
</div>
)}
</div>
{championships.map((championship) => (
<div
key={championship.id}
className="border border-charcoal-outline rounded-lg bg-iron-gray/40 p-4 space-y-4"
>
<div className="flex items-center justify-between gap-4">
<div>
<h3 className="text-lg font-semibold text-white">
{championship.name}
</h3>
<p className="text-xs uppercase tracking-wide text-gray-500">
{championship.type === 'driver'
? 'Driver championship'
: championship.type === 'team'
? 'Team championship'
: championship.type === 'nations'
? 'Nations championship'
: 'Trophy championship'}
</p>
</div>
{championship.sessionTypes.length > 0 && (
<div className="flex flex-wrap gap-1 justify-end">
{championship.sessionTypes.map((session: string) => (
<span
key={session}
className="px-2 py-0.5 rounded-full bg-charcoal-outline/60 text-xs text-gray-200"
>
{session}
</span>
))}
</div>
)}
</div>
{championship.pointsPreview.length > 0 && (
<div>
<h4 className="text-xs font-semibold text-gray-400 mb-2">
Points preview (top positions)
</h4>
<div className="overflow-x-auto">
<table className="w-full text-xs">
<thead>
<tr className="border-b border-charcoal-outline/60">
<th className="text-left py-2 pr-2 text-gray-400">
Session
</th>
<th className="text-left py-2 px-2 text-gray-400">
Position
</th>
<th className="text-left py-2 px-2 text-gray-400">
Points
</th>
</tr>
</thead>
<tbody>
{championship.pointsPreview.map((row, index: number) => {
const typedRow = row as { sessionType: string; position: number; points: number };
return (
<tr
key={`${typedRow.sessionType}-${typedRow.position}-${index}`}
className="border-b border-charcoal-outline/30"
>
<td className="py-1.5 pr-2 text-gray-200">
{typedRow.sessionType}
</td>
<td className="py-1.5 px-2 text-gray-200">
P{typedRow.position}
</td>
<td className="py-1.5 px-2 text-white">
{typedRow.points}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
)}
{championship.bonusSummary.length > 0 && (
<div className="p-3 bg-yellow-500/5 border border-yellow-500/20 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<Zap className="w-4 h-4 text-yellow-400" />
<h4 className="text-xs font-semibold text-yellow-400">
Bonus points
</h4>
</div>
<ul className="list-disc list-inside text-xs text-gray-300 space-y-1">
{championship.bonusSummary.map((item: string, index: number) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)}
<div className="p-3 bg-primary-blue/5 border border-primary-blue/20 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<Info className="w-4 h-4 text-primary-blue" />
<h4 className="text-xs font-semibold text-primary-blue">
Drop score policy
</h4>
</div>
<p className="text-xs text-gray-300">
{championship.dropPolicyDescription ?? ''}
</p>
</div>
</div>
))}
</div>
);
}