332 lines
16 KiB
TypeScript
332 lines
16 KiB
TypeScript
import type { DashboardViewData } from '@/lib/view-data/DashboardViewData';
|
|
|
|
interface DashboardTemplateProps {
|
|
viewData: DashboardViewData;
|
|
}
|
|
|
|
export function DashboardTemplate({ viewData }: DashboardTemplateProps) {
|
|
const {
|
|
currentDriver,
|
|
nextRace,
|
|
upcomingRaces,
|
|
leagueStandings,
|
|
feedItems,
|
|
friends,
|
|
activeLeaguesCount,
|
|
friendCount,
|
|
hasUpcomingRaces,
|
|
hasLeagueStandings,
|
|
hasFeedItems,
|
|
hasFriends,
|
|
} = viewData;
|
|
|
|
return (
|
|
<main className="min-h-screen bg-deep-graphite">
|
|
{/* Hero Section */}
|
|
<section className="relative overflow-hidden">
|
|
{/* Background Pattern */}
|
|
<div className="absolute inset-0 bg-gradient-to-br from-primary-blue/10 via-deep-graphite to-purple-600/5" />
|
|
<div className="absolute inset-0 opacity-5">
|
|
<div className="absolute inset-0" style={{
|
|
backgroundImage: `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.4'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`,
|
|
}} />
|
|
</div>
|
|
|
|
<div className="relative max-w-7xl mx-auto px-6 py-10">
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-8">
|
|
{/* Welcome Message */}
|
|
<div className="flex items-start gap-5">
|
|
<div className="relative">
|
|
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-primary-blue to-purple-600 p-0.5 shadow-xl shadow-primary-blue/20">
|
|
<div className="w-full h-full rounded-xl overflow-hidden bg-iron-gray">
|
|
<img
|
|
src={currentDriver.avatarUrl}
|
|
alt={currentDriver.name}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="absolute -bottom-1 -right-1 w-5 h-5 rounded-full bg-performance-green border-3 border-deep-graphite" />
|
|
</div>
|
|
<div>
|
|
<p className="text-gray-400 text-sm mb-1">Good morning,</p>
|
|
<h1 className="text-3xl md:text-4xl font-bold text-white mb-2">
|
|
{currentDriver.name}
|
|
<span className="ml-3 text-2xl">{currentDriver.country}</span>
|
|
</h1>
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<div className="flex items-center gap-1.5 px-3 py-1 rounded-full bg-primary-blue/10 border border-primary-blue/30">
|
|
<span className="text-sm font-semibold text-primary-blue">{currentDriver.rating}</span>
|
|
</div>
|
|
<div className="flex items-center gap-1.5 px-3 py-1 rounded-full bg-yellow-400/10 border border-yellow-400/30">
|
|
<span className="text-sm font-semibold text-yellow-400">#{currentDriver.rank}</span>
|
|
</div>
|
|
<span className="text-xs text-gray-500">{currentDriver.totalRaces} races completed</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Quick Actions */}
|
|
<div className="flex flex-wrap gap-3">
|
|
<a href="/leagues" className="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg text-white text-sm font-medium transition-colors flex items-center gap-2">
|
|
<span>Flag</span>
|
|
Browse Leagues
|
|
</a>
|
|
<a href="/profile" className="px-4 py-2 bg-blue-600 hover:bg-blue-500 rounded-lg text-white text-sm font-medium transition-colors flex items-center gap-2">
|
|
<span>Activity</span>
|
|
View Profile
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Quick Stats Row */}
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mt-8">
|
|
<div className="p-4 rounded-xl bg-iron-gray/50 border border-charcoal-outline backdrop-blur-sm">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-performance-green/20 text-performance-green">
|
|
<span>Trophy</span>
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-white">{currentDriver.wins}</p>
|
|
<p className="text-xs text-gray-500">Wins</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-4 rounded-xl bg-iron-gray/50 border border-charcoal-outline backdrop-blur-sm">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-warning-amber/20 text-warning-amber">
|
|
<span>Medal</span>
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-white">{currentDriver.podiums}</p>
|
|
<p className="text-xs text-gray-500">Podiums</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-4 rounded-xl bg-iron-gray/50 border border-charcoal-outline backdrop-blur-sm">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-blue/20 text-primary-blue">
|
|
<span>Target</span>
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-white">{currentDriver.consistency}</p>
|
|
<p className="text-xs text-gray-500">Consistency</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="p-4 rounded-xl bg-iron-gray/50 border border-charcoal-outline backdrop-blur-sm">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-purple-500/20 text-purple-400">
|
|
<span>Users</span>
|
|
</div>
|
|
<div>
|
|
<p className="text-2xl font-bold text-white">{activeLeaguesCount}</p>
|
|
<p className="text-xs text-gray-500">Active Leagues</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Main Content */}
|
|
<section className="max-w-7xl mx-auto px-6 py-8">
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Left Column - Main Content */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Next Race Card */}
|
|
{nextRace && (
|
|
<div className="relative overflow-hidden bg-gradient-to-br from-iron-gray to-iron-gray/80 border border-primary-blue/30 rounded-xl p-6">
|
|
<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">
|
|
<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">
|
|
<span>Calendar</span>
|
|
{nextRace.formattedDate}
|
|
</span>
|
|
<span className="flex items-center gap-1.5 text-gray-400">
|
|
<span>Clock</span>
|
|
{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>
|
|
<a href={`/races/${nextRace.id}`} className="px-4 py-2 bg-blue-600 hover:bg-blue-500 rounded-lg text-white text-sm font-medium transition-colors flex items-center gap-2">
|
|
View Details
|
|
<span>ChevronRight</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* League Standings Preview */}
|
|
{hasLeagueStandings && (
|
|
<div className="bg-iron-gray/30 border border-charcoal-outline rounded-xl p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-semibold text-white flex items-center gap-2">
|
|
<span>Award</span>
|
|
Your Championship Standings
|
|
</h2>
|
|
<a href="/profile/leagues" className="text-sm text-primary-blue hover:underline flex items-center gap-1">
|
|
View all <span>ChevronRight</span>
|
|
</a>
|
|
</div>
|
|
<div className="space-y-3">
|
|
{leagueStandings.map((summary) => (
|
|
<div key={summary.leagueId} className="flex items-center justify-between p-3 bg-deep-graphite rounded-lg">
|
|
<div>
|
|
<p className="text-white font-medium">{summary.leagueName}</p>
|
|
<p className="text-xs text-gray-500">Position {summary.position} • {summary.points} points</p>
|
|
</div>
|
|
<span className="text-xs text-gray-400">{summary.totalDrivers} drivers</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Activity Feed */}
|
|
<div className="bg-iron-gray/30 border border-charcoal-outline rounded-xl p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-semibold text-white flex items-center gap-2">
|
|
<span>Activity</span>
|
|
Recent Activity
|
|
</h2>
|
|
</div>
|
|
{hasFeedItems ? (
|
|
<div className="space-y-4">
|
|
{feedItems.slice(0, 5).map((item) => (
|
|
<div key={item.id} className="flex items-start gap-3 p-3 bg-deep-graphite rounded-lg">
|
|
<div className="flex-1">
|
|
<p className="text-white font-medium">{item.headline}</p>
|
|
{item.body && <p className="text-sm text-gray-400 mt-1">{item.body}</p>}
|
|
<p className="text-xs text-gray-500 mt-1">{item.formattedTime}</p>
|
|
</div>
|
|
{item.ctaHref && item.ctaLabel && (
|
|
<a href={item.ctaHref} className="text-xs text-primary-blue hover:underline">
|
|
{item.ctaLabel}
|
|
</a>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-8">
|
|
<span className="text-4xl text-gray-600 mx-auto mb-3">Activity</span>
|
|
<p className="text-gray-400 mb-2">No activity yet</p>
|
|
<p className="text-sm text-gray-500">Join leagues and add friends to see activity here</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right Column - Sidebar */}
|
|
<div className="space-y-6">
|
|
{/* Upcoming Races */}
|
|
<div className="bg-iron-gray/30 border border-charcoal-outline rounded-xl p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
|
|
<span>Calendar</span>
|
|
Upcoming Races
|
|
</h3>
|
|
<a href="/races" className="text-xs text-primary-blue hover:underline">
|
|
View all
|
|
</a>
|
|
</div>
|
|
{hasUpcomingRaces ? (
|
|
<div className="space-y-3">
|
|
{upcomingRaces.slice(0, 5).map((race) => (
|
|
<div key={race.id} className="p-3 bg-deep-graphite rounded-lg">
|
|
<p className="text-white font-medium">{race.track}</p>
|
|
<p className="text-sm text-gray-400">{race.car}</p>
|
|
<div className="flex items-center gap-2 mt-1 text-xs text-gray-500">
|
|
<span>{race.formattedDate}</span>
|
|
<span>•</span>
|
|
<span>{race.formattedTime}</span>
|
|
</div>
|
|
{race.isMyLeague && (
|
|
<span className="inline-block mt-1 px-2 py-0.5 rounded-full bg-performance-green/20 text-performance-green text-xs font-medium">
|
|
Your League
|
|
</span>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-gray-500 text-sm text-center py-4">No upcoming races</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Friends */}
|
|
<div className="bg-iron-gray/30 border border-charcoal-outline rounded-xl p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
|
|
<span>Users</span>
|
|
Friends
|
|
</h3>
|
|
<span className="text-xs text-gray-500">{friends.length} friends</span>
|
|
</div>
|
|
{hasFriends ? (
|
|
<div className="space-y-2">
|
|
{friends.slice(0, 6).map((friend) => (
|
|
<div key={friend.id} className="flex items-center gap-3 p-2 rounded-lg hover:bg-deep-graphite transition-colors">
|
|
<div className="w-9 h-9 rounded-full overflow-hidden bg-gradient-to-br from-primary-blue to-purple-600">
|
|
<img
|
|
src={friend.avatarUrl}
|
|
alt={friend.name}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-white text-sm font-medium truncate">{friend.name}</p>
|
|
<p className="text-xs text-gray-500">{friend.country}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{friends.length > 6 && (
|
|
<a
|
|
href="/profile"
|
|
className="block text-center py-2 text-sm text-primary-blue hover:underline"
|
|
>
|
|
+{friends.length - 6} more
|
|
</a>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-6">
|
|
<span className="text-3xl text-gray-600 mx-auto mb-2">UserPlus</span>
|
|
<p className="text-sm text-gray-400 mb-2">No friends yet</p>
|
|
<a href="/drivers" className="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded text-xs text-white transition-colors">
|
|
Find Drivers
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
);
|
|
} |