I have been experementing with printing invoices using Javascript and Tailwindcss. After several trials and errors the following is the best configuration I found to get the optimal results.
(optional) Configure Tailwindcss
If you are using tailwindcss to style your invoice then you can set the following configuration to access to 'print' and 'screen' prefixes that you can use to hide/show content based on your requirements.
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
screens: {
print: { raw: 'print' },
screen: { raw: 'screen' }
},
// ...
}
},
plugins: []
};
Now you can utilize this as follows:
<div class="screen:bg-red-300 print:bg-white" />
Add this to your primary CSS file
/* This will hide the extra header and footer contents added by the browser. */
@media print {
@page {
margin: 0.3in 0.7in 0.3in 0.7in !important;
}
}
/* Change this as you like */
@media screen {
html,
body {
width: 100vw;
height: 100vh;
display: flex;
overflow: auto;
background-color: #982b44;
}
}
Always use a separate route, do not use a pop up window.
Also, set the document title for better experience.
<head>
...
<title>your-file-name</title>
...
</head>
or
document.title = "your-file-name"
Use join('') to hide the unnecessary commas, if you are looping through the items like below.
const tableRows = orders.map((item, index) => {
// ...
return `
<tr class="border-b border-gray-200 break-inside-avoid break-before-auto">
<td class="max-w-0 py-2 pl-4 pr-3 text-sm sm:pl-0">
<div class="font-medium text-gray-900"><span class="mr-4 text-gray-500">${index + 1}.</span>${item.name}</div>
</td>
<td class="hidden px-3 py-2 text-right text-sm text-gray-500 sm:table-cell">${quantity}${weightUnit}</td>
<td class="hidden px-3 py-2 text-right text-sm text-gray-500 sm:table-cell">${price}</td>
<td class="py-2 pl-3 pr-4 text-right text-sm text-gray-500 sm:pr-0"><span class="text-xs">${item.currency}</span> ${total}</td>
</tr>
`;
});
and display this as,
<tbody>
${tableRows.join('')}
</tbody>
Sample Receipt
export function receiptGenerator(seller: any, order: any): string {
const panNum = 'XXXXXXXX';
const companyLogo = // your-company-logo
const deliveryAddr = order.deliveryAddress;
let vat = 0.0;
let subTotal = 0;
let currency = '';
const tableRows = order.items.map((item, index) => {
// ...
return `
<tr class="border-b border-gray-200 break-inside-avoid break-before-auto">
<td class="max-w-0 py-2 pl-4 pr-3 text-sm sm:pl-0">
<div class="font-medium text-gray-900"><span class="mr-4 text-gray-500">${index + 1}.</span>${item.name}</div>
</td>
<td class="hidden px-3 py-2 text-right text-sm text-gray-500 sm:table-cell">${quantity}${weightUnit}</td>
<td class="hidden px-3 py-2 text-right text-sm text-gray-500 sm:table-cell">${price}</td>
<td class="py-2 pl-3 pr-4 text-right text-sm text-gray-500 sm:pr-0"><span class="text-xs">${item.currency}</span> ${total}</td>
</tr>
`;
});
return `
<div class="mx-auto my-6 max-w-max rounded bg-white p-6 shadow-sm text-black">
<div class="grid grid-cols-2 items-center">
<div>
<img width="100" height="100" alt="company-logo" src="${companyLogo}" />
</div>
<div class="text-right">
<p>${seller.name}</p>
<p class="text-sm text-gray-500">${seller.email}</p>
<p class="mt-1 text-sm text-gray-500">${seller.phoneNumber}</p>
<p class="mt-1 text-sm text-gray-500">VAT: ${panNum}</p>
</div>
</div>
<!-- Client info -->
<div class="mt-8 grid grid-cols-2 items-center">
<div>
<p class="font-bold text-gray-800">Bill to :</p>
<p class="text-gray-500">
${deliveryAddr.addressLine1}
<br />
${deliveryAddr.state}, ${deliveryAddr.city} ${deliveryAddr.postcode}, ${deliveryAddr.country}
</p>
<p class="text-gray-500">info@email.com</p>
</div>
<div class="text-right">
<p class="">
Invoice number:
<span class="text-gray-500">${displayInvoiceNumber(order.orderNumber)}</span>
</p>
<p class="text-sm">
Invoice Date: <span class="text-gray-500">${dateTime.objectIdToDate(order.id)}</span>
<br />
Due Date: <span class="text-gray-500">${dateTime.objectIdToDate(order.id)}</span>
</p>
</div>
</div>
<!-- Invoice Items -->
<div class="-mx-4 mt-8 flow-root sm:mx-0">
<table class="min-w-full break-inside-auto">
<colgroup class="break-inside-auto">
<col class="w-full sm:w-1/2" />
<col class="sm:w-auto" />
<col class="sm:w-auto" />
<col class="sm:w-1/5" />
</colgroup>
<thead class="border-b border-gray-300 text-gray-900 table-header-group">
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">Items</th>
<th scope="col" class="hidden px-3 py-3.5 text-right text-sm font-semibold text-gray-900 sm:table-cell">Quantity</th>
<th scope="col" class="hidden px-3 py-3.5 text-right text-sm font-semibold text-gray-900 sm:table-cell">Price</th>
<th scope="col" class="py-3.5 pl-3 pr-4 text-right text-sm font-semibold text-gray-900 sm:pr-0">Amount</th>
</tr>
</thead>
<tbody>
${tableRows.join('')}
</tbody>
</table>
<table class="min-w-full">
<colgroup>
<col class="w-full sm:w-1/2" />
<col class="sm:w-auto" />
<col class="sm:w-auto" />
<col class="sm:w-1/5" />
</colgroup>
<thead>
<tr>
<th scope="row" colspan="3" class="hidden pl-4 pr-3 pt-6 text-right text-sm font-normal text-gray-500 sm:table-cell sm:pl-0">Subtotal</th>
<th scope="row" class="pl-6 pr-3 pt-2 text-left text-sm font-normal text-gray-500 sm:hidden">Subtotal</th>
<td class="pl-3 pr-6 pt-2 text-right text-sm text-gray-500 sm:pr-0"><span class="text-xs">${currency}</span> ${Math.round(subTotal) / 100}</td>
</tr>
<tr>
<th scope="row" colspan="3" class="hidden pl-4 pr-3 pt-2 text-right text-sm font-normal text-gray-500 sm:table-cell sm:pl-0">Tax</th>
<th scope="row" class="pl-6 pr-3 pt-2 text-left text-sm font-normal text-gray-500 sm:hidden">Tax</th>
<td class="pl-3 pr-6 pt-2 text-right text-sm text-gray-500 sm:pr-0"><span class="text-xs">${currency}</span> ${Math.round(subTotal * vat * 100) / 100}</td>
</tr>
<tr>
<th scope="row" colspan="3" class="hidden pl-4 pr-3 pt-2 text-right text-sm font-normal text-gray-500 sm:table-cell sm:pl-0">Discount</th>
<th scope="row" class="pl-6 pr-3 pt-2 text-left text-sm font-normal text-gray-500 sm:hidden">Discount</th>
<td class="pl-3 pr-6 pt-2 text-right text-sm text-gray-500 sm:pr-0">-</td>
</tr>
<tr>
<th scope="row" colspan="3" class="hidden pl-4 pr-3 pt-2 text-right text-sm font-semibold text-gray-900 sm:table-cell sm:pl-0">Total</th>
<th scope="row" class="pl-6 pr-3 pt-2 text-left text-sm font-semibold text-gray-900 sm:hidden">Total</th>
<td class="pl-3 pt-2 text-right text-sm font-semibold text-gray-900 sm:pr-0"><span class="text-xs">${currency}</span> ${Math.round((subTotal + subTotal * vat) * 100) / 100}</td>
</tr>
</thead>
</table>
</div>
<!-- Footer -->
<div class="mt-16 border-t-2 pt-4 text-center text-xs text-gray-500">Please pay the invoice before the due date. You can pay the invoice by logging in to your account from our client portal.</div>
</div>
`;
}
Hope this helps! Took me two days to make this perfect!
Top comments (0)