auth rework
This commit is contained in:
@@ -1,40 +1,78 @@
|
||||
'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';
|
||||
import {
|
||||
CreditCard,
|
||||
DollarSign,
|
||||
import StatCard from '@/components/ui/StatCard';
|
||||
import SectionHeader from '@/components/ui/SectionHeader';
|
||||
import StatusBadge from '@/components/ui/StatusBadge';
|
||||
import InfoBanner from '@/components/ui/InfoBanner';
|
||||
import PageHeader from '@/components/ui/PageHeader';
|
||||
import { siteConfig } from '@/lib/siteConfig';
|
||||
import {
|
||||
CreditCard,
|
||||
DollarSign,
|
||||
Calendar,
|
||||
Download,
|
||||
Plus,
|
||||
Check,
|
||||
AlertTriangle,
|
||||
FileText,
|
||||
ArrowRight
|
||||
ArrowRight,
|
||||
TrendingUp,
|
||||
Receipt,
|
||||
Building2,
|
||||
Wallet,
|
||||
Clock,
|
||||
ChevronRight,
|
||||
Info,
|
||||
ExternalLink,
|
||||
Percent
|
||||
} from 'lucide-react';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
interface PaymentMethod {
|
||||
id: string;
|
||||
type: 'card' | 'bank';
|
||||
type: 'card' | 'bank' | 'sepa';
|
||||
last4: string;
|
||||
brand?: string;
|
||||
isDefault: boolean;
|
||||
expiryMonth?: number;
|
||||
expiryYear?: number;
|
||||
bankName?: string;
|
||||
}
|
||||
|
||||
interface Invoice {
|
||||
id: string;
|
||||
invoiceNumber: string;
|
||||
date: Date;
|
||||
dueDate: Date;
|
||||
amount: number;
|
||||
status: 'paid' | 'pending' | 'failed';
|
||||
vatAmount: number;
|
||||
totalAmount: number;
|
||||
status: 'paid' | 'pending' | 'overdue' | 'failed';
|
||||
description: string;
|
||||
sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform';
|
||||
pdfUrl: string;
|
||||
}
|
||||
|
||||
// Mock data
|
||||
interface BillingStats {
|
||||
totalSpent: number;
|
||||
pendingAmount: number;
|
||||
nextPaymentDate: Date;
|
||||
nextPaymentAmount: number;
|
||||
activeSponsorships: number;
|
||||
averageMonthlySpend: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Mock Data
|
||||
// ============================================================================
|
||||
|
||||
const MOCK_PAYMENT_METHODS: PaymentMethod[] = [
|
||||
{
|
||||
id: 'pm-1',
|
||||
@@ -54,49 +92,142 @@ const MOCK_PAYMENT_METHODS: PaymentMethod[] = [
|
||||
expiryMonth: 6,
|
||||
expiryYear: 2026,
|
||||
},
|
||||
{
|
||||
id: 'pm-3',
|
||||
type: 'sepa',
|
||||
last4: '8901',
|
||||
bankName: 'Deutsche Bank',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const MOCK_INVOICES: Invoice[] = [
|
||||
{
|
||||
id: 'inv-1',
|
||||
invoiceNumber: 'GP-2025-001234',
|
||||
date: new Date('2025-11-01'),
|
||||
amount: 1200,
|
||||
dueDate: new Date('2025-11-15'),
|
||||
amount: 1090.91,
|
||||
vatAmount: 207.27,
|
||||
totalAmount: 1298.18,
|
||||
status: 'paid',
|
||||
description: 'GT3 Pro Championship - Main Sponsor (Q4 2025)',
|
||||
description: 'GT3 Pro Championship - Primary Sponsor (Q4 2025)',
|
||||
sponsorshipType: 'league',
|
||||
pdfUrl: '#',
|
||||
},
|
||||
{
|
||||
id: 'inv-2',
|
||||
invoiceNumber: 'GP-2025-001235',
|
||||
date: new Date('2025-10-01'),
|
||||
amount: 400,
|
||||
dueDate: new Date('2025-10-15'),
|
||||
amount: 363.64,
|
||||
vatAmount: 69.09,
|
||||
totalAmount: 432.73,
|
||||
status: 'paid',
|
||||
description: 'Formula Sim Series - Secondary Sponsor (Q4 2025)',
|
||||
description: 'Team Velocity - Gear Sponsor (Q4 2025)',
|
||||
sponsorshipType: 'team',
|
||||
pdfUrl: '#',
|
||||
},
|
||||
{
|
||||
id: 'inv-3',
|
||||
invoiceNumber: 'GP-2025-001236',
|
||||
date: new Date('2025-12-01'),
|
||||
amount: 350,
|
||||
dueDate: new Date('2025-12-15'),
|
||||
amount: 318.18,
|
||||
vatAmount: 60.45,
|
||||
totalAmount: 378.63,
|
||||
status: 'pending',
|
||||
description: 'Alex Thompson - Driver Sponsorship (Dec 2025)',
|
||||
sponsorshipType: 'driver',
|
||||
pdfUrl: '#',
|
||||
},
|
||||
{
|
||||
id: 'inv-4',
|
||||
invoiceNumber: 'GP-2025-001237',
|
||||
date: new Date('2025-11-15'),
|
||||
dueDate: new Date('2025-11-29'),
|
||||
amount: 454.55,
|
||||
vatAmount: 86.36,
|
||||
totalAmount: 540.91,
|
||||
status: 'overdue',
|
||||
description: 'Touring Car Cup - Secondary Sponsor (Q1 2026)',
|
||||
sponsorshipType: 'league',
|
||||
pdfUrl: '#',
|
||||
},
|
||||
];
|
||||
|
||||
function PaymentMethodCard({ method, onSetDefault }: { method: PaymentMethod; onSetDefault: () => void }) {
|
||||
const MOCK_STATS: BillingStats = {
|
||||
totalSpent: 12450,
|
||||
pendingAmount: 919.54,
|
||||
nextPaymentDate: new Date('2025-12-15'),
|
||||
nextPaymentAmount: 378.63,
|
||||
activeSponsorships: 6,
|
||||
averageMonthlySpend: 2075,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Components
|
||||
// ============================================================================
|
||||
|
||||
function PaymentMethodCard({
|
||||
method,
|
||||
onSetDefault,
|
||||
onRemove
|
||||
}: {
|
||||
method: PaymentMethod;
|
||||
onSetDefault: () => void;
|
||||
onRemove: () => void;
|
||||
}) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
const getIcon = () => {
|
||||
if (method.type === 'sepa') return Building2;
|
||||
return CreditCard;
|
||||
};
|
||||
|
||||
const Icon = getIcon();
|
||||
|
||||
const getLabel = () => {
|
||||
if (method.type === 'sepa' && method.bankName) {
|
||||
return `${method.bankName} •••• ${method.last4}`;
|
||||
}
|
||||
return `${method.brand} •••• ${method.last4}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`p-4 rounded-lg border ${method.isDefault ? 'border-primary-blue/50 bg-primary-blue/5' : 'border-charcoal-outline bg-iron-gray/30'}`}>
|
||||
<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)]'
|
||||
: 'border-charcoal-outline bg-iron-gray/30 hover:border-charcoal-outline/80'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-iron-gray flex items-center justify-center">
|
||||
<CreditCard className="w-5 h-5 text-gray-400" />
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
|
||||
method.isDefault ? 'bg-primary-blue/20' : 'bg-iron-gray'
|
||||
}`}>
|
||||
<Icon className={`w-6 h-6 ${method.isDefault ? 'text-primary-blue' : 'text-gray-400'}`} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-white">{method.brand} •••• {method.last4}</span>
|
||||
<span className="font-medium text-white">{getLabel()}</span>
|
||||
{method.isDefault && (
|
||||
<span className="px-2 py-0.5 rounded text-xs bg-primary-blue/20 text-primary-blue">Default</span>
|
||||
<span className="px-2 py-0.5 rounded-full text-xs bg-primary-blue/20 text-primary-blue font-medium">
|
||||
Default
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{method.expiryMonth && method.expiryYear && (
|
||||
<span className="text-sm text-gray-500">Expires {method.expiryMonth}/{method.expiryYear}</span>
|
||||
<span className="text-sm text-gray-500">
|
||||
Expires {String(method.expiryMonth).padStart(2, '0')}/{method.expiryYear}
|
||||
</span>
|
||||
)}
|
||||
{method.type === 'sepa' && (
|
||||
<span className="text-sm text-gray-500">SEPA Direct Debit</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,58 +237,120 @@ function PaymentMethodCard({ method, onSetDefault }: { method: PaymentMethod; on
|
||||
Set Default
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="secondary" className="text-xs text-gray-400">
|
||||
<Button variant="secondary" onClick={onRemove} className="text-xs text-gray-400 hover:text-racing-red">
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function InvoiceRow({ invoice }: { invoice: Invoice }) {
|
||||
function InvoiceRow({ invoice, index }: { invoice: Invoice; index: number }) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
const statusConfig = {
|
||||
paid: { icon: Check, color: 'text-performance-green', bg: 'bg-performance-green/10' },
|
||||
pending: { icon: AlertTriangle, color: 'text-warning-amber', bg: 'bg-warning-amber/10' },
|
||||
failed: { icon: AlertTriangle, color: 'text-racing-red', bg: 'bg-racing-red/10' },
|
||||
paid: {
|
||||
icon: Check,
|
||||
label: 'Paid',
|
||||
color: 'text-performance-green',
|
||||
bg: 'bg-performance-green/10',
|
||||
border: 'border-performance-green/30'
|
||||
},
|
||||
pending: {
|
||||
icon: Clock,
|
||||
label: 'Pending',
|
||||
color: 'text-warning-amber',
|
||||
bg: 'bg-warning-amber/10',
|
||||
border: 'border-warning-amber/30'
|
||||
},
|
||||
overdue: {
|
||||
icon: AlertTriangle,
|
||||
label: 'Overdue',
|
||||
color: 'text-racing-red',
|
||||
bg: 'bg-racing-red/10',
|
||||
border: 'border-racing-red/30'
|
||||
},
|
||||
failed: {
|
||||
icon: AlertTriangle,
|
||||
label: 'Failed',
|
||||
color: 'text-racing-red',
|
||||
bg: 'bg-racing-red/10',
|
||||
border: 'border-racing-red/30'
|
||||
},
|
||||
};
|
||||
|
||||
const typeLabels = {
|
||||
league: 'League',
|
||||
team: 'Team',
|
||||
driver: 'Driver',
|
||||
race: 'Race',
|
||||
platform: 'Platform',
|
||||
};
|
||||
|
||||
const status = statusConfig[invoice.status];
|
||||
const StatusIcon = status.icon;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-4 border-b border-charcoal-outline last:border-b-0 hover:bg-iron-gray/20 transition-colors">
|
||||
<div className="flex items-center gap-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: shouldReduceMotion ? 0 : 0.2, delay: shouldReduceMotion ? 0 : index * 0.05 }}
|
||||
className="flex items-center justify-between p-4 border-b border-charcoal-outline/50 last:border-b-0 hover:bg-iron-gray/20 transition-colors group"
|
||||
>
|
||||
<div className="flex items-center gap-4 flex-1">
|
||||
<div className="w-10 h-10 rounded-lg bg-iron-gray flex items-center justify-center">
|
||||
<FileText className="w-5 h-5 text-gray-400" />
|
||||
<Receipt className="w-5 h-5 text-gray-400" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-white">{invoice.description}</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{invoice.date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-0.5">
|
||||
<span className="font-medium text-white truncate">{invoice.description}</span>
|
||||
<span className="px-2 py-0.5 rounded text-xs bg-iron-gray text-gray-400 flex-shrink-0">
|
||||
{typeLabels[invoice.sponsorshipType]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm text-gray-500">
|
||||
<span>{invoice.invoiceNumber}</span>
|
||||
<span>•</span>
|
||||
<span>
|
||||
{invoice.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="text-right">
|
||||
<div className="font-semibold text-white">${invoice.amount.toLocaleString()}</div>
|
||||
<div className={`flex items-center gap-1 text-xs ${status.color}`}>
|
||||
<StatusIcon className="w-3 h-3" />
|
||||
{invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1)}
|
||||
<div className="font-semibold text-white">
|
||||
€{invoice.totalAmount.toLocaleString('de-DE', { minimumFractionDigits: 2 })}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
incl. €{invoice.vatAmount.toLocaleString('de-DE', { minimumFractionDigits: 2 })} VAT
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="secondary" className="text-xs">
|
||||
|
||||
<div className={`flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${status.bg} ${status.color} border ${status.border}`}>
|
||||
<StatusIcon className="w-3 h-3" />
|
||||
{status.label}
|
||||
</div>
|
||||
|
||||
<Button variant="secondary" className="text-xs opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<Download className="w-3 h-3 mr-1" />
|
||||
PDF
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main Component
|
||||
// ============================================================================
|
||||
|
||||
export default function SponsorBillingPage() {
|
||||
const router = useRouter();
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const [paymentMethods, setPaymentMethods] = useState(MOCK_PAYMENT_METHODS);
|
||||
const [showAllInvoices, setShowAllInvoices] = useState(false);
|
||||
|
||||
const handleSetDefault = (methodId: string) => {
|
||||
setPaymentMethods(methods =>
|
||||
@@ -165,103 +358,229 @@ export default function SponsorBillingPage() {
|
||||
);
|
||||
};
|
||||
|
||||
const totalSpent = MOCK_INVOICES.filter(i => i.status === 'paid').reduce((sum, i) => sum + i.amount, 0);
|
||||
const pendingAmount = MOCK_INVOICES.filter(i => i.status === 'pending').reduce((sum, i) => sum + i.amount, 0);
|
||||
const handleRemoveMethod = (methodId: string) => {
|
||||
if (confirm('Remove this payment method?')) {
|
||||
setPaymentMethods(methods => methods.filter(m => m.id !== methodId));
|
||||
}
|
||||
};
|
||||
|
||||
const displayedInvoices = showAllInvoices ? MOCK_INVOICES : MOCK_INVOICES.slice(0, 4);
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: shouldReduceMotion ? 0 : 0.1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: { opacity: 1, y: 0 },
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto py-8 px-4">
|
||||
<motion.div
|
||||
className="max-w-5xl mx-auto py-8 px-4"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-white flex items-center gap-3">
|
||||
<CreditCard className="w-7 h-7 text-warning-amber" />
|
||||
Billing & Payments
|
||||
</h1>
|
||||
<p className="text-gray-400 mt-1">Manage payment methods and view invoices</p>
|
||||
</div>
|
||||
<motion.div variants={itemVariants}>
|
||||
<PageHeader
|
||||
icon={Wallet}
|
||||
title="Billing & Payments"
|
||||
description="Manage payment methods, view invoices, and track your sponsorship spending"
|
||||
iconGradient="from-warning-amber/20 to-warning-amber/5"
|
||||
iconBorder="border-warning-amber/30"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<DollarSign className="w-5 h-5 text-performance-green" />
|
||||
<span className="text-sm text-gray-400">Total Spent</span>
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-white">${totalSpent.toLocaleString()}</div>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<AlertTriangle className="w-5 h-5 text-warning-amber" />
|
||||
<span className="text-sm text-gray-400">Pending</span>
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-warning-amber">${pendingAmount.toLocaleString()}</div>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<Calendar className="w-5 h-5 text-primary-blue" />
|
||||
<span className="text-sm text-gray-400">Next Payment</span>
|
||||
</div>
|
||||
<div className="text-lg font-bold text-white">Dec 15, 2025</div>
|
||||
</Card>
|
||||
</div>
|
||||
{/* Stats Grid */}
|
||||
<motion.div variants={itemVariants} className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||
<StatCard
|
||||
icon={DollarSign}
|
||||
label="Total Spent"
|
||||
value={`€${MOCK_STATS.totalSpent.toLocaleString('de-DE')}`}
|
||||
subValue="All time"
|
||||
color="text-performance-green"
|
||||
bgColor="bg-performance-green/10"
|
||||
/>
|
||||
<StatCard
|
||||
icon={AlertTriangle}
|
||||
label="Pending Payments"
|
||||
value={`€${MOCK_STATS.pendingAmount.toLocaleString('de-DE', { minimumFractionDigits: 2 })}`}
|
||||
subValue={`${MOCK_INVOICES.filter(i => i.status === 'pending' || i.status === 'overdue').length} invoices`}
|
||||
color="text-warning-amber"
|
||||
bgColor="bg-warning-amber/10"
|
||||
/>
|
||||
<StatCard
|
||||
icon={Calendar}
|
||||
label="Next Payment"
|
||||
value={MOCK_STATS.nextPaymentDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
|
||||
subValue={`€${MOCK_STATS.nextPaymentAmount.toLocaleString('de-DE', { minimumFractionDigits: 2 })}`}
|
||||
color="text-primary-blue"
|
||||
bgColor="bg-primary-blue/10"
|
||||
/>
|
||||
<StatCard
|
||||
icon={TrendingUp}
|
||||
label="Monthly Average"
|
||||
value={`€${MOCK_STATS.averageMonthlySpend.toLocaleString('de-DE')}`}
|
||||
subValue="Last 6 months"
|
||||
color="text-gray-400"
|
||||
bgColor="bg-iron-gray"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Payment Methods */}
|
||||
<Card className="mb-8">
|
||||
<div className="flex items-center justify-between p-4 border-b border-charcoal-outline">
|
||||
<h2 className="text-lg font-semibold text-white">Payment Methods</h2>
|
||||
<Button variant="secondary" className="text-sm">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Add Payment Method
|
||||
</Button>
|
||||
</div>
|
||||
<div className="p-4 space-y-3">
|
||||
{paymentMethods.map((method) => (
|
||||
<PaymentMethodCard
|
||||
key={method.id}
|
||||
method={method}
|
||||
onSetDefault={() => handleSetDefault(method.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<motion.div variants={itemVariants}>
|
||||
<Card className="mb-8 overflow-hidden">
|
||||
<SectionHeader
|
||||
icon={CreditCard}
|
||||
title="Payment Methods"
|
||||
action={
|
||||
<Button variant="secondary" className="text-sm">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Add Payment Method
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<div className="p-5 space-y-3">
|
||||
{paymentMethods.map((method) => (
|
||||
<PaymentMethodCard
|
||||
key={method.id}
|
||||
method={method}
|
||||
onSetDefault={() => handleSetDefault(method.id)}
|
||||
onRemove={() => handleRemoveMethod(method.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="px-5 pb-5">
|
||||
<InfoBanner type="info">
|
||||
<p className="mb-1">We support Visa, Mastercard, American Express, and SEPA Direct Debit.</p>
|
||||
<p>All payment information is securely processed and stored by our payment provider.</p>
|
||||
</InfoBanner>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
{/* Billing History */}
|
||||
<Card>
|
||||
<div className="flex items-center justify-between p-4 border-b border-charcoal-outline">
|
||||
<h2 className="text-lg font-semibold text-white">Billing History</h2>
|
||||
<Button variant="secondary" className="text-sm">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Export All
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
{MOCK_INVOICES.map((invoice) => (
|
||||
<InvoiceRow key={invoice.id} invoice={invoice} />
|
||||
))}
|
||||
</div>
|
||||
<div className="p-4 border-t border-charcoal-outline">
|
||||
<Button variant="secondary" className="w-full">
|
||||
View All Invoices
|
||||
<ArrowRight className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<motion.div variants={itemVariants}>
|
||||
<Card className="mb-8 overflow-hidden">
|
||||
<SectionHeader
|
||||
icon={FileText}
|
||||
title="Billing History"
|
||||
color="text-warning-amber"
|
||||
action={
|
||||
<Button variant="secondary" className="text-sm">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Export All
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
{displayedInvoices.map((invoice, index) => (
|
||||
<InvoiceRow key={invoice.id} invoice={invoice} index={index} />
|
||||
))}
|
||||
</div>
|
||||
{MOCK_INVOICES.length > 4 && (
|
||||
<div className="p-4 border-t border-charcoal-outline">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="w-full"
|
||||
onClick={() => setShowAllInvoices(!showAllInvoices)}
|
||||
>
|
||||
{showAllInvoices ? 'Show Less' : `View All ${MOCK_INVOICES.length} Invoices`}
|
||||
<ChevronRight className={`w-4 h-4 ml-2 transition-transform ${showAllInvoices ? 'rotate-90' : ''}`} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
{/* Platform Fee Notice */}
|
||||
<div className="mt-6 rounded-lg bg-iron-gray/50 border border-charcoal-outline p-4">
|
||||
<h3 className="font-medium text-white mb-2">Platform Fee</h3>
|
||||
<p className="text-sm text-gray-400">
|
||||
A 10% platform fee is applied to all sponsorship payments. This fee helps maintain the platform,
|
||||
provide analytics, and ensure quality sponsorship placements.
|
||||
</p>
|
||||
</div>
|
||||
{/* Platform Fee & VAT Information */}
|
||||
<motion.div variants={itemVariants} className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Platform Fee */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="p-5 border-b border-charcoal-outline bg-gradient-to-r from-iron-gray/30 to-transparent">
|
||||
<h3 className="font-semibold text-white flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-iron-gray/50">
|
||||
<Percent className="w-4 h-4 text-primary-blue" />
|
||||
</div>
|
||||
Platform Fee
|
||||
</h3>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<div className="text-3xl font-bold text-white mb-2">
|
||||
{siteConfig.fees.platformFeePercent}%
|
||||
</div>
|
||||
<p className="text-sm text-gray-400 mb-4">
|
||||
{siteConfig.fees.description}
|
||||
</p>
|
||||
<div className="text-xs text-gray-500 space-y-1">
|
||||
<p>• Applied to all sponsorship payments</p>
|
||||
<p>• Covers platform maintenance and analytics</p>
|
||||
<p>• Ensures quality sponsorship placements</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Alpha Notice */}
|
||||
<div className="mt-6 rounded-lg bg-warning-amber/10 border border-warning-amber/30 p-4">
|
||||
<p className="text-xs text-gray-400">
|
||||
<strong className="text-warning-amber">Alpha Note:</strong> Payment processing is demonstration-only.
|
||||
Real billing will be available when the payment system is fully implemented.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* VAT Information */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="p-5 border-b border-charcoal-outline bg-gradient-to-r from-iron-gray/30 to-transparent">
|
||||
<h3 className="font-semibold text-white flex items-center gap-3">
|
||||
<div className="p-2 rounded-lg bg-iron-gray/50">
|
||||
<Receipt className="w-4 h-4 text-performance-green" />
|
||||
</div>
|
||||
VAT Information
|
||||
</h3>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<p className="text-sm text-gray-400 mb-4">
|
||||
{siteConfig.vat.notice}
|
||||
</p>
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between items-center py-2 border-b border-charcoal-outline/50">
|
||||
<span className="text-gray-500">Standard VAT Rate</span>
|
||||
<span className="text-white font-medium">{siteConfig.vat.standardRate}%</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="text-gray-500">B2B Reverse Charge</span>
|
||||
<span className="text-performance-green font-medium">Available</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-4">
|
||||
Enter your VAT ID in Settings to enable reverse charge for B2B transactions.
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
{/* Billing Support */}
|
||||
<motion.div variants={itemVariants} className="mt-6">
|
||||
<Card className="p-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 rounded-xl bg-iron-gray">
|
||||
<Info className="w-5 h-5 text-gray-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-white">Need help with billing?</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
Contact our billing support for questions about invoices, payments, or refunds.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="secondary">
|
||||
Contact Support
|
||||
<ExternalLink className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user