import type { RacesPageDataDTO } from '@/lib/types/generated/RacesPageDataDTO'; import type { RacesViewData, RaceViewData } from '@/lib/view-data/RacesViewData'; export class RacesViewDataBuilder { static build(apiDto: RacesPageDataDTO): RacesViewData { const races = apiDto.races.map((race): RaceViewData => { const scheduledAt = new Date(race.scheduledAt); return { id: race.id, track: race.track, car: race.car, scheduledAt: race.scheduledAt, scheduledAtLabel: scheduledAt.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', }), timeLabel: scheduledAt.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', }), relativeTimeLabel: this.getRelativeTime(scheduledAt), status: race.status as RaceViewData['status'], statusLabel: this.getStatusLabel(race.status), sessionType: 'Race', leagueId: race.leagueId, leagueName: race.leagueName, strengthOfField: race.strengthOfField ?? null, isUpcoming: race.isUpcoming, isLive: race.isLive, isPast: race.isPast, }; }); const leagues = Array.from( new Map( races .filter(r => r.leagueId && r.leagueName) .map(r => [r.leagueId, { id: r.leagueId!, name: r.leagueName! }]) ).values() ); const groupedRaces = new Map(); races.forEach((race) => { const dateKey = race.scheduledAt.split('T')[0]!; if (!groupedRaces.has(dateKey)) { groupedRaces.set(dateKey, []); } groupedRaces.get(dateKey)!.push(race); }); const racesByDate = Array.from(groupedRaces.entries()).map(([dateKey, dayRaces]) => ({ dateKey, dateLabel: dayRaces[0]?.scheduledAtLabel || '', races: dayRaces, })); return { races, totalCount: races.length, scheduledCount: races.filter(r => r.status === 'scheduled').length, runningCount: races.filter(r => r.status === 'running').length, completedCount: races.filter(r => r.status === 'completed').length, leagues, upcomingRaces: races.filter(r => r.isUpcoming).slice(0, 5), liveRaces: races.filter(r => r.isLive), recentResults: races.filter(r => r.isPast).slice(0, 5), racesByDate, }; } private static getStatusLabel(status: string): string { switch (status) { case 'scheduled': return 'Scheduled'; case 'running': return 'LIVE'; case 'completed': return 'Completed'; case 'cancelled': return 'Cancelled'; default: return status; } } private static getRelativeTime(date: Date): string { const now = new Date(); const diffMs = date.getTime() - now.getTime(); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); if (diffMs < 0) return 'Past'; if (diffHours < 1) return 'Starting soon'; if (diffHours < 24) return `In ${diffHours}h`; if (diffDays === 1) return 'Tomorrow'; if (diffDays < 7) return `In ${diffDays} days`; return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', }); } }