Files
gridpilot.gg/apps/website/lib/view-models/SponsorDashboardViewModel.ts
2025-12-19 23:18:53 +01:00

135 lines
4.6 KiB
TypeScript

import type { SponsorDashboardDTO } from '../types/generated/SponsorDashboardDTO';
import { SponsorshipViewModel } from './SponsorshipViewModel';
import { ActivityItemViewModel } from './ActivityItemViewModel';
import { RenewalAlertViewModel } from './RenewalAlertViewModel';
/**
* Sponsor Dashboard View Model
*
* View model for sponsor dashboard data with UI-specific transformations.
*/
export class SponsorDashboardViewModel {
sponsorId: string;
sponsorName: string;
metrics: any;
sponsorships: {
leagues: SponsorshipViewModel[];
teams: SponsorshipViewModel[];
drivers: SponsorshipViewModel[];
races: SponsorshipViewModel[];
platform: SponsorshipViewModel[];
};
recentActivity: ActivityItemViewModel[];
upcomingRenewals: RenewalAlertViewModel[];
constructor(dto: SponsorDashboardDTO) {
this.sponsorId = dto.sponsorId;
this.sponsorName = dto.sponsorName;
this.metrics = dto.metrics;
this.sponsorships = {
leagues: (dto.sponsorships?.leagues || []).map(s => new SponsorshipViewModel(s)),
teams: (dto.sponsorships?.teams || []).map(s => new SponsorshipViewModel(s)),
drivers: (dto.sponsorships?.drivers || []).map(s => new SponsorshipViewModel(s)),
races: (dto.sponsorships?.races || []).map(s => new SponsorshipViewModel(s)),
platform: (dto.sponsorships?.platform || []).map(s => new SponsorshipViewModel(s)),
};
this.recentActivity = (dto.recentActivity || []).map(a => new ActivityItemViewModel(a));
this.upcomingRenewals = (dto.upcomingRenewals || []).map(r => new RenewalAlertViewModel(r));
}
get totalSponsorships(): number {
return this.sponsorships.leagues.length +
this.sponsorships.teams.length +
this.sponsorships.drivers.length +
this.sponsorships.races.length +
this.sponsorships.platform.length;
}
get activeSponsorships(): number {
const all = [
...this.sponsorships.leagues,
...this.sponsorships.teams,
...this.sponsorships.drivers,
...this.sponsorships.races,
...this.sponsorships.platform,
];
return all.filter(s => s.status === 'active').length;
}
get totalInvestment(): number {
const all = [
...this.sponsorships.leagues,
...this.sponsorships.teams,
...this.sponsorships.drivers,
...this.sponsorships.races,
...this.sponsorships.platform,
];
return all.filter(s => s.status === 'active').reduce((sum, s) => sum + s.price, 0);
}
get totalImpressions(): number {
const all = [
...this.sponsorships.leagues,
...this.sponsorships.teams,
...this.sponsorships.drivers,
...this.sponsorships.races,
...this.sponsorships.platform,
];
return all.reduce((sum, s) => sum + s.impressions, 0);
}
/** UI-specific: Formatted total investment */
get formattedTotalInvestment(): string {
return `$${this.totalInvestment.toLocaleString()}`;
}
/** UI-specific: Active percentage */
get activePercentage(): number {
if (this.totalSponsorships === 0) return 0;
return Math.round((this.activeSponsorships / this.totalSponsorships) * 100);
}
/** UI-specific: Has sponsorships */
get hasSponsorships(): boolean {
return this.totalSponsorships > 0;
}
/** UI-specific: Status text */
get statusText(): string {
if (this.activeSponsorships === 0) return 'No active sponsorships';
if (this.activeSponsorships === this.totalSponsorships) return 'All sponsorships active';
return `${this.activeSponsorships} of ${this.totalSponsorships} active`;
}
/** UI-specific: Cost per 1K views */
get costPerThousandViews(): string {
if (this.totalImpressions === 0) return '$0.00';
return `$${(this.totalInvestment / this.totalImpressions * 1000).toFixed(2)}`;
}
/** UI-specific: Category data for charts */
get categoryData() {
return {
leagues: {
count: this.sponsorships.leagues.length,
impressions: this.sponsorships.leagues.reduce((sum, l) => sum + l.impressions, 0),
},
teams: {
count: this.sponsorships.teams.length,
impressions: this.sponsorships.teams.reduce((sum, t) => sum + t.impressions, 0),
},
drivers: {
count: this.sponsorships.drivers.length,
impressions: this.sponsorships.drivers.reduce((sum, d) => sum + d.impressions, 0),
},
races: {
count: this.sponsorships.races.length,
impressions: this.sponsorships.races.reduce((sum, r) => sum + r.impressions, 0),
},
platform: {
count: this.sponsorships.platform.length,
impressions: this.sponsorships.platform.reduce((sum, p) => sum + p.impressions, 0),
},
};
}
}