177 lines
5.6 KiB
TypeScript
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}
|
|
/>
|
|
);
|
|
} |