fix data flow issues
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
import Link from 'next/link';
|
||||
import Card from '@/components/ui/Card';
|
||||
import Button from '@/components/ui/Button';
|
||||
import { siteConfig } from '@/lib/siteConfig';
|
||||
import { AvailableLeaguesViewModel } from '@/lib/view-models/AvailableLeaguesViewModel';
|
||||
import { SponsorService } from '@/lib/services/sponsors/SponsorService';
|
||||
import { ServiceFactory } from '@/lib/services/ServiceFactory';
|
||||
import {
|
||||
Trophy,
|
||||
Users,
|
||||
@@ -38,98 +41,12 @@ interface AvailableLeague {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const MOCK_AVAILABLE_LEAGUES: AvailableLeague[] = [
|
||||
{
|
||||
id: 'league-1',
|
||||
name: 'GT3 Masters Championship',
|
||||
game: 'iRacing',
|
||||
drivers: 48,
|
||||
avgViewsPerRace: 8200,
|
||||
mainSponsorSlot: { available: true, price: 1200 },
|
||||
secondarySlots: { available: 1, total: 2, price: 400 },
|
||||
rating: 4.8,
|
||||
tier: 'premium',
|
||||
nextRace: 'Dec 20 - Spa',
|
||||
seasonStatus: 'active',
|
||||
description: 'Premier GT3 racing with top-tier drivers. Weekly broadcasts and active community.',
|
||||
},
|
||||
{
|
||||
id: 'league-2',
|
||||
name: 'Endurance Pro Series',
|
||||
game: 'ACC',
|
||||
drivers: 72,
|
||||
avgViewsPerRace: 12500,
|
||||
mainSponsorSlot: { available: false, price: 1500 },
|
||||
secondarySlots: { available: 2, total: 2, price: 500 },
|
||||
rating: 4.9,
|
||||
tier: 'premium',
|
||||
nextRace: 'Jan 5 - Nürburgring 24h',
|
||||
seasonStatus: 'active',
|
||||
description: 'Multi-class endurance racing. High engagement from dedicated endurance fans.',
|
||||
},
|
||||
{
|
||||
id: 'league-3',
|
||||
name: 'Formula Sim League',
|
||||
game: 'iRacing',
|
||||
drivers: 24,
|
||||
avgViewsPerRace: 5400,
|
||||
mainSponsorSlot: { available: true, price: 800 },
|
||||
secondarySlots: { available: 2, total: 2, price: 300 },
|
||||
rating: 4.5,
|
||||
tier: 'standard',
|
||||
nextRace: 'Dec 22 - Monza',
|
||||
seasonStatus: 'active',
|
||||
description: 'Open-wheel racing excellence. Competitive field with consistent racing.',
|
||||
},
|
||||
{
|
||||
id: 'league-4',
|
||||
name: 'Touring Car Masters',
|
||||
game: 'rFactor 2',
|
||||
drivers: 32,
|
||||
avgViewsPerRace: 3200,
|
||||
mainSponsorSlot: { available: true, price: 500 },
|
||||
secondarySlots: { available: 2, total: 2, price: 200 },
|
||||
rating: 4.2,
|
||||
tier: 'starter',
|
||||
nextRace: 'Jan 10 - Brands Hatch',
|
||||
seasonStatus: 'upcoming',
|
||||
description: 'Touring car action with close racing. Great for building brand awareness.',
|
||||
},
|
||||
{
|
||||
id: 'league-5',
|
||||
name: 'LMP Challenge',
|
||||
game: 'Le Mans Ultimate',
|
||||
drivers: 36,
|
||||
avgViewsPerRace: 6800,
|
||||
mainSponsorSlot: { available: true, price: 900 },
|
||||
secondarySlots: { available: 1, total: 2, price: 350 },
|
||||
rating: 4.6,
|
||||
tier: 'standard',
|
||||
nextRace: 'Dec 28 - Sebring',
|
||||
seasonStatus: 'active',
|
||||
description: 'Prototype racing at its finest. Growing community with passionate fans.',
|
||||
},
|
||||
{
|
||||
id: 'league-6',
|
||||
name: 'Rally Championship',
|
||||
game: 'EA WRC',
|
||||
drivers: 28,
|
||||
avgViewsPerRace: 4500,
|
||||
mainSponsorSlot: { available: true, price: 650 },
|
||||
secondarySlots: { available: 2, total: 2, price: 250 },
|
||||
rating: 4.4,
|
||||
tier: 'standard',
|
||||
nextRace: 'Jan 15 - Monte Carlo',
|
||||
seasonStatus: 'upcoming',
|
||||
description: 'Thrilling rally stages. Unique sponsorship exposure in rallying content.',
|
||||
},
|
||||
];
|
||||
|
||||
type SortOption = 'rating' | 'drivers' | 'price' | 'views';
|
||||
type TierFilter = 'all' | 'premium' | 'standard' | 'starter';
|
||||
type AvailabilityFilter = 'all' | 'main' | 'secondary';
|
||||
|
||||
function LeagueCard({ league, index }: { league: AvailableLeague; index: number }) {
|
||||
function LeagueCard({ league, index }: { league: any; index: number }) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
const tierConfig = {
|
||||
@@ -159,9 +76,8 @@ function LeagueCard({ league, index }: { league: AvailableLeague; index: number
|
||||
completed: { color: 'text-gray-400', bg: 'bg-gray-400/10', label: 'Season Ended' },
|
||||
};
|
||||
|
||||
const config = tierConfig[league.tier];
|
||||
const status = statusConfig[league.seasonStatus];
|
||||
const cpm = (league.mainSponsorSlot.price / league.avgViewsPerRace * 1000).toFixed(0);
|
||||
const config = league.tierConfig;
|
||||
const status = league.statusConfig;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@@ -201,11 +117,11 @@ function LeagueCard({ league, index }: { league: AvailableLeague; index: number
|
||||
<div className="text-xs text-gray-500">Drivers</div>
|
||||
</div>
|
||||
<div className="text-center p-2 bg-iron-gray/50 rounded-lg">
|
||||
<div className="text-lg font-bold text-white">{(league.avgViewsPerRace / 1000).toFixed(1)}k</div>
|
||||
<div className="text-lg font-bold text-white">{league.formattedAvgViews}</div>
|
||||
<div className="text-xs text-gray-500">Avg Views</div>
|
||||
</div>
|
||||
<div className="text-center p-2 bg-iron-gray/50 rounded-lg">
|
||||
<div className="text-lg font-bold text-performance-green">${cpm}</div>
|
||||
<div className="text-lg font-bold text-performance-green">{league.formattedCpm}</div>
|
||||
<div className="text-xs text-gray-500">CPM</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -282,9 +198,50 @@ export default function SponsorLeaguesPage() {
|
||||
const [tierFilter, setTierFilter] = useState<TierFilter>('all');
|
||||
const [availabilityFilter, setAvailabilityFilter] = useState<AvailabilityFilter>('all');
|
||||
const [sortBy, setSortBy] = useState<SortOption>('rating');
|
||||
const [data, setData] = useState<AvailableLeaguesViewModel | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadLeagues = async () => {
|
||||
try {
|
||||
const sponsorService = ServiceFactory.getSponsorService();
|
||||
const leaguesData = await sponsorService.getAvailableLeagues();
|
||||
setData(new AvailableLeaguesViewModel(leaguesData));
|
||||
} catch (err) {
|
||||
console.error('Error loading leagues:', err);
|
||||
setError('Failed to load leagues data');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadLeagues();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center">
|
||||
<div className="w-8 h-8 border-2 border-primary-blue border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
||||
<p className="text-gray-400">Loading leagues...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center">
|
||||
<p className="text-gray-400">{error || 'No leagues data available'}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Filter and sort leagues
|
||||
const filteredLeagues = MOCK_AVAILABLE_LEAGUES
|
||||
const filteredLeagues = data.leagues
|
||||
.filter(league => {
|
||||
if (searchQuery && !league.name.toLowerCase().includes(searchQuery.toLowerCase())) {
|
||||
return false;
|
||||
@@ -312,13 +269,12 @@ export default function SponsorLeaguesPage() {
|
||||
|
||||
// Calculate summary stats
|
||||
const stats = {
|
||||
total: MOCK_AVAILABLE_LEAGUES.length,
|
||||
mainAvailable: MOCK_AVAILABLE_LEAGUES.filter(l => l.mainSponsorSlot.available).length,
|
||||
secondaryAvailable: MOCK_AVAILABLE_LEAGUES.reduce((sum, l) => sum + l.secondarySlots.available, 0),
|
||||
totalDrivers: MOCK_AVAILABLE_LEAGUES.reduce((sum, l) => sum + l.drivers, 0),
|
||||
total: data.leagues.length,
|
||||
mainAvailable: data.leagues.filter(l => l.mainSponsorSlot.available).length,
|
||||
secondaryAvailable: data.leagues.reduce((sum, l) => sum + l.secondarySlots.available, 0),
|
||||
totalDrivers: data.leagues.reduce((sum, l) => sum + l.drivers, 0),
|
||||
avgCpm: Math.round(
|
||||
MOCK_AVAILABLE_LEAGUES.reduce((sum, l) => sum + (l.mainSponsorSlot.price / l.avgViewsPerRace * 1000), 0) /
|
||||
MOCK_AVAILABLE_LEAGUES.length
|
||||
data.leagues.reduce((sum, l) => sum + l.cpm, 0) / data.leagues.length
|
||||
),
|
||||
};
|
||||
|
||||
@@ -448,7 +404,7 @@ export default function SponsorLeaguesPage() {
|
||||
{/* Results Count */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<p className="text-sm text-gray-400">
|
||||
Showing {filteredLeagues.length} of {MOCK_AVAILABLE_LEAGUES.length} leagues
|
||||
Showing {filteredLeagues.length} of {data.leagues.length} leagues
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link href="/teams">
|
||||
|
||||
Reference in New Issue
Block a user