import { notFound, redirect } from 'next/navigation'; import { ContainerManager } from '@/lib/di/container'; import { DASHBOARD_API_CLIENT_TOKEN } from '@/lib/di/tokens'; import type { DashboardApiClient } from '@/lib/api/dashboard/DashboardApiClient'; import type { DashboardOverviewDTO } from '@/lib/types/generated/DashboardOverviewDTO'; import type { DashboardOverviewViewModelData } from '@/lib/view-models/DashboardOverviewViewModelData'; /** * PageQueryResult discriminated union for SSR page queries */ export type PageQueryResult = | { status: 'ok'; data: TData } | { status: 'notFound' } | { status: 'redirect'; destination: string } | { status: 'error'; error: Error }; /** * Transform DashboardOverviewDTO to DashboardOverviewViewModelData * Converts string dates to ISO strings for JSON serialization */ function transformDtoToViewModelData(dto: DashboardOverviewDTO): DashboardOverviewViewModelData { return { currentDriver: dto.currentDriver ? { id: dto.currentDriver.id, name: dto.currentDriver.name, avatarUrl: dto.currentDriver.avatarUrl || '', country: dto.currentDriver.country, totalRaces: dto.currentDriver.totalRaces, wins: dto.currentDriver.wins, podiums: dto.currentDriver.podiums, rating: dto.currentDriver.rating ?? 0, globalRank: dto.currentDriver.globalRank ?? 0, consistency: dto.currentDriver.consistency ?? 0, } : undefined, myUpcomingRaces: dto.myUpcomingRaces.map(race => ({ id: race.id, track: race.track, car: race.car, scheduledAt: new Date(race.scheduledAt).toISOString(), status: race.status, isMyLeague: race.isMyLeague, })), otherUpcomingRaces: dto.otherUpcomingRaces.map(race => ({ id: race.id, track: race.track, car: race.car, scheduledAt: new Date(race.scheduledAt).toISOString(), status: race.status, isMyLeague: race.isMyLeague, })), upcomingRaces: dto.upcomingRaces.map(race => ({ id: race.id, track: race.track, car: race.car, scheduledAt: new Date(race.scheduledAt).toISOString(), status: race.status, isMyLeague: race.isMyLeague, })), activeLeaguesCount: dto.activeLeaguesCount, nextRace: dto.nextRace ? { id: dto.nextRace.id, track: dto.nextRace.track, car: dto.nextRace.car, scheduledAt: new Date(dto.nextRace.scheduledAt).toISOString(), status: dto.nextRace.status, isMyLeague: dto.nextRace.isMyLeague, } : undefined, recentResults: dto.recentResults.map(result => ({ id: result.raceId, track: result.raceName, car: '', // Not in DTO, will need to handle position: result.position, date: new Date(result.finishedAt).toISOString(), })), leagueStandingsSummaries: dto.leagueStandingsSummaries.map(standing => ({ leagueId: standing.leagueId, leagueName: standing.leagueName, position: standing.position, points: standing.points, totalDrivers: standing.totalDrivers, })), feedSummary: { notificationCount: dto.feedSummary.notificationCount, items: dto.feedSummary.items.map(item => ({ id: item.id, type: item.type, headline: item.headline, body: item.body, timestamp: new Date(item.timestamp).toISOString(), ctaHref: item.ctaHref, ctaLabel: item.ctaLabel, })), }, friends: dto.friends.map(friend => ({ id: friend.id, name: friend.name, avatarUrl: friend.avatarUrl || '', country: friend.country, })), }; } /** * Dashboard page query that returns transformed ViewModelData * Returns a discriminated union instead of nullable data */ export class DashboardPageQuery { static async execute(): Promise> { try { const container = ContainerManager.getInstance().getContainer(); const apiClient = container.get(DASHBOARD_API_CLIENT_TOKEN); const dto = await apiClient.getDashboardOverview(); if (!dto) { return { status: 'notFound' }; } const viewModelData = transformDtoToViewModelData(dto); return { status: 'ok', data: viewModelData }; } catch (error) { // Handle specific error types if (error instanceof Error) { // Check if it's a not found error if (error.message.includes('not found') || (error as any).statusCode === 404) { return { status: 'notFound' }; } // Check if it's a redirect error if (error.message.includes('redirect') || (error as any).statusCode === 302) { return { status: 'redirect', destination: '/' }; } return { status: 'error', error }; } return { status: 'error', error: new Error(String(error)) }; } } }