Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
498816dddb
commit
e1ef9495cb
@@ -0,0 +1,112 @@
|
||||
import jsPDF from "jspdf";
|
||||
import autoTable from "jspdf-autotable";
|
||||
|
||||
export interface TimeReportRow {
|
||||
work_date: string;
|
||||
case_label: string;
|
||||
user_name: string;
|
||||
description: string;
|
||||
hours: number;
|
||||
rate: number;
|
||||
amount: number;
|
||||
billable: boolean;
|
||||
}
|
||||
|
||||
export interface ExpenseReportRow {
|
||||
expense_date: string;
|
||||
case_label: string;
|
||||
user_name: string;
|
||||
description: string;
|
||||
amount: number;
|
||||
billable: boolean;
|
||||
}
|
||||
|
||||
interface BaseOpts {
|
||||
clientName: string;
|
||||
fromDate?: string;
|
||||
toDate?: string;
|
||||
}
|
||||
|
||||
function fmtDate(iso?: string | null) {
|
||||
if (!iso) return "—";
|
||||
const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
||||
if (m) {
|
||||
const d = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3]));
|
||||
return d.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", timeZone: "UTC" });
|
||||
}
|
||||
return new Date(iso).toLocaleDateString("en-US");
|
||||
}
|
||||
|
||||
function fmtCurrency(n: number) {
|
||||
return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(n || 0);
|
||||
}
|
||||
|
||||
function header(doc: jsPDF, title: string, opts: BaseOpts) {
|
||||
const margin = 54;
|
||||
doc.setFont("times", "bold");
|
||||
doc.setFontSize(18);
|
||||
doc.text(title, margin, margin);
|
||||
doc.setFont("times", "normal");
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(90);
|
||||
doc.text(opts.clientName, margin, margin + 22);
|
||||
if (opts.fromDate || opts.toDate) {
|
||||
const range = `${opts.fromDate ? fmtDate(opts.fromDate) : "—"} to ${opts.toDate ? fmtDate(opts.toDate) : "—"}`;
|
||||
doc.text(range, margin, margin + 38);
|
||||
}
|
||||
doc.setTextColor(0);
|
||||
}
|
||||
|
||||
export function generateTimeReportPdf(opts: BaseOpts & { rows: TimeReportRow[] }): jsPDF {
|
||||
const doc = new jsPDF({ unit: "pt", format: "letter" });
|
||||
header(doc, "Time Report", opts);
|
||||
const totalHours = opts.rows.reduce((a, r) => a + (r.hours || 0), 0);
|
||||
const totalAmt = opts.rows.reduce((a, r) => a + (r.amount || 0), 0);
|
||||
autoTable(doc, {
|
||||
startY: 110,
|
||||
head: [["Date", "Case", "User", "Description", "Hours", "Rate", "Amount"]],
|
||||
body: opts.rows.map((r) => [
|
||||
fmtDate(r.work_date),
|
||||
r.case_label,
|
||||
r.user_name,
|
||||
r.description,
|
||||
r.hours.toFixed(2),
|
||||
fmtCurrency(r.rate),
|
||||
fmtCurrency(r.amount),
|
||||
]),
|
||||
foot: [["", "", "", "Totals", totalHours.toFixed(2), "", fmtCurrency(totalAmt)]],
|
||||
styles: { font: "times", fontSize: 9, cellPadding: 4 },
|
||||
headStyles: { fillColor: [240, 240, 240], textColor: 20 },
|
||||
footStyles: { fillColor: [240, 240, 240], textColor: 20, fontStyle: "bold" },
|
||||
columnStyles: {
|
||||
4: { halign: "right" },
|
||||
5: { halign: "right" },
|
||||
6: { halign: "right" },
|
||||
},
|
||||
});
|
||||
return doc;
|
||||
}
|
||||
|
||||
export function generateExpenseReportPdf(opts: BaseOpts & { rows: ExpenseReportRow[] }): jsPDF {
|
||||
const doc = new jsPDF({ unit: "pt", format: "letter" });
|
||||
header(doc, "Expense Report", opts);
|
||||
const totalAmt = opts.rows.reduce((a, r) => a + (r.amount || 0), 0);
|
||||
autoTable(doc, {
|
||||
startY: 110,
|
||||
head: [["Date", "Case", "User", "Description", "Billable", "Amount"]],
|
||||
body: opts.rows.map((r) => [
|
||||
fmtDate(r.expense_date),
|
||||
r.case_label,
|
||||
r.user_name,
|
||||
r.description,
|
||||
r.billable ? "Yes" : "No",
|
||||
fmtCurrency(r.amount),
|
||||
]),
|
||||
foot: [["", "", "", "", "Total", fmtCurrency(totalAmt)]],
|
||||
styles: { font: "times", fontSize: 9, cellPadding: 4 },
|
||||
headStyles: { fillColor: [240, 240, 240], textColor: 20 },
|
||||
footStyles: { fillColor: [240, 240, 240], textColor: 20, fontStyle: "bold" },
|
||||
columnStyles: { 5: { halign: "right" } },
|
||||
});
|
||||
return doc;
|
||||
}
|
||||
Reference in New Issue
Block a user