Files
gridpilot.gg/apps/website/lib/view-models/AvailableLeaguesViewModel.ts
Marc Mintel d97f50ed72
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 6m4s
Contract Testing / contract-snapshot (pull_request) Has been skipped
view data fixes
2026-01-23 11:59:49 +01:00

84 lines
2.8 KiB
TypeScript

/**
* 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);
}
}