add download pdf functionality - MVP V1

This commit is contained in:
2026-05-07 12:24:07 +05:30
parent ce49cf353b
commit 62e6759047
3 changed files with 146 additions and 0 deletions
+10
View File
@@ -1,4 +1,5 @@
import { useBill } from "../hooks"; import { useBill } from "../hooks";
import { generatePDF } from "../utils";
import { BillHeader } from "./BillHeader"; import { BillHeader } from "./BillHeader";
import { BillItemRow } from "./BillItemRow"; import { BillItemRow } from "./BillItemRow";
import { BillSummary } from "./BillSummary"; import { BillSummary } from "./BillSummary";
@@ -45,6 +46,15 @@ export default function App() {
<BillSummary bill={computedBill} /> <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 && ( {computedBill.items.length > 0 && (
<button <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" 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"
+135
View File
@@ -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
View File
@@ -1 +1,2 @@
export * from "./calculator"; export * from "./calculator";
export * from "./generatePDF";