website refactor

This commit is contained in:
2026-01-16 01:00:03 +01:00
parent ce7be39155
commit a98e3e3166
286 changed files with 5522 additions and 5261 deletions

View File

@@ -2,12 +2,16 @@
import { useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { motion, useReducedMotion, AnimatePresence } from 'framer-motion';
import { motion, useReducedMotion } from 'framer-motion';
import Link from 'next/link';
import { Card } from '@/ui/Card';
import { Button } from '@/ui/Button';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Heading } from '@/ui/Heading';
import { InfoBanner } from '@/ui/InfoBanner';
import { useSponsorSponsorships } from "@/lib/hooks/sponsor/useSponsorSponsorships";
import { useSponsorSponsorships } from "@/hooks/sponsor/useSponsorSponsorships";
import {
Megaphone,
Trophy,
@@ -23,17 +27,14 @@ import {
Car,
Flag,
Search,
Filter,
TrendingUp,
BarChart3,
ArrowUpRight,
ArrowDownRight,
AlertCircle,
Send,
ThumbsUp,
ThumbsDown,
RefreshCw,
Handshake
} from 'lucide-react';
// ============================================================================
@@ -98,26 +99,27 @@ const STATUS_CONFIG = {
// Components
// ============================================================================
function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
function SponsorshipCard({ sponsorship }: { sponsorship: unknown }) {
const shouldReduceMotion = useReducedMotion();
const typeConfig = TYPE_CONFIG[sponsorship.type as keyof typeof TYPE_CONFIG];
const statusConfig = STATUS_CONFIG[sponsorship.status as keyof typeof STATUS_CONFIG];
const s = sponsorship as any; // Temporary cast to avoid breaking logic
const typeConfig = TYPE_CONFIG[s.type as keyof typeof TYPE_CONFIG];
const statusConfig = STATUS_CONFIG[s.status as keyof typeof STATUS_CONFIG];
const TypeIcon = typeConfig.icon;
const StatusIcon = statusConfig.icon;
const daysRemaining = Math.ceil((sponsorship.endDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
const daysRemaining = Math.ceil((s.endDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
const isExpiringSoon = daysRemaining > 0 && daysRemaining <= 30;
const isPending = sponsorship.status === 'pending_approval';
const isRejected = sponsorship.status === 'rejected';
const isApproved = sponsorship.status === 'approved';
const isPending = s.status === 'pending_approval';
const isRejected = s.status === 'rejected';
const isApproved = s.status === 'approved';
const getEntityLink = () => {
switch (sponsorship.type) {
case 'leagues': return `/leagues/${sponsorship.entityId}`;
case 'teams': return `/teams/${sponsorship.entityId}`;
case 'drivers': return `/drivers/${sponsorship.entityId}`;
case 'races': return `/races/${sponsorship.entityId}`;
switch (s.type) {
case 'leagues': return `/leagues/${s.entityId}`;
case 'teams': return `/teams/${s.entityId}`;
case 'drivers': return `/drivers/${s.entityId}`;
case 'races': return `/races/${s.entityId}`;
default: return '#';
}
};
@@ -135,172 +137,166 @@ function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
isApproved ? 'border-primary-blue/30' : ''
}`}>
{/* Header */}
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-lg ${typeConfig.bgColor} flex items-center justify-center`}>
<Stack direction="row" align="start" justify="between" mb={4}>
<Stack direction="row" align="center" gap={3}>
<Box w="10" h="10" rounded="lg" bg={typeConfig.bgColor} display="flex" alignItems="center" justifyContent="center">
<TypeIcon className={`w-5 h-5 ${typeConfig.color}`} />
</div>
<div>
<div className="flex items-center gap-2 flex-wrap">
<span className={`text-xs font-medium px-2 py-0.5 rounded ${typeConfig.bgColor} ${typeConfig.color}`}>
</Box>
<Box>
<Box display="flex" alignItems="center" gap={2} flexWrap="wrap">
<Text size="xs" weight="medium" px={2} py={0.5} rounded="md" bg={typeConfig.bgColor} color={typeConfig.color}>
{typeConfig.label}
</span>
{sponsorship.tier && (
<span className={`text-xs font-medium px-2 py-0.5 rounded ${
sponsorship.tier === 'main'
? 'bg-primary-blue/20 text-primary-blue'
: 'bg-purple-400/20 text-purple-400'
}`}>
{sponsorship.tier === 'main' ? 'Main Sponsor' : 'Secondary'}
</span>
</Text>
{s.tier && (
<Text size="xs" weight="medium" px={2} py={0.5} rounded="md" bg={s.tier === 'main' ? 'bg-primary-blue/20' : 'bg-purple-400/20'} color={s.tier === 'main' ? 'text-primary-blue' : 'text-purple-400'}>
{s.tier === 'main' ? 'Main Sponsor' : 'Secondary'}
</Text>
)}
</div>
</div>
</div>
<div className={`flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium border ${statusConfig.bgColor} ${statusConfig.color} ${statusConfig.borderColor}`}>
</Box>
</Box>
</Stack>
<Box display="flex" alignItems="center" gap={1} px={2.5} py={1} rounded="full" border bg={statusConfig.bgColor} color={statusConfig.color} borderColor={statusConfig.borderColor}>
<StatusIcon className="w-3 h-3" />
{statusConfig.label}
</div>
</div>
<Text size="xs" weight="medium">{statusConfig.label}</Text>
</Box>
</Stack>
{/* Entity Name */}
<h3 className="text-lg font-semibold text-white mb-1">{sponsorship.entityName}</h3>
{sponsorship.details && (
<p className="text-sm text-gray-500 mb-3">{sponsorship.details}</p>
<Heading level={3} fontSize="lg" weight="semibold" color="text-white" mb={1}>{s.entityName}</Heading>
{s.details && (
<Text size="sm" color="text-gray-500" block mb={3}>{s.details}</Text>
)}
{/* Application/Approval Info for non-active states */}
{isPending && (
<div className="mb-4 p-3 rounded-lg bg-warning-amber/5 border border-warning-amber/20">
<div className="flex items-center gap-2 text-warning-amber text-sm mb-2">
<Box mb={4} p={3} rounded="lg" bg="bg-warning-amber/5" border borderColor="border-warning-amber/20">
<Stack direction="row" align="center" gap={2} color="text-warning-amber" mb={2}>
<Send className="w-4 h-4" />
<span className="font-medium">Application Pending</span>
</div>
<p className="text-xs text-gray-400 mb-2">
Sent to <span className="text-gray-300">{sponsorship.entityOwner}</span> on{' '}
{sponsorship.applicationDate?.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</p>
{sponsorship.applicationMessage && (
<p className="text-xs text-gray-500 italic">"{sponsorship.applicationMessage}"</p>
<Text size="sm" weight="medium">Application Pending</Text>
</Stack>
<Text size="xs" color="text-gray-400" block mb={2}>
Sent to <Text color="text-gray-300">{s.entityOwner}</Text> on{' '}
{s.applicationDate?.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</Text>
{s.applicationMessage && (
<Text size="xs" color="text-gray-500" italic block>&quot;{s.applicationMessage}&quot;</Text>
)}
</div>
</Box>
)}
{isApproved && (
<div className="mb-4 p-3 rounded-lg bg-primary-blue/5 border border-primary-blue/20">
<div className="flex items-center gap-2 text-primary-blue text-sm mb-1">
<Box mb={4} p={3} rounded="lg" bg="bg-primary-blue/5" border borderColor="border-primary-blue/20">
<Stack direction="row" align="center" gap={2} color="text-primary-blue" mb={1}>
<ThumbsUp className="w-4 h-4" />
<span className="font-medium">Approved!</span>
</div>
<p className="text-xs text-gray-400">
Approved by <span className="text-gray-300">{sponsorship.entityOwner}</span> on{' '}
{sponsorship.approvalDate?.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</p>
<p className="text-xs text-gray-500 mt-1">
Starts {sponsorship.startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
</p>
</div>
<Text size="sm" weight="medium">Approved!</Text>
</Stack>
<Text size="xs" color="text-gray-400" block>
Approved by <Text color="text-gray-300">{s.entityOwner}</Text> on{' '}
{s.approvalDate?.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</Text>
<Text size="xs" color="text-gray-500" block mt={1}>
Starts {s.startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
</Text>
</Box>
)}
{isRejected && (
<div className="mb-4 p-3 rounded-lg bg-racing-red/5 border border-racing-red/20">
<div className="flex items-center gap-2 text-racing-red text-sm mb-1">
<Box mb={4} p={3} rounded="lg" bg="bg-racing-red/5" border borderColor="border-racing-red/20">
<Stack direction="row" align="center" gap={2} color="text-racing-red" mb={1}>
<ThumbsDown className="w-4 h-4" />
<span className="font-medium">Application Declined</span>
</div>
{sponsorship.rejectionReason && (
<p className="text-xs text-gray-400 mt-1">
Reason: <span className="text-gray-300">{sponsorship.rejectionReason}</span>
</p>
<Text size="sm" weight="medium">Application Declined</Text>
</Stack>
{s.rejectionReason && (
<Text size="xs" color="text-gray-400" block mt={1}>
Reason: <Text color="text-gray-300">{s.rejectionReason}</Text>
</Text>
)}
<Button variant="secondary" className="mt-2 text-xs">
<RefreshCw className="w-3 h-3 mr-1" />
Reapply
</Button>
</div>
</Box>
)}
{/* Metrics Grid - Only show for active sponsorships */}
{sponsorship.status === 'active' && (
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
<div className="bg-iron-gray/50 rounded-lg p-3">
<div className="flex items-center gap-1 text-gray-400 text-xs mb-1">
{s.status === 'active' && (
<Box display="grid" gridCols={{ base: 2, md: 4 }} gap={3} mb={4}>
<Box bg="bg-iron-gray/50" rounded="lg" p={3}>
<Box display="flex" alignItems="center" gap={1} color="text-gray-400" mb={1}>
<Eye className="w-3 h-3" />
Impressions
</div>
<div className="flex items-center gap-2">
<span className="text-white font-semibold">{sponsorship.formattedImpressions}</span>
{sponsorship.impressionsChange !== undefined && sponsorship.impressionsChange !== 0 && (
<span className={`text-xs flex items-center ${
sponsorship.impressionsChange > 0 ? 'text-performance-green' : 'text-racing-red'
}`}>
{sponsorship.impressionsChange > 0 ? <ArrowUpRight className="w-3 h-3" /> : <ArrowDownRight className="w-3 h-3" />}
{Math.abs(sponsorship.impressionsChange)}%
</span>
<Text size="xs">Impressions</Text>
</Box>
<Stack direction="row" align="center" gap={2}>
<Text color="text-white" weight="semibold">{s.formattedImpressions}</Text>
{s.impressionsChange !== undefined && s.impressionsChange !== 0 && (
<Text size="xs" display="flex" alignItems="center" color={s.impressionsChange > 0 ? 'text-performance-green' : 'text-racing-red'}>
{s.impressionsChange > 0 ? <ArrowUpRight className="w-3 h-3" /> : <ArrowDownRight className="w-3 h-3" />}
{Math.abs(s.impressionsChange)}%
</Text>
)}
</div>
</div>
</Stack>
</Box>
{sponsorship.engagement && (
<div className="bg-iron-gray/50 rounded-lg p-3">
<div className="flex items-center gap-1 text-gray-400 text-xs mb-1">
{s.engagement && (
<Box bg="bg-iron-gray/50" rounded="lg" p={3}>
<Box display="flex" alignItems="center" gap={1} color="text-gray-400" mb={1}>
<TrendingUp className="w-3 h-3" />
Engagement
</div>
<div className="text-white font-semibold">{sponsorship.engagement}%</div>
</div>
<Text size="xs">Engagement</Text>
</Box>
<Text color="text-white" weight="semibold">{s.engagement}%</Text>
</Box>
)}
<div className="bg-iron-gray/50 rounded-lg p-3">
<div className="flex items-center gap-1 text-gray-400 text-xs mb-1">
<Box bg="bg-iron-gray/50" rounded="lg" p={3}>
<Box display="flex" alignItems="center" gap={1} color="text-gray-400" mb={1}>
<Calendar className="w-3 h-3" />
Period
</div>
<div className="text-white font-semibold text-xs">
{sponsorship.startDate.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })} - {sponsorship.endDate.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
</div>
</div>
<Text size="xs">Period</Text>
</Box>
<Text color="text-white" weight="semibold" size="xs">
{s.startDate.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })} - {s.endDate.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
</Text>
</Box>
<div className="bg-iron-gray/50 rounded-lg p-3">
<div className="flex items-center gap-1 text-gray-400 text-xs mb-1">
<Box bg="bg-iron-gray/50" rounded="lg" p={3}>
<Box display="flex" alignItems="center" gap={1} color="text-gray-400" mb={1}>
<Trophy className="w-3 h-3" />
Investment
</div>
<div className="text-white font-semibold">{sponsorship.formattedPrice}</div>
</div>
</div>
<Text size="xs">Investment</Text>
</Box>
<Text color="text-white" weight="semibold">{s.formattedPrice}</Text>
</Box>
</Box>
)}
{/* Basic info for non-active */}
{sponsorship.status !== 'active' && (
<div className="flex items-center gap-4 mb-4 text-sm">
<div className="flex items-center gap-1 text-gray-400">
{s.status !== 'active' && (
<Stack direction="row" align="center" gap={4} mb={4}>
<Box display="flex" alignItems="center" gap={1} color="text-gray-400">
<Calendar className="w-3.5 h-3.5" />
{sponsorship.periodDisplay}
</div>
<div className="flex items-center gap-1 text-gray-400">
<Text size="sm">{s.periodDisplay}</Text>
</Box>
<Box display="flex" alignItems="center" gap={1} color="text-gray-400">
<Trophy className="w-3.5 h-3.5" />
{sponsorship.formattedPrice}
</div>
</div>
<Text size="sm">{s.formattedPrice}</Text>
</Box>
</Stack>
)}
{/* Footer */}
<div className="flex items-center justify-between pt-3 border-t border-charcoal-outline/50">
<div className="flex items-center gap-2">
{sponsorship.status === 'active' && (
<span className={`text-xs ${isExpiringSoon ? 'text-warning-amber' : 'text-gray-500'}`}>
<Box display="flex" alignItems="center" justifyContent="between" pt={3} borderTop borderColor="border-charcoal-outline/50">
<Box display="flex" alignItems="center" gap={2}>
{s.status === 'active' && (
<Text size="xs" color={isExpiringSoon ? 'text-warning-amber' : 'text-gray-500'}>
{daysRemaining > 0 ? `${daysRemaining} days remaining` : 'Ended'}
</span>
</Text>
)}
{isPending && (
<span className="text-xs text-gray-500">
<Text size="xs" color="text-gray-500">
Waiting for response...
</span>
</Text>
)}
</div>
<div className="flex items-center gap-2">
{sponsorship.type !== 'platform' && (
</Box>
<Stack direction="row" align="center" gap={2}>
{s.type !== 'platform' && (
<Link href={getEntityLink()}>
<Button variant="secondary" className="text-xs">
<ExternalLink className="w-3 h-3 mr-1" />
@@ -313,14 +309,14 @@ function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
Cancel Application
</Button>
)}
{sponsorship.status === 'active' && (
{s.status === 'active' && (
<Button variant="secondary" className="text-xs">
Details
<ChevronRight className="w-3 h-3 ml-1" />
</Button>
)}
</div>
</div>
</Stack>
</Box>
</Card>
</motion.div>
);
@@ -370,53 +366,65 @@ export default function SponsorCampaignsPage() {
const data = sponsorshipsData;
// Filter sponsorships
const filteredSponsorships = data.sponsorships.filter((s: any) => {
if (typeFilter !== 'all' && s.type !== typeFilter) return false;
if (statusFilter !== 'all' && s.status !== statusFilter) return false;
if (searchQuery && !s.entityName.toLowerCase().includes(searchQuery.toLowerCase())) return false;
const filteredSponsorships = data.sponsorships.filter((s: unknown) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const sponsorship = s as any;
if (typeFilter !== 'all' && sponsorship.type !== typeFilter) return false;
if (statusFilter !== 'all' && sponsorship.status !== statusFilter) return false;
if (searchQuery && !sponsorship.entityName.toLowerCase().includes(searchQuery.toLowerCase())) return false;
return true;
});
// Calculate stats
const stats = {
total: data.sponsorships.length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
active: data.sponsorships.filter((s: any) => s.status === 'active').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pending: data.sponsorships.filter((s: any) => s.status === 'pending_approval').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
approved: data.sponsorships.filter((s: any) => s.status === 'approved').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
rejected: data.sponsorships.filter((s: any) => s.status === 'rejected').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
totalInvestment: data.sponsorships.filter((s: any) => s.status === 'active').reduce((sum: number, s: any) => sum + s.price, 0),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
totalImpressions: data.sponsorships.reduce((sum: number, s: any) => sum + s.impressions, 0),
};
// Stats by type
const statsByType = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
leagues: data.sponsorships.filter((s: any) => s.type === 'leagues').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
teams: data.sponsorships.filter((s: any) => s.type === 'teams').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
drivers: data.sponsorships.filter((s: any) => s.type === 'drivers').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
races: data.sponsorships.filter((s: any) => s.type === 'races').length,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
platform: data.sponsorships.filter((s: any) => s.type === 'platform').length,
};
return (
<div className="max-w-7xl mx-auto py-8 px-4">
<Box maxWidth="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 flex items-center gap-3">
<Megaphone className="w-7 h-7 text-primary-blue" />
<Stack direction={{ base: 'col', md: 'row' }} align="center" justify="between" gap={4} mb={8}>
<Box>
<Heading level={1} fontSize="2xl" weight="bold" color="text-white" icon={<Megaphone className="w-7 h-7 text-primary-blue" />}>
My Sponsorships
</h1>
<p className="text-gray-400 mt-1">Manage applications and active sponsorship campaigns</p>
</div>
<div className="flex items-center gap-3">
</Heading>
<Text color="text-gray-400" mt={1} block>Manage applications and active sponsorship campaigns</Text>
</Box>
<Box display="flex" alignItems="center" gap={3}>
<Link href="/leagues">
<Button variant="primary">
<Plus className="w-4 h-4 mr-2" />
Find Opportunities
</Button>
</Link>
</div>
</div>
</Box>
</Stack>
{/* Info Banner about how sponsorships work */}
{stats.pending > 0 && (
@@ -426,23 +434,23 @@ export default function SponsorCampaignsPage() {
className="mb-6"
>
<InfoBanner type="info" title="Sponsorship Applications">
<p>
You have <strong className="text-white">{stats.pending} pending application{stats.pending !== 1 ? 's' : ''}</strong> waiting for approval.
<Text size="sm">
You have <Text weight="bold" color="text-white">{stats.pending} pending application{stats.pending !== 1 ? 's' : ''}</Text> waiting for approval.
League admins, team owners, and drivers review applications before accepting sponsorships.
</p>
</Text>
</InfoBanner>
</motion.div>
)}
{/* Quick Stats */}
<div className="grid grid-cols-2 md:grid-cols-6 gap-4 mb-8">
<Box display="grid" gridCols={{ base: 2, md: 6 }} gap={4} mb={8}>
<motion.div
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
<Card className="p-4">
<div className="text-2xl font-bold text-white">{stats.total}</div>
<div className="text-sm text-gray-400">Total</div>
<Text size="2xl" weight="bold" color="text-white" block>{stats.total}</Text>
<Text size="sm" color="text-gray-400">Total</Text>
</Card>
</motion.div>
<motion.div
@@ -451,8 +459,8 @@ export default function SponsorCampaignsPage() {
transition={{ delay: 0.05 }}
>
<Card className="p-4">
<div className="text-2xl font-bold text-performance-green">{stats.active}</div>
<div className="text-sm text-gray-400">Active</div>
<Text size="2xl" weight="bold" color="text-performance-green" block>{stats.active}</Text>
<Text size="sm" color="text-gray-400">Active</Text>
</Card>
</motion.div>
<motion.div
@@ -461,8 +469,8 @@ export default function SponsorCampaignsPage() {
transition={{ delay: 0.1 }}
>
<Card className={`p-4 ${stats.pending > 0 ? 'border-warning-amber/30' : ''}`}>
<div className="text-2xl font-bold text-warning-amber">{stats.pending}</div>
<div className="text-sm text-gray-400">Pending</div>
<Text size="2xl" weight="bold" color="text-warning-amber" block>{stats.pending}</Text>
<Text size="sm" color="text-gray-400">Pending</Text>
</Card>
</motion.div>
<motion.div
@@ -471,8 +479,8 @@ export default function SponsorCampaignsPage() {
transition={{ delay: 0.15 }}
>
<Card className="p-4">
<div className="text-2xl font-bold text-primary-blue">{stats.approved}</div>
<div className="text-sm text-gray-400">Approved</div>
<Text size="2xl" weight="bold" color="text-primary-blue" block>{stats.approved}</Text>
<Text size="sm" color="text-gray-400">Approved</Text>
</Card>
</motion.div>
<motion.div
@@ -481,8 +489,8 @@ export default function SponsorCampaignsPage() {
transition={{ delay: 0.2 }}
>
<Card className="p-4">
<div className="text-2xl font-bold text-white">${stats.totalInvestment.toLocaleString()}</div>
<div className="text-sm text-gray-400">Active Investment</div>
<Text size="2xl" weight="bold" color="text-white" block>${stats.totalInvestment.toLocaleString()}</Text>
<Text size="sm" color="text-gray-400">Active Investment</Text>
</Card>
</motion.div>
<motion.div
@@ -491,16 +499,16 @@ export default function SponsorCampaignsPage() {
transition={{ delay: 0.25 }}
>
<Card className="p-4">
<div className="text-2xl font-bold text-primary-blue">{(stats.totalImpressions / 1000).toFixed(0)}k</div>
<div className="text-sm text-gray-400">Impressions</div>
<Text size="2xl" weight="bold" color="text-primary-blue" block>{(stats.totalImpressions / 1000).toFixed(0)}k</Text>
<Text size="sm" color="text-gray-400">Impressions</Text>
</Card>
</motion.div>
</div>
</Box>
{/* Filters */}
<div className="flex flex-col lg:flex-row gap-4 mb-6">
<Stack direction={{ base: 'col', lg: 'row' }} gap={4} mb={6}>
{/* Search */}
<div className="relative flex-1">
<Box position="relative" flexGrow={1}>
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500" />
<input
type="text"
@@ -509,10 +517,10 @@ export default function SponsorCampaignsPage() {
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-charcoal-outline bg-iron-gray text-white placeholder-gray-500 focus:border-primary-blue focus:outline-none"
/>
</div>
</Box>
{/* Type Filter */}
<div className="flex items-center gap-2 overflow-x-auto pb-2 lg:pb-0">
<Box display="flex" alignItems="center" gap={2} overflow="auto" pb={{ base: 2, lg: 0 }}>
{(['all', 'leagues', 'teams', 'drivers', 'races', 'platform'] as const).map((type) => {
const config = TYPE_CONFIG[type];
const Icon = config.icon;
@@ -525,22 +533,20 @@ export default function SponsorCampaignsPage() {
typeFilter === type
? 'bg-primary-blue text-white'
: 'bg-iron-gray/50 text-gray-400 hover:bg-iron-gray'
}`}
} border-0 cursor-pointer`}
>
<Icon className="w-4 h-4" />
{config.label}
<span className={`px-1.5 py-0.5 rounded text-xs ${
typeFilter === type ? 'bg-white/20' : 'bg-charcoal-outline'
}`}>
<Text size="xs" px={1.5} py={0.5} rounded="sm" bg={typeFilter === type ? 'bg-white/20' : 'bg-charcoal-outline'}>
{count}
</span>
</Text>
</button>
);
})}
</div>
</Box>
{/* Status Filter */}
<div className="flex items-center gap-2 overflow-x-auto">
<Box display="flex" alignItems="center" gap={2} overflow="auto">
{(['all', 'active', 'pending_approval', 'approved', 'rejected'] as const).map((status) => {
const config = status === 'all'
? { label: 'All', color: 'text-gray-400' }
@@ -556,33 +562,31 @@ export default function SponsorCampaignsPage() {
statusFilter === status
? 'bg-iron-gray text-white border border-charcoal-outline'
: 'text-gray-500 hover:text-gray-300'
}`}
} border-0 cursor-pointer`}
>
{config.label}
{count > 0 && status !== 'all' && (
<span className={`ml-1.5 px-1.5 py-0.5 rounded text-xs ${
status === 'pending_approval' ? 'bg-warning-amber/20 text-warning-amber' : 'bg-charcoal-outline'
}`}>
<Text size="xs" ml={1.5} px={1.5} py={0.5} rounded="sm" bg={status === 'pending_approval' ? 'bg-warning-amber/20' : 'bg-charcoal-outline'} color={status === 'pending_approval' ? 'text-warning-amber' : ''}>
{count}
</span>
</Text>
)}
</button>
);
})}
</div>
</div>
</Box>
</Stack>
{/* Sponsorship List */}
{filteredSponsorships.length === 0 ? (
<Card className="text-center py-16">
<Megaphone className="w-12 h-12 text-gray-600 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-white mb-2">No sponsorships found</h3>
<p className="text-gray-400 mb-6 max-w-md mx-auto">
<Heading level={3} fontSize="lg" weight="semibold" color="text-white" mb={2}>No sponsorships found</Heading>
<Text color="text-gray-400" mb={6} maxWidth="md" mx="auto" block>
{searchQuery || typeFilter !== 'all' || statusFilter !== 'all'
? 'Try adjusting your filters to see more results.'
: 'Start sponsoring leagues, teams, or drivers to grow your brand visibility.'}
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
</Text>
<Stack direction={{ base: 'col', md: 'row' }} gap={3} justify="center">
<Link href="/leagues">
<Button variant="primary">
<Trophy className="w-4 h-4 mr-2" />
@@ -601,15 +605,15 @@ export default function SponsorCampaignsPage() {
Browse Drivers
</Button>
</Link>
</div>
</Stack>
</Card>
) : (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<Box display="grid" gridCols={{ base: 1, lg: 2 }} gap={4}>
{filteredSponsorships.map((sponsorship: any) => (
<SponsorshipCard key={sponsorship.id} sponsorship={sponsorship} />
))}
</div>
</Box>
)}
</div>
</Box>
);
}