// DTO matching the backend RacesPageDataRaceDTO interface RaceListItemDTO { id: string; track: string; car: string; scheduledAt: string; status: string; leagueId: string; leagueName: string; strengthOfField: number | null; isUpcoming: boolean; isLive: boolean; isPast: boolean; } export class RaceListItemViewModel { id: string; track: string; car: string; scheduledAt: string; status: string; leagueId: string; leagueName: string; strengthOfField: number | null; isUpcoming: boolean; isLive: boolean; isPast: boolean; constructor(dto: RaceListItemDTO) { this.id = dto.id; this.track = dto.track; this.car = dto.car; this.scheduledAt = dto.scheduledAt; this.status = dto.status; this.leagueId = dto.leagueId; this.leagueName = dto.leagueName; this.strengthOfField = dto.strengthOfField; this.isUpcoming = dto.isUpcoming; this.isLive = dto.isLive; this.isPast = dto.isPast; } /** UI-specific: Formatted scheduled time */ get formattedScheduledTime(): string { return new Date(this.scheduledAt).toLocaleString(); } /** UI-specific: Badge variant for status */ get statusBadgeVariant(): string { switch (this.status) { case 'scheduled': return 'info'; case 'running': return 'success'; case 'completed': return 'secondary'; case 'cancelled': return 'danger'; default: return 'default'; } } /** UI-specific: Time until start in minutes */ get timeUntilStart(): number { const now = new Date(); const scheduled = new Date(this.scheduledAt); return Math.max(0, Math.floor((scheduled.getTime() - now.getTime()) / (1000 * 60))); } /** UI-specific: Display for time until start */ get timeUntilStartDisplay(): string { const minutes = this.timeUntilStart; if (minutes < 60) return `${minutes}m`; const hours = Math.floor(minutes / 60); return `${hours}h ${minutes % 60}m`; } }