error and load state

This commit is contained in:
2026-01-06 11:05:16 +01:00
parent 4a1bfa57a3
commit 6aad7897db
29 changed files with 5172 additions and 1462 deletions

View File

@@ -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>
);
}
}