104 lines
3.9 KiB
TypeScript
104 lines
3.9 KiB
TypeScript
import { ViewModel } from "../contracts/view-models/ViewModel";
|
|
import { RaceViewModel } from './RaceViewModel';
|
|
import { DriverViewModel } from './DriverViewModel';
|
|
import type { LeagueDetailPageViewData, LeagueMembershipWithRole, SponsorInfo } from '../view-data/LeagueDetailPageViewData';
|
|
|
|
export interface DriverSummary {
|
|
driver: DriverViewModel;
|
|
rating: number | null;
|
|
rank: number | null;
|
|
}
|
|
|
|
export class LeagueDetailPageViewModel extends ViewModel {
|
|
private readonly data: LeagueDetailPageViewData;
|
|
readonly allRaces: RaceViewModel[];
|
|
readonly runningRaces: RaceViewModel[];
|
|
readonly ownerSummary: DriverSummary | null;
|
|
readonly adminSummaries: DriverSummary[];
|
|
readonly stewardSummaries: DriverSummary[];
|
|
|
|
constructor(data: LeagueDetailPageViewData) {
|
|
super();
|
|
this.data = data;
|
|
|
|
this.allRaces = data.allRaces.map(r => r instanceof RaceViewModel ? r : new RaceViewModel(r));
|
|
this.runningRaces = this.allRaces.filter(r => r.status === 'running');
|
|
|
|
// Build driver summaries
|
|
this.ownerSummary = this.buildDriverSummary(data.ownerId);
|
|
this.adminSummaries = data.memberships
|
|
.filter(m => m.role === 'admin')
|
|
.slice(0, 3)
|
|
.map(m => this.buildDriverSummary(m.driverId))
|
|
.filter((s): s is DriverSummary => s !== null);
|
|
this.stewardSummaries = data.memberships
|
|
.filter(m => m.role === 'steward')
|
|
.slice(0, 3)
|
|
.map(m => this.buildDriverSummary(m.driverId))
|
|
.filter((s): s is DriverSummary => s !== null);
|
|
}
|
|
|
|
private buildDriverSummary(driverId: string): DriverSummary | null {
|
|
const driverData = this.data.drivers.find(d => d.id === driverId) ||
|
|
(this.data.owner?.id === driverId ? this.data.owner : null);
|
|
|
|
if (!driverData) return null;
|
|
|
|
const driver = new DriverViewModel(driverData);
|
|
|
|
return {
|
|
driver,
|
|
rating: null,
|
|
rank: null,
|
|
};
|
|
}
|
|
|
|
get id(): string { return this.data.id; }
|
|
get name(): string { return this.data.name; }
|
|
get description(): string { return this.data.description ?? ''; }
|
|
get ownerId(): string { return this.data.ownerId; }
|
|
get createdAt(): string { return this.data.createdAt; }
|
|
get settings() { return this.data.settings; }
|
|
get socialLinks() { return this.data.socialLinks; }
|
|
get owner() { return this.data.owner; }
|
|
get scoringConfig() { return this.data.scoringConfig; }
|
|
get drivers() { return this.data.drivers; }
|
|
get memberships() { return this.data.memberships; }
|
|
get averageSOF(): number | null { return this.data.averageSOF; }
|
|
get completedRacesCount(): number { return this.data.completedRacesCount; }
|
|
get sponsors(): SponsorInfo[] { return this.data.sponsors; }
|
|
|
|
get sponsorInsights() {
|
|
const memberCount = this.memberships.length;
|
|
const mainSponsorTaken = this.sponsors.some(s => s.tier === 'main');
|
|
const secondaryTaken = this.sponsors.filter(s => s.tier === 'secondary').length;
|
|
|
|
return {
|
|
avgViewsPerRace: 5400 + memberCount * 50,
|
|
totalImpressions: 45000 + memberCount * 500,
|
|
engagementRate: (3.5 + (memberCount / 50)).toFixed(1),
|
|
estimatedReach: memberCount * 150,
|
|
mainSponsorAvailable: !mainSponsorTaken,
|
|
secondarySlotsAvailable: Math.max(0, 2 - secondaryTaken),
|
|
mainSponsorPrice: 800 + Math.floor(memberCount * 10),
|
|
secondaryPrice: 250 + Math.floor(memberCount * 3),
|
|
tier: (this.averageSOF && this.averageSOF > 3000 ? 'premium' : this.averageSOF && this.averageSOF > 2000 ? 'standard' : 'starter') as 'premium' | 'standard' | 'starter',
|
|
trustScore: Math.min(100, 60 + memberCount + this.completedRacesCount),
|
|
discordMembers: memberCount * 3,
|
|
monthlyActivity: Math.min(100, 40 + this.completedRacesCount * 2),
|
|
};
|
|
}
|
|
|
|
get isSponsorMode(): boolean {
|
|
return false;
|
|
}
|
|
|
|
get currentUserMembership(): LeagueMembershipWithRole | null {
|
|
return null;
|
|
}
|
|
|
|
get canEndRaces(): boolean {
|
|
return this.currentUserMembership?.role === 'admin' || this.currentUserMembership?.role === 'owner';
|
|
}
|
|
}
|