diff --git a/apps/website/components/drivers/DriverCard.tsx b/apps/website/components/drivers/DriverCard.tsx
index 538a027bd..606120996 100644
--- a/apps/website/components/drivers/DriverCard.tsx
+++ b/apps/website/components/drivers/DriverCard.tsx
@@ -1,70 +1,66 @@
-import { DriverIdentity } from '@/ui/DriverIdentity';
-import { DriverStats } from '@/components/drivers/DriverStats';
-import { RankBadge } from '@/components/leaderboards/RankBadge';
-import { routes } from '@/lib/routing/RouteConfig';
-import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
-import { Card } from '@/ui/Card';
-import { Stack } from '@/ui/Stack';
+'use client';
-export interface DriverCardProps {
- id: string;
- name: string;
- rating: number;
- skillLevel: 'beginner' | 'intermediate' | 'advanced' | 'pro';
- nationality: string;
- racesCompleted: number;
- wins: number;
- podiums: number;
- rank: number;
- onClick?: () => void;
+import { DriverIdentity } from '@/ui/DriverIdentity';
+import { ProfileCard } from '@/ui/ProfileCard';
+import { StatGrid } from '@/ui/StatGrid';
+import { Badge } from '@/ui/Badge';
+import { Flag, Trophy, Medal } from 'lucide-react';
+
+interface DriverCardProps {
+ driver: {
+ id: string;
+ name: string;
+ avatarUrl?: string;
+ rating: number;
+ ratingLabel: string;
+ nationality: string;
+ racesCompleted: number;
+ wins: number;
+ podiums: number;
+ rank: number;
+ };
+ onClick: (id: string) => void;
}
-export function DriverCard(props: DriverCardProps) {
- const {
- id,
- name,
- rating,
- nationality,
- racesCompleted,
- wins,
- podiums,
- rank,
- onClick,
- } = props;
-
- // Create a proper DriverViewModel instance
- const driverViewModel = new DriverViewModel({
- id,
- name,
- avatarUrl: null,
- });
-
- const winRate = racesCompleted > 0 ? ((wins / racesCompleted) * 100).toFixed(0) : '0';
+export function DriverCard({ driver, onClick }: DriverCardProps) {
+ const stats = [
+ { label: 'Races', value: driver.racesCompleted, intent: 'low', icon: Flag },
+ { label: 'Wins', value: driver.wins, intent: 'primary', icon: Trophy },
+ { label: 'Podiums', value: driver.podiums, intent: 'warning', icon: Medal },
+ ];
return (
-
-
-
-
-
-
-
-
- onClick(driver.id)}
+ variant="muted"
+ identity={
+
-
-
+ }
+ actions={
+
+ {driver.ratingLabel}
+
+ }
+ stats={
+ ({
+ label: s.label,
+ value: s.value,
+ intent: s.intent as any,
+ icon: s.icon
+ }))}
+ columns={3}
+ variant="box"
+ />
+ }
+ />
);
}
diff --git a/apps/website/components/drivers/DriverSearchBar.tsx b/apps/website/components/drivers/DriverSearchBar.tsx
index bc9361471..c15543fc5 100644
--- a/apps/website/components/drivers/DriverSearchBar.tsx
+++ b/apps/website/components/drivers/DriverSearchBar.tsx
@@ -1,6 +1,6 @@
'use client';
-import { Group } from '@/ui/Group';
+import { Box } from '@/ui/Box';
import { Input } from '@/ui/Input';
import { Search } from 'lucide-react';
@@ -11,13 +11,14 @@ interface DriverSearchBarProps {
export function DriverSearchBar({ query, onChange }: DriverSearchBarProps) {
return (
-
+
onChange(e.target.value)}
- placeholder="Search drivers by name or nationality..."
- icon={}
+ placeholder="Search competitors..."
+ icon={}
+ size="sm"
/>
-
+
);
}
diff --git a/apps/website/components/drivers/DriverStatsHeader.tsx b/apps/website/components/drivers/DriverStatsHeader.tsx
new file mode 100644
index 000000000..db082e2e1
--- /dev/null
+++ b/apps/website/components/drivers/DriverStatsHeader.tsx
@@ -0,0 +1,25 @@
+'use client';
+
+import { StatGrid } from '@/ui/StatGrid';
+import { Users, Trophy, Activity } from 'lucide-react';
+
+interface DriverStatsHeaderProps {
+ totalDrivers: string;
+ activeDrivers: string;
+ totalRaces: string;
+}
+
+export function DriverStatsHeader({ totalDrivers, activeDrivers, totalRaces }: DriverStatsHeaderProps) {
+ return (
+
+ );
+}
diff --git a/apps/website/components/drivers/DriverTable.tsx b/apps/website/components/drivers/DriverTable.tsx
index 1bd4f0bce..0626c7ced 100644
--- a/apps/website/components/drivers/DriverTable.tsx
+++ b/apps/website/components/drivers/DriverTable.tsx
@@ -2,11 +2,12 @@
import { Heading } from '@/ui/Heading';
import { Text } from '@/ui/Text';
-import { Group } from '@/ui/Group';
+import { Box } from '@/ui/Box';
import { Table, TableHead, TableBody, TableRow, TableHeaderCell } from '@/ui/Table';
import { TrendingUp } from 'lucide-react';
-import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
+import { Surface } from '@/ui/Surface';
+import { Stack } from '@/ui/Stack';
import React from 'react';
interface DriverTableProps {
@@ -15,31 +16,31 @@ interface DriverTableProps {
export function DriverTable({ children }: DriverTableProps) {
return (
-
-
-
+
+
+
-
-
- Driver Rankings
- Top performers by skill rating
-
-
+
+
+ Driver Rankings
+ Performance metrics based on sanctioned race results
+
+
- #
- Driver
- Nationality
- Rating
- Wins
+ Rank
+ Competitor
+ Nationality
+ Skill Rating
+ Victories
{children}
-
+
);
}
diff --git a/apps/website/components/drivers/DriverTableRow.tsx b/apps/website/components/drivers/DriverTableRow.tsx
index b7d96e1f2..965a6c259 100644
--- a/apps/website/components/drivers/DriverTableRow.tsx
+++ b/apps/website/components/drivers/DriverTableRow.tsx
@@ -2,9 +2,10 @@
import { RatingBadge } from '@/components/drivers/RatingBadge';
import { Text } from '@/ui/Text';
-import { Group } from '@/ui/Group';
+import { Box } from '@/ui/Box';
import { TableRow, TableCell } from '@/ui/Table';
import { Avatar } from '@/ui/Avatar';
+import { CountryFlag } from '@/ui/CountryFlag';
interface DriverTableRowProps {
rank: number;
@@ -28,19 +29,19 @@ export function DriverTableRow({
onClick,
}: DriverTableRowProps) {
return (
-
+
- {rank}
+ {rank.toString().padStart(2, '0')}
-
+
{name}
-
+
- {nationality}
+
+
+ {nationality}
+
-
+
+ {rating}
+
+
-
+
{winsLabel}
diff --git a/apps/website/components/drivers/DriversDirectoryHeader.tsx b/apps/website/components/drivers/DriversDirectoryHeader.tsx
index 21f8debf2..8a8f32ebd 100644
--- a/apps/website/components/drivers/DriversDirectoryHeader.tsx
+++ b/apps/website/components/drivers/DriversDirectoryHeader.tsx
@@ -2,19 +2,15 @@
import { Button } from '@/ui/Button';
import { Heading } from '@/ui/Heading';
-import { Group } from '@/ui/Group';
import { Text } from '@/ui/Text';
import { Section } from '@/ui/Section';
import { Container } from '@/ui/Container';
-import { Card } from '@/ui/Card';
import { Icon } from '@/ui/Icon';
-import { Trophy, Users } from 'lucide-react';
-
-interface DriverStat {
- label: string;
- value: string | number;
- intent?: 'primary' | 'success' | 'warning' | 'telemetry';
-}
+import { MetricCard } from '@/ui/MetricCard';
+import { Trophy, Users, Zap, Flag } from 'lucide-react';
+import { Grid } from '@/ui/Grid';
+import { Box } from '@/ui/Box';
+import { Stack } from '@/ui/Stack';
interface DriversDirectoryHeaderProps {
totalDriversLabel: string;
@@ -31,53 +27,68 @@ export function DriversDirectoryHeader({
totalRacesLabel,
onViewLeaderboard,
}: DriversDirectoryHeaderProps) {
- const stats: DriverStat[] = [
- { label: 'drivers', value: totalDriversLabel, intent: 'primary' },
- { label: 'active', value: activeDriversLabel, intent: 'success' },
- { label: 'total wins', value: totalWinsLabel, intent: 'warning' },
- { label: 'races', value: totalRacesLabel, intent: 'telemetry' },
- ];
-
return (
-
+
-
-
-
-
+
+
+
+
-
- Drivers
-
-
-
- Meet the racers who make every lap count. From rookies to champions, track their journey and see who's dominating the grid.
-
+
+ Driver Directory
+
+
+
+ The official registry of GridPilot competitors. Tracking performance,
+ reliability, and championship progress across all sanctioned events.
+
+
-
- {stats.map((stat, index) => (
-
-
- {stat.value} {stat.label}
-
-
- ))}
-
-
-
-
}
+ icon={}
>
- View Leaderboard
+ Global Rankings
-
- See full driver rankings
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
diff --git a/apps/website/templates/DriversTemplate.tsx b/apps/website/templates/DriversTemplate.tsx
index 2590e584b..2bde53b83 100644
--- a/apps/website/templates/DriversTemplate.tsx
+++ b/apps/website/templates/DriversTemplate.tsx
@@ -1,18 +1,16 @@
'use client';
-import { DriversDirectoryHeader } from '@/components/drivers/DriversDirectoryHeader';
-import { DriverSearchBar } from '@/components/drivers/DriverSearchBar';
-import { DriverTable } from '@/components/drivers/DriverTable';
-import { DriverTableRow } from '@/components/drivers/DriverTableRow';
+import { DriversViewData } from '@/lib/types/view-data/DriversViewData';
+import { DriverCard } from '@/components/drivers/DriverCard';
+import { DriverStatsHeader } from '@/components/drivers/DriverStatsHeader';
+import { PageHeader } from '@/ui/PageHeader';
+import { Input } from '@/ui/Input';
+import { Box } from '@/ui/Box';
+import { Search, Users } from 'lucide-react';
import { EmptyState } from '@/ui/EmptyState';
-import type { DriversViewData } from '@/lib/types/view-data/DriversViewData';
-import { Container } from '@/ui/Container';
-import { Group } from '@/ui/Group';
-import { Search } from 'lucide-react';
-import React from 'react';
interface DriversTemplateProps {
- viewData: DriversViewData | null;
+ viewData: DriversViewData;
searchQuery: string;
onSearchChange: (query: string) => void;
filteredDrivers: DriversViewData['drivers'];
@@ -20,7 +18,7 @@ interface DriversTemplateProps {
onViewLeaderboard: () => void;
}
-export function DriversTemplate({
+export function DriversTemplate({
viewData,
searchQuery,
onSearchChange,
@@ -29,47 +27,59 @@ export function DriversTemplate({
onViewLeaderboard
}: DriversTemplateProps) {
return (
-
-
-
+
+
+ Leaderboard
+
+ }
/>
+
-
+
+
+
-
- {filteredDrivers.map((driver, index) => (
-
+ onSearchChange(e.target.value)}
+ icon={Search}
+ variant="search"
+ />
+
+
+ {filteredDrivers.length > 0 ? (
+
+ {filteredDrivers.map(driver => (
+ onDriverClick(driver.id)}
+ driver={driver}
+ onClick={onDriverClick}
/>
))}
-
-
- {filteredDrivers.length === 0 && (
- onSearchChange(''),
- variant: 'secondary'
- }}
- />
- )}
-
-
+
+ ) : (
+
+ )}
+
);
}
diff --git a/apps/website/ui/ProfileCard.tsx b/apps/website/ui/ProfileCard.tsx
new file mode 100644
index 000000000..a84d0aeff
--- /dev/null
+++ b/apps/website/ui/ProfileCard.tsx
@@ -0,0 +1,38 @@
+import { ReactNode } from 'react';
+import { Card } from './Card';
+import { Box } from './Box';
+
+export interface ProfileCardProps {
+ identity: ReactNode;
+ stats?: ReactNode;
+ actions?: ReactNode;
+ variant?: 'default' | 'muted' | 'outline' | 'glass';
+ onClick?: () => void;
+}
+
+export const ProfileCard = ({ identity, stats, actions, variant = 'default', onClick }: ProfileCardProps) => {
+ return (
+
+
+
+ {identity}
+
+ {actions && (
+
+ {actions}
+
+ )}
+
+ {stats && (
+
+ {stats}
+
+ )}
+
+ );
+};