Files
gridpilot.gg/apps/website/lib/view-models/RaceDetailViewModel.ts
2025-12-26 11:49:20 +01:00

81 lines
2.9 KiB
TypeScript

import { RaceDetailLeagueDTO } from '../types/generated/RaceDetailLeagueDTO';
import { RaceDetailRaceDTO } from '../types/generated/RaceDetailRaceDTO';
import { RaceDetailRegistrationDTO } from '../types/generated/RaceDetailRegistrationDTO';
import { RaceDetailUserResultDTO } from '../types/generated/RaceDetailUserResultDTO';
import { RaceDetailEntryDTO } from '../types/generated/RaceDetailEntryDTO';
import { RaceDetailEntryViewModel } from './RaceDetailEntryViewModel';
import { RaceDetailUserResultViewModel } from './RaceDetailUserResultViewModel';
export class RaceDetailViewModel {
race: RaceDetailRaceDTO | null;
league: RaceDetailLeagueDTO | null;
entryList: RaceDetailEntryViewModel[];
registration: RaceDetailRegistrationDTO;
userResult: RaceDetailUserResultViewModel | null;
error?: string;
constructor(dto: {
race: RaceDetailRaceDTO | null;
league: RaceDetailLeagueDTO | null;
entryList: RaceDetailEntryDTO[];
registration: RaceDetailRegistrationDTO;
userResult: RaceDetailUserResultDTO | null;
error?: string;
}, currentDriverId: string) {
this.race = dto.race;
this.league = dto.league;
this.entryList = dto.entryList.map(entry => new RaceDetailEntryViewModel(entry, currentDriverId));
this.registration = dto.registration;
this.userResult = dto.userResult ? new RaceDetailUserResultViewModel(dto.userResult) : null;
this.error = dto.error;
}
/** UI-specific: Whether user is registered */
get isRegistered(): boolean {
return (this.registration as any).isUserRegistered ?? (this.registration as any).isRegistered ?? false;
}
/** UI-specific: Whether user can register */
get canRegister(): boolean {
return this.registration.canRegister;
}
/** UI-specific: Race status display */
get raceStatusDisplay(): string {
if (!this.race) return 'Unknown';
switch (this.race.status) {
case 'upcoming': return 'Upcoming';
case 'live': return 'Live';
case 'finished': return 'Finished';
default: return this.race.status;
}
}
/** UI-specific: Formatted scheduled time */
get formattedScheduledTime(): string {
return this.race ? new Date(this.race.scheduledAt).toLocaleString() : '';
}
/** UI-specific: Entry list count */
get entryCount(): number {
return this.entryList.length;
}
/** UI-specific: Whether race has results */
get hasResults(): boolean {
return this.userResult !== null;
}
/** UI-specific: Registration status message */
get registrationStatusMessage(): string {
if (this.isRegistered) return 'You are registered for this race';
if (this.canRegister) return 'You can register for this race';
return 'Registration not available';
}
/** UI-specific: Whether race can be re-opened */
get canReopenRace(): boolean {
if (!this.race) return false;
return this.race.status === 'completed' || this.race.status === 'cancelled';
}
}