refactor page to use services

This commit is contained in:
2025-12-18 15:58:09 +01:00
parent f54fa5de5b
commit fc386db06a
45 changed files with 2254 additions and 1292 deletions

View File

@@ -1,5 +1,6 @@
'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import {
@@ -9,144 +10,24 @@ import {
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';
// TODO: Re-enable API integration once backend is ready
// import type {
// DashboardOverviewViewModel,
// DashboardFeedItemSummaryViewModel,
// } from '@core/racing/application/presenters/IDashboardOverviewPresenter';
// Dashboard service imports
import { ServiceFactory } from '@/lib/services/ServiceFactory';
import { DashboardOverviewViewModel } from '@/lib/view-models/DashboardOverviewViewModel';
// 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 {
@@ -197,29 +78,56 @@ function getGreeting(): string {
return 'Good evening';
}
interface FeedItem {
id: string;
type: string;
headline: string;
body: string | null;
timestamp: Date;
ctaHref?: string;
ctaLabel?: string;
}
import { DashboardFeedItemSummaryViewModel } from '@/lib/view-models/DashboardOverviewViewModel';
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 [dashboardData, setDashboardData] = useState<DashboardOverviewViewModel | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const { totalRaces, wins, podiums, rating, globalRank, consistency } = currentDriver;
useEffect(() => {
const fetchDashboardData = async () => {
try {
const serviceFactory = new ServiceFactory(process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001');
const dashboardService = serviceFactory.createDashboardService();
const data = await dashboardService.getDashboardOverview();
setDashboardData(data);
} catch (err) {
console.error('Failed to fetch dashboard data:', err);
setError('Failed to load dashboard data');
} finally {
setIsLoading(false);
}
};
fetchDashboardData();
}, []);
if (isLoading) {
return (
<main className="min-h-screen bg-deep-graphite flex items-center justify-center">
<div className="text-white">Loading dashboard...</div>
</main>
);
}
if (error || !dashboardData) {
return (
<main className="min-h-screen bg-deep-graphite flex items-center justify-center">
<div className="text-red-400">{error || 'Failed to load dashboard'}</div>
</main>
);
}
const currentDriver = dashboardData.currentDriver;
const nextRace = dashboardData.nextRace;
const upcomingRaces = dashboardData.upcomingRaces;
const leagueStandingsSummaries = dashboardData.leagueStandings;
const feedSummary = { items: dashboardData.feedItems };
const friends = dashboardData.friends;
const activeLeaguesCount = dashboardData.activeLeaguesCount;
const { totalRaces, wins, podiums, rating, globalRank, consistency } = currentDriver;
return (
<main className="min-h-screen bg-deep-graphite">
@@ -581,7 +489,7 @@ export default function DashboardPage() {
}
// Feed Item Row Component
function FeedItemRow({ item }: { item: FeedItem }) {
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' };