website refactor

This commit is contained in:
2026-01-14 02:02:24 +01:00
parent 8d7c709e0c
commit 4522d41aef
291 changed files with 12763 additions and 9309 deletions

View File

@@ -1,10 +1,10 @@
import { notFound } from 'next/navigation';
import { PageWrapper } from '@/components/shared/state/PageWrapper';
import { RaceDetailTemplate } from '@/templates/RaceDetailTemplate';
import { PageDataFetcher } from '@/lib/page/PageDataFetcher';
import { RACE_SERVICE_TOKEN } from '@/lib/di/tokens';
import type { RaceService } from '@/lib/services/races/RaceService';
import type { RaceDetailViewModel } from '@/lib/view-models/RaceDetailViewModel';
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
interface RaceDetailPageProps {
params: {
@@ -19,13 +19,20 @@ export default async function RaceDetailPage({ params }: RaceDetailPageProps) {
notFound();
}
// Fetch initial race data
const data = await PageDataFetcher.fetch<RaceService, 'getRaceDetail'>(
RACE_SERVICE_TOKEN,
'getRaceDetail',
raceId,
'' // currentDriverId - will be handled client-side for auth
);
// Manual wiring: create dependencies
const baseUrl = getWebsiteApiBaseUrl();
const logger = new ConsoleLogger();
const errorReporter = new EnhancedErrorReporter(logger, {
showUserNotifications: true,
logToConsole: true,
reportToExternal: process.env.NODE_ENV === 'production',
});
// Create API client
const apiClient = new RacesApiClient(baseUrl, errorReporter, logger);
// Fetch initial race data (empty driverId for now, handled client-side)
const data = await apiClient.getDetail(raceId, '');
if (!data) notFound();
@@ -66,7 +73,7 @@ export default async function RaceDetailPage({ params }: RaceDetailPageProps) {
isPodium: data.userResult.isPodium,
ratingChange: data.userResult.ratingChange,
} : undefined,
canReopenRace: data.canReopenRace,
canReopenRace: false, // Not provided by API, default to false
} : undefined;
return (

View File

@@ -4,15 +4,30 @@ 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 { RacesApiClient } from '@/lib/api/races/RacesApiClient';
import { ProtestsApiClient } from '@/lib/api/protests/ProtestsApiClient';
import { PenaltiesApiClient } from '@/lib/api/penalties/PenaltiesApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
import { useLeagueMemberships } from "@/lib/hooks/league/useLeagueMemberships";
import { useEffectiveDriverId } from "@/lib/hooks/useEffectiveDriverId";
import { LeagueRoleUtility } from '@/lib/utilities/LeagueRoleUtility';
import { Gavel } from 'lucide-react';
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
// Define the view model structure locally to avoid type issues
interface RaceStewardingViewModel {
race: any;
league: any;
protests: any[];
penalties: any[];
driverMap: Record<string, any>;
pendingProtests: any[];
resolvedProtests: any[];
pendingCount: number;
resolvedCount: number;
penaltiesCount: number;
}
export default function RaceStewardingPage() {
const router = useRouter();
@@ -37,12 +52,61 @@ export default function RaceStewardingPage() {
setIsLoading(true);
setError(null);
const data = await PageDataFetcher.fetch<RaceStewardingService, 'getRaceStewardingData'>(
RACE_STEWARDING_SERVICE_TOKEN,
'getRaceStewardingData',
raceId,
currentDriverId
// Manual wiring: create dependencies
const baseUrl = getWebsiteApiBaseUrl();
const logger = new ConsoleLogger();
const errorReporter = new EnhancedErrorReporter(logger, {
showUserNotifications: true,
logToConsole: true,
reportToExternal: process.env.NODE_ENV === 'production',
});
// Create API clients
const racesApiClient = new RacesApiClient(baseUrl, errorReporter, logger);
const protestsApiClient = new ProtestsApiClient(baseUrl, errorReporter, logger);
const penaltiesApiClient = new PenaltiesApiClient(baseUrl, errorReporter, logger);
// Fetch data in parallel
const [raceDetail, protests, penalties] = await Promise.all([
racesApiClient.getDetail(raceId, currentDriverId),
protestsApiClient.getRaceProtests(raceId),
penaltiesApiClient.getRacePenalties(raceId),
]);
// Transform data to match view model structure
const data: RaceStewardingViewModel = {
race: raceDetail.race,
league: raceDetail.league,
protests: protests.protests.map(p => ({
id: p.id,
protestingDriverId: p.protestingDriverId,
accusedDriverId: p.accusedDriverId,
incident: {
lap: p.lap,
description: p.description,
},
filedAt: p.filedAt,
status: p.status,
})),
penalties: penalties.penalties,
driverMap: { ...protests.driverMap, ...penalties.driverMap },
pendingProtests: [],
resolvedProtests: [],
pendingCount: 0,
resolvedCount: 0,
penaltiesCount: 0,
};
// Calculate derived properties
data.pendingProtests = data.protests.filter(p => p.status === 'pending' || p.status === 'under_review');
data.resolvedProtests = data.protests.filter(p =>
p.status === 'upheld' ||
p.status === 'dismissed' ||
p.status === 'withdrawn'
);
data.pendingCount = data.pendingProtests.length;
data.resolvedCount = data.resolvedProtests.length;
data.penaltiesCount = data.penalties.length;
if (data) {
setPageData(data);
@@ -93,12 +157,61 @@ export default function RaceStewardingPage() {
setIsLoading(true);
setError(null);
const data = await PageDataFetcher.fetch<RaceStewardingService, 'getRaceStewardingData'>(
RACE_STEWARDING_SERVICE_TOKEN,
'getRaceStewardingData',
raceId,
currentDriverId
// Manual wiring: create dependencies
const baseUrl = getWebsiteApiBaseUrl();
const logger = new ConsoleLogger();
const errorReporter = new EnhancedErrorReporter(logger, {
showUserNotifications: true,
logToConsole: true,
reportToExternal: process.env.NODE_ENV === 'production',
});
// Create API clients
const racesApiClient = new RacesApiClient(baseUrl, errorReporter, logger);
const protestsApiClient = new ProtestsApiClient(baseUrl, errorReporter, logger);
const penaltiesApiClient = new PenaltiesApiClient(baseUrl, errorReporter, logger);
// Fetch data in parallel
const [raceDetail, protests, penalties] = await Promise.all([
racesApiClient.getDetail(raceId, currentDriverId),
protestsApiClient.getRaceProtests(raceId),
penaltiesApiClient.getRacePenalties(raceId),
]);
// Transform data to match view model structure
const data: RaceStewardingViewModel = {
race: raceDetail.race,
league: raceDetail.league,
protests: protests.protests.map(p => ({
id: p.id,
protestingDriverId: p.protestingDriverId,
accusedDriverId: p.accusedDriverId,
incident: {
lap: p.lap,
description: p.description,
},
filedAt: p.filedAt,
status: p.status,
})),
penalties: penalties.penalties,
driverMap: { ...protests.driverMap, ...penalties.driverMap },
pendingProtests: [],
resolvedProtests: [],
pendingCount: 0,
resolvedCount: 0,
penaltiesCount: 0,
};
// Calculate derived properties
data.pendingProtests = data.protests.filter(p => p.status === 'pending' || p.status === 'under_review');
data.resolvedProtests = data.protests.filter(p =>
p.status === 'upheld' ||
p.status === 'dismissed' ||
p.status === 'withdrawn'
);
data.pendingCount = data.pendingProtests.length;
data.resolvedCount = data.resolvedProtests.length;
data.penaltiesCount = data.penalties.length;
if (data) {
setPageData(data);

View File

@@ -1,12 +1,11 @@
import { RacesTemplate } from '@/templates/RacesTemplate';
import { RaceService } from '@/lib/services/races/RaceService';
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
export default async function Page() {
// Create dependencies for API clients
// Manual wiring: create dependencies
const baseUrl = getWebsiteApiBaseUrl();
const logger = new ConsoleLogger();
const errorReporter = new EnhancedErrorReporter(logger, {
@@ -18,12 +17,10 @@ export default async function Page() {
// Create API client
const racesApiClient = new RacesApiClient(baseUrl, errorReporter, logger);
// Create service
const service = new RaceService(racesApiClient);
// Fetch data
const data = await racesApiClient.getPageData();
const data = await service.getRacesPageData();
// Transform data for template
// Transform races
const transformRace = (race: any) => ({
id: race.id,
track: race.track,
@@ -34,16 +31,16 @@ export default async function Page() {
leagueId: race.leagueId,
leagueName: race.leagueName,
strengthOfField: race.strengthOfField ?? undefined,
isUpcoming: race.isUpcoming,
isLive: race.isLive,
isPast: race.isPast,
isUpcoming: race.status === 'scheduled',
isLive: race.status === 'running',
isPast: race.status === 'completed',
});
const races = data.races.map(transformRace);
const scheduledRaces = data.scheduledRaces.map(transformRace);
const runningRaces = data.runningRaces.map(transformRace);
const completedRaces = data.completedRaces.map(transformRace);
const totalCount = data.totalCount;
const scheduledRaces = races.filter(r => r.isUpcoming);
const runningRaces = races.filter(r => r.isLive);
const completedRaces = races.filter(r => r.isPast);
const totalCount = races.length;
return <RacesTemplate
races={races}