198 lines
5.1 KiB
TypeScript
198 lines
5.1 KiB
TypeScript
import type { DashboardOverviewViewModelData } from './DashboardOverviewViewModelData';
|
|
import {
|
|
dashboardStatDisplay,
|
|
formatDashboardDate,
|
|
formatRating,
|
|
formatRank,
|
|
formatConsistency,
|
|
formatRaceCount,
|
|
formatFriendCount,
|
|
formatLeaguePosition,
|
|
formatPoints,
|
|
formatTotalDrivers,
|
|
} from '@/lib/display-objects/DashboardDisplay';
|
|
|
|
/**
|
|
* Dashboard Overview ViewModel
|
|
*
|
|
* Clean class that accepts DTO only and exposes derived values.
|
|
* This is client-only and instantiated after hydration.
|
|
*/
|
|
export class DashboardOverviewViewModel {
|
|
constructor(private readonly dto: DashboardOverviewViewModelData) {}
|
|
|
|
// Current Driver - Derived Values
|
|
get currentDriverName(): string {
|
|
return this.dto.currentDriver?.name || '';
|
|
}
|
|
|
|
get currentDriverAvatarUrl(): string {
|
|
return this.dto.currentDriver?.avatarUrl || '';
|
|
}
|
|
|
|
get currentDriverCountry(): string {
|
|
return this.dto.currentDriver?.country || '';
|
|
}
|
|
|
|
get currentDriverRating(): string {
|
|
return this.dto.currentDriver ? formatRating(this.dto.currentDriver.rating) : '0.0';
|
|
}
|
|
|
|
get currentDriverRank(): string {
|
|
return this.dto.currentDriver ? formatRank(this.dto.currentDriver.globalRank) : '0';
|
|
}
|
|
|
|
get currentDriverTotalRaces(): string {
|
|
return this.dto.currentDriver ? formatRaceCount(this.dto.currentDriver.totalRaces) : '0';
|
|
}
|
|
|
|
get currentDriverWins(): string {
|
|
return this.dto.currentDriver ? formatRaceCount(this.dto.currentDriver.wins) : '0';
|
|
}
|
|
|
|
get currentDriverPodiums(): string {
|
|
return this.dto.currentDriver ? formatRaceCount(this.dto.currentDriver.podiums) : '0';
|
|
}
|
|
|
|
get currentDriverConsistency(): string {
|
|
return this.dto.currentDriver ? formatConsistency(this.dto.currentDriver.consistency) : '0%';
|
|
}
|
|
|
|
// Next Race - Derived Values
|
|
get nextRace(): {
|
|
id: string;
|
|
track: string;
|
|
car: string;
|
|
scheduledAt: string;
|
|
status: string;
|
|
isMyLeague: boolean;
|
|
formattedDate: string;
|
|
formattedTime: string;
|
|
timeUntil: string;
|
|
} | null {
|
|
if (!this.dto.nextRace) return null;
|
|
|
|
const dateInfo = formatDashboardDate(new Date(this.dto.nextRace.scheduledAt));
|
|
|
|
return {
|
|
id: this.dto.nextRace.id,
|
|
track: this.dto.nextRace.track,
|
|
car: this.dto.nextRace.car,
|
|
scheduledAt: this.dto.nextRace.scheduledAt,
|
|
status: this.dto.nextRace.status,
|
|
isMyLeague: this.dto.nextRace.isMyLeague,
|
|
formattedDate: dateInfo.date,
|
|
formattedTime: dateInfo.time,
|
|
timeUntil: dateInfo.relative,
|
|
};
|
|
}
|
|
|
|
// Upcoming Races - Derived Values
|
|
get upcomingRaces(): Array<{
|
|
id: string;
|
|
track: string;
|
|
car: string;
|
|
scheduledAt: string;
|
|
status: string;
|
|
isMyLeague: boolean;
|
|
formattedDate: string;
|
|
formattedTime: string;
|
|
timeUntil: string;
|
|
}> {
|
|
return this.dto.upcomingRaces.map((race) => {
|
|
const dateInfo = formatDashboardDate(new Date(race.scheduledAt));
|
|
return {
|
|
id: race.id,
|
|
track: race.track,
|
|
car: race.car,
|
|
scheduledAt: race.scheduledAt,
|
|
status: race.status,
|
|
isMyLeague: race.isMyLeague,
|
|
formattedDate: dateInfo.date,
|
|
formattedTime: dateInfo.time,
|
|
timeUntil: dateInfo.relative,
|
|
};
|
|
});
|
|
}
|
|
|
|
// League Standings - Derived Values
|
|
get leagueStandings(): Array<{
|
|
leagueId: string;
|
|
leagueName: string;
|
|
position: string;
|
|
points: string;
|
|
totalDrivers: string;
|
|
}> {
|
|
return this.dto.leagueStandingsSummaries.map((standing) => ({
|
|
leagueId: standing.leagueId,
|
|
leagueName: standing.leagueName,
|
|
position: formatLeaguePosition(standing.position),
|
|
points: formatPoints(standing.points),
|
|
totalDrivers: formatTotalDrivers(standing.totalDrivers),
|
|
}));
|
|
}
|
|
|
|
// Feed Items - Derived Values
|
|
get feedItems(): Array<{
|
|
id: string;
|
|
type: string;
|
|
headline: string;
|
|
body?: string;
|
|
timestamp: string;
|
|
ctaHref?: string;
|
|
ctaLabel?: string;
|
|
formattedTime: string;
|
|
}> {
|
|
return this.dto.feedSummary.items.map((item) => ({
|
|
id: item.id,
|
|
type: item.type,
|
|
headline: item.headline,
|
|
body: item.body,
|
|
timestamp: item.timestamp,
|
|
ctaHref: item.ctaHref,
|
|
ctaLabel: item.ctaLabel,
|
|
formattedTime: formatDashboardDate(new Date(item.timestamp)).relative,
|
|
}));
|
|
}
|
|
|
|
// Friends - Derived Values
|
|
get friends(): Array<{
|
|
id: string;
|
|
name: string;
|
|
avatarUrl: string;
|
|
country: string;
|
|
}> {
|
|
// No additional formatting needed for friends
|
|
return this.dto.friends;
|
|
}
|
|
|
|
// Active Leagues Count
|
|
get activeLeaguesCount(): string {
|
|
return formatRaceCount(this.dto.activeLeaguesCount);
|
|
}
|
|
|
|
// Convenience getters for display
|
|
get hasNextRace(): boolean {
|
|
return this.dto.nextRace !== undefined;
|
|
}
|
|
|
|
get hasUpcomingRaces(): boolean {
|
|
return this.dto.upcomingRaces.length > 0;
|
|
}
|
|
|
|
get hasLeagueStandings(): boolean {
|
|
return this.dto.leagueStandingsSummaries.length > 0;
|
|
}
|
|
|
|
get hasFeedItems(): boolean {
|
|
return this.dto.feedSummary.items.length > 0;
|
|
}
|
|
|
|
get hasFriends(): boolean {
|
|
return this.dto.friends.length > 0;
|
|
}
|
|
|
|
get friendCount(): string {
|
|
return formatFriendCount(this.dto.friends.length);
|
|
}
|
|
} |