/** * Available Leagues View Model * * View model for leagues available for sponsorship. * * Accepts AvailableLeaguesViewData as input and produces UI-ready data. */ import { ViewModel } from "../contracts/view-models/ViewModel"; import { AvailableLeaguesViewData, AvailableLeagueViewData } from "../view-data/AvailableLeaguesViewData"; import { NumberDisplay } from "../display-objects/NumberDisplay"; import { CurrencyDisplay } from "../display-objects/CurrencyDisplay"; import { LeagueTierDisplay } from "../display-objects/LeagueTierDisplay"; import { SeasonStatusDisplay } from "../display-objects/SeasonStatusDisplay"; export class AvailableLeaguesViewModel extends ViewModel { readonly leagues: AvailableLeagueViewModel[]; constructor(viewData: AvailableLeaguesViewData) { super(); this.leagues = viewData.leagues.map(league => new AvailableLeagueViewModel(league)); } } export class AvailableLeagueViewModel extends ViewModel { readonly id: string; readonly name: string; readonly game: string; readonly drivers: number; readonly avgViewsPerRace: number; readonly mainSponsorSlot: { available: boolean; price: number }; readonly secondarySlots: { available: number; total: number; price: number }; readonly rating: number; readonly tier: 'premium' | 'standard' | 'starter'; readonly nextRace?: string; readonly seasonStatus: 'active' | 'upcoming' | 'completed'; readonly description: string; constructor(viewData: AvailableLeagueViewData) { super(); this.id = viewData.id; this.name = viewData.name; this.game = viewData.game; this.drivers = viewData.drivers; this.avgViewsPerRace = viewData.avgViewsPerRace; this.mainSponsorSlot = viewData.mainSponsorSlot; this.secondarySlots = viewData.secondarySlots; this.rating = viewData.rating; this.tier = viewData.tier; this.nextRace = viewData.nextRace; this.seasonStatus = viewData.seasonStatus; this.description = viewData.description; } /** UI-specific: Formatted average views */ get formattedAvgViews(): string { return NumberDisplay.formatCompact(this.avgViewsPerRace); } /** UI-specific: CPM calculation */ get cpm(): number { return Math.round((this.mainSponsorSlot.price / this.avgViewsPerRace) * 1000); } /** UI-specific: Formatted CPM */ get formattedCpm(): string { return CurrencyDisplay.formatCompact(this.cpm); } /** UI-specific: Check if any sponsor slots are available */ get hasAvailableSlots(): boolean { return this.mainSponsorSlot.available || this.secondarySlots.available > 0; } /** UI-specific: Tier configuration for badge styling */ get tierConfig() { return LeagueTierDisplay.getDisplay(this.tier); } /** UI-specific: Status configuration for season state */ get statusConfig() { return SeasonStatusDisplay.getDisplay(this.seasonStatus); } }