136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import { DriverViewModel as SharedDriverViewModel } from "./DriverViewModel";
|
|
import { RaceViewModel as SharedRaceViewModel } from "./RaceViewModel";
|
|
|
|
/**
|
|
* League Detail View Model
|
|
*
|
|
* View model for detailed league information for sponsors.
|
|
*/
|
|
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
|
|
export class LeagueDetailViewModel extends ViewModel {
|
|
league: LeagueViewModel;
|
|
drivers: LeagueDetailDriverViewModel[];
|
|
races: LeagueDetailRaceViewModel[];
|
|
|
|
constructor(data: { league: unknown; drivers: unknown[]; races: unknown[] }) {
|
|
this.league = new LeagueViewModel(data.league);
|
|
this.drivers = data.drivers.map(driver => new LeagueDetailDriverViewModel(driver as any));
|
|
this.races = data.races.map(race => new LeagueDetailRaceViewModel(race as any));
|
|
}
|
|
}
|
|
|
|
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
|
|
export class LeagueDetailDriverViewModel extends SharedDriverViewModel extends ViewModel {
|
|
impressions: number;
|
|
|
|
constructor(dto: any) {
|
|
super(dto);
|
|
this.impressions = dto.impressions || 0;
|
|
}
|
|
|
|
get formattedImpressions(): string {
|
|
return this.impressions.toLocaleString();
|
|
}
|
|
}
|
|
|
|
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
|
|
export class LeagueDetailRaceViewModel extends SharedRaceViewModel extends ViewModel {
|
|
views: number;
|
|
|
|
constructor(dto: any) {
|
|
super(dto);
|
|
this.views = dto.views || 0;
|
|
}
|
|
|
|
get formattedViews(): string {
|
|
return this.views.toLocaleString();
|
|
}
|
|
}
|
|
|
|
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
|
|
export class LeagueViewModel extends ViewModel {
|
|
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];
|
|
}
|
|
}
|