di usage in website

This commit is contained in:
2026-01-06 19:36:03 +01:00
parent 589b55a87e
commit e589c30bf8
191 changed files with 6367 additions and 4253 deletions

View File

@@ -2,7 +2,6 @@
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { motion, useReducedMotion, AnimatePresence } from 'framer-motion';
import Card from '@/components/ui/Card';
@@ -38,69 +37,46 @@ import {
RefreshCw
} from 'lucide-react';
import Link from 'next/link';
import { useServices } from '@/lib/services/ServiceProvider';
import { SponsorDashboardViewModel } from '@/lib/view-models/SponsorDashboardViewModel';
import { useInject } from '@/lib/di/hooks/useInject';
import { SPONSOR_SERVICE_TOKEN, POLICY_SERVICE_TOKEN } from '@/lib/di/tokens';
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
export default function SponsorDashboardPage() {
const shouldReduceMotion = useReducedMotion();
const { sponsorService, policyService } = useServices();
const [timeRange, setTimeRange] = useState<'7d' | '30d' | '90d' | 'all'>('30d');
const [loading, setLoading] = useState(true);
const [data, setData] = useState<SponsorDashboardViewModel | null>(null);
const [error, setError] = useState<string | null>(null);
const sponsorService = useInject(SPONSOR_SERVICE_TOKEN);
const policyService = useInject(POLICY_SERVICE_TOKEN);
const {
data: policySnapshot,
isLoading: policyLoading,
isError: policyError,
} = useQuery({
const policyQuery = useQuery({
queryKey: ['policySnapshot'],
queryFn: () => policyService.getSnapshot(),
staleTime: 60_000,
gcTime: 5 * 60_000,
});
const enhancedPolicyQuery = enhanceQueryResult(policyQuery);
const policySnapshot = enhancedPolicyQuery.data;
const policyLoading = enhancedPolicyQuery.isLoading;
const policyError = enhancedPolicyQuery.error;
const sponsorPortalState = policySnapshot
? policyService.getCapabilityState(policySnapshot, 'sponsors.portal')
: null;
useEffect(() => {
if (policyLoading) {
return;
}
const dashboardQuery = useQuery({
queryKey: ['sponsorDashboard', 'demo-sponsor-1', sponsorPortalState],
queryFn: () => sponsorService.getSponsorDashboard('demo-sponsor-1'),
enabled: !!policySnapshot && sponsorPortalState === 'enabled',
staleTime: 300_000,
gcTime: 10 * 60_000,
});
if (policyError || sponsorPortalState !== 'enabled') {
setError(
sponsorPortalState === 'coming_soon'
? 'Sponsor portal is coming soon.'
: 'Sponsor portal is currently unavailable.',
);
setLoading(false);
return;
}
const enhancedDashboardQuery = enhanceQueryResult(dashboardQuery);
const dashboardData = enhancedDashboardQuery.data;
const dashboardLoading = enhancedDashboardQuery.isLoading;
const dashboardError = enhancedDashboardQuery.error;
const loadDashboard = async () => {
try {
const dashboardData = await sponsorService.getSponsorDashboard('demo-sponsor-1');
if (dashboardData) {
setData(dashboardData);
} else {
setError('Failed to load dashboard data');
}
} catch (err) {
console.error('Error loading dashboard:', err);
setError('Failed to load dashboard data');
} finally {
setLoading(false);
}
};
void loadDashboard();
}, [policyLoading, policyError, sponsorPortalState, sponsorService]);
const loading = policyLoading || dashboardLoading;
const error = policyError || dashboardError || (sponsorPortalState !== 'enabled' && sponsorPortalState !== null);
if (loading) {
return (
@@ -113,36 +89,31 @@ export default function SponsorDashboardPage() {
);
}
if (error || !data) {
if (error || !dashboardData) {
const errorMessage = sponsorPortalState === 'coming_soon'
? 'Sponsor portal is coming soon.'
: sponsorPortalState === 'disabled'
? 'Sponsor portal is currently unavailable.'
: 'Failed to load dashboard data';
return (
<div className="max-w-7xl mx-auto py-8 px-4 flex items-center justify-center min-h-[600px]">
<div className="text-center">
<p className="text-gray-400">{error || 'No dashboard data available'}</p>
<p className="text-gray-400">{errorMessage}</p>
</div>
</div>
);
}
const categoryData = data.categoryData;
if (loading) {
return (
<div className="max-w-7xl mx-auto py-8 px-4 flex items-center justify-center min-h-[600px]">
<div className="text-center">
<Loader2 className="w-8 h-8 animate-spin text-primary-blue mx-auto mb-4" />
<p className="text-gray-400">Loading dashboard...</p>
</div>
</div>
);
}
const categoryData = dashboardData.categoryData;
return (
<div className="max-w-7xl mx-auto py-8 px-4">
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-8">
<div>
<h1 className="text-2xl font-bold text-white">Sponsor Dashboard</h1>
<p className="text-gray-400">Welcome back, {data.sponsorName}</p>
<h2 className="text-2xl font-bold text-white">Sponsor Dashboard</h2>
<p className="text-gray-400">Welcome back, {dashboardData.sponsorName}</p>
</div>
<div className="flex items-center gap-3">
{/* Time Range Selector */}
@@ -150,9 +121,9 @@ export default function SponsorDashboardPage() {
{(['7d', '30d', '90d', 'all'] as const).map((range) => (
<button
key={range}
onClick={() => setTimeRange(range)}
onClick={() => {}}
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
timeRange === range
false
? 'bg-primary-blue text-white'
: 'text-gray-400 hover:text-white'
}`}
@@ -178,29 +149,29 @@ export default function SponsorDashboardPage() {
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<MetricCard
title="Total Impressions"
value={data.totalImpressions}
change={data.metrics.impressionsChange}
value={dashboardData.totalImpressions}
change={dashboardData.metrics.impressionsChange}
icon={Eye}
delay={0}
/>
<MetricCard
title="Unique Viewers"
value={data.metrics.uniqueViewers}
change={data.metrics.viewersChange}
value={dashboardData.metrics.uniqueViewers}
change={dashboardData.metrics.viewersChange}
icon={Users}
delay={0.1}
/>
<MetricCard
title="Engagement Rate"
value={data.metrics.exposure}
change={data.metrics.exposureChange}
value={dashboardData.metrics.exposure}
change={dashboardData.metrics.exposureChange}
icon={TrendingUp}
suffix="%"
delay={0.2}
/>
<MetricCard
title="Total Investment"
value={data.totalInvestment}
value={dashboardData.totalInvestment}
icon={DollarSign}
prefix="$"
delay={0.3}
@@ -210,7 +181,7 @@ export default function SponsorDashboardPage() {
{/* Sponsorship Categories */}
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-white">Your Sponsorships</h2>
<h3 className="text-lg font-semibold text-white">Your Sponsorships</h3>
<Link href="/sponsor/campaigns">
<Button variant="secondary" className="text-sm">
View All
@@ -270,7 +241,7 @@ export default function SponsorDashboardPage() {
{/* Top Performing Sponsorships */}
<Card>
<div className="flex items-center justify-between p-4 border-b border-charcoal-outline">
<h2 className="text-lg font-semibold text-white">Top Performing</h2>
<h3 className="text-lg font-semibold text-white">Top Performing</h3>
<Link href="/leagues">
<Button variant="secondary" className="text-sm">
<Plus className="w-4 h-4 mr-1" />
@@ -280,7 +251,7 @@ export default function SponsorDashboardPage() {
</div>
<div className="divide-y divide-charcoal-outline/50">
{/* Leagues */}
{data.sponsorships.leagues.map((league) => (
{dashboardData.sponsorships.leagues.map((league: any) => (
<div key={league.id} className="flex items-center justify-between p-4 hover:bg-iron-gray/30 transition-colors">
<div className="flex items-center gap-4">
<div className={`px-2 py-1 rounded text-xs font-medium ${
@@ -313,7 +284,7 @@ export default function SponsorDashboardPage() {
))}
{/* Teams */}
{data.sponsorships.teams.map((team) => (
{dashboardData.sponsorships.teams.map((team: any) => (
<div key={team.id} className="flex items-center justify-between p-4 hover:bg-iron-gray/30 transition-colors">
<div className="flex items-center gap-4">
<div className="px-2 py-1 rounded text-xs font-medium bg-purple-500/20 text-purple-400 border border-purple-500/30">
@@ -340,7 +311,7 @@ export default function SponsorDashboardPage() {
))}
{/* Drivers */}
{data.sponsorships.drivers.slice(0, 2).map((driver) => (
{dashboardData.sponsorships.drivers.slice(0, 2).map((driver: any) => (
<div key={driver.id} className="flex items-center justify-between p-4 hover:bg-iron-gray/30 transition-colors">
<div className="flex items-center gap-4">
<div className="px-2 py-1 rounded text-xs font-medium bg-performance-green/20 text-performance-green border border-performance-green/30">
@@ -371,15 +342,15 @@ export default function SponsorDashboardPage() {
{/* Upcoming Events */}
<Card>
<div className="p-4 border-b border-charcoal-outline">
<h2 className="text-lg font-semibold text-white flex items-center gap-2">
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
<Calendar className="w-5 h-5 text-warning-amber" />
Upcoming Sponsored Events
</h2>
</h3>
</div>
<div className="p-4">
{data.sponsorships.races.length > 0 ? (
{dashboardData.sponsorships.races.length > 0 ? (
<div className="space-y-3">
{data.sponsorships.races.map((race) => (
{dashboardData.sponsorships.races.map((race: any) => (
<div key={race.id} className="flex items-center justify-between p-3 rounded-lg bg-iron-gray/30">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-warning-amber/10 flex items-center justify-center">
@@ -448,14 +419,14 @@ export default function SponsorDashboardPage() {
</Card>
{/* Renewal Alerts */}
{data.upcomingRenewals.length > 0 && (
{dashboardData.upcomingRenewals.length > 0 && (
<Card className="p-4">
<h3 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
<Bell className="w-5 h-5 text-warning-amber" />
Upcoming Renewals
</h3>
<div className="space-y-3">
{data.upcomingRenewals.map((renewal) => (
{dashboardData.upcomingRenewals.map((renewal: any) => (
<RenewalAlert key={renewal.id} renewal={renewal} />
))}
</div>
@@ -466,7 +437,7 @@ export default function SponsorDashboardPage() {
<Card className="p-4">
<h3 className="text-lg font-semibold text-white mb-4">Recent Activity</h3>
<div>
{data.recentActivity.map((activity) => (
{dashboardData.recentActivity.map((activity: any) => (
<ActivityItem key={activity.id} activity={activity} />
))}
</div>
@@ -481,16 +452,16 @@ export default function SponsorDashboardPage() {
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-gray-400">Active Sponsorships</span>
<span className="font-medium text-white">{data.activeSponsorships}</span>
<span className="font-medium text-white">{dashboardData.activeSponsorships}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-400">Total Investment</span>
<span className="font-medium text-white">{data.formattedTotalInvestment}</span>
<span className="font-medium text-white">{dashboardData.formattedTotalInvestment}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-400">Cost per 1K Views</span>
<span className="font-medium text-performance-green">
{data.costPerThousandViews}
{dashboardData.costPerThousandViews}
</span>
</div>
<div className="flex items-center justify-between">