website refactor
This commit is contained in:
216
apps/website/templates/SponsorBillingTemplate.tsx
Normal file
216
apps/website/templates/SponsorBillingTemplate.tsx
Normal file
@@ -0,0 +1,216 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Container } from '@/ui/Container';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { InfoBanner } from '@/ui/InfoBanner';
|
||||
import { SponsorDashboardHeader } from '@/components/sponsors/SponsorDashboardHeader';
|
||||
import { BillingSummaryPanel } from '@/components/sponsors/BillingSummaryPanel';
|
||||
import { SponsorPayoutQueueTable, PayoutItem } from '@/components/sponsors/SponsorPayoutQueueTable';
|
||||
import {
|
||||
CreditCard,
|
||||
Building2,
|
||||
Download,
|
||||
Percent,
|
||||
Receipt,
|
||||
ExternalLink,
|
||||
LucideIcon
|
||||
} from 'lucide-react';
|
||||
import { siteConfig } from '@/lib/siteConfig';
|
||||
import type { PaymentMethodDTO, InvoiceDTO } from '@/lib/types/tbd/SponsorBillingDTO';
|
||||
|
||||
export interface SponsorBillingViewData {
|
||||
stats: {
|
||||
totalSpent: number;
|
||||
pendingAmount: number;
|
||||
nextPaymentDate: string;
|
||||
nextPaymentAmount: number;
|
||||
averageMonthlySpend: number;
|
||||
};
|
||||
paymentMethods: PaymentMethodDTO[];
|
||||
invoices: InvoiceDTO[];
|
||||
}
|
||||
|
||||
interface SponsorBillingTemplateProps {
|
||||
viewData: SponsorBillingViewData;
|
||||
billingStats: Array<{
|
||||
label: string;
|
||||
value: string;
|
||||
subValue?: string;
|
||||
icon: LucideIcon;
|
||||
variant: 'success' | 'warning' | 'info' | 'default';
|
||||
}>;
|
||||
transactions: Array<{
|
||||
id: string;
|
||||
date: string;
|
||||
amount: number | string;
|
||||
status: string;
|
||||
recipient?: string;
|
||||
description?: string;
|
||||
invoiceNumber?: string;
|
||||
}>;
|
||||
onSetDefaultPaymentMethod: (id: string) => void;
|
||||
onDownloadInvoice: (id: string) => void;
|
||||
}
|
||||
|
||||
export function SponsorBillingTemplate({
|
||||
viewData,
|
||||
billingStats,
|
||||
transactions,
|
||||
onSetDefaultPaymentMethod,
|
||||
onDownloadInvoice
|
||||
}: SponsorBillingTemplateProps) {
|
||||
// Map transactions to PayoutItems for the new table
|
||||
const payoutItems: PayoutItem[] = transactions.map(t => ({
|
||||
id: t.id,
|
||||
date: t.date,
|
||||
amount: typeof t.amount === 'number' ? t.amount.toFixed(2) : t.amount,
|
||||
status: t.status === 'paid' ? 'completed' : t.status === 'pending' ? 'pending' : 'failed',
|
||||
recipient: t.recipient || 'GridPilot Platform',
|
||||
description: t.description || `Invoice ${t.invoiceNumber}`
|
||||
}));
|
||||
|
||||
return (
|
||||
<Container size="lg" py={8}>
|
||||
<Stack gap={8}>
|
||||
<SponsorDashboardHeader
|
||||
sponsorName="Sponsor"
|
||||
onRefresh={() => console.log('Refresh')}
|
||||
/>
|
||||
|
||||
<BillingSummaryPanel stats={billingStats} />
|
||||
|
||||
<Box display="grid" gridCols={{ base: 1, lg: 12 }} gap={8}>
|
||||
<Box colSpan={{ base: 1, lg: 8 }}>
|
||||
<Stack gap={8}>
|
||||
{/* Billing History */}
|
||||
<Box>
|
||||
<Stack direction="row" align="center" justify="between" mb={4}>
|
||||
<Heading level={3}>Billing History</Heading>
|
||||
<Button variant="secondary" size="sm" icon={<Icon icon={Download} size={4} />} onClick={() => onDownloadInvoice('all')}>
|
||||
Export All
|
||||
</Button>
|
||||
</Stack>
|
||||
<Card p={0} overflow="hidden">
|
||||
<SponsorPayoutQueueTable payouts={payoutItems} />
|
||||
</Card>
|
||||
</Box>
|
||||
|
||||
{/* Platform Fees & VAT */}
|
||||
<Box display="grid" gridCols={{ base: 1, md: 2 }} gap={6}>
|
||||
<Card overflow="hidden">
|
||||
<Box p={4} borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/10">
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Percent} size={4} color="text-primary-blue" />
|
||||
<Text size="xs" weight="bold" uppercase letterSpacing="wider" color="text-gray-400">
|
||||
Platform Fee
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box p={5}>
|
||||
<Text size="3xl" weight="bold" color="text-white" block mb={2}>
|
||||
{siteConfig.fees.platformFeePercent}%
|
||||
</Text>
|
||||
<Text size="sm" color="text-gray-400" block>
|
||||
{siteConfig.fees.description}
|
||||
</Text>
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
<Card overflow="hidden">
|
||||
<Box p={4} borderBottom borderColor="border-charcoal-outline" bg="bg-iron-gray/10">
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Receipt} size={4} color="text-performance-green" />
|
||||
<Text size="xs" weight="bold" uppercase letterSpacing="wider" color="text-gray-400">
|
||||
VAT Information
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box p={5}>
|
||||
<Text size="sm" color="text-gray-400" block mb={4}>
|
||||
{siteConfig.vat.notice}
|
||||
</Text>
|
||||
<Stack gap={2}>
|
||||
<Box display="flex" justifyContent="between">
|
||||
<Text size="sm" color="text-gray-500">Standard Rate</Text>
|
||||
<Text size="sm" color="text-white" weight="medium">{siteConfig.vat.standardRate}%</Text>
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="between">
|
||||
<Text size="sm" color="text-gray-500">B2B Reverse Charge</Text>
|
||||
<Text size="sm" color="text-performance-green" weight="medium">Available</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Box colSpan={{ base: 1, lg: 4 }}>
|
||||
<Stack gap={6}>
|
||||
{/* Payment Methods */}
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Heading level={3}>Payment Methods</Heading>
|
||||
<Stack gap={3}>
|
||||
{viewData.paymentMethods.map((method: PaymentMethodDTO) => (
|
||||
<Box
|
||||
key={method.id}
|
||||
p={3}
|
||||
rounded="lg"
|
||||
border
|
||||
borderColor={method.isDefault ? 'border-primary-blue/50' : 'border-charcoal-outline/50'}
|
||||
bg={method.isDefault ? 'bg-primary-blue/5' : 'bg-iron-gray/5'}
|
||||
>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Icon icon={method.type === 'sepa' ? Building2 : CreditCard} size={5} color={method.isDefault ? 'text-primary-blue' : 'text-gray-400'} />
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-white">
|
||||
{method.brand || 'Bank Account'} •••• {method.last4}
|
||||
</Text>
|
||||
{method.isDefault && (
|
||||
<Text size="xs" color="text-primary-blue" weight="medium" block>Default</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
{!method.isDefault && (
|
||||
<Button variant="ghost" size="sm" onClick={() => onSetDefaultPaymentMethod(method.id)}>
|
||||
Set Default
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
<InfoBanner type="info">
|
||||
<Text size="xs">All payments are securely processed by our payment provider.</Text>
|
||||
</InfoBanner>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
{/* Support */}
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Heading level={3}>Billing Support</Heading>
|
||||
<Text size="sm" color="text-gray-400">
|
||||
Need help with an invoice or have questions about your plan?
|
||||
</Text>
|
||||
<Button variant="secondary" fullWidth icon={<Icon icon={ExternalLink} size={4} />}>
|
||||
Contact Support
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user