website cleanup

This commit is contained in:
2025-12-24 14:01:52 +01:00
parent a7aee42409
commit 9b683a59d3
65 changed files with 880 additions and 745 deletions

View File

@@ -11,6 +11,8 @@ import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
import { useRaceDetail, useRegisterForRace, useWithdrawFromRace, useCancelRace, useCompleteRace, useReopenRace } from '@/hooks/useRaceService';
import { useLeagueMembership } from '@/hooks/useLeagueMembershipService';
import { LeagueMembershipUtility } from '@/lib/utilities/LeagueMembershipUtility';
import { RaceDetailEntryViewModel } from '@/lib/view-models/RaceDetailEntryViewModel';
import { RaceDetailUserResultViewModel } from '@/lib/view-models/RaceDetailUserResultViewModel';
import {
AlertTriangle,
ArrowLeft,
@@ -95,14 +97,10 @@ export default function RaceDetailPage() {
if (!confirmed) return;
setCancelling(true);
try {
await raceService.cancelRace(race.id);
await loadRaceData();
await cancelMutation.mutateAsync(race.id);
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to cancel race');
} finally {
setCancelling(false);
}
};
@@ -117,14 +115,10 @@ export default function RaceDetailPage() {
if (!confirmed) return;
setRegistering(true);
try {
await raceService.registerForRace(race.id, league.id, currentDriverId);
await loadRaceData();
await registerMutation.mutateAsync({ raceId: race.id, leagueId: league.id, driverId: currentDriverId });
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to register for race');
} finally {
setRegistering(false);
}
};
@@ -139,14 +133,10 @@ export default function RaceDetailPage() {
if (!confirmed) return;
setRegistering(true);
try {
await raceService.withdrawFromRace(race.id, currentDriverId);
await loadRaceData();
await withdrawMutation.mutateAsync({ raceId: race.id, driverId: currentDriverId });
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to withdraw from race');
} finally {
setRegistering(false);
}
};
@@ -160,14 +150,10 @@ export default function RaceDetailPage() {
if (!confirmed) return;
setReopening(true);
try {
await raceService.reopenRace(race.id);
await loadRaceData();
await reopenMutation.mutateAsync(race.id);
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to re-open race');
} finally {
setReopening(false);
}
};
@@ -268,7 +254,7 @@ export default function RaceDetailPage() {
<AlertTriangle className="w-8 h-8 text-warning-amber" />
</div>
<div>
<p className="text-white font-medium mb-1">{error || 'Race not found'}</p>
<p className="text-white font-medium mb-1">{error instanceof Error ? error.message : error || 'Race not found'}</p>
<p className="text-sm text-gray-500">
The race you're looking for doesn't exist or has been removed.
</p>
@@ -292,9 +278,9 @@ export default function RaceDetailPage() {
const entryList: RaceDetailEntryViewModel[] = viewModel.entryList;
const registration = viewModel.registration;
const userResult: RaceDetailUserResultViewModel | null = viewModel.userResult;
const raceSOF = race.strengthOfField;
const raceSOF = null; // TODO: Add strengthOfField to RaceDetailRaceDTO
const config = statusConfig[race.status];
const config = statusConfig[race.status as keyof typeof statusConfig];
const StatusIcon = config.icon;
const timeUntil = race.status === 'scheduled' ? getTimeUntil(new Date(race.scheduledAt)) : null;
@@ -322,7 +308,7 @@ export default function RaceDetailPage() {
const raceMetrics = [
MetricBuilders.views(entryList.length * 12),
MetricBuilders.engagement(78),
{ label: 'SOF', value: raceSOF != null ? raceSOF.toString() : '—', icon: Zap, color: 'text-warning-amber' as const },
{ label: 'SOF', value: raceSOF != null ? String(raceSOF) : '—', icon: Zap, color: 'text-warning-amber' as const },
MetricBuilders.reach(entryList.length * 45),
];
@@ -650,7 +636,8 @@ export default function RaceDetailPage() {
{raceSOF ?? '—'}
</p>
</div>
{race.registeredCount !== undefined && (
{/* TODO: Add registeredCount and maxParticipants to RaceDetailRaceDTO */}
{/* {race.registeredCount !== undefined && (
<div className="p-4 bg-deep-graphite rounded-lg">
<p className="text-xs text-gray-500 uppercase tracking-wide mb-1">Registered</p>
<p className="text-white font-medium">
@@ -658,7 +645,7 @@ export default function RaceDetailPage() {
{race.maxParticipants && ` / ${race.maxParticipants}`}
</p>
</div>
)}
)} */}
</div>
</Card>
@@ -797,12 +784,12 @@ export default function RaceDetailPage() {
<div className="grid grid-cols-2 gap-3 mb-4">
<div className="p-3 rounded-lg bg-deep-graphite">
<p className="text-xs text-gray-500 mb-1">Max Drivers</p>
<p className="text-white font-medium">{league.settings.maxDrivers ?? 32}</p>
<p className="text-white font-medium">{(league.settings as any).maxDrivers ?? 32}</p>
</div>
<div className="p-3 rounded-lg bg-deep-graphite">
<p className="text-xs text-gray-500 mb-1">Format</p>
<p className="text-white font-medium capitalize">
{league.settings.qualifyingFormat ?? 'Open'}
{(league.settings as any).qualifyingFormat ?? 'Open'}
</p>
</div>
</div>
@@ -828,10 +815,10 @@ export default function RaceDetailPage() {
variant="primary"
className="w-full flex items-center justify-center gap-2"
onClick={handleRegister}
disabled={registering}
disabled={registerMutation.isPending}
>
<UserPlus className="w-4 h-4" />
{registering ? 'Registering...' : 'Register for Race'}
{registerMutation.isPending ? 'Registering...' : 'Register for Race'}
</Button>
)}
@@ -845,10 +832,10 @@ export default function RaceDetailPage() {
variant="secondary"
className="w-full flex items-center justify-center gap-2"
onClick={handleWithdraw}
disabled={registering}
disabled={withdrawMutation.isPending}
>
<UserMinus className="w-4 h-4" />
{registering ? 'Withdrawing...' : 'Withdraw'}
{withdrawMutation.isPending ? 'Withdrawing...' : 'Withdraw'}
</Button>
</>
)}
@@ -856,13 +843,13 @@ export default function RaceDetailPage() {
{viewModel.canReopenRace &&
LeagueMembershipUtility.isOwnerOrAdmin(viewModel.league?.id || '', currentDriverId) && (
<Button
variant="outline"
variant="secondary"
className="w-full flex items-center justify-center gap-2"
onClick={handleReopenRace}
disabled={reopening}
disabled={reopenMutation.isPending}
>
<PlayCircle className="w-4 h-4" />
{reopening ? 'Re-opening...' : 'Re-open Race'}
{reopenMutation.isPending ? 'Re-opening...' : 'Re-open Race'}
</Button>
)}
@@ -900,13 +887,13 @@ export default function RaceDetailPage() {
{viewModel.canReopenRace &&
LeagueMembershipUtility.isOwnerOrAdmin(viewModel.league?.id || '', currentDriverId) && (
<Button
variant="outline"
variant="secondary"
className="w-full flex items-center justify-center gap-2"
onClick={handleReopenRace}
disabled={reopening}
disabled={reopenMutation.isPending}
>
<PlayCircle className="w-4 h-4" />
{reopening ? 'Re-opening...' : 'Re-open Race'}
{reopenMutation.isPending ? 'Re-opening...' : 'Re-open Race'}
</Button>
)}
@@ -926,10 +913,10 @@ export default function RaceDetailPage() {
variant="secondary"
className="w-full flex items-center justify-center gap-2"
onClick={handleCancelRace}
disabled={cancelling}
disabled={cancelMutation.isPending}
>
<XCircle className="w-4 h-4" />
{cancelling ? 'Cancelling...' : 'Cancel Race'}
{cancelMutation.isPending ? 'Cancelling...' : 'Cancel Race'}
</Button>
)}
</div>
@@ -968,8 +955,7 @@ export default function RaceDetailPage() {
raceName={race.track}
onConfirm={async () => {
try {
await raceService.completeRace(race.id);
await loadRaceData();
await completeMutation.mutateAsync(race.id);
setShowEndRaceModal(false);
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to complete race');