website refactor

This commit is contained in:
2026-01-14 10:51:05 +01:00
parent 4522d41aef
commit 0d89ad027e
291 changed files with 6887 additions and 3685 deletions

View File

@@ -11,8 +11,6 @@ import InfoBanner from '@/components/ui/InfoBanner';
import PageHeader from '@/components/ui/PageHeader';
import { siteConfig } from '@/lib/siteConfig';
import { useSponsorBilling } from "@/lib/hooks/sponsor/useSponsorBilling";
import { useInject } from '@/lib/di/hooks/useInject';
import { SPONSOR_SERVICE_TOKEN } from '@/lib/di/tokens';
import {
CreditCard,
DollarSign,
@@ -107,13 +105,13 @@ function PaymentMethodCard({
};
return (
<motion.div
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.2 }}
className={`p-4 rounded-xl border transition-all ${
method.isDefault
? 'border-primary-blue/50 bg-gradient-to-r from-primary-blue/10 to-transparent shadow-[0_0_20px_rgba(25,140,255,0.1)]'
method.isDefault
? 'border-primary-blue/50 bg-gradient-to-r from-primary-blue/10 to-transparent shadow-[0_0_20px_rgba(25,140,255,0.1)]'
: 'border-charcoal-outline bg-iron-gray/30 hover:border-charcoal-outline/80'
}`}
>
@@ -162,31 +160,31 @@ function InvoiceRow({ invoice, index }: { invoice: any; index: number }) {
const shouldReduceMotion = useReducedMotion();
const statusConfig = {
paid: {
icon: Check,
paid: {
icon: Check,
label: 'Paid',
color: 'text-performance-green',
color: 'text-performance-green',
bg: 'bg-performance-green/10',
border: 'border-performance-green/30'
},
pending: {
icon: Clock,
pending: {
icon: Clock,
label: 'Pending',
color: 'text-warning-amber',
color: 'text-warning-amber',
bg: 'bg-warning-amber/10',
border: 'border-warning-amber/30'
},
overdue: {
icon: AlertTriangle,
overdue: {
icon: AlertTriangle,
label: 'Overdue',
color: 'text-racing-red',
color: 'text-racing-red',
bg: 'bg-racing-red/10',
border: 'border-racing-red/30'
},
failed: {
icon: AlertTriangle,
failed: {
icon: AlertTriangle,
label: 'Failed',
color: 'text-racing-red',
color: 'text-racing-red',
bg: 'bg-racing-red/10',
border: 'border-racing-red/30'
},
@@ -204,7 +202,7 @@ function InvoiceRow({ invoice, index }: { invoice: any; index: number }) {
const StatusIcon = status.icon;
return (
<motion.div
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.2, delay: shouldReduceMotion ? 0 : index * 0.05 }}
@@ -261,7 +259,6 @@ function InvoiceRow({ invoice, index }: { invoice: any; index: number }) {
export default function SponsorBillingPage() {
const shouldReduceMotion = useReducedMotion();
const sponsorService = useInject(SPONSOR_SERVICE_TOKEN);
const [showAllInvoices, setShowAllInvoices] = useState(false);
const { data: billingData, isLoading, error, retry } = useSponsorBilling('demo-sponsor-1');
@@ -322,7 +319,7 @@ export default function SponsorBillingPage() {
};
return (
<motion.div
<motion.div
className="max-w-5xl mx-auto py-8 px-4"
variants={containerVariants}
initial="hidden"
@@ -353,7 +350,7 @@ export default function SponsorBillingPage() {
icon={AlertTriangle}
label="Pending Payments"
value={data.stats.formattedPendingAmount}
subValue={`${data.invoices.filter(i => i.status === 'pending' || i.status === 'overdue').length} invoices`}
subValue={`${data.invoices.filter((i: { status: string }) => i.status === 'pending' || i.status === 'overdue').length} invoices`}
color="text-warning-amber"
bgColor="bg-warning-amber/10"
/>
@@ -378,8 +375,8 @@ export default function SponsorBillingPage() {
{/* Payment Methods */}
<motion.div variants={itemVariants}>
<Card className="mb-8 overflow-hidden">
<SectionHeader
icon={CreditCard}
<SectionHeader
icon={CreditCard}
title="Payment Methods"
action={
<Button variant="secondary" className="text-sm">
@@ -389,7 +386,7 @@ export default function SponsorBillingPage() {
}
/>
<div className="p-5 space-y-3">
{data.paymentMethods.map((method) => (
{data.paymentMethods.map((method: { id: string; type: string; last4: string; brand: string; default: boolean }) => (
<PaymentMethodCard
key={method.id}
method={method}
@@ -410,8 +407,8 @@ export default function SponsorBillingPage() {
{/* Billing History */}
<motion.div variants={itemVariants}>
<Card className="mb-8 overflow-hidden">
<SectionHeader
icon={FileText}
<SectionHeader
icon={FileText}
title="Billing History"
color="text-warning-amber"
action={
@@ -422,7 +419,7 @@ export default function SponsorBillingPage() {
}
/>
<div>
{data.invoices.slice(0, showAllInvoices ? data.invoices.length : 4).map((invoice, index) => (
{data.invoices.slice(0, showAllInvoices ? data.invoices.length : 4).map((invoice: { id: string; date: string; amount: number; status: string }, index: number) => (
<InvoiceRow key={invoice.id} invoice={invoice} index={index} />
))}
</div>

View File

@@ -1,12 +1,11 @@
'use client';
import { useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';
import { motion, useReducedMotion, AnimatePresence } from 'framer-motion';
import Link from 'next/link';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
import StatusBadge from '@/components/ui/StatusBadge';
import InfoBanner from '@/components/ui/InfoBanner';
import { useSponsorSponsorships } from "@/lib/hooks/sponsor/useSponsorSponsorships";
import {
@@ -44,33 +43,6 @@ import {
type SponsorshipType = 'all' | 'leagues' | 'teams' | 'drivers' | 'races' | 'platform';
type SponsorshipStatus = 'all' | 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired';
interface Sponsorship {
id: string;
type: SponsorshipType;
entityId: string;
entityName: string;
tier?: 'main' | 'secondary';
status: 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired';
applicationDate?: Date;
approvalDate?: Date;
rejectionReason?: string;
startDate: Date;
endDate: Date;
price: number;
impressions: number;
impressionsChange?: number;
engagement?: number;
details?: string;
// For pending approvals
entityOwner?: string;
applicationMessage?: string;
}
// ============================================================================
// Mock Data - Updated to show application workflow
// ============================================================================
// ============================================================================
// Configuration
// ============================================================================
@@ -85,40 +57,40 @@ const TYPE_CONFIG = {
};
const STATUS_CONFIG = {
active: {
icon: Check,
color: 'text-performance-green',
bgColor: 'bg-performance-green/10',
active: {
icon: Check,
color: 'text-performance-green',
bgColor: 'bg-performance-green/10',
borderColor: 'border-performance-green/30',
label: 'Active'
label: 'Active'
},
pending_approval: {
icon: Clock,
color: 'text-warning-amber',
bgColor: 'bg-warning-amber/10',
pending_approval: {
icon: Clock,
color: 'text-warning-amber',
bgColor: 'bg-warning-amber/10',
borderColor: 'border-warning-amber/30',
label: 'Awaiting Approval'
label: 'Awaiting Approval'
},
approved: {
icon: ThumbsUp,
color: 'text-primary-blue',
bgColor: 'bg-primary-blue/10',
approved: {
icon: ThumbsUp,
color: 'text-primary-blue',
bgColor: 'bg-primary-blue/10',
borderColor: 'border-primary-blue/30',
label: 'Approved'
label: 'Approved'
},
rejected: {
icon: ThumbsDown,
color: 'text-racing-red',
bgColor: 'bg-racing-red/10',
rejected: {
icon: ThumbsDown,
color: 'text-racing-red',
bgColor: 'bg-racing-red/10',
borderColor: 'border-racing-red/30',
label: 'Declined'
label: 'Declined'
},
expired: {
icon: XCircle,
color: 'text-gray-400',
bgColor: 'bg-gray-400/10',
expired: {
icon: XCircle,
color: 'text-gray-400',
bgColor: 'bg-gray-400/10',
borderColor: 'border-gray-400/30',
label: 'Expired'
label: 'Expired'
},
};
@@ -127,7 +99,6 @@ const STATUS_CONFIG = {
// ============================================================================
function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
const router = useRouter();
const shouldReduceMotion = useReducedMotion();
const typeConfig = TYPE_CONFIG[sponsorship.type as keyof typeof TYPE_CONFIG];
@@ -159,8 +130,8 @@ function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
transition={{ duration: 0.2 }}
>
<Card className={`hover:border-primary-blue/30 transition-all duration-300 ${
isPending ? 'border-warning-amber/30' :
isRejected ? 'border-racing-red/20 opacity-75' :
isPending ? 'border-warning-amber/30' :
isRejected ? 'border-racing-red/20 opacity-75' :
isApproved ? 'border-primary-blue/30' : ''
}`}>
{/* Header */}
@@ -176,8 +147,8 @@ function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
</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'
sponsorship.tier === 'main'
? 'bg-primary-blue/20 text-primary-blue'
: 'bg-purple-400/20 text-purple-400'
}`}>
{sponsorship.tier === 'main' ? 'Main Sponsor' : 'Secondary'}
@@ -360,7 +331,6 @@ function SponsorshipCard({ sponsorship }: { sponsorship: any }) {
// ============================================================================
export default function SponsorCampaignsPage() {
const router = useRouter();
const searchParams = useSearchParams();
const shouldReduceMotion = useReducedMotion();
@@ -400,7 +370,7 @@ export default function SponsorCampaignsPage() {
const data = sponsorshipsData;
// Filter sponsorships
const filteredSponsorships = data.sponsorships.filter(s => {
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;
@@ -410,21 +380,21 @@ export default function SponsorCampaignsPage() {
// Calculate stats
const stats = {
total: data.sponsorships.length,
active: data.sponsorships.filter(s => s.status === 'active').length,
pending: data.sponsorships.filter(s => s.status === 'pending_approval').length,
approved: data.sponsorships.filter(s => s.status === 'approved').length,
rejected: data.sponsorships.filter(s => s.status === 'rejected').length,
totalInvestment: data.sponsorships.filter(s => s.status === 'active').reduce((sum, s) => sum + s.price, 0),
totalImpressions: data.sponsorships.reduce((sum, s) => sum + s.impressions, 0),
active: data.sponsorships.filter((s: any) => s.status === 'active').length,
pending: data.sponsorships.filter((s: any) => s.status === 'pending_approval').length,
approved: data.sponsorships.filter((s: any) => s.status === 'approved').length,
rejected: data.sponsorships.filter((s: any) => s.status === 'rejected').length,
totalInvestment: data.sponsorships.filter((s: any) => s.status === 'active').reduce((sum: number, s: any) => sum + s.price, 0),
totalImpressions: data.sponsorships.reduce((sum: number, s: any) => sum + s.impressions, 0),
};
// Stats by type
const statsByType = {
leagues: data.sponsorships.filter(s => s.type === 'leagues').length,
teams: data.sponsorships.filter(s => s.type === 'teams').length,
drivers: data.sponsorships.filter(s => s.type === 'drivers').length,
races: data.sponsorships.filter(s => s.type === 'races').length,
platform: data.sponsorships.filter(s => s.type === 'platform').length,
leagues: data.sponsorships.filter((s: any) => s.type === 'leagues').length,
teams: data.sponsorships.filter((s: any) => s.type === 'teams').length,
drivers: data.sponsorships.filter((s: any) => s.type === 'drivers').length,
races: data.sponsorships.filter((s: any) => s.type === 'races').length,
platform: data.sponsorships.filter((s: any) => s.type === 'platform').length,
};
return (
@@ -457,7 +427,7 @@ export default function SponsorCampaignsPage() {
>
<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.
You have <strong className="text-white">{stats.pending} pending application{stats.pending !== 1 ? 's' : ''}</strong> waiting for approval.
League admins, team owners, and drivers review applications before accepting sponsorships.
</p>
</InfoBanner>
@@ -540,7 +510,7 @@ export default function SponsorCampaignsPage() {
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>
{/* Type Filter */}
<div className="flex items-center gap-2 overflow-x-auto pb-2 lg:pb-0">
{(['all', 'leagues', 'teams', 'drivers', 'races', 'platform'] as const).map((type) => {
@@ -572,12 +542,12 @@ export default function SponsorCampaignsPage() {
{/* Status Filter */}
<div className="flex items-center gap-2 overflow-x-auto">
{(['all', 'active', 'pending_approval', 'approved', 'rejected'] as const).map((status) => {
const config = status === 'all'
? { label: 'All', color: 'text-gray-400' }
const config = status === 'all'
? { label: 'All', color: 'text-gray-400' }
: STATUS_CONFIG[status];
const count = status === 'all'
? stats.total
: data.sponsorships.filter(s => s.status === status).length;
: data.sponsorships.filter((s: any) => s.status === status).length;
return (
<button
key={status}
@@ -635,7 +605,7 @@ export default function SponsorCampaignsPage() {
</Card>
) : (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{filteredSponsorships.map((sponsorship) => (
{filteredSponsorships.map((sponsorship: any) => (
<SponsorshipCard key={sponsorship.id} sponsorship={sponsorship} />
))}
</div>

View File

@@ -2,7 +2,6 @@
export const dynamic = 'force-dynamic';
import { useQuery } from '@tanstack/react-query';
import { motion, useReducedMotion, AnimatePresence } from 'framer-motion';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
@@ -37,48 +36,15 @@ import {
RefreshCw
} from 'lucide-react';
import Link from 'next/link';
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';
import { useSponsorDashboard } from '@/lib/hooks/sponsor/useSponsorDashboard';
export default function SponsorDashboardPage() {
const shouldReduceMotion = useReducedMotion();
const sponsorService = useInject(SPONSOR_SERVICE_TOKEN);
const policyService = useInject(POLICY_SERVICE_TOKEN);
// Use the hook instead of manual query construction
const { data: dashboardData, isLoading, error, retry } = useSponsorDashboard('demo-sponsor-1');
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;
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,
});
const enhancedDashboardQuery = enhanceQueryResult(dashboardQuery);
const dashboardData = enhancedDashboardQuery.data;
const dashboardLoading = enhancedDashboardQuery.isLoading;
const dashboardError = enhancedDashboardQuery.error;
const loading = policyLoading || dashboardLoading;
const error = policyError || dashboardError || (sponsorPortalState !== 'enabled' && sponsorPortalState !== null);
if (loading) {
if (isLoading) {
return (
<div className="max-w-7xl mx-auto py-8 px-4 flex items-center justify-center min-h-[600px]">
<div className="text-center">
@@ -90,16 +56,15 @@ export default function SponsorDashboardPage() {
}
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">{errorMessage}</p>
<p className="text-gray-400">{error?.getUserMessage() || 'Failed to load dashboard data'}</p>
{error && (
<Button variant="secondary" onClick={retry} className="mt-4">
Retry
</Button>
)}
</div>
</div>
);

View File

@@ -24,5 +24,6 @@ export default async function Page({ params }: { params: { id: string } }) {
if (!data) notFound();
// Data is already in the right format from API client
return <PageWrapper data={data} Template={SponsorLeagueDetailTemplate} />;
}

View File

@@ -4,7 +4,6 @@ import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
import { AvailableLeaguesViewModel } from '@/lib/view-models/AvailableLeaguesViewModel';
export default async function Page() {
// Manual wiring: create dependencies
@@ -22,26 +21,24 @@ export default async function Page() {
// Fetch data
const leaguesData = await apiClient.getAvailableLeagues();
// Process data with view model to calculate stats
// Process data - move business logic to template
if (!leaguesData) {
return <PageWrapper data={undefined} Template={SponsorLeaguesTemplate} />;
}
const viewModel = new AvailableLeaguesViewModel(leaguesData);
// Calculate summary stats
// Calculate summary stats (business logic moved from view model)
const stats = {
total: viewModel.leagues.length,
mainAvailable: viewModel.leagues.filter(l => l.mainSponsorSlot.available).length,
secondaryAvailable: viewModel.leagues.reduce((sum, l) => sum + l.secondarySlots.available, 0),
totalDrivers: viewModel.leagues.reduce((sum, l) => sum + l.drivers, 0),
total: leaguesData.length,
mainAvailable: leaguesData.filter((l: any) => l.mainSponsorSlot.available).length,
secondaryAvailable: leaguesData.reduce((sum: number, l: any) => sum + l.secondarySlots.available, 0),
totalDrivers: leaguesData.reduce((sum: number, l: any) => sum + l.drivers, 0),
avgCpm: Math.round(
viewModel.leagues.reduce((sum, l) => sum + l.cpm, 0) / viewModel.leagues.length
leaguesData.reduce((sum: number, l: any) => sum + l.cpm, 0) / leaguesData.length
),
};
const processedData = {
leagues: viewModel.leagues,
leagues: leaguesData,
stats,
};

View File

@@ -1,9 +1,9 @@
import { redirect } from 'next/navigation';
import { routes } from '@/lib/routing/RouteConfig';
export const dynamic = 'force-dynamic';
export default function SponsorPage() {
// Redirect to dashboard - this will be handled by middleware for auth
// Using permanent redirect to avoid cookie loss
redirect('/sponsor/dashboard');
// Redirect to dashboard
redirect(routes.sponsor.dashboard);
}

View File

@@ -1,7 +1,6 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { motion, useReducedMotion } from 'framer-motion';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
@@ -157,7 +156,6 @@ function SavedIndicator({ visible }: { visible: boolean }) {
// ============================================================================
export default function SponsorSettingsPage() {
const router = useRouter();
const shouldReduceMotion = useReducedMotion();
const [profile, setProfile] = useState(MOCK_PROFILE);
const [notifications, setNotifications] = useState(MOCK_NOTIFICATIONS);
@@ -173,10 +171,17 @@ export default function SponsorSettingsPage() {
setTimeout(() => setSaved(false), 3000);
};
const handleDeleteAccount = () => {
const handleDeleteAccount = async () => {
if (confirm('Are you sure you want to delete your sponsor account? This action cannot be undone. All sponsorship data will be permanently removed.')) {
// Call the logout action directly
logoutAction();
// Call the logout action and handle result
const result = await logoutAction();
if (result.isErr()) {
console.error('Logout failed:', result.getError());
// Could show error toast here
return;
}
// Redirect to login after successful logout
window.location.href = '/auth/login';
}
};
@@ -196,7 +201,7 @@ export default function SponsorSettingsPage() {
};
return (
<motion.div
<motion.div
className="max-w-4xl mx-auto py-8 px-4"
variants={containerVariants}
initial="hidden"
@@ -215,9 +220,9 @@ export default function SponsorSettingsPage() {
{/* Company Profile */}
<motion.div variants={itemVariants}>
<Card className="mb-6 overflow-hidden">
<SectionHeader
icon={Building2}
title="Company Profile"
<SectionHeader
icon={Building2}
title="Company Profile"
description="Your public-facing company information"
/>
<div className="p-6 space-y-6">
@@ -300,9 +305,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.address.street}
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, street: e.target.value }
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, street: e.target.value }
})}
placeholder="123 Main Street"
/>
@@ -313,9 +318,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.address.city}
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, city: e.target.value }
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, city: e.target.value }
})}
placeholder="City"
/>
@@ -325,9 +330,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.address.postalCode}
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, postalCode: e.target.value }
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, postalCode: e.target.value }
})}
placeholder="12345"
/>
@@ -337,9 +342,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.address.country}
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, country: e.target.value }
onChange={(e) => setProfile({
...profile,
address: { ...profile.address, country: e.target.value }
})}
placeholder="Country"
/>
@@ -382,9 +387,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.socialLinks.twitter}
onChange={(e) => setProfile({
...profile,
socialLinks: { ...profile.socialLinks, twitter: e.target.value }
onChange={(e) => setProfile({
...profile,
socialLinks: { ...profile.socialLinks, twitter: e.target.value }
})}
placeholder="@username"
/>
@@ -394,9 +399,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.socialLinks.linkedin}
onChange={(e) => setProfile({
...profile,
socialLinks: { ...profile.socialLinks, linkedin: e.target.value }
onChange={(e) => setProfile({
...profile,
socialLinks: { ...profile.socialLinks, linkedin: e.target.value }
})}
placeholder="company-name"
/>
@@ -406,9 +411,9 @@ export default function SponsorSettingsPage() {
<Input
type="text"
value={profile.socialLinks.instagram}
onChange={(e) => setProfile({
...profile,
socialLinks: { ...profile.socialLinks, instagram: e.target.value }
onChange={(e) => setProfile({
...profile,
socialLinks: { ...profile.socialLinks, instagram: e.target.value }
})}
placeholder="@username"
/>
@@ -482,9 +487,9 @@ export default function SponsorSettingsPage() {
{/* Notification Preferences */}
<motion.div variants={itemVariants}>
<Card className="mb-6 overflow-hidden">
<SectionHeader
icon={Bell}
title="Email Notifications"
<SectionHeader
icon={Bell}
title="Email Notifications"
description="Control which emails you receive from GridPilot"
color="text-warning-amber"
/>
@@ -534,9 +539,9 @@ export default function SponsorSettingsPage() {
{/* Privacy & Visibility */}
<motion.div variants={itemVariants}>
<Card className="mb-6 overflow-hidden">
<SectionHeader
icon={Eye}
title="Privacy & Visibility"
<SectionHeader
icon={Eye}
title="Privacy & Visibility"
description="Control how your profile appears to others"
color="text-performance-green"
/>
@@ -574,9 +579,9 @@ export default function SponsorSettingsPage() {
{/* Security */}
<motion.div variants={itemVariants}>
<Card className="mb-6 overflow-hidden">
<SectionHeader
icon={Shield}
title="Account Security"
<SectionHeader
icon={Shield}
title="Account Security"
description="Protect your sponsor account"
color="text-primary-blue"
/>
@@ -654,8 +659,8 @@ export default function SponsorSettingsPage() {
</p>
</div>
</div>
<Button
variant="secondary"
<Button
variant="secondary"
onClick={handleDeleteAccount}
className="text-racing-red border-racing-red/30 hover:bg-racing-red/10"
>

View File

@@ -1,7 +1,6 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { motion, useReducedMotion } from 'framer-motion';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
@@ -10,14 +9,14 @@ import SponsorHero from '@/components/sponsors/SponsorHero';
import SponsorWorkflowMockup from '@/components/sponsors/SponsorWorkflowMockup';
import SponsorBenefitCard from '@/components/sponsors/SponsorBenefitCard';
import { siteConfig } from '@/lib/siteConfig';
import {
Building2,
Mail,
Globe,
Upload,
Eye,
TrendingUp,
Users,
import {
Building2,
Mail,
Globe,
Upload,
Eye,
TrendingUp,
Users,
ArrowRight,
Trophy,
Car,
@@ -123,7 +122,6 @@ const PLATFORM_STATS = [
];
export default function SponsorSignupPage() {
const router = useRouter();
const shouldReduceMotion = useReducedMotion();
const [mode, setMode] = useState<'landing' | 'signup' | 'login'>('landing');
const [formData, setFormData] = useState({
@@ -183,8 +181,8 @@ export default function SponsorSignupPage() {
setSubmitting(true);
try {
// Create a sponsor account using the normal signup flow
// The backend will handle creating the sponsor user with the appropriate role
// Note: Business logic for auth should be moved to a mutation
// This is a temporary implementation for contract compliance
const response = await fetch('/api/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -192,7 +190,6 @@ export default function SponsorSignupPage() {
email: formData.contactEmail,
password: formData.password,
displayName: formData.companyName,
// Additional sponsor-specific data
sponsorData: {
companyName: formData.companyName,
websiteUrl: formData.websiteUrl,
@@ -206,7 +203,6 @@ export default function SponsorSignupPage() {
throw new Error(errorData.message || 'Signup failed');
}
// Auto-login after successful signup
const loginResponse = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -220,7 +216,8 @@ export default function SponsorSignupPage() {
throw new Error('Auto-login failed');
}
router.push('/sponsor/dashboard');
// Navigate to dashboard
window.location.href = '/sponsor/dashboard';
} catch (err) {
console.error('Sponsor signup failed:', err);
alert('Registration failed. ' + (err instanceof Error ? err.message : 'Try again.'));
@@ -293,7 +290,7 @@ export default function SponsorSignupPage() {
Sponsorship Opportunities
</h2>
<p className="text-gray-400 max-w-2xl mx-auto">
Choose how you want to connect with the sim racing community.
Choose how you want to connect with the sim racing community.
Multiple sponsorship tiers and types to fit every budget and goal.
</p>
</div>
@@ -629,8 +626,8 @@ export default function SponsorSignupPage() {
onClick={() => toggleInterest(type.id)}
className={`
p-3 rounded-lg border text-left transition-all
${isSelected
? 'bg-primary-blue/10 border-primary-blue/50'
${isSelected
? 'bg-primary-blue/10 border-primary-blue/50'
: 'bg-iron-gray/50 border-charcoal-outline hover:border-charcoal-outline/80'
}
`}