import { redirect } from 'next/navigation';
import Image from 'next/image';
import Link from 'next/link';
import {
Calendar,
Trophy,
Users,
Star,
Clock,
Flag,
TrendingUp,
ChevronRight,
Zap,
Target,
Award,
Activity,
Play,
Bell,
Medal,
Crown,
Heart,
MessageCircle,
UserPlus,
} from 'lucide-react';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
import { getAuthService } from '@/lib/auth';
import { getGetDashboardOverviewUseCase } from '@/lib/di-container';
import { DashboardOverviewPresenter } from '@/lib/presenters/DashboardOverviewPresenter';
import type {
DashboardOverviewViewModel,
DashboardFeedItemSummaryViewModel,
} from '@gridpilot/racing/application/presenters/IDashboardOverviewPresenter';
export const dynamic = 'force-dynamic';
// Helper functions
function getCountryFlag(countryCode: string): string {
const code = countryCode.toUpperCase();
if (code.length === 2) {
const codePoints = [...code].map(char => 127397 + char.charCodeAt(0));
return String.fromCodePoint(...codePoints);
}
return '🏁';
}
function timeUntil(date: Date): string {
const now = new Date();
const diffMs = date.getTime() - now.getTime();
if (diffMs < 0) return 'Started';
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffDays = Math.floor(diffHours / 24);
if (diffDays > 0) {
return `${diffDays}d ${diffHours % 24}h`;
}
if (diffHours > 0) {
const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
return `${diffHours}h ${diffMinutes}m`;
}
const diffMinutes = Math.floor(diffMs / (1000 * 60));
return `${diffMinutes}m`;
}
function timeAgo(timestamp: Date | string): string {
const time = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
const diffMs = Date.now() - time.getTime();
const diffMinutes = Math.floor(diffMs / 60000);
if (diffMinutes < 1) return 'Just now';
if (diffMinutes < 60) return `${diffMinutes}m ago`;
const diffHours = Math.floor(diffMinutes / 60);
if (diffHours < 24) return `${diffHours}h ago`;
const diffDays = Math.floor(diffHours / 24);
return `${diffDays}d ago`;
}
function getGreeting(): string {
const hour = new Date().getHours();
if (hour < 12) return 'Good morning';
if (hour < 18) return 'Good afternoon';
return 'Good evening';
}
export default async function DashboardPage() {
const authService = getAuthService();
const session = await authService.getCurrentSession();
if (!session) {
redirect('/auth/iracing?returnTo=/dashboard');
}
const currentDriverId = session.user.primaryDriverId ?? '';
const useCase = getGetDashboardOverviewUseCase();
const presenter = new DashboardOverviewPresenter();
await useCase.execute({ driverId: currentDriverId }, presenter);
const viewModel = presenter.getViewModel();
if (!viewModel) {
return null;
}
const {
currentDriver,
myUpcomingRaces,
otherUpcomingRaces,
nextRace: nextRaceSummary,
recentResults,
leagueStandingsSummaries,
feedSummary,
friends,
upcomingRaces,
activeLeaguesCount,
} = viewModel;
const nextRace =
nextRaceSummary != null
? {
...nextRaceSummary,
scheduledAt: new Date(nextRaceSummary.scheduledAt),
}
: null;
const upcomingRacesForDisplay = upcomingRaces.map(race => ({
...race,
scheduledAt: new Date(race.scheduledAt),
}));
const totalRaces = currentDriver?.totalRaces ?? 0;
const wins = currentDriver?.wins ?? 0;
const podiums = currentDriver?.podiums ?? 0;
const rating = currentDriver?.rating ?? 1500;
const globalRank = currentDriver?.globalRank ?? 0;
const consistency = currentDriver?.consistency ?? 0;
return (
{/* Hero Section */}
{/* Background Pattern */}
{/* Welcome Message */}
{currentDriver && (
)}
{getGreeting()},
{currentDriver?.name ?? 'Racer'}
{currentDriver ? getCountryFlag(currentDriver.country) : '🏁'}
{rating}
#{globalRank}
{totalRaces} races completed
{/* Quick Actions */}
{/* Quick Stats Row */}
{consistency}%
Consistency
{activeLeaguesCount}
Active Leagues
{/* Main Content */}
{/* Left Column - Main Content */}
{/* Next Race Card */}
{nextRace && (
{nextRace.isMyLeague && (
Your League
)}
{nextRace.track}
{nextRace.car}
{nextRace.scheduledAt.toLocaleDateString('en-US', {
weekday: 'long',
month: 'short',
day: 'numeric',
})}
{nextRace.scheduledAt.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
})}
Starts in
{timeUntil(nextRace.scheduledAt)}
)}
{/* League Standings Preview */}
{leagueStandingsSummaries.length > 0 && (
Your Championship Standings
View all
{leagueStandingsSummaries.map(({ leagueId, leagueName, position, points, totalDrivers }) => (
{position > 0 ? `P${position}` : '-'}
{leagueName}
{points} points • {totalDrivers} drivers
{position <= 3 && position > 0 && (
)}
))}
)}
{/* Activity Feed */}
{feedSummary.items.length > 0 ? (
{feedSummary.items.slice(0, 5).map((item) => (
))}
) : (
No activity yet
Join leagues and add friends to see activity here
)}
{/* Right Column - Sidebar */}
{/* Upcoming Races */}
Upcoming Races
View all
{upcomingRacesForDisplay.length > 0 ? (
{upcomingRacesForDisplay.slice(0, 5).map((race) => {
const isMyRace = race.isMyLeague;
return (
{race.track}
{isMyRace && (
)}
{race.car}
{race.scheduledAt.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
{timeUntil(race.scheduledAt)}
);
})}
) : (
No upcoming races
)}
{/* Friends */}
Friends
{friends.length} friends
{friends.length > 0 ? (
{friends.slice(0, 6).map((friend) => (
{friend.name}
{getCountryFlag(friend.country)}
))}
{friends.length > 6 && (
+{friends.length - 6} more
)}
) : (
No friends yet
)}
);
}
// Feed Item Row Component
function FeedItemRow({ item }: { item: DashboardFeedItemSummaryViewModel }) {
const getActivityIcon = (type: string) => {
if (type.includes('win')) return { icon: Trophy, color: 'text-yellow-400 bg-yellow-400/10' };
if (type.includes('podium')) return { icon: Medal, color: 'text-warning-amber bg-warning-amber/10' };
if (type.includes('join')) return { icon: UserPlus, color: 'text-performance-green bg-performance-green/10' };
if (type.includes('friend')) return { icon: Heart, color: 'text-pink-400 bg-pink-400/10' };
if (type.includes('league')) return { icon: Flag, color: 'text-primary-blue bg-primary-blue/10' };
if (type.includes('race')) return { icon: Play, color: 'text-red-400 bg-red-400/10' };
return { icon: Activity, color: 'text-gray-400 bg-gray-400/10' };
};
const { icon: Icon, color } = getActivityIcon(item.type);
return (
{item.headline}
{item.body && (
{item.body}
)}
{timeAgo(item.timestamp)}
{item.ctaHref && (
)}
);
}