import type { IRacesPagePresenter, RacesPageViewModel, RaceListItemViewModel, } from '@gridpilot/racing/application/presenters/IRacesPagePresenter'; interface RacesPageInput { id: string; track: string; car: string; scheduledAt: string | Date; status: string; leagueId: string; leagueName: string; strengthOfField: number | null; isUpcoming: boolean; isLive: boolean; isPast: boolean; } export class RacesPagePresenter implements IRacesPagePresenter { private viewModel: RacesPageViewModel | null = null; present(races: RacesPageInput[]): void { const now = new Date(); const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); const raceViewModels: RaceListItemViewModel[] = races.map((race) => { const scheduledAt = typeof race.scheduledAt === 'string' ? race.scheduledAt : race.scheduledAt.toISOString(); const allowedStatuses: RaceListItemViewModel['status'][] = [ 'scheduled', 'running', 'completed', 'cancelled', ]; const status: RaceListItemViewModel['status'] = allowedStatuses.includes(race.status as RaceListItemViewModel['status']) ? (race.status as RaceListItemViewModel['status']) : 'scheduled'; return { id: race.id, track: race.track, car: race.car, scheduledAt, status, leagueId: race.leagueId, leagueName: race.leagueName, strengthOfField: race.strengthOfField, isUpcoming: race.isUpcoming, isLive: race.isLive, isPast: race.isPast, }; }); const stats = { total: raceViewModels.length, scheduled: raceViewModels.filter(r => r.status === 'scheduled').length, running: raceViewModels.filter(r => r.status === 'running').length, completed: raceViewModels.filter(r => r.status === 'completed').length, }; const liveRaces = raceViewModels.filter(r => r.isLive); const upcomingThisWeek = raceViewModels .filter(r => { const scheduledDate = new Date(r.scheduledAt); return r.isUpcoming && scheduledDate >= now && scheduledDate <= nextWeek; }) .slice(0, 5); const recentResults = raceViewModels .filter(r => r.status === 'completed') .sort((a, b) => new Date(b.scheduledAt).getTime() - new Date(a.scheduledAt).getTime()) .slice(0, 3); this.viewModel = { races: raceViewModels, stats, liveRaces, upcomingThisWeek, recentResults, }; } getViewModel(): RacesPageViewModel { if (!this.viewModel) { throw new Error('ViewModel not yet generated. Call present() first.'); } return this.viewModel; } }