error and load state
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { LeagueDetailTemplate } from '@/templates/LeagueDetailTemplate';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import { useSponsorMode } from '@/components/sponsors/SponsorInsightsCard';
|
||||
import { LeagueDetailPageViewModel } from '@/lib/view-models/LeagueDetailPageViewModel';
|
||||
import EndRaceModal from '@/components/leagues/EndRaceModal';
|
||||
|
||||
// Shared state components
|
||||
import { useDataFetching } from '@/components/shared/hooks/useDataFetching';
|
||||
import { StateContainer } from '@/components/shared/state/StateContainer';
|
||||
import { Trophy } from 'lucide-react';
|
||||
|
||||
export default function LeagueDetailInteractive() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
@@ -16,39 +20,18 @@ export default function LeagueDetailInteractive() {
|
||||
const isSponsor = useSponsorMode();
|
||||
const { leagueService, leagueMembershipService, raceService } = useServices();
|
||||
|
||||
const [viewModel, setViewModel] = useState<LeagueDetailPageViewModel | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [endRaceModalRaceId, setEndRaceModalRaceId] = useState<string | null>(null);
|
||||
|
||||
const currentDriverId = useEffectiveDriverId();
|
||||
const membership = leagueMembershipService.getMembership(leagueId, currentDriverId);
|
||||
|
||||
const loadLeagueData = async () => {
|
||||
try {
|
||||
const viewModelData = await leagueService.getLeagueDetailPageData(leagueId);
|
||||
|
||||
if (!viewModelData) {
|
||||
setError('League not found');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setViewModel(viewModelData);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load league');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadLeagueData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [leagueId]);
|
||||
const { data: viewModel, isLoading, error, retry } = useDataFetching({
|
||||
queryKey: ['leagueDetailPage', leagueId],
|
||||
queryFn: () => leagueService.getLeagueDetailPageData(leagueId),
|
||||
});
|
||||
|
||||
const handleMembershipChange = () => {
|
||||
loadLeagueData();
|
||||
retry();
|
||||
};
|
||||
|
||||
const handleEndRaceModalOpen = (raceId: string) => {
|
||||
@@ -68,7 +51,7 @@ export default function LeagueDetailInteractive() {
|
||||
|
||||
try {
|
||||
await raceService.completeRace(endRaceModalRaceId);
|
||||
await loadLeagueData();
|
||||
await retry();
|
||||
setEndRaceModalRaceId(null);
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to complete race');
|
||||
@@ -79,46 +62,51 @@ export default function LeagueDetailInteractive() {
|
||||
setEndRaceModalRaceId(null);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="text-center text-gray-400">Loading league...</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !viewModel) {
|
||||
return (
|
||||
<div className="text-center text-warning-amber">
|
||||
{error || 'League not found'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<LeagueDetailTemplate
|
||||
viewModel={viewModel}
|
||||
leagueId={leagueId}
|
||||
isSponsor={isSponsor}
|
||||
membership={membership}
|
||||
currentDriverId={currentDriverId}
|
||||
onMembershipChange={handleMembershipChange}
|
||||
onEndRaceModalOpen={handleEndRaceModalOpen}
|
||||
onLiveRaceClick={handleLiveRaceClick}
|
||||
onBackToLeagues={handleBackToLeagues}
|
||||
>
|
||||
{/* End Race Modal */}
|
||||
{endRaceModalRaceId && viewModel && (() => {
|
||||
const race = viewModel.runningRaces.find(r => r.id === endRaceModalRaceId);
|
||||
return race ? (
|
||||
<EndRaceModal
|
||||
raceId={race.id}
|
||||
raceName={race.name}
|
||||
onConfirm={handleEndRaceConfirm}
|
||||
onCancel={handleEndRaceCancel}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
</LeagueDetailTemplate>
|
||||
</>
|
||||
<StateContainer
|
||||
data={viewModel}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
retry={retry}
|
||||
config={{
|
||||
loading: { variant: 'skeleton', message: 'Loading league...' },
|
||||
error: { variant: 'full-screen' },
|
||||
empty: {
|
||||
icon: Trophy,
|
||||
title: 'League not found',
|
||||
description: 'The league may have been deleted or you may not have access',
|
||||
action: { label: 'Back to Leagues', onClick: handleBackToLeagues }
|
||||
}
|
||||
}}
|
||||
>
|
||||
{(leagueData) => (
|
||||
<>
|
||||
<LeagueDetailTemplate
|
||||
viewModel={leagueData}
|
||||
leagueId={leagueId}
|
||||
isSponsor={isSponsor}
|
||||
membership={membership}
|
||||
currentDriverId={currentDriverId}
|
||||
onMembershipChange={handleMembershipChange}
|
||||
onEndRaceModalOpen={handleEndRaceModalOpen}
|
||||
onLiveRaceClick={handleLiveRaceClick}
|
||||
onBackToLeagues={handleBackToLeagues}
|
||||
>
|
||||
{/* End Race Modal */}
|
||||
{endRaceModalRaceId && leagueData && (() => {
|
||||
const race = leagueData.runningRaces.find(r => r.id === endRaceModalRaceId);
|
||||
return race ? (
|
||||
<EndRaceModal
|
||||
raceId={race.id}
|
||||
raceName={race.name}
|
||||
onConfirm={handleEndRaceConfirm}
|
||||
onCancel={handleEndRaceCancel}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
</LeagueDetailTemplate>
|
||||
</>
|
||||
)}
|
||||
</StateContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user