Files
gridpilot.gg/apps/website/app/sponsor/billing/page.tsx
2025-12-10 12:38:55 +01:00

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>
);
}