view data fixes
This commit is contained in:
@@ -2,77 +2,83 @@
|
||||
* Available Leagues View Model
|
||||
*
|
||||
* View model for leagues available for sponsorship.
|
||||
*
|
||||
* Accepts AvailableLeaguesViewData as input and produces UI-ready data.
|
||||
*/
|
||||
export class AvailableLeaguesViewModel {
|
||||
leagues: AvailableLeagueViewModel[];
|
||||
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";
|
||||
|
||||
constructor(leagues: unknown[]) {
|
||||
this.leagues = leagues.map(league => new AvailableLeagueViewModel(league));
|
||||
export class AvailableLeaguesViewModel extends ViewModel {
|
||||
readonly leagues: AvailableLeagueViewModel[];
|
||||
|
||||
constructor(viewData: AvailableLeaguesViewData) {
|
||||
super();
|
||||
this.leagues = viewData.leagues.map(league => new AvailableLeagueViewModel(league));
|
||||
}
|
||||
}
|
||||
|
||||
export class AvailableLeagueViewModel {
|
||||
id: string;
|
||||
name: string;
|
||||
game: string;
|
||||
drivers: number;
|
||||
avgViewsPerRace: number;
|
||||
mainSponsorSlot: { available: boolean; price: number };
|
||||
secondarySlots: { available: number; total: number; price: number };
|
||||
rating: number;
|
||||
tier: 'premium' | 'standard' | 'starter';
|
||||
nextRace?: string;
|
||||
seasonStatus: 'active' | 'upcoming' | 'completed';
|
||||
description: string;
|
||||
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(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.drivers = d.drivers;
|
||||
this.avgViewsPerRace = d.avgViewsPerRace;
|
||||
this.mainSponsorSlot = d.mainSponsorSlot;
|
||||
this.secondarySlots = d.secondarySlots;
|
||||
this.rating = d.rating;
|
||||
this.tier = d.tier;
|
||||
this.nextRace = d.nextRace;
|
||||
this.seasonStatus = d.seasonStatus;
|
||||
this.description = d.description;
|
||||
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 `${(this.avgViewsPerRace / 1000).toFixed(1)}k`;
|
||||
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 `$${this.cpm}`;
|
||||
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() {
|
||||
const configs = {
|
||||
premium: { color: 'text-yellow-400', bgColor: 'bg-yellow-500/10', border: 'border-yellow-500/30', icon: '⭐' },
|
||||
standard: { color: 'text-primary-blue', bgColor: 'bg-primary-blue/10', border: 'border-primary-blue/30', icon: '🏆' },
|
||||
starter: { color: 'text-gray-400', bgColor: 'bg-gray-500/10', border: 'border-gray-500/30', icon: '🚀' },
|
||||
};
|
||||
return configs[this.tier];
|
||||
return LeagueTierDisplay.getDisplay(this.tier);
|
||||
}
|
||||
|
||||
/** UI-specific: Status configuration for season state */
|
||||
get statusConfig() {
|
||||
const configs = {
|
||||
active: { color: 'text-performance-green', bg: 'bg-performance-green/10', label: 'Active Season' },
|
||||
upcoming: { color: 'text-warning-amber', bg: 'bg-warning-amber/10', label: 'Starting Soon' },
|
||||
completed: { color: 'text-gray-400', bg: 'bg-gray-400/10', label: 'Season Ended' },
|
||||
};
|
||||
return configs[this.seasonStatus];
|
||||
return SeasonStatusDisplay.getDisplay(this.seasonStatus);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user