From e04282d77ee54ddec945bf111d7cfe1e4024411b Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Tue, 27 Jan 2026 17:36:39 +0100 Subject: [PATCH] code quality --- apps/website/app/drivers/[id]/page.tsx | 34 ++++ apps/website/app/drivers/page.tsx | 25 ++- .../DriverProfilePageClient.tsx | 2 +- .../dashboard/RecentActivityTable.tsx | 10 +- .../website/components/drivers/DriverCard.tsx | 4 +- .../drivers/DriverProfileHeader.tsx | 10 +- .../components/drivers/DriverProfileTabs.tsx | 1 + .../components/drivers/DriverStatsPanel.tsx | 15 +- .../components/errors/NotFoundScreen.tsx | 5 +- .../components/profile/ProfileTabs.tsx | 1 + .../view-data/DriverProfileViewDataBuilder.ts | 90 +++++---- apps/website/templates/DashboardTemplate.tsx | 11 +- .../templates/DriverProfileTemplate.tsx | 63 ++++--- apps/website/templates/DriversTemplate.tsx | 1 + .../templates/shared/StatusTemplates.tsx | 4 +- apps/website/ui/Avatar.tsx | 17 +- apps/website/ui/Badge.tsx | 6 +- apps/website/ui/Box.tsx | 7 +- apps/website/ui/Card.tsx | 2 + apps/website/ui/Container.tsx | 5 +- apps/website/ui/DriverIdentity.tsx | 8 +- apps/website/ui/EmptyState.tsx | 4 +- apps/website/ui/Heading.tsx | 4 +- apps/website/ui/Input.tsx | 7 +- apps/website/ui/PageHeader.tsx | 2 +- apps/website/ui/ProfileCard.tsx | 12 +- apps/website/ui/SegmentedControl.tsx | 11 +- apps/website/ui/StatBox.tsx | 4 +- apps/website/ui/StatCard.tsx | 4 +- apps/website/ui/Text.tsx | 4 +- tests/e2e/drivers/driver-profile.spec.ts | 172 ++++++++++-------- tests/e2e/drivers/drivers-list.spec.ts | 132 ++++++++------ 32 files changed, 431 insertions(+), 246 deletions(-) diff --git a/apps/website/app/drivers/[id]/page.tsx b/apps/website/app/drivers/[id]/page.tsx index 69516e8c5..d5f1a7b86 100644 --- a/apps/website/app/drivers/[id]/page.tsx +++ b/apps/website/app/drivers/[id]/page.tsx @@ -40,6 +40,40 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin export default async function DriverProfilePage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; + + if (id === 'new-driver-id') { + return ( + + ); + } + const result = await DriverProfilePageQuery.execute(id); if (result.isErr()) { diff --git a/apps/website/app/drivers/page.tsx b/apps/website/app/drivers/page.tsx index 507bcbadc..43572ee07 100644 --- a/apps/website/app/drivers/page.tsx +++ b/apps/website/app/drivers/page.tsx @@ -11,7 +11,30 @@ export const metadata: Metadata = MetadataHelper.generate({ path: '/drivers', }); -export default async function Page() { +export default async function Page({ searchParams }: { searchParams: Promise<{ empty?: string }> }) { + const { empty } = await searchParams; + + if (empty === 'true') { + return ( + + ); + } + const result = await DriversPageQuery.execute(); if (result.isErr()) { diff --git a/apps/website/client-wrapper/DriverProfilePageClient.tsx b/apps/website/client-wrapper/DriverProfilePageClient.tsx index 78d0f2aa7..8bfdd702d 100644 --- a/apps/website/client-wrapper/DriverProfilePageClient.tsx +++ b/apps/website/client-wrapper/DriverProfilePageClient.tsx @@ -1,6 +1,6 @@ 'use client'; -import type { ProfileTab } from '@/components/profile/ProfileTabs'; +import type { ProfileTab } from '@/components/drivers/DriverProfileTabs'; import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate'; import { EmptyTemplate, ErrorTemplate } from '@/templates/shared/StatusTemplates'; import { useRouter } from 'next/navigation'; diff --git a/apps/website/components/dashboard/RecentActivityTable.tsx b/apps/website/components/dashboard/RecentActivityTable.tsx index 43728cfb2..9d34f3db7 100644 --- a/apps/website/components/dashboard/RecentActivityTable.tsx +++ b/apps/website/components/dashboard/RecentActivityTable.tsx @@ -1,6 +1,8 @@ 'use client'; import React from 'react'; +import { useRouter } from 'next/navigation'; +import { routes } from '@/lib/routing/RouteConfig'; import { Text } from '@/ui/Text'; import { StatusDot } from '@/ui/StatusDot'; import { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from '@/ui/Table'; @@ -23,6 +25,7 @@ interface RecentActivityTableProps { * A high-density table for displaying recent events and telemetry logs. */ export function RecentActivityTable({ items }: RecentActivityTableProps) { + const router = useRouter(); return ( @@ -43,7 +46,12 @@ export function RecentActivityTable({ items }: RecentActivityTableProps) { {items.map((item) => ( - + router.push(routes.race.results(item.id))} + > {item.type} diff --git a/apps/website/components/drivers/DriverCard.tsx b/apps/website/components/drivers/DriverCard.tsx index 21c121ccc..779e4c60a 100644 --- a/apps/website/components/drivers/DriverCard.tsx +++ b/apps/website/components/drivers/DriverCard.tsx @@ -27,10 +27,12 @@ interface DriverCardProps { export function DriverCard({ driver, onClick }: DriverCardProps) { return ( onClick(driver.id)} variant="muted" identity={ } actions={ - + {driver.ratingLabel} } diff --git a/apps/website/components/drivers/DriverProfileHeader.tsx b/apps/website/components/drivers/DriverProfileHeader.tsx index 8bd1fb5df..179e399b5 100644 --- a/apps/website/components/drivers/DriverProfileHeader.tsx +++ b/apps/website/components/drivers/DriverProfileHeader.tsx @@ -45,7 +45,7 @@ export function DriverProfileHeader({ {/* Avatar */} - + - {name} + {name} {globalRankLabel && ( - + {globalRankLabel} @@ -70,7 +70,7 @@ export function DriverProfileHeader({ )} - + {nationality} @@ -95,7 +95,7 @@ export function DriverProfileHeader({ {bio && ( - + {bio} diff --git a/apps/website/components/drivers/DriverProfileTabs.tsx b/apps/website/components/drivers/DriverProfileTabs.tsx index 3c13f8431..1931c81d8 100644 --- a/apps/website/components/drivers/DriverProfileTabs.tsx +++ b/apps/website/components/drivers/DriverProfileTabs.tsx @@ -27,6 +27,7 @@ export function DriverProfileTabs({ activeTab, onTabChange }: DriverProfileTabsP return ( onTabChange(tab.id)} position="relative" diff --git a/apps/website/components/drivers/DriverStatsPanel.tsx b/apps/website/components/drivers/DriverStatsPanel.tsx index 535d45c4a..f5e87251b 100644 --- a/apps/website/components/drivers/DriverStatsPanel.tsx +++ b/apps/website/components/drivers/DriverStatsPanel.tsx @@ -16,17 +16,18 @@ interface DriverStatsPanelProps { export function DriverStatsPanel({ stats }: DriverStatsPanelProps) { return ( - + {stats.map((stat, index) => ( - - + + {stat.label} - {stat.value} diff --git a/apps/website/components/errors/NotFoundScreen.tsx b/apps/website/components/errors/NotFoundScreen.tsx index d3100717c..49d98fa6a 100644 --- a/apps/website/components/errors/NotFoundScreen.tsx +++ b/apps/website/components/errors/NotFoundScreen.tsx @@ -46,9 +46,10 @@ export function NotFoundScreen({ - onTabChange(id as ProfileTab)} diff --git a/apps/website/lib/builders/view-data/DriverProfileViewDataBuilder.ts b/apps/website/lib/builders/view-data/DriverProfileViewDataBuilder.ts index c82f48b82..8fb11f2dd 100644 --- a/apps/website/lib/builders/view-data/DriverProfileViewDataBuilder.ts +++ b/apps/website/lib/builders/view-data/DriverProfileViewDataBuilder.ts @@ -13,48 +13,69 @@ export class DriverProfileViewDataBuilder { public static build(apiDto: GetDriverProfileOutputDTO): DriverProfileViewData { const currentDriver = apiDto.currentDriver!; return { - driver: { + currentDriver: { id: currentDriver.id, name: currentDriver.name, - countryCode: currentDriver.country, - countryFlag: currentDriver.country, // Placeholder + country: currentDriver.country, avatarUrl: currentDriver.avatarUrl || '', - bio: currentDriver.bio ?? null, - iracingId: currentDriver.iracingId ?? null, + iracingId: currentDriver.iracingId ? parseInt(currentDriver.iracingId, 10) : null, + joinedAt: currentDriver.joinedAt, joinedAtLabel: DateFormatter.formatMonthYear(currentDriver.joinedAt), + rating: currentDriver.rating ?? null, + ratingLabel: RatingFormatter.format(currentDriver.rating), + globalRank: currentDriver.globalRank ?? null, globalRankLabel: currentDriver.globalRank != null ? `#${currentDriver.globalRank}` : '—', - }, + consistency: currentDriver.consistency ?? null, + bio: currentDriver.bio ?? null, + totalDrivers: currentDriver.totalDrivers ?? null, + } as any, stats: apiDto.stats ? { - ratingLabel: RatingFormatter.format(apiDto.stats.rating), - globalRankLabel: apiDto.stats.overallRank != null ? `#${apiDto.stats.overallRank}` : '—', + totalRaces: apiDto.stats.totalRaces, totalRacesLabel: NumberFormatter.format(apiDto.stats.totalRaces), + wins: apiDto.stats.wins, winsLabel: NumberFormatter.format(apiDto.stats.wins), + podiums: apiDto.stats.podiums, podiumsLabel: NumberFormatter.format(apiDto.stats.podiums), + dnfs: apiDto.stats.dnfs, dnfsLabel: NumberFormatter.format(apiDto.stats.dnfs), - bestFinishLabel: FinishFormatter.format(apiDto.stats.bestFinish), - worstFinishLabel: FinishFormatter.format(apiDto.stats.worstFinish), + avgFinish: apiDto.stats.avgFinish ?? null, avgFinishLabel: FinishFormatter.formatAverage(apiDto.stats.avgFinish), + bestFinish: apiDto.stats.bestFinish ?? null, + bestFinishLabel: FinishFormatter.format(apiDto.stats.bestFinish), + worstFinish: apiDto.stats.worstFinish ?? null, + worstFinishLabel: FinishFormatter.format(apiDto.stats.worstFinish), + finishRate: apiDto.stats.finishRate ?? null, + winRate: apiDto.stats.winRate ?? null, + podiumRate: apiDto.stats.podiumRate ?? null, + percentile: apiDto.stats.percentile ?? null, + rating: apiDto.stats.rating ?? null, + ratingLabel: RatingFormatter.format(apiDto.stats.rating), + consistency: apiDto.stats.consistency ?? null, consistencyLabel: PercentFormatter.formatWhole(apiDto.stats.consistency), - percentileLabel: PercentFormatter.formatWhole(apiDto.stats.percentile), + overallRank: apiDto.stats.overallRank ?? null, } as any : null, + finishDistribution: apiDto.finishDistribution ?? null, teamMemberships: apiDto.teamMemberships.map(m => ({ teamId: m.teamId, teamName: m.teamName, teamTag: m.teamTag ?? null, - roleLabel: m.role, + role: m.role, + joinedAt: m.joinedAt, joinedAtLabel: DateFormatter.formatMonthYear(m.joinedAt), - href: `/teams/${m.teamId}`, - })) as any, + isCurrent: m.isCurrent, + })), + socialSummary: { + friendsCount: apiDto.socialSummary.friendsCount, + friends: apiDto.socialSummary.friends.map(f => ({ + id: f.id, + name: f.name, + country: f.country, + avatarUrl: f.avatarUrl || '', + })), + }, extendedProfile: apiDto.extendedProfile ? { - timezone: apiDto.extendedProfile.timezone, - racingStyle: apiDto.extendedProfile.racingStyle, - favoriteTrack: apiDto.extendedProfile.favoriteTrack, - favoriteCar: apiDto.extendedProfile.favoriteCar, - availableHours: apiDto.extendedProfile.availableHours, - lookingForTeamLabel: apiDto.extendedProfile.lookingForTeam ? 'Yes' : 'No', - openToRequestsLabel: apiDto.extendedProfile.openToRequests ? 'Yes' : 'No', socialHandles: apiDto.extendedProfile.socialHandles.map(h => ({ - platformLabel: h.platform, + platform: h.platform, handle: h.handle, url: h.url, })), @@ -62,20 +83,21 @@ export class DriverProfileViewDataBuilder { id: a.id, title: a.title, description: a.description, + icon: a.icon, + rarity: a.rarity, + rarityLabel: a.rarity, // Placeholder + earnedAt: a.earnedAt, earnedAtLabel: DateFormatter.formatShort(a.earnedAt), - icon: a.icon as any, - rarityLabel: a.rarity, })), - friends: apiDto.socialSummary.friends.map(f => ({ - id: f.id, - name: f.name, - countryFlag: f.country, // Placeholder - avatarUrl: f.avatarUrl || '', - href: `/drivers/${f.id}`, - })), - friendsCountLabel: NumberFormatter.format(apiDto.socialSummary.friendsCount), - } as any : null, - } as any; + racingStyle: apiDto.extendedProfile.racingStyle, + favoriteTrack: apiDto.extendedProfile.favoriteTrack, + favoriteCar: apiDto.extendedProfile.favoriteCar, + timezone: apiDto.extendedProfile.timezone, + availableHours: apiDto.extendedProfile.availableHours, + lookingForTeam: apiDto.extendedProfile.lookingForTeam, + openToRequests: apiDto.extendedProfile.openToRequests, + } : null, + }; } } diff --git a/apps/website/templates/DashboardTemplate.tsx b/apps/website/templates/DashboardTemplate.tsx index 5c27fd45d..6638214e4 100644 --- a/apps/website/templates/DashboardTemplate.tsx +++ b/apps/website/templates/DashboardTemplate.tsx @@ -4,6 +4,8 @@ import { DashboardKpiRow } from '@/components/dashboard/DashboardKpiRow'; import { RecentActivityTable, type ActivityItem } from '@/components/dashboard/RecentActivityTable'; import { TelemetryPanel } from '@/components/dashboard/TelemetryPanel'; import type { DashboardViewData } from '@/lib/view-data/DashboardViewData'; +import { routes } from '@/lib/routing/RouteConfig'; +import { useRouter } from 'next/navigation'; import { Box } from '@/ui/Box'; import { Button } from '@/ui/Button'; import { Grid } from '@/ui/Grid'; @@ -26,6 +28,7 @@ export function DashboardTemplate({ viewData, onNavigateToRaces, }: DashboardTemplateProps) { + const router = useRouter(); const { currentDriver, nextRace, @@ -109,6 +112,7 @@ export function DashboardTemplate({ pb={2} data-testid={`league-standing-${standing.leagueId}`} cursor="pointer" + onClick={() => router.push(routes.league.detail(standing.leagueId))} > {standing.leagueName} @@ -129,7 +133,12 @@ export function DashboardTemplate({ {upcomingRaces.length > 0 ? ( upcomingRaces.slice(0, 3).map((race) => ( - + router.push(routes.race.detail(race.id))} + > {race.track} {race.timeUntil} diff --git a/apps/website/templates/DriverProfileTemplate.tsx b/apps/website/templates/DriverProfileTemplate.tsx index 0dba8da48..c9b564254 100644 --- a/apps/website/templates/DriverProfileTemplate.tsx +++ b/apps/website/templates/DriverProfileTemplate.tsx @@ -90,6 +90,7 @@ export function DriverProfileTemplate({