add download pdf functionality - MVP V1
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useBill } from "../hooks";
|
||||
import { generatePDF } from "../utils";
|
||||
import { BillHeader } from "./BillHeader";
|
||||
import { BillItemRow } from "./BillItemRow";
|
||||
import { BillSummary } from "./BillSummary";
|
||||
@@ -45,6 +46,15 @@ export default function App() {
|
||||
|
||||
<BillSummary bill={computedBill} />
|
||||
|
||||
{computedBill.items.length > 0 && (
|
||||
<button
|
||||
onClick={() => generatePDF(computedBill)}
|
||||
className="w-full bg-indigo-500 hover:bg-indigo-600 active:scale-95 text-white font-semibold py-3 rounded-xl text-sm transition-all"
|
||||
>
|
||||
Download PDF
|
||||
</button>
|
||||
)}
|
||||
|
||||
{computedBill.items.length > 0 && (
|
||||
<button
|
||||
className="w-full border border-gray-200 text-gray-400 hover:text-red-400 hover:border-red-200 py-3 rounded-xl text-sm transition-all cursor-pointer"
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import jsPDF from "jspdf";
|
||||
import type { ComputedBill } from "../types";
|
||||
import { formatCurrency } from "./calculator";
|
||||
|
||||
export function generatePDF(bill: ComputedBill): void {
|
||||
const doc = new jsPDF("p", "mm", "a4");
|
||||
|
||||
const pageWidth = doc.internal.pageSize.getWidth();
|
||||
const margin = 20;
|
||||
const contentWidth = pageWidth - margin * 2;
|
||||
|
||||
let currentY = 20;
|
||||
|
||||
doc.setFontSize(22);
|
||||
doc.setFont("helvetica", "bold");
|
||||
doc.setTextColor(60, 60, 60);
|
||||
doc.text(bill.title, margin, currentY);
|
||||
currentY += 8;
|
||||
|
||||
doc.setFontSize(10);
|
||||
doc.setFont("helvetica", "normal");
|
||||
doc.setTextColor(150, 150, 150);
|
||||
doc.text(
|
||||
bill.createdAt.toLocaleDateString("en-IN", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}),
|
||||
margin,
|
||||
currentY,
|
||||
);
|
||||
currentY += 6;
|
||||
|
||||
doc.setDrawColor(220, 220, 220);
|
||||
doc.line(margin, currentY, margin + contentWidth, currentY);
|
||||
currentY += 8;
|
||||
|
||||
doc.setFontSize(9);
|
||||
doc.setFont("helvetica", "bold");
|
||||
doc.setTextColor(120, 120, 120);
|
||||
|
||||
doc.text("ITEM", margin, currentY);
|
||||
doc.text("QTY", margin + contentWidth * 0.6, currentY, { align: "center" });
|
||||
doc.text("AMOUNT", margin + contentWidth, currentY, { align: "right" });
|
||||
currentY += 5;
|
||||
|
||||
doc.setDrawColor(230, 230, 230);
|
||||
doc.line(margin, currentY, margin + contentWidth, currentY);
|
||||
currentY += 5;
|
||||
|
||||
doc.setFont("helvetica", "normal");
|
||||
doc.setTextColor(60, 60, 60);
|
||||
|
||||
bill.items.forEach((item) => {
|
||||
doc.setFontSize(11);
|
||||
doc.text(item.name, margin, currentY);
|
||||
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(120, 120, 120);
|
||||
doc.text(
|
||||
`${item.quantity} × ${formatCurrency(item.unitPrice, bill.currency)}`,
|
||||
margin,
|
||||
currentY + 4,
|
||||
);
|
||||
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(60, 60, 60);
|
||||
doc.text(String(item.quantity), margin + contentWidth * 0.6, currentY, {
|
||||
align: "center",
|
||||
});
|
||||
doc.text(
|
||||
formatCurrency(item.finalPrice, bill.currency),
|
||||
margin + contentWidth,
|
||||
currentY,
|
||||
{ align: "right" },
|
||||
);
|
||||
|
||||
currentY += 12;
|
||||
|
||||
if (currentY > 270) {
|
||||
doc.addPage();
|
||||
currentY = 20;
|
||||
}
|
||||
});
|
||||
|
||||
currentY += 4;
|
||||
|
||||
doc.setDrawColor(220, 220, 220);
|
||||
doc.line(margin, currentY, margin + contentWidth, currentY);
|
||||
currentY += 8;
|
||||
|
||||
doc.setFontSize(11);
|
||||
doc.setFont("helvetica", "normal");
|
||||
doc.setTextColor(120, 120, 120);
|
||||
doc.text("Subtotal", margin, currentY);
|
||||
doc.text(
|
||||
formatCurrency(bill.subtotal, bill.currency),
|
||||
margin + contentWidth,
|
||||
currentY,
|
||||
{ align: "right" },
|
||||
);
|
||||
currentY += 7;
|
||||
|
||||
if (bill.taxPercent > 0) {
|
||||
doc.text(`${bill.taxType} (${bill.taxPercent}%)`, margin, currentY);
|
||||
doc.text(
|
||||
formatCurrency(bill.taxAmount, bill.currency),
|
||||
margin + contentWidth,
|
||||
currentY,
|
||||
{ align: "right" },
|
||||
);
|
||||
currentY += 7;
|
||||
}
|
||||
|
||||
doc.setDrawColor(220, 220, 220);
|
||||
doc.line(margin, currentY, margin + contentWidth, currentY);
|
||||
currentY += 7;
|
||||
|
||||
doc.setFont("helvetica", "bold");
|
||||
doc.setFontSize(13);
|
||||
doc.setTextColor(60, 60, 60);
|
||||
doc.text("Total", margin, currentY);
|
||||
|
||||
// Total amount in indigo-like colour
|
||||
doc.setTextColor(99, 102, 241);
|
||||
doc.text(
|
||||
formatCurrency(bill.total, bill.currency),
|
||||
margin + contentWidth,
|
||||
currentY,
|
||||
{ align: "right" },
|
||||
);
|
||||
|
||||
const fileName = `${bill.title.replace(/\s+/g, "-").toLowerCase()}-bill.pdf`;
|
||||
doc.save(fileName);
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./calculator";
|
||||
export * from "./generatePDF";
|
||||
|
||||
Reference in New Issue
Block a user