page wrapper

This commit is contained in:
2026-01-07 12:40:52 +01:00
parent e589c30bf8
commit 0db80fa98d
128 changed files with 7386 additions and 8096 deletions

View File

@@ -1,87 +0,0 @@
'use client';
import { useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { RaceStewardingTemplate, StewardingTab } from '@/templates/RaceStewardingTemplate';
import { useLeagueMemberships } from '@/hooks/league/useLeagueMemberships';
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
import { LeagueRoleUtility } from '@/lib/utilities/LeagueRoleUtility';
// Shared state components
import { StateContainer } from '@/components/shared/state/StateContainer';
import { useRaceStewardingData } from '@/hooks/race/useRaceStewardingData';
import { Gavel } from 'lucide-react';
export function RaceStewardingInteractive() {
const router = useRouter();
const params = useParams();
const raceId = params.id as string;
const currentDriverId = useEffectiveDriverId();
// Fetch data using existing hooks
const { data: stewardingData, isLoading, error, retry } = useRaceStewardingData(raceId, currentDriverId);
// Fetch membership
const { data: membershipsData } = useLeagueMemberships(stewardingData?.league?.id || '', currentDriverId || '');
// UI State
const [activeTab, setActiveTab] = useState<StewardingTab>('pending');
const currentMembership = membershipsData?.memberships.find(m => m.driverId === currentDriverId);
const isAdmin = currentMembership ? LeagueRoleUtility.isLeagueAdminOrHigherRole(currentMembership.role) : false;
// Actions
const handleBack = () => {
router.push(`/races/${raceId}`);
};
const handleReviewProtest = (protestId: string) => {
// Navigate to protest review page
router.push(`/leagues/${stewardingData?.league?.id}/stewarding/protests/${protestId}`);
};
// Transform data for template
const templateData = stewardingData ? {
race: stewardingData.race,
league: stewardingData.league,
pendingProtests: stewardingData.pendingProtests,
resolvedProtests: stewardingData.resolvedProtests,
penalties: stewardingData.penalties,
driverMap: stewardingData.driverMap,
pendingCount: stewardingData.pendingCount,
resolvedCount: stewardingData.resolvedCount,
penaltiesCount: stewardingData.penaltiesCount,
} : undefined;
return (
<StateContainer
data={stewardingData}
isLoading={isLoading}
error={error}
retry={retry}
config={{
loading: { variant: 'skeleton', message: 'Loading stewarding data...' },
error: { variant: 'full-screen' },
empty: {
icon: Gavel,
title: 'No stewarding data',
description: 'No protests or penalties for this race',
action: { label: 'Back to Race', onClick: handleBack }
}
}}
>
{(stewardingData) => (
<RaceStewardingTemplate
stewardingData={templateData}
isLoading={false}
error={null}
onBack={handleBack}
onReviewProtest={handleReviewProtest}
isAdmin={isAdmin}
activeTab={activeTab}
setActiveTab={setActiveTab}
/>
)}
</StateContainer>
);
}

View File

@@ -1,3 +1,140 @@
import { RaceStewardingInteractive } from './RaceStewardingInteractive';
'use client';
export default RaceStewardingInteractive;
import { useState, useEffect } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { PageWrapper } from '@/components/shared/state/PageWrapper';
import { RaceStewardingTemplate, StewardingTab } from '@/templates/RaceStewardingTemplate';
import { PageDataFetcher } from '@/lib/page/PageDataFetcher';
import { RACE_STEWARDING_SERVICE_TOKEN } from '@/lib/di/tokens';
import { RaceStewardingService } from '@/lib/services/races/RaceStewardingService';
import type { RaceStewardingViewModel } from '@/lib/view-models/RaceStewardingViewModel';
import { useLeagueMemberships } from '@/hooks/league/useLeagueMemberships';
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
import { LeagueRoleUtility } from '@/lib/utilities/LeagueRoleUtility';
import { Gavel } from 'lucide-react';
export default function RaceStewardingPage() {
const router = useRouter();
const params = useParams();
const raceId = params.id as string;
const currentDriverId = useEffectiveDriverId() || '';
// Data state
const [pageData, setPageData] = useState<RaceStewardingViewModel | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
// UI State
const [activeTab, setActiveTab] = useState<StewardingTab>('pending');
// Fetch data on mount and when raceId/currentDriverId changes
useEffect(() => {
async function fetchData() {
if (!raceId) return;
try {
setIsLoading(true);
setError(null);
const data = await PageDataFetcher.fetch<RaceStewardingService, 'getRaceStewardingData'>(
RACE_STEWARDING_SERVICE_TOKEN,
'getRaceStewardingData',
raceId,
currentDriverId
);
if (data) {
setPageData(data);
} else {
setPageData(null);
}
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to fetch stewarding data'));
setPageData(null);
} finally {
setIsLoading(false);
}
}
fetchData();
}, [raceId, currentDriverId]);
// Fetch membership
const { data: membershipsData } = useLeagueMemberships(pageData?.league?.id || '', currentDriverId || '');
const currentMembership = membershipsData?.memberships.find(m => m.driverId === currentDriverId);
const isAdmin = currentMembership ? LeagueRoleUtility.isLeagueAdminOrHigherRole(currentMembership.role) : false;
// Actions
const handleBack = () => {
router.push(`/races/${raceId}`);
};
const handleReviewProtest = (protestId: string) => {
// Navigate to protest review page
router.push(`/leagues/${pageData?.league?.id}/stewarding/protests/${protestId}`);
};
// Transform data for template
const templateData = pageData ? {
race: pageData.race,
league: pageData.league,
pendingProtests: pageData.pendingProtests,
resolvedProtests: pageData.resolvedProtests,
penalties: pageData.penalties,
driverMap: pageData.driverMap,
pendingCount: pageData.pendingCount,
resolvedCount: pageData.resolvedCount,
penaltiesCount: pageData.penaltiesCount,
} : undefined;
const retry = async () => {
try {
setIsLoading(true);
setError(null);
const data = await PageDataFetcher.fetch<RaceStewardingService, 'getRaceStewardingData'>(
RACE_STEWARDING_SERVICE_TOKEN,
'getRaceStewardingData',
raceId,
currentDriverId
);
if (data) {
setPageData(data);
}
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to fetch stewarding data'));
} finally {
setIsLoading(false);
}
};
return (
<PageWrapper
data={pageData}
isLoading={isLoading}
error={error}
retry={retry}
Template={({ data }) => (
<RaceStewardingTemplate
stewardingData={templateData}
isLoading={false}
error={null}
onBack={handleBack}
onReviewProtest={handleReviewProtest}
isAdmin={isAdmin}
activeTab={activeTab}
setActiveTab={setActiveTab}
/>
)}
loading={{ variant: 'skeleton', message: 'Loading stewarding data...' }}
errorConfig={{ variant: 'full-screen' }}
empty={{
icon: Gavel,
title: 'No stewarding data',
description: 'No protests or penalties for this race',
action: { label: 'Back to Race', onClick: handleBack }
}}
/>
);
}