267 lines
9.0 KiB
TypeScript
267 lines
9.0 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import Card from '@/components/ui/Card';
|
|
import Button from '@/components/ui/Button';
|
|
import {
|
|
CreditCard,
|
|
DollarSign,
|
|
Calendar,
|
|
Download,
|
|
Plus,
|
|
Check,
|
|
AlertTriangle,
|
|
FileText,
|
|
ArrowRight
|
|
} from 'lucide-react';
|
|
|
|
interface PaymentMethod {
|
|
id: string;
|
|
type: 'card' | 'bank';
|
|
last4: string;
|
|
brand?: string;
|
|
isDefault: boolean;
|
|
expiryMonth?: number;
|
|
expiryYear?: number;
|
|
}
|
|
|
|
interface Invoice {
|
|
id: string;
|
|
date: Date;
|
|
amount: number;
|
|
status: 'paid' | 'pending' | 'failed';
|
|
description: string;
|
|
}
|
|
|
|
// Mock data
|
|
const MOCK_PAYMENT_METHODS: PaymentMethod[] = [
|
|
{
|
|
id: 'pm-1',
|
|
type: 'card',
|
|
last4: '4242',
|
|
brand: 'Visa',
|
|
isDefault: true,
|
|
expiryMonth: 12,
|
|
expiryYear: 2027,
|
|
},
|
|
{
|
|
id: 'pm-2',
|
|
type: 'card',
|
|
last4: '5555',
|
|
brand: 'Mastercard',
|
|
isDefault: false,
|
|
expiryMonth: 6,
|
|
expiryYear: 2026,
|
|
},
|
|
];
|
|
|
|
const MOCK_INVOICES: Invoice[] = [
|
|
{
|
|
id: 'inv-1',
|
|
date: new Date('2025-11-01'),
|
|
amount: 1200,
|
|
status: 'paid',
|
|
description: 'GT3 Pro Championship - Main Sponsor (Q4 2025)',
|
|
},
|
|
{
|
|
id: 'inv-2',
|
|
date: new Date('2025-10-01'),
|
|
amount: 400,
|
|
status: 'paid',
|
|
description: 'Formula Sim Series - Secondary Sponsor (Q4 2025)',
|
|
},
|
|
{
|
|
id: 'inv-3',
|
|
date: new Date('2025-12-01'),
|
|
amount: 350,
|
|
status: 'pending',
|
|
description: 'Touring Car Cup - Secondary Sponsor (Q1 2026)',
|
|
},
|
|
];
|
|
|
|
function PaymentMethodCard({ method, onSetDefault }: { method: PaymentMethod; onSetDefault: () => void }) {
|
|
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'}`}>
|
|
<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>
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="font-medium text-white">{method.brand} •••• {method.last4}</span>
|
|
{method.isDefault && (
|
|
<span className="px-2 py-0.5 rounded text-xs bg-primary-blue/20 text-primary-blue">Default</span>
|
|
)}
|
|
</div>
|
|
{method.expiryMonth && method.expiryYear && (
|
|
<span className="text-sm text-gray-500">Expires {method.expiryMonth}/{method.expiryYear}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{!method.isDefault && (
|
|
<Button variant="secondary" onClick={onSetDefault} className="text-xs">
|
|
Set Default
|
|
</Button>
|
|
)}
|
|
<Button variant="secondary" className="text-xs text-gray-400">
|
|
Remove
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function InvoiceRow({ invoice }: { invoice: Invoice }) {
|
|
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' },
|
|
};
|
|
|
|
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">
|
|
<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" />
|
|
</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>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<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>
|
|
</div>
|
|
<Button variant="secondary" className="text-xs">
|
|
<Download className="w-3 h-3 mr-1" />
|
|
PDF
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function SponsorBillingPage() {
|
|
const router = useRouter();
|
|
const [paymentMethods, setPaymentMethods] = useState(MOCK_PAYMENT_METHODS);
|
|
|
|
const handleSetDefault = (methodId: string) => {
|
|
setPaymentMethods(methods =>
|
|
methods.map(m => ({ ...m, isDefault: m.id === methodId }))
|
|
);
|
|
};
|
|
|
|
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);
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto py-8 px-4">
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
);
|
|
} |