Files
gridpilot.gg/apps/website/lib/presenters/RacesPagePresenter.ts
2025-12-11 21:06:25 +01:00

97 lines
2.7 KiB
TypeScript

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;
}
}