138 lines
4.7 KiB
TypeScript
138 lines
4.7 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;
|
|
|
|
// Cast sponsorships to proper type
|
|
const sponsorships = dto.sponsorships as any;
|
|
this.sponsorships = {
|
|
leagues: (sponsorships?.leagues || []).map((s: any) => new SponsorshipViewModel(s)),
|
|
teams: (sponsorships?.teams || []).map((s: any) => new SponsorshipViewModel(s)),
|
|
drivers: (sponsorships?.drivers || []).map((s: any) => new SponsorshipViewModel(s)),
|
|
races: (sponsorships?.races || []).map((s: any) => new SponsorshipViewModel(s)),
|
|
platform: (sponsorships?.platform || []).map((s: any) => new SponsorshipViewModel(s)),
|
|
};
|
|
this.recentActivity = (dto.recentActivity || []).map((a: any) => new ActivityItemViewModel(a));
|
|
this.upcomingRenewals = (dto.upcomingRenewals || []).map((r: any) => 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),
|
|
},
|
|
};
|
|
}
|
|
} |