190 lines
6.6 KiB
TypeScript
190 lines
6.6 KiB
TypeScript
'use client';
|
|
|
|
import type { LeagueScoringConfigDTO } from '@gridpilot/racing/application/dto/LeagueScoringConfigDTO';
|
|
|
|
interface LeagueScoringTabProps {
|
|
scoringConfig: LeagueScoringConfigDTO | null;
|
|
practiceMinutes?: number;
|
|
qualifyingMinutes?: number;
|
|
sprintRaceMinutes?: number;
|
|
mainRaceMinutes?: number;
|
|
}
|
|
|
|
export default function LeagueScoringTab({
|
|
scoringConfig,
|
|
practiceMinutes,
|
|
qualifyingMinutes,
|
|
sprintRaceMinutes,
|
|
mainRaceMinutes,
|
|
}: LeagueScoringTabProps) {
|
|
if (!scoringConfig) {
|
|
return (
|
|
<div className="text-sm text-gray-400 py-6">
|
|
Scoring configuration is not available for this league yet.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const primaryChampionship =
|
|
scoringConfig.championships.find((c) => c.type === 'driver') ??
|
|
scoringConfig.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">
|
|
<h2 className="text-xl font-semibold text-white mb-1">
|
|
Scoring overview
|
|
</h2>
|
|
<p className="text-sm text-gray-400">
|
|
{scoringConfig.gameName}{' '}
|
|
{scoringConfig.scoringPresetName
|
|
? `• ${scoringConfig.scoringPresetName}`
|
|
: '• Custom scoring'}{' '}
|
|
• {scoringConfig.dropPolicySummary}
|
|
</p>
|
|
|
|
{primaryChampionship && (
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-medium text-gray-200">
|
|
Weekend structure & timings
|
|
</h3>
|
|
<div className="flex flex-wrap gap-2 text-xs">
|
|
{primaryChampionship.sessionTypes.map((session) => (
|
|
<span
|
|
key={session}
|
|
className="px-2 py-0.5 rounded-full bg-charcoal-outline/60 text-xs text-gray-200"
|
|
>
|
|
{session}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs text-gray-300">
|
|
<p>
|
|
<span className="text-gray-400">Practice:</span>{' '}
|
|
{resolvedPractice ? `${resolvedPractice} min` : '—'}
|
|
</p>
|
|
<p>
|
|
<span className="text-gray-400">Qualifying:</span>{' '}
|
|
{resolvedQualifying} min
|
|
</p>
|
|
<p>
|
|
<span className="text-gray-400">Sprint:</span>{' '}
|
|
{resolvedSprint ? `${resolvedSprint} min` : '—'}
|
|
</p>
|
|
<p>
|
|
<span className="text-gray-400">Main race:</span>{' '}
|
|
{resolvedMain} min
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{scoringConfig.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) => (
|
|
<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) => (
|
|
<tr
|
|
key={`${row.sessionType}-${row.position}-${index}`}
|
|
className="border-b border-charcoal-outline/30"
|
|
>
|
|
<td className="py-1.5 pr-2 text-gray-200">
|
|
{row.sessionType}
|
|
</td>
|
|
<td className="py-1.5 px-2 text-gray-200">
|
|
P{row.position}
|
|
</td>
|
|
<td className="py-1.5 px-2 text-white">
|
|
{row.points}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{championship.bonusSummary.length > 0 && (
|
|
<div>
|
|
<h4 className="text-xs font-semibold text-gray-400 mb-1">
|
|
Bonus points
|
|
</h4>
|
|
<ul className="list-disc list-inside text-xs text-gray-300 space-y-1">
|
|
{championship.bonusSummary.map((item, index) => (
|
|
<li key={index}>{item}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<h4 className="text-xs font-semibold text-gray-400 mb-1">
|
|
Drop score policy
|
|
</h4>
|
|
<p className="text-xs text-gray-300">
|
|
{championship.dropPolicyDescription}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
} |