diff --git a/apps/website/app/dashboard/page.tsx b/apps/website/app/dashboard/page.tsx index cbf8dd365..a8966d478 100644 --- a/apps/website/app/dashboard/page.tsx +++ b/apps/website/app/dashboard/page.tsx @@ -1,4 +1,5 @@ -import { redirect } from 'next/navigation'; +'use client'; + import Image from 'next/image'; import Link from 'next/link'; import { @@ -25,12 +26,127 @@ import { import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; -import type { - DashboardOverviewViewModel, - DashboardFeedItemSummaryViewModel, -} from '@core/racing/application/presenters/IDashboardOverviewPresenter'; -export const dynamic = 'force-dynamic'; +// TODO: Re-enable API integration once backend is ready +// import type { +// DashboardOverviewViewModel, +// DashboardFeedItemSummaryViewModel, +// } from '@core/racing/application/presenters/IDashboardOverviewPresenter'; + +// Mock data for prototype +const MOCK_CURRENT_DRIVER = { + id: 'driver-1', + name: 'Max Verstappen', + avatarUrl: 'https://api.dicebear.com/7.x/avataaars/svg?seed=MaxV', + country: 'NL', + totalRaces: 142, + wins: 28, + podiums: 67, + rating: 2847, + globalRank: 15, + consistency: 94, +}; + +const MOCK_NEXT_RACE = { + id: 'race-1', + track: 'Spa-Francorchamps', + car: 'Porsche 911 GT3 R', + scheduledAt: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now + isMyLeague: true, + leagueName: 'GT3 Masters Series', +}; + +const MOCK_UPCOMING_RACES = [ + { + id: 'race-1', + track: 'Spa-Francorchamps', + car: 'Porsche 911 GT3 R', + scheduledAt: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), + isMyLeague: true, + }, + { + id: 'race-2', + track: 'Nürburgring GP', + car: 'BMW M4 GT3', + scheduledAt: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000), + isMyLeague: true, + }, + { + id: 'race-3', + track: 'Monza', + car: 'Ferrari 296 GT3', + scheduledAt: new Date(Date.now() + 8 * 24 * 60 * 60 * 1000), + isMyLeague: false, + }, + { + id: 'race-4', + track: 'Silverstone', + car: 'Aston Martin Vantage GT3', + scheduledAt: new Date(Date.now() + 12 * 24 * 60 * 60 * 1000), + isMyLeague: true, + }, +]; + +const MOCK_LEAGUE_STANDINGS = [ + { + leagueId: 'league-1', + leagueName: 'GT3 Masters Series', + position: 2, + points: 186, + totalDrivers: 24, + }, + { + leagueId: 'league-2', + leagueName: 'Endurance Pro League', + position: 5, + points: 142, + totalDrivers: 32, + }, + { + leagueId: 'league-3', + leagueName: 'F1 Weekend Warriors', + position: 1, + points: 225, + totalDrivers: 18, + }, +]; + +const MOCK_FEED_ITEMS = [ + { + id: 'feed-1', + type: 'win', + headline: 'You won the race at Spa-Francorchamps!', + body: 'Great driving! You finished P1 with a 3.2s gap to second place.', + timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000), + ctaHref: '/races/race-prev-1', + ctaLabel: 'View Results', + }, + { + id: 'feed-2', + type: 'friend_join', + headline: 'Lewis Hamilton joined GT3 Masters Series', + body: null, + timestamp: new Date(Date.now() - 8 * 60 * 60 * 1000), + ctaHref: '/leagues/league-1', + ctaLabel: 'View League', + }, + { + id: 'feed-3', + type: 'podium', + headline: 'Charles Leclerc finished P2 at Monza', + body: 'Your friend had a great race!', + timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000), + ctaHref: '/drivers/driver-2', + ctaLabel: 'View Profile', + }, +]; + +const MOCK_FRIENDS = [ + { id: 'friend-1', name: 'Lewis Hamilton', avatarUrl: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Lewis', country: 'GB' }, + { id: 'friend-2', name: 'Charles Leclerc', avatarUrl: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Charles', country: 'MC' }, + { id: 'friend-3', name: 'Lando Norris', avatarUrl: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Lando', country: 'GB' }, + { id: 'friend-4', name: 'Oscar Piastri', avatarUrl: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Oscar', country: 'AU' }, +]; // Helper functions function getCountryFlag(countryCode: string): string { @@ -81,58 +197,29 @@ function getGreeting(): string { return 'Good evening'; } -export default async function DashboardPage() { - const authService = getAuthService(); - const session = await authService.getCurrentSession(); +interface FeedItem { + id: string; + type: string; + headline: string; + body: string | null; + timestamp: Date; + ctaHref?: string; + ctaLabel?: string; +} - if (!session) { - redirect('/auth/iracing?returnTo=/dashboard'); - } +export default function DashboardPage() { + // TODO: Re-enable API integration once backend is ready + // Currently using mock data for prototype + + const currentDriver = MOCK_CURRENT_DRIVER; + const nextRace = MOCK_NEXT_RACE; + const upcomingRaces = MOCK_UPCOMING_RACES; + const leagueStandingsSummaries = MOCK_LEAGUE_STANDINGS; + const feedSummary = { items: MOCK_FEED_ITEMS }; + const friends = MOCK_FRIENDS; + const activeLeaguesCount = 3; - const currentDriverId = session.user.primaryDriverId ?? ''; - - const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/races/dashboard/overview?driverId=${currentDriverId}`); - if (!response.ok) { - throw new Error('Failed to fetch dashboard overview'); - } - const viewModel: DashboardOverviewViewModel = await response.json(); - - 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; + const { totalRaces, wins, podiums, rating, globalRank, consistency } = currentDriver; return (
@@ -150,27 +237,25 @@ export default async function DashboardPage() {
{/* Welcome Message */}
- {currentDriver && ( -
-
-
- {currentDriver.name} -
+
+
+
+ {currentDriver.name}
-
- )} +
+

{getGreeting()},

- {currentDriver?.name ?? 'Racer'} - {currentDriver ? getCountryFlag(currentDriver.country) : '🏁'} + {currentDriver.name} + {getCountryFlag(currentDriver.country)}

@@ -370,7 +455,7 @@ export default async function DashboardPage() {

- + Recent Activity

@@ -403,9 +488,9 @@ export default async function DashboardPage() { View all
- {upcomingRacesForDisplay.length > 0 ? ( + {upcomingRaces.length > 0 ? (
- {upcomingRacesForDisplay.slice(0, 5).map((race) => { + {upcomingRaces.slice(0, 5).map((race) => { const isMyRace = race.isMyLeague; return ( { 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' }; diff --git a/apps/website/app/onboarding/page.tsx b/apps/website/app/onboarding/page.tsx index 849544535..a8171bd16 100644 --- a/apps/website/app/onboarding/page.tsx +++ b/apps/website/app/onboarding/page.tsx @@ -1,20 +1,51 @@ -import { redirect } from 'next/navigation'; +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; import OnboardingWizard from '@/components/onboarding/OnboardingWizard'; +import { Loader2 } from 'lucide-react'; -export const dynamic = 'force-dynamic'; +// TODO: Re-enable API integration once backend is ready +// import { redirect } from 'next/navigation'; -export default async function OnboardingPage() { - const authService = getAuthService(); - const session = await authService.getCurrentSession(); +export default function OnboardingPage() { + const router = useRouter(); + const [checking, setChecking] = useState(true); - if (!session) { - redirect('/auth/iracing?returnTo=/onboarding'); - } + useEffect(() => { + // TODO: Re-enable auth check once backend is ready + // For now, just show onboarding after a brief check + const checkDemoMode = () => { + // Check if user has demo mode cookie + const cookies = document.cookie.split(';'); + const demoModeCookie = cookies.find(c => c.trim().startsWith('gridpilot_demo_mode=')); + + if (!demoModeCookie) { + // Not logged in, redirect to auth + router.push('/auth/login?returnTo=/onboarding'); + return; + } + + // For demo, skip onboarding and go to dashboard + // In production, this would check if onboarding is complete + router.push('/dashboard'); + }; - const primaryDriverId = session.user.primaryDriverId ?? ''; + // Brief delay to prevent flash + const timer = setTimeout(() => { + checkDemoMode(); + }, 500); - if (primaryDriverId) { - redirect('/dashboard'); + return () => clearTimeout(timer); + }, [router]); + + // Show loading while checking + if (checking) { + return ( +
+ +
+ ); } return (