Files
gridpilot.gg/apps/website/app/races/RacesInteractive.tsx
2026-01-05 19:35:49 +01:00

177 lines
5.6 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { RacesTemplate, TimeFilter, RaceStatusFilter } from '@/templates/RacesTemplate';
import { useRacesPageData, useRegisterForRace, useWithdrawFromRace, useCancelRace } from '@/hooks/useRaceService';
import { LeagueMembershipUtility } from '@/lib/utilities/LeagueMembershipUtility';
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
export function RacesInteractive() {
const router = useRouter();
const currentDriverId = useEffectiveDriverId();
// Fetch data
const { data: pageData, isLoading } = useRacesPageData();
// Mutations
const registerMutation = useRegisterForRace();
const withdrawMutation = useWithdrawFromRace();
const cancelMutation = useCancelRace();
// Filter state
const [statusFilter, setStatusFilter] = useState<RaceStatusFilter>('all');
const [leagueFilter, setLeagueFilter] = useState<string>('all');
const [timeFilter, setTimeFilter] = useState<TimeFilter>('upcoming');
const [showFilterModal, setShowFilterModal] = useState(false);
// Transform data for template
const races = pageData?.races.map(race => ({
id: race.id,
track: race.track,
car: race.car,
scheduledAt: race.scheduledAt,
status: race.status as 'scheduled' | 'running' | 'completed' | 'cancelled',
sessionType: 'race', // Not in RaceListItemViewModel, using default
leagueId: race.leagueId,
leagueName: race.leagueName,
strengthOfField: race.strengthOfField ?? undefined,
isUpcoming: race.isUpcoming,
isLive: race.isLive,
isPast: race.isPast,
})) ?? [];
const scheduledRaces = pageData?.scheduledRaces.map(race => ({
id: race.id,
track: race.track,
car: race.car,
scheduledAt: race.scheduledAt,
status: race.status as 'scheduled' | 'running' | 'completed' | 'cancelled',
sessionType: 'race',
leagueId: race.leagueId,
leagueName: race.leagueName,
strengthOfField: race.strengthOfField ?? undefined,
isUpcoming: race.isUpcoming,
isLive: race.isLive,
isPast: race.isPast,
})) ?? [];
const runningRaces = pageData?.runningRaces.map(race => ({
id: race.id,
track: race.track,
car: race.car,
scheduledAt: race.scheduledAt,
status: race.status as 'scheduled' | 'running' | 'completed' | 'cancelled',
sessionType: 'race',
leagueId: race.leagueId,
leagueName: race.leagueName,
strengthOfField: race.strengthOfField ?? undefined,
isUpcoming: race.isUpcoming,
isLive: race.isLive,
isPast: race.isPast,
})) ?? [];
const completedRaces = pageData?.completedRaces.map(race => ({
id: race.id,
track: race.track,
car: race.car,
scheduledAt: race.scheduledAt,
status: race.status as 'scheduled' | 'running' | 'completed' | 'cancelled',
sessionType: 'race',
leagueId: race.leagueId,
leagueName: race.leagueName,
strengthOfField: race.strengthOfField ?? undefined,
isUpcoming: race.isUpcoming,
isLive: race.isLive,
isPast: race.isPast,
})) ?? [];
// Actions
const handleRaceClick = (raceId: string) => {
router.push(`/races/${raceId}`);
};
const handleLeagueClick = (leagueId: string) => {
router.push(`/leagues/${leagueId}`);
};
const handleRegister = async (raceId: string, leagueId: string) => {
if (!currentDriverId) {
router.push('/auth/login');
return;
}
const confirmed = window.confirm(
`Register for this race?\n\nYou'll be added to the entry list.`,
);
if (!confirmed) return;
try {
await registerMutation.mutateAsync({ raceId, leagueId, driverId: currentDriverId });
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to register for race');
}
};
const handleWithdraw = async (raceId: string) => {
if (!currentDriverId) 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, driverId: currentDriverId });
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to withdraw from race');
}
};
const handleCancel = async (raceId: string) => {
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(raceId);
} catch (err) {
alert(err instanceof Error ? err.message : 'Failed to cancel race');
}
};
// User memberships for admin check
// For now, we'll handle permissions in the template using LeagueMembershipUtility
// This would need actual membership data to work properly
const userMemberships: Array<{ leagueId: string; role: string }> = [];
return (
<RacesTemplate
races={races}
totalCount={pageData?.totalCount ?? 0}
scheduledRaces={scheduledRaces}
runningRaces={runningRaces}
completedRaces={completedRaces}
isLoading={isLoading}
statusFilter={statusFilter}
setStatusFilter={setStatusFilter}
leagueFilter={leagueFilter}
setLeagueFilter={setLeagueFilter}
timeFilter={timeFilter}
setTimeFilter={setTimeFilter}
onRaceClick={handleRaceClick}
onLeagueClick={handleLeagueClick}
onRegister={handleRegister}
onWithdraw={handleWithdraw}
onCancel={handleCancel}
showFilterModal={showFilterModal}
setShowFilterModal={setShowFilterModal}
currentDriverId={currentDriverId}
userMemberships={userMemberships}
/>
);
}