'use client' import { useParams } from 'next/navigation' import { useEffect, useState } from 'react' interface InvoiceItem { description: string quantity: number unitAmount: number id?: string } interface Customer { name: string email: string phone?: string company?: string taxId?: string billingAddress?: { line1: string line2?: string city: string state?: string postalCode: string country: string } } interface Invoice { id: string invoiceNumber: string customer: Customer currency: string items: InvoiceItem[] subtotal: number taxAmount?: number total: number status: string customMessage?: string issuedAt?: string dueDate?: string createdAt: string } export default function InvoiceViewPage() { const params = useParams() const invoiceId = params.id as string const [invoice, setInvoice] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState('') useEffect(() => { fetchInvoice() }, [invoiceId]) const fetchInvoice = async () => { try { const response = await fetch(`/api/demo/invoice/${invoiceId}`) const data = await response.json() if (data.success) { setInvoice(data.invoice) } else { setError(data.error || 'Failed to load invoice') } } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred') } finally { setLoading(false) } } const handlePrint = () => { window.print() } if (loading) { return (
Loading invoice...
) } if (error || !invoice) { return (
⚠️

Invoice Not Found

{error || 'The requested invoice could not be found.'}

Back to Demo
) } const formatCurrency = (amount: number) => { return `${invoice.currency.toUpperCase()} ${(amount / 100).toFixed(2)}` } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', }) } return (
{/* Print Button - Hidden when printing */}
{/* Invoice Container */}
{/* Header */}

INVOICE

Invoice #{invoice.invoiceNumber}

@xtr-dev/payload-billing

Test Provider Demo

{/* Bill To */}

Bill To

{invoice.customer.name}

{invoice.customer.company && (

{invoice.customer.company}

)}

{invoice.customer.email}

{invoice.customer.phone && (

{invoice.customer.phone}

)} {invoice.customer.billingAddress && (

{invoice.customer.billingAddress.line1}

{invoice.customer.billingAddress.line2 && (

{invoice.customer.billingAddress.line2}

)}

{invoice.customer.billingAddress.city} {invoice.customer.billingAddress.state && `, ${invoice.customer.billingAddress.state}`} {invoice.customer.billingAddress.postalCode}

{invoice.customer.billingAddress.country}

)} {invoice.customer.taxId && (

Tax ID: {invoice.customer.taxId}

)}
{/* Invoice Details */}

Invoice Details

Status: {invoice.status.toUpperCase()}
Issued: {formatDate(invoice.issuedAt || invoice.createdAt)}
{invoice.dueDate && (
Due: {formatDate(invoice.dueDate)}
)}
{/* Custom Message */} {invoice.customMessage && (

Message

{invoice.customMessage}

)} {/* Line Items Table */}
{invoice.items.map((item, index) => ( ))}
Description Qty Unit Price Amount
{item.description} {item.quantity} {formatCurrency(item.unitAmount)} {formatCurrency(item.unitAmount * item.quantity)}
{/* Totals */}
Subtotal: {formatCurrency(invoice.subtotal)}
{invoice.taxAmount !== undefined && invoice.taxAmount > 0 && (
Tax: {formatCurrency(invoice.taxAmount)}
)}
Total: {formatCurrency(invoice.total)}
{/* Footer */}

Thank you for your business!

This is a demo invoice generated by @xtr-dev/payload-billing plugin

{/* Back Button - Hidden when printing */}
← Back to Demo
{/* Print Styles */}
) }