Files
gridpilot.gg/apps/website/components/leagues/LeagueReviewSummary.tsx
2025-12-05 12:24:38 +01:00

210 lines
8.0 KiB
TypeScript

'use client';
import Card from '@/components/ui/Card';
import type { LeagueConfigFormModel } from '@gridpilot/racing/application';
import type { LeagueScoringPresetDTO } from '@gridpilot/racing/application/ports/LeagueScoringPresetProvider';
interface LeagueReviewSummaryProps {
form: LeagueConfigFormModel;
presets: LeagueScoringPresetDTO[];
}
export default function LeagueReviewSummary({ form, presets }: LeagueReviewSummaryProps) {
const { basics, structure, timings, scoring, championships, dropPolicy } = form;
const modeLabel =
structure.mode === 'solo'
? 'Drivers only (solo)'
: 'Teams with fixed drivers per team';
const capacitySentence = (() => {
if (structure.mode === 'solo') {
if (typeof structure.maxDrivers === 'number') {
return `Up to ${structure.maxDrivers} drivers`;
}
return 'Capacity not fully specified';
}
const parts: string[] = [];
if (typeof structure.maxTeams === 'number') {
parts.push(`Teams: ${structure.maxTeams}`);
}
if (typeof structure.driversPerTeam === 'number') {
parts.push(`Drivers per team: ${structure.driversPerTeam}`);
}
if (typeof structure.maxDrivers === 'number') {
parts.push(`Max grid: ${structure.maxDrivers}`);
}
if (parts.length === 0) {
return '—';
}
return parts.join(', ');
})();
const formatMinutes = (value: number | undefined) => {
if (typeof value !== 'number' || value <= 0) return '—';
return `${value} min`;
};
const dropRuleSentence = (() => {
if (dropPolicy.strategy === 'none') {
return 'All results will count towards the championship.';
}
if (dropPolicy.strategy === 'bestNResults') {
if (typeof dropPolicy.n === 'number' && dropPolicy.n > 0) {
return `Best ${dropPolicy.n} results will count; others are ignored.`;
}
return 'Best N results will count; others are ignored.';
}
if (dropPolicy.strategy === 'dropWorstN') {
if (typeof dropPolicy.n === 'number' && dropPolicy.n > 0) {
return `Worst ${dropPolicy.n} results will be dropped from the standings.`;
}
return 'Worst N results will be dropped from the standings.';
}
return 'All results will count towards the championship.';
})();
const preset =
presets.find((p) => p.id === scoring.patternId) ?? null;
const scoringPresetName = preset ? preset.name : scoring.patternId ? 'Preset not found' : '—';
const scoringPatternSummary = preset?.sessionSummary ?? '—';
const dropPolicySummary = dropRuleSentence;
const enabledChampionshipsLabels: string[] = [];
if (championships.enableDriverChampionship) enabledChampionshipsLabels.push('Driver');
if (championships.enableTeamChampionship) enabledChampionshipsLabels.push('Team');
if (championships.enableNationsChampionship) enabledChampionshipsLabels.push('Nations Cup');
if (championships.enableTrophyChampionship) enabledChampionshipsLabels.push('Trophy');
const championshipsSummary =
enabledChampionshipsLabels.length === 0
? 'None enabled yet.'
: enabledChampionshipsLabels.join(', ');
const visibilityLabel = basics.visibility === 'public' ? 'Public' : 'Private';
const gameLabel = 'iRacing';
return (
<Card className="bg-iron-gray/80">
<div className="space-y-6 text-sm text-gray-200">
{/* 1. Basics & visibility */}
<section className="space-y-3">
<h3 className="text-[11px] font-semibold text-gray-400 uppercase tracking-wide">
Basics & visibility
</h3>
<dl className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-1">
<dt className="text-xs text-gray-500">Name</dt>
<dd className="font-medium text-white">{basics.name || '—'}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Visibility</dt>
<dd>{visibilityLabel}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Game</dt>
<dd>{gameLabel}</dd>
</div>
{basics.description && (
<div className="space-y-1 md:col-span-2">
<dt className="text-xs text-gray-500">Description</dt>
<dd className="text-gray-300">{basics.description}</dd>
</div>
)}
</dl>
</section>
{/* 2. Structure & capacity */}
<section className="space-y-3">
<h3 className="text-[11px] font-semibold text-gray-400 uppercase tracking-wide">
Structure & capacity
</h3>
<dl className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-1">
<dt className="text-xs text-gray-500">Mode</dt>
<dd>{modeLabel}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Capacity</dt>
<dd>{capacitySentence}</dd>
</div>
</dl>
</section>
{/* 3. Schedule & timings */}
<section className="space-y-3">
<h3 className="text-[11px] font-semibold text-gray-400 uppercase tracking-wide">
Schedule & timings
</h3>
<dl className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-1">
<dt className="text-xs text-gray-500">Planned rounds</dt>
<dd>{typeof timings.roundsPlanned === 'number' ? timings.roundsPlanned : '—'}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Sessions per weekend</dt>
<dd>{timings.sessionCount ?? '—'}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Practice</dt>
<dd>{formatMinutes(timings.practiceMinutes)}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Qualifying</dt>
<dd>{formatMinutes(timings.qualifyingMinutes)}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Sprint</dt>
<dd>{formatMinutes(timings.sprintRaceMinutes)}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Main race</dt>
<dd>{formatMinutes(timings.mainRaceMinutes)}</dd>
</div>
</dl>
</section>
{/* 4. Scoring & drops */}
<section className="space-y-3">
<h3 className="text-[11px] font-semibold text-gray-400 uppercase tracking-wide">
Scoring & drops
</h3>
<dl className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-1">
<dt className="text-xs text-gray-500">Scoring pattern</dt>
<dd>{scoringPresetName}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Pattern summary</dt>
<dd>{scoringPatternSummary}</dd>
</div>
<div className="space-y-1">
<dt className="text-xs text-gray-500">Drop rule</dt>
<dd>{dropPolicySummary}</dd>
</div>
{scoring.customScoringEnabled && (
<div className="space-y-1">
<dt className="text-xs text-gray-500">Custom scoring</dt>
<dd>Custom scoring flagged</dd>
</div>
)}
</dl>
</section>
{/* 5. Championships */}
<section className="space-y-3">
<h3 className="text-[11px] font-semibold text-gray-400 uppercase tracking-wide">
Championships
</h3>
<dl className="grid grid-cols-1 gap-4">
<div className="space-y-1">
<dt className="text-xs text-gray-500">Enabled championships</dt>
<dd>{championshipsSummary}</dd>
</div>
</dl>
</section>
</div>
</Card>
);
}