Files
gridpilot.gg/apps/website/lib/view-models/LeagueDetailViewModel.ts
2026-01-16 01:00:03 +01:00

150 lines
4.0 KiB
TypeScript

/**
* League Detail View Model
*
* View model for detailed league information for sponsors.
*/
export class LeagueDetailViewModel {
league: LeagueViewModel;
drivers: DriverViewModel[];
races: RaceViewModel[];
constructor(data: { league: unknown; drivers: unknown[]; races: unknown[] }) {
this.league = new LeagueViewModel(data.league);
this.drivers = data.drivers.map(driver => new DriverViewModel(driver));
this.races = data.races.map(race => new RaceViewModel(race));
}
}
export class LeagueViewModel {
id: string;
name: string;
game: string;
tier: 'premium' | 'standard' | 'starter';
season: string;
description: string;
drivers: number;
races: number;
completedRaces: number;
totalImpressions: number;
avgViewsPerRace: number;
engagement: number;
rating: number;
seasonStatus: 'active' | 'upcoming' | 'completed';
seasonDates: { start: string; end: string };
nextRace?: { name: string; date: string };
sponsorSlots: {
main: { available: boolean; price: number; benefits: string[] };
secondary: { available: number; total: number; price: number; benefits: string[] };
};
constructor(data: unknown) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const d = data as any;
this.id = d.id;
this.name = d.name;
this.game = d.game;
this.tier = d.tier;
this.season = d.season;
this.description = d.description;
this.drivers = d.drivers;
this.races = d.races;
this.completedRaces = d.completedRaces;
this.totalImpressions = d.totalImpressions;
this.avgViewsPerRace = d.avgViewsPerRace;
this.engagement = d.engagement;
this.rating = d.rating;
this.seasonStatus = d.seasonStatus;
this.seasonDates = d.seasonDates;
this.nextRace = d.nextRace;
this.sponsorSlots = d.sponsorSlots;
}
get formattedTotalImpressions(): string {
return this.totalImpressions.toLocaleString();
}
get formattedAvgViewsPerRace(): string {
return this.avgViewsPerRace.toLocaleString();
}
get projectedTotalViews(): number {
return Math.round(this.avgViewsPerRace * this.races);
}
get formattedProjectedTotal(): string {
return this.projectedTotalViews.toLocaleString();
}
get mainSponsorCpm(): number {
return Math.round((this.sponsorSlots.main.price / this.projectedTotalViews) * 1000);
}
get formattedMainSponsorCpm(): string {
return `$${this.mainSponsorCpm.toFixed(2)}`;
}
get racesLeft(): number {
return this.races - this.completedRaces;
}
get tierConfig() {
const configs = {
premium: { color: 'text-yellow-400', bgColor: 'bg-yellow-500/10', border: 'border-yellow-500/30' },
standard: { color: 'text-primary-blue', bgColor: 'bg-primary-blue/10', border: 'border-primary-blue/30' },
starter: { color: 'text-gray-400', bgColor: 'bg-gray-500/10', border: 'border-gray-500/30' },
};
return configs[this.tier];
}
}
export class DriverViewModel {
id: string;
name: string;
country: string;
position: number;
races: number;
impressions: number;
team: string;
constructor(data: unknown) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const d = data as any;
this.id = d.id;
this.name = d.name;
this.country = d.country;
this.position = d.position;
this.races = d.races;
this.impressions = d.impressions;
this.team = d.team;
}
get formattedImpressions(): string {
return this.impressions.toLocaleString();
}
}
export class RaceViewModel {
id: string;
name: string;
date: Date;
views: number;
status: 'upcoming' | 'completed';
constructor(data: unknown) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const d = data as any;
this.id = d.id;
this.name = d.name;
this.date = new Date(d.date);
this.views = d.views;
this.status = d.status;
}
get formattedDate(): string {
return this.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}
get formattedViews(): string {
return this.views.toLocaleString();
}
}