di usage in website
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user