fix e2e
This commit is contained in:
217
apps/website/app/races/[id]/RaceDetailInteractive.tsx
Normal file
217
apps/website/app/races/[id]/RaceDetailInteractive.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { RaceDetailTemplate } from '@/templates/RaceDetailTemplate';
|
||||
import {
|
||||
useRaceDetail,
|
||||
useRegisterForRace,
|
||||
useWithdrawFromRace,
|
||||
useCancelRace,
|
||||
useCompleteRace,
|
||||
useReopenRace
|
||||
} from '@/hooks/useRaceService';
|
||||
import { useLeagueMembership } from '@/hooks/useLeagueMembershipService';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import { LeagueMembershipUtility } from '@/lib/utilities/LeagueMembershipUtility';
|
||||
|
||||
export function RaceDetailInteractive() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const raceId = params.id as string;
|
||||
const currentDriverId = useEffectiveDriverId();
|
||||
|
||||
// Fetch data
|
||||
const { data: viewModel, isLoading, error } = useRaceDetail(raceId, currentDriverId);
|
||||
const { data: membership } = useLeagueMembership(viewModel?.league?.id || '', currentDriverId);
|
||||
|
||||
// UI State
|
||||
const [showProtestModal, setShowProtestModal] = useState(false);
|
||||
const [showEndRaceModal, setShowEndRaceModal] = useState(false);
|
||||
|
||||
// Mutations
|
||||
const registerMutation = useRegisterForRace();
|
||||
const withdrawMutation = useWithdrawFromRace();
|
||||
const cancelMutation = useCancelRace();
|
||||
const completeMutation = useCompleteRace();
|
||||
const reopenMutation = useReopenRace();
|
||||
|
||||
// Determine if user is owner/admin
|
||||
const isOwnerOrAdmin = membership
|
||||
? LeagueMembershipUtility.isOwnerOrAdmin(viewModel?.league?.id || '', currentDriverId)
|
||||
: false;
|
||||
|
||||
// Actions
|
||||
const handleBack = () => {
|
||||
router.back();
|
||||
};
|
||||
|
||||
const handleRegister = async () => {
|
||||
const race = viewModel?.race;
|
||||
const league = viewModel?.league;
|
||||
if (!race || !league) return;
|
||||
|
||||
const confirmed = window.confirm(
|
||||
`Register for ${race.track}?\n\nYou'll be added to the entry list for this race.`,
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await registerMutation.mutateAsync({ raceId: race.id, leagueId: league.id, driverId: currentDriverId });
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to register for race');
|
||||
}
|
||||
};
|
||||
|
||||
const handleWithdraw = async () => {
|
||||
const race = viewModel?.race;
|
||||
const league = viewModel?.league;
|
||||
if (!race || !league) return;
|
||||
|
||||
const confirmed = window.confirm(
|
||||
'Withdraw from this race?\n\nYou can register again later if you change your mind.',
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await withdrawMutation.mutateAsync({ raceId: race.id, driverId: currentDriverId });
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to withdraw from race');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = async () => {
|
||||
const race = viewModel?.race;
|
||||
if (!race || race.status !== 'scheduled') return;
|
||||
|
||||
const confirmed = window.confirm(
|
||||
'Are you sure you want to cancel this race? This action cannot be undone.',
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await cancelMutation.mutateAsync(race.id);
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to cancel race');
|
||||
}
|
||||
};
|
||||
|
||||
const handleReopen = async () => {
|
||||
const race = viewModel?.race;
|
||||
if (!race || !viewModel?.canReopenRace) return;
|
||||
|
||||
const confirmed = window.confirm(
|
||||
'Re-open this race? This will allow re-registration and re-running. Results will be archived.',
|
||||
);
|
||||
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await reopenMutation.mutateAsync(race.id);
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to re-open race');
|
||||
}
|
||||
};
|
||||
|
||||
const handleEndRace = async () => {
|
||||
const race = viewModel?.race;
|
||||
if (!race) return;
|
||||
|
||||
setShowEndRaceModal(true);
|
||||
};
|
||||
|
||||
const handleFileProtest = () => {
|
||||
setShowProtestModal(true);
|
||||
};
|
||||
|
||||
const handleResultsClick = () => {
|
||||
router.push(`/races/${raceId}/results`);
|
||||
};
|
||||
|
||||
const handleStewardingClick = () => {
|
||||
router.push(`/races/${raceId}/stewarding`);
|
||||
};
|
||||
|
||||
const handleLeagueClick = (leagueId: string) => {
|
||||
router.push(`/leagues/${leagueId}`);
|
||||
};
|
||||
|
||||
const handleDriverClick = (driverId: string) => {
|
||||
router.push(`/drivers/${driverId}`);
|
||||
};
|
||||
|
||||
// Transform data for template - handle null values
|
||||
const templateViewModel = viewModel && viewModel.race ? {
|
||||
race: {
|
||||
id: viewModel.race.id,
|
||||
track: viewModel.race.track,
|
||||
car: viewModel.race.car,
|
||||
scheduledAt: viewModel.race.scheduledAt,
|
||||
status: viewModel.race.status as 'scheduled' | 'running' | 'completed' | 'cancelled',
|
||||
sessionType: viewModel.race.sessionType,
|
||||
},
|
||||
league: viewModel.league ? {
|
||||
id: viewModel.league.id,
|
||||
name: viewModel.league.name,
|
||||
description: viewModel.league.description || undefined,
|
||||
settings: viewModel.league.settings as { maxDrivers: number; qualifyingFormat: string },
|
||||
} : undefined,
|
||||
entryList: viewModel.entryList.map(entry => ({
|
||||
id: entry.id,
|
||||
name: entry.name,
|
||||
avatarUrl: entry.avatarUrl,
|
||||
country: entry.country,
|
||||
rating: entry.rating,
|
||||
isCurrentUser: entry.isCurrentUser,
|
||||
})),
|
||||
registration: {
|
||||
isUserRegistered: viewModel.registration.isUserRegistered,
|
||||
canRegister: viewModel.registration.canRegister,
|
||||
},
|
||||
userResult: viewModel.userResult ? {
|
||||
position: viewModel.userResult.position,
|
||||
startPosition: viewModel.userResult.startPosition,
|
||||
positionChange: viewModel.userResult.positionChange,
|
||||
incidents: viewModel.userResult.incidents,
|
||||
isClean: viewModel.userResult.isClean,
|
||||
isPodium: viewModel.userResult.isPodium,
|
||||
ratingChange: viewModel.userResult.ratingChange,
|
||||
} : undefined,
|
||||
canReopenRace: viewModel.canReopenRace,
|
||||
} : undefined;
|
||||
|
||||
return (
|
||||
<RaceDetailTemplate
|
||||
viewModel={templateViewModel}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
onBack={handleBack}
|
||||
onRegister={handleRegister}
|
||||
onWithdraw={handleWithdraw}
|
||||
onCancel={handleCancel}
|
||||
onReopen={handleReopen}
|
||||
onEndRace={handleEndRace}
|
||||
onFileProtest={handleFileProtest}
|
||||
onResultsClick={handleResultsClick}
|
||||
onStewardingClick={handleStewardingClick}
|
||||
onLeagueClick={handleLeagueClick}
|
||||
onDriverClick={handleDriverClick}
|
||||
currentDriverId={currentDriverId}
|
||||
isOwnerOrAdmin={isOwnerOrAdmin}
|
||||
showProtestModal={showProtestModal}
|
||||
setShowProtestModal={setShowProtestModal}
|
||||
showEndRaceModal={showEndRaceModal}
|
||||
setShowEndRaceModal={setShowEndRaceModal}
|
||||
mutationLoading={{
|
||||
register: registerMutation.isPending,
|
||||
withdraw: withdrawMutation.isPending,
|
||||
cancel: cancelMutation.isPending,
|
||||
reopen: reopenMutation.isPending,
|
||||
complete: completeMutation.isPending,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user