92 lines
3.6 KiB
TypeScript
92 lines
3.6 KiB
TypeScript
import type { DashboardPageDto } from '@/lib/page-queries/DashboardPageDto';
|
|
import type { DashboardViewData } from '@/templates/DashboardViewData';
|
|
import {
|
|
formatDashboardDate,
|
|
formatRating,
|
|
formatRank,
|
|
formatConsistency,
|
|
formatRaceCount,
|
|
formatFriendCount,
|
|
formatLeaguePosition,
|
|
formatPoints,
|
|
formatTotalDrivers,
|
|
} from '@/lib/display-objects/DashboardDisplay';
|
|
|
|
/**
|
|
* DashboardPresenter - Client-side presenter for dashboard page
|
|
* Transforms Page DTO into ViewData for the template
|
|
* Deterministic; no hooks; no side effects
|
|
*/
|
|
export class DashboardPresenter {
|
|
static createViewData(pageDto: DashboardPageDto): DashboardViewData {
|
|
return {
|
|
currentDriver: {
|
|
name: pageDto.currentDriver?.name || '',
|
|
avatarUrl: pageDto.currentDriver?.avatarUrl || '',
|
|
country: pageDto.currentDriver?.country || '',
|
|
rating: pageDto.currentDriver ? formatRating(pageDto.currentDriver.rating) : '0.0',
|
|
rank: pageDto.currentDriver ? formatRank(pageDto.currentDriver.globalRank) : '0',
|
|
totalRaces: pageDto.currentDriver ? formatRaceCount(pageDto.currentDriver.totalRaces) : '0',
|
|
wins: pageDto.currentDriver ? formatRaceCount(pageDto.currentDriver.wins) : '0',
|
|
podiums: pageDto.currentDriver ? formatRaceCount(pageDto.currentDriver.podiums) : '0',
|
|
consistency: pageDto.currentDriver ? formatConsistency(pageDto.currentDriver.consistency) : '0%',
|
|
},
|
|
nextRace: pageDto.nextRace ? (() => {
|
|
const dateInfo = formatDashboardDate(new Date(pageDto.nextRace.scheduledAt));
|
|
return {
|
|
id: pageDto.nextRace.id,
|
|
track: pageDto.nextRace.track,
|
|
car: pageDto.nextRace.car,
|
|
scheduledAt: pageDto.nextRace.scheduledAt,
|
|
formattedDate: dateInfo.date,
|
|
formattedTime: dateInfo.time,
|
|
timeUntil: dateInfo.relative,
|
|
isMyLeague: pageDto.nextRace.isMyLeague,
|
|
};
|
|
})() : null,
|
|
upcomingRaces: pageDto.upcomingRaces.map((race) => {
|
|
const dateInfo = formatDashboardDate(new Date(race.scheduledAt));
|
|
return {
|
|
id: race.id,
|
|
track: race.track,
|
|
car: race.car,
|
|
scheduledAt: race.scheduledAt,
|
|
formattedDate: dateInfo.date,
|
|
formattedTime: dateInfo.time,
|
|
timeUntil: dateInfo.relative,
|
|
isMyLeague: race.isMyLeague,
|
|
};
|
|
}),
|
|
leagueStandings: pageDto.leagueStandingsSummaries.map((standing) => ({
|
|
leagueId: standing.leagueId,
|
|
leagueName: standing.leagueName,
|
|
position: formatLeaguePosition(standing.position),
|
|
points: formatPoints(standing.points),
|
|
totalDrivers: formatTotalDrivers(standing.totalDrivers),
|
|
})),
|
|
feedItems: pageDto.feedSummary.items.map((item) => ({
|
|
id: item.id,
|
|
type: item.type,
|
|
headline: item.headline,
|
|
body: item.body,
|
|
timestamp: item.timestamp,
|
|
formattedTime: formatDashboardDate(new Date(item.timestamp)).relative,
|
|
ctaHref: item.ctaHref,
|
|
ctaLabel: item.ctaLabel,
|
|
})),
|
|
friends: pageDto.friends.map((friend) => ({
|
|
id: friend.id,
|
|
name: friend.name,
|
|
avatarUrl: friend.avatarUrl,
|
|
country: friend.country,
|
|
})),
|
|
activeLeaguesCount: formatRaceCount(pageDto.activeLeaguesCount),
|
|
friendCount: formatFriendCount(pageDto.friends.length),
|
|
hasUpcomingRaces: pageDto.upcomingRaces.length > 0,
|
|
hasLeagueStandings: pageDto.leagueStandingsSummaries.length > 0,
|
|
hasFeedItems: pageDto.feedSummary.items.length > 0,
|
|
hasFriends: pageDto.friends.length > 0,
|
|
};
|
|
}
|
|
}
|