feat: add standalone printable invoice page with paper size selection

This commit is contained in:
2026-10-21 12:54:51 +05:30
parent f2d5c9f50a
commit 5f8bc70174
@@ -0,0 +1,68 @@
'use client'
import { useEffect, useState } from 'react'
import { useParams } from 'next/navigation'
import { Button } from '@/components/ui/button'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { InvoiceTemplate, type InvoicePaper } from '@/components/invoice/invoice-template'
import { useInvoiceDocument } from '@/components/invoice/use-invoice-document'
// Browser page rule per paper size. Receipts are one continuous strip (auto height). Margin is 0 so the
// browser has no margin area to print its own header/footer (page title, URL, date) into; the sheet pads itself.
const PAGE_RULE: Record<InvoicePaper, string> = {
A4: '@page { size: A4; margin: 0; }',
'80mm': '@page { size: 80mm auto; margin: 0; }',
'58mm': '@page { size: 58mm auto; margin: 0; }',
}
// Standalone printable invoice (no dashboard chrome). Opened in a new tab after "Print".
export default function InvoicePrintPage() {
const params = useParams<{ id: string }>()
const { invoice, payments, customer, error } = useInvoiceDocument(params.id)
const [paper, setPaper] = useState<InvoicePaper>('A4')
// Open the browser print dialog once, when the invoice first appears.
useEffect(() => {
if (!invoice) return
const t = setTimeout(() => window.print(), 600)
return () => clearTimeout(t)
}, [invoice])
if (error) return <p className="p-8 text-sm text-destructive">Failed to load invoice: {error}</p>
if (!invoice) return <p className="p-8 text-sm text-muted-foreground">Loading invoice…</p>
return (
<div className="mx-auto max-w-4xl p-6 print:max-w-none print:p-0">
<style>{`${PAGE_RULE[paper]} @media print { html, body { background: #fff !important; } }`}</style>
<div className="mb-4 flex items-center justify-end gap-2 print:hidden">
<div className="w-32">
<Select value={paper} onValueChange={(v) => setPaper((v as InvoicePaper) ?? 'A4')}>
<SelectTrigger><SelectValue placeholder="Paper size" /></SelectTrigger>
<SelectContent>
<SelectItem value="A4">A4</SelectItem>
<SelectItem value="80mm">80mm receipt</SelectItem>
<SelectItem value="58mm">58mm receipt</SelectItem>
</SelectContent>
</Select>
</div>
<Button type="button" variant="outline" onClick={() => window.close()}>Close</Button>
<Button type="button" onClick={() => window.print()}>Print</Button>
</div>
<InvoiceTemplate
invoice={invoice}
payments={payments}
customer={customer}
paper={paper}
showAgentEdits={invoice.status !== 'Finalized'}
/>
</div>
)
}