This commit is contained in:
2026-01-11 13:04:33 +01:00
parent 6f2ab9fc56
commit 971aa7288b
44 changed files with 2168 additions and 1240 deletions

View File

@@ -27,12 +27,73 @@ import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';
import { getCountryFlag } from '@/lib/utilities/country';
import { getGreeting, timeUntil } from '@/lib/utilities/time';
import { getGreeting } from '@/lib/utilities/time';
import { DashboardOverviewViewModel } from '@/lib/view-models/DashboardOverviewViewModel';
interface DashboardViewData {
currentDriver: {
name: string;
avatarUrl: string;
country: string;
rating: string;
rank: string;
totalRaces: string;
wins: string;
podiums: string;
consistency: string;
};
nextRace: {
id: string;
track: string;
car: string;
scheduledAt: string;
formattedDate: string;
formattedTime: string;
timeUntil: string;
isMyLeague: boolean;
} | null;
upcomingRaces: Array<{
id: string;
track: string;
car: string;
scheduledAt: string;
formattedDate: string;
formattedTime: string;
timeUntil: string;
isMyLeague: boolean;
}>;
leagueStandings: Array<{
leagueId: string;
leagueName: string;
position: string;
points: string;
totalDrivers: string;
}>;
feedItems: Array<{
id: string;
type: string;
headline: string;
body?: string;
timestamp: string;
formattedTime: string;
ctaHref?: string;
ctaLabel?: string;
}>;
friends: Array<{
id: string;
name: string;
avatarUrl: string;
country: string;
}>;
activeLeaguesCount: string;
friendCount: string;
hasUpcomingRaces: boolean;
hasLeagueStandings: boolean;
hasFeedItems: boolean;
hasFriends: boolean;
}
interface DashboardTemplateProps {
data: DashboardOverviewViewModel;
data: DashboardViewData;
}
export function DashboardTemplate({ data }: DashboardTemplateProps) {
@@ -44,7 +105,7 @@ export function DashboardTemplate({ data }: DashboardTemplateProps) {
const friends = data.friends;
const activeLeaguesCount = data.activeLeaguesCount;
const { totalRaces, wins, podiums, rating, globalRank, consistency } = currentDriver;
const { totalRaces, wins, podiums, rating, rank, consistency } = currentDriver;
return (
<main className="min-h-screen bg-deep-graphite">
@@ -89,7 +150,7 @@ export function DashboardTemplate({ data }: DashboardTemplateProps) {
</div>
<div className="flex items-center gap-1.5 px-3 py-1 rounded-full bg-yellow-400/10 border border-yellow-400/30">
<Trophy className="w-3.5 h-3.5 text-yellow-400" />
<span className="text-sm font-semibold text-yellow-400">#{globalRank}</span>
<span className="text-sm font-semibold text-yellow-400">#{rank}</span>
</div>
<span className="text-xs text-gray-500">{totalRaces} races completed</span>
</div>
@@ -129,61 +190,54 @@ export function DashboardTemplate({ data }: DashboardTemplateProps) {
{/* Left Column - Main Content */}
<div className="lg:col-span-2 space-y-6">
{/* Next Race Card */}
{nextRace && (
<Card className="relative overflow-hidden bg-gradient-to-br from-iron-gray to-iron-gray/80 border-primary-blue/30">
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-bl from-primary-blue/20 to-transparent rounded-bl-full" />
<div className="relative">
<div className="flex items-center gap-2 mb-4">
<div className="flex items-center gap-2 px-3 py-1 rounded-full bg-primary-blue/20 border border-primary-blue/30">
<Play className="w-3.5 h-3.5 text-primary-blue" />
<span className="text-xs font-semibold text-primary-blue uppercase tracking-wider">Next Race</span>
</div>
{nextRace.isMyLeague && (
<span className="px-2 py-0.5 rounded-full bg-performance-green/20 text-performance-green text-xs font-medium">
Your League
</span>
)}
</div>
<div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4">
<div>
<h2 className="text-2xl font-bold text-white mb-2">{nextRace.track}</h2>
<p className="text-gray-400 mb-3">{nextRace.car}</p>
<div className="flex flex-wrap items-center gap-4 text-sm">
<span className="flex items-center gap-1.5 text-gray-400">
<Calendar className="w-4 h-4" />
{nextRace.scheduledAt.toLocaleDateString('en-US', {
weekday: 'long',
month: 'short',
day: 'numeric',
})}
</span>
<span className="flex items-center gap-1.5 text-gray-400">
<Clock className="w-4 h-4" />
{nextRace.scheduledAt.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
})}
</span>
</div>
</div>
<div className="flex flex-col items-end gap-3">
<div className="text-right">
<p className="text-xs text-gray-500 uppercase tracking-wider mb-1">Starts in</p>
<p className="text-3xl font-bold text-primary-blue font-mono">{timeUntil(nextRace.scheduledAt)}</p>
</div>
<Link href={`/races/${nextRace.id}`}>
<Button variant="primary" className="flex items-center gap-2">
View Details
<ChevronRight className="w-4 h-4" />
</Button>
</Link>
</div>
</div>
</div>
</Card>
)}
{nextRace && (
<Card className="relative overflow-hidden bg-gradient-to-br from-iron-gray to-iron-gray/80 border-primary-blue/30">
<div className="absolute top-0 right-0 w-40 h-40 bg-gradient-to-bl from-primary-blue/20 to-transparent rounded-bl-full" />
<div className="relative">
<div className="flex items-center gap-2 mb-4">
<div className="flex items-center gap-2 px-3 py-1 rounded-full bg-primary-blue/20 border border-primary-blue/30">
<Play className="w-3.5 h-3.5 text-primary-blue" />
<span className="text-xs font-semibold text-primary-blue uppercase tracking-wider">Next Race</span>
</div>
{nextRace.isMyLeague && (
<span className="px-2 py-0.5 rounded-full bg-performance-green/20 text-performance-green text-xs font-medium">
Your League
</span>
)}
</div>
<div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4">
<div>
<h2 className="text-2xl font-bold text-white mb-2">{nextRace.track}</h2>
<p className="text-gray-400 mb-3">{nextRace.car}</p>
<div className="flex flex-wrap items-center gap-4 text-sm">
<span className="flex items-center gap-1.5 text-gray-400">
<Calendar className="w-4 h-4" />
{nextRace.formattedDate}
</span>
<span className="flex items-center gap-1.5 text-gray-400">
<Clock className="w-4 h-4" />
{nextRace.formattedTime}
</span>
</div>
</div>
<div className="flex flex-col items-end gap-3">
<div className="text-right">
<p className="text-xs text-gray-500 uppercase tracking-wider mb-1">Starts in</p>
<p className="text-3xl font-bold text-primary-blue font-mono">{nextRace.timeUntil}</p>
</div>
<Link href={`/races/${nextRace.id}`}>
<Button variant="primary" className="flex items-center gap-2">
View Details
<ChevronRight className="w-4 h-4" />
</Button>
</Link>
</div>
</div>
</div>
</Card>
)}
{/* League Standings Preview */}
{leagueStandingsSummaries.length > 0 && (