This commit is contained in:
2025-12-10 18:28:32 +01:00
parent 6d61be9c51
commit 1303a14493
108 changed files with 3366 additions and 1559 deletions

View File

@@ -32,57 +32,73 @@ interface Sponsorship {
drivers: number;
}
// Mock data - in production would come from repository
const MOCK_SPONSORSHIPS: Sponsorship[] = [
{
id: 'sp-1',
leagueId: 'league-1',
leagueName: 'GT3 Pro Championship',
tier: 'main',
status: 'active',
startDate: new Date('2025-01-01'),
endDate: new Date('2025-06-30'),
price: 1200,
impressions: 45200,
drivers: 32,
},
{
id: 'sp-2',
leagueId: 'league-2',
leagueName: 'Endurance Masters',
tier: 'main',
status: 'active',
startDate: new Date('2025-02-01'),
endDate: new Date('2025-07-31'),
price: 1000,
impressions: 38100,
drivers: 48,
},
{
id: 'sp-3',
leagueId: 'league-3',
leagueName: 'Formula Sim Series',
tier: 'secondary',
status: 'active',
startDate: new Date('2025-03-01'),
endDate: new Date('2025-08-31'),
price: 400,
impressions: 22800,
drivers: 24,
},
{
id: 'sp-4',
leagueId: 'league-4',
leagueName: 'Touring Car Cup',
tier: 'secondary',
status: 'pending',
startDate: new Date('2025-04-01'),
endDate: new Date('2025-09-30'),
price: 350,
impressions: 0,
drivers: 28,
},
];
interface SponsorshipDetailApi {
id: string;
leagueId: string;
leagueName: string;
seasonId: string;
seasonName: string;
seasonStartDate?: string;
seasonEndDate?: string;
tier: 'main' | 'secondary';
status: string;
pricing: {
amount: number;
currency: string;
};
metrics: {
drivers: number;
races: number;
completedRaces: number;
impressions: number;
};
createdAt: string;
activatedAt?: string;
}
interface SponsorSponsorshipsResponse {
sponsorId: string;
sponsorName: string;
sponsorships: SponsorshipDetailApi[];
summary: {
totalSponsorships: number;
activeSponsorships: number;
totalInvestment: number;
totalPlatformFees: number;
currency: string;
};
}
function mapSponsorshipStatus(status: string): 'active' | 'pending' | 'expired' {
switch (status) {
case 'active':
return 'active';
case 'pending':
return 'pending';
default:
return 'expired';
}
}
function mapApiToSponsorships(response: SponsorSponsorshipsResponse): Sponsorship[] {
return response.sponsorships.map((s) => {
const start = s.seasonStartDate ? new Date(s.seasonStartDate) : new Date(s.createdAt);
const end = s.seasonEndDate ? new Date(s.seasonEndDate) : start;
return {
id: s.id,
leagueId: s.leagueId,
leagueName: s.leagueName,
tier: s.tier,
status: mapSponsorshipStatus(s.status),
startDate: start,
endDate: end,
price: s.pricing.amount,
impressions: s.metrics.impressions,
drivers: s.metrics.drivers,
};
});
}
function SponsorshipCard({ sponsorship }: { sponsorship: Sponsorship }) {
const router = useRouter();
@@ -179,18 +195,59 @@ function SponsorshipCard({ sponsorship }: { sponsorship: Sponsorship }) {
export default function SponsorCampaignsPage() {
const router = useRouter();
const [filter, setFilter] = useState<'all' | 'active' | 'pending' | 'expired'>('all');
const filteredSponsorships = filter === 'all'
? MOCK_SPONSORSHIPS
: MOCK_SPONSORSHIPS.filter(s => s.status === filter);
const [sponsorships, setSponsorships] = useState<Sponsorship[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
let isMounted = true;
async function fetchSponsorships() {
try {
const response = await fetch('/api/sponsors/sponsorships');
if (!response.ok) {
if (!isMounted) return;
setSponsorships([]);
return;
}
const json: SponsorSponsorshipsResponse = await response.json();
if (!isMounted) return;
setSponsorships(mapApiToSponsorships(json));
} catch {
if (!isMounted) return;
setSponsorships([]);
} finally {
if (isMounted) {
setLoading(false);
}
}
}
fetchSponsorships();
return () => {
isMounted = false;
};
}, []);
const filteredSponsorships = filter === 'all'
? sponsorships
: sponsorships.filter(s => s.status === filter);
const stats = {
total: MOCK_SPONSORSHIPS.length,
active: MOCK_SPONSORSHIPS.filter(s => s.status === 'active').length,
pending: MOCK_SPONSORSHIPS.filter(s => s.status === 'pending').length,
totalInvestment: MOCK_SPONSORSHIPS.reduce((sum, s) => sum + s.price, 0),
total: sponsorships.length,
active: sponsorships.filter(s => s.status === 'active').length,
pending: sponsorships.filter(s => s.status === 'pending').length,
totalInvestment: sponsorships.reduce((sum, s) => sum + s.price, 0),
};
if (loading) {
return (
<div className="max-w-6xl mx-auto py-8 px-4">
<p className="text-gray-400">Loading sponsorships</p>
</div>
);
}
return (
<div className="max-w-6xl mx-auto py-8 px-4">
{/* Header */}

View File

@@ -52,64 +52,6 @@ interface SponsorDashboardData {
};
}
// Fallback mock data for demo mode
const MOCK_DASHBOARD: SponsorDashboardData = {
sponsorId: 'demo-sponsor',
sponsorName: 'Demo Sponsor',
metrics: {
impressions: 124500,
impressionsChange: 12.5,
uniqueViewers: 8420,
viewersChange: 8.3,
races: 24,
drivers: 156,
exposure: 87.5,
exposureChange: 5.2,
},
sponsoredLeagues: [
{
id: 'league-1',
name: 'GT3 Pro Championship',
tier: 'main',
drivers: 32,
races: 12,
impressions: 45200,
status: 'active',
},
{
id: 'league-2',
name: 'Endurance Masters',
tier: 'main',
drivers: 48,
races: 6,
impressions: 38100,
status: 'active',
},
{
id: 'league-3',
name: 'Formula Sim Series',
tier: 'secondary',
drivers: 24,
races: 8,
impressions: 22800,
status: 'active',
},
{
id: 'league-4',
name: 'Touring Car Cup',
tier: 'secondary',
drivers: 28,
races: 10,
impressions: 18400,
status: 'upcoming',
},
],
investment: {
activeSponsorships: 4,
totalInvestment: 2400,
costPerThousandViews: 19.28,
},
};
function MetricCard({
title,
@@ -205,15 +147,13 @@ export default function SponsorDashboardPage() {
try {
const response = await fetch('/api/sponsors/dashboard');
if (response.ok) {
const dashboardData = await response.json();
const dashboardData: SponsorDashboardData = await response.json();
setData(dashboardData);
} else {
// Use mock data for demo mode
setData(MOCK_DASHBOARD);
setError('Failed to load sponsor dashboard');
}
} catch {
// Use mock data on error
setData(MOCK_DASHBOARD);
setError('Failed to load sponsor dashboard');
} finally {
setLoading(false);
}
@@ -230,7 +170,19 @@ export default function SponsorDashboardPage() {
);
}
const dashboardData = data || MOCK_DASHBOARD;
if (!data) {
return (
<div className="max-w-7xl mx-auto py-8 px-4">
<div className="rounded-lg bg-warning-amber/10 border border-warning-amber/30 p-4">
<p className="text-sm text-warning-amber">
{error ?? 'No sponsor dashboard data available yet.'}
</p>
</div>
</div>
);
}
const dashboardData = data;
return (
<div className="max-w-7xl mx-auto py-8 px-4">