feat: add standalone printable invoice page with paper size selection
This commit is contained in:
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user