import { formatCurrency } from "@/lib/utils" import { Document, Font, Image, Page, StyleSheet, Text, View } from "@react-pdf/renderer" import { formatDate } from "date-fns" import { ReactElement } from "react" import { AdditionalFee, AdditionalTax, InvoiceFormData, InvoiceItem } from "./invoice-page" Font.register({ family: "Inter", fonts: [ { src: "public/fonts/Inter/Inter-Regular.otf", fontWeight: 400, fontStyle: "normal", }, { src: "public/fonts/Inter/Inter-Medium.otf", fontWeight: 500, fontStyle: "normal", }, { src: "public/fonts/Inter/Inter-SemiBold.otf", fontWeight: 600, fontStyle: "normal", }, { src: "public/fonts/Inter/Inter-Bold.otf", fontWeight: 700, fontStyle: "normal", }, { src: "public/fonts/Inter/Inter-ExtraBold.otf", fontWeight: 800, fontStyle: "normal", }, { src: "public/fonts/Inter/Inter-Black.otf", fontWeight: 900, fontStyle: "normal", }, { src: "public/fonts/Inter-Italic.otf", fontWeight: 400, fontStyle: "italic", }, { src: "public/fonts/Inter/Inter-MediumItalic.otf", fontWeight: 500, fontStyle: "italic", }, { src: "public/fonts/Inter/Inter-SemiBoldItalic.otf", fontWeight: 600, fontStyle: "italic", }, { src: "public/fonts/Inter/Inter-BoldItalic.otf", fontWeight: 700, fontStyle: "italic", }, { src: "public/fonts/Inter/Inter-ExtraBoldItalic.otf", fontWeight: 800, fontStyle: "italic", }, { src: "public/fonts/Inter/Inter-BlackItalic.otf", fontWeight: 900, fontStyle: "italic", }, ], }) Font.registerEmojiSource({ format: "png", url: "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/72x72/", }) const styles = StyleSheet.create({ page: { fontFamily: "Inter", padding: 30, backgroundColor: "#ffffff", }, header: { marginBottom: 30, position: "relative", }, gradientBackground: { position: "absolute", top: 0, left: 0, right: 0, height: "120px", backgroundColor: "#eef2ff", opacity: 0.8, }, headerContent: { flexDirection: "row", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 30, }, headerLeft: { flex: 1, }, headerRight: { width: 110, height: 60, }, title: { fontSize: 28, marginBottom: 10, fontWeight: "extrabold", color: "#000000", }, subtitle: { fontSize: 13, color: "#666666", }, logo: { width: "100%", height: "100%", objectFit: "contain", }, companyDetails: { flexDirection: "row", justifyContent: "space-between", marginBottom: 30, }, companySection: { flex: 1, marginRight: 30, }, sectionLabel: { fontSize: 12, fontWeight: "bold", marginBottom: 8, color: "#000000", }, sectionContent: { fontSize: 12, lineHeight: 1.3, color: "#000000", }, datesAndCurrency: { flexDirection: "row", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 30, }, dateGroup: { flexDirection: "row", gap: 20, }, dateItem: { marginRight: 20, }, dateLabel: { fontSize: 12, fontWeight: "bold", marginBottom: 8, color: "#000000", }, dateValue: { fontSize: 12, color: "#000000", }, itemsTable: { marginBottom: 30, }, tableHeader: { flexDirection: "row", borderBottomWidth: 1, borderBottomColor: "#E5E7EB", paddingVertical: 8, backgroundColor: "#F9FAFB", textTransform: "uppercase", }, tableRow: { flexDirection: "row", borderBottomWidth: 1, borderBottomColor: "#E5E7EB", paddingVertical: 8, }, colDescription: { flex: 2, paddingHorizontal: 10, }, colQuantity: { flex: 1, paddingHorizontal: 10, textAlign: "right", }, colPrice: { flex: 1, paddingHorizontal: 10, textAlign: "right", }, colSubtotal: { flex: 1, paddingHorizontal: 10, textAlign: "right", }, colHeader: { fontSize: 10, fontWeight: "bold", color: "#6B7280", }, colValue: { fontSize: 12, color: "#000000", }, colName: { fontWeight: "semibold", }, itemSubtitle: { fontSize: 10, color: "#6B7280", marginTop: 2, }, notes: { marginBottom: 30, fontSize: 12, }, notesLabel: { fontWeight: "bold", marginBottom: 5, color: "#000000", }, summary: { width: "50%", alignSelf: "flex-end", }, summaryRow: { flexDirection: "row", justifyContent: "space-between", marginBottom: 5, }, summaryLabel: { fontSize: 12, color: "#4B5563", }, summaryValue: { fontSize: 12, color: "#000000", }, taxRow: { flexDirection: "row", justifyContent: "space-between", marginBottom: 5, }, totalRow: { flexDirection: "row", justifyContent: "space-between", marginTop: 10, paddingTop: 10, borderTopWidth: 1, borderTopColor: "#000000", }, totalLabel: { fontSize: 14, fontWeight: "bold", color: "#000000", }, totalValue: { fontSize: 14, fontWeight: "bold", color: "#000000", }, bankDetails: { marginTop: 30, paddingTop: 20, borderTopWidth: 1, borderTopColor: "#E5E7EB", textAlign: "center", fontSize: 11, color: "#6B7280", }, }) export function InvoicePDF({ data }: { data: InvoiceFormData }): ReactElement { const calculateSubtotal = (): number => { return data.items.reduce((sum: number, item: InvoiceItem) => sum + item.subtotal, 0) } const calculateTaxes = (): number => { return data.additionalTaxes.reduce((sum: number, tax: AdditionalTax) => sum + tax.amount, 0) } const calculateTotal = (): number => { const subtotal = calculateSubtotal() const taxes = calculateTaxes() return data.taxIncluded ? subtotal : subtotal + taxes } return ( {/* Header */} {data.title} {data.invoiceNumber} {data.businessLogo && ( )} {/* Company Details and Bill To */} {data.companyDetailsLabel} {data.companyDetails} {data.billToLabel} {data.billTo} {/* Dates and Currency */} {data.issueDateLabel} {formatDate(data.date, "yyyy-MM-dd")} {data.dueDateLabel} {formatDate(data.dueDate, "yyyy-MM-dd")} {/* Items Table */} {data.itemLabel} {data.quantityLabel} {data.unitPriceLabel} {data.subtotalLabel} {data.items.map((item: InvoiceItem, index: number) => ( {item.name} {item.showSubtitle && item.subtitle && {item.subtitle}} {item.quantity} {formatCurrency(item.unitPrice * 100, data.currency)} {formatCurrency(item.subtotal * 100, data.currency)} ))} {/* Notes */} {data.notes && ( Notes: {data.notes} )} {/* Summary */} {data.summarySubtotalLabel} {formatCurrency(calculateSubtotal() * 100, data.currency)} {data.additionalTaxes.map((tax: AdditionalTax, index: number) => ( {tax.name} ({tax.rate}%): {formatCurrency(tax.amount * 100, data.currency)} ))} {data.additionalFees.map((fee: AdditionalFee, index: number) => ( {fee.name} {formatCurrency(fee.amount * 100, data.currency)} ))} {data.summaryTotalLabel} {formatCurrency(calculateTotal() * 100, data.currency)} {/* Bank Details */} {data.bankDetails && ( {data.bankDetails} )} ) }