In-shell receipt preview before the print dialog fires. Operators get to eyeball the receipt before handing it to a customer or before committing to a reprint. Reuses the existing <KhmerReceipt> 80mm template under the hood, so what you see is exactly what the printer renders.
components/receipt/receipt-preview-modal.tsx. Renders the receipt centered + Cancel and Print buttons. Esc-to-close + backdrop click-outside both wired./orders per-row Print button now both open the preview instead of firing a print directly. Modal stays open during print so a quick reprint is one click.openPrintWindow calls have been removed from both client files (only the modal calls it under the hood). lib/native-receipt.ts still exports the iframe-based helper unchanged./cafe/products /pos /orders /reports/daily all 307 cleanly.nix-cafe:
components/receipt/receipt-preview-modal.tsx — NEW shared modal
app/(authed)/pos/starter-register-client.tsx — LastOrderBanner uses modal
app/(authed)/orders/starter-orders-client.tsx — per-row Print uses modal
(drops the hidden mount approach)
1. Commit nix-cafe (single -m line) — only nix-cafe touched, no backend, no migrations.
2. Push → CF Pages auto-redeploys cafe Worker.
3. Run new test-receipt-preview-prod.mjs:
- Ring 1 product, pay → success banner shows Print button
- Click Print → preview modal opens; receipt body has the order number
- Click Cancel → modal closes, no print fired (mock window.print)
- Click Print again → preview opens; click "Print receipt" in modal →
iframe element with receipt-print-iframe-{orderNumber} testid appears
- On /orders, click Print on a row → preview opens; same assertions
4. Regression sweep: round 3 + round 2 + round 1 + R2.4b + 70.2 + phase1 (70/70 already green).
5. Publish prod gallery + ask for final approval.