Unify financial report styling with branded cover page

Extract the general Report Generator's branded cover (cover image/band,
logo, title, prepared-for/by) into shared src/lib/reportCover.ts. Financial
reports now open with the same cover: platform AccountingReportsPage via new
renderReportPdfWithCover(), and the Zoho/Board financial reports
(zohoFinancialReportPdf generators). ReportGeneratorPage refactored to use
the shared module (removes duplicated cover code).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 21:49:34 -04:00
parent 3c32f8ac47
commit 8360363a15
5 changed files with 196 additions and 125 deletions
+28 -4
View File
@@ -1,4 +1,5 @@
import jsPDF from "jspdf";
import { drawReportCoverPage, type ReportCoverData } from "@/lib/reportCover";
export type RowKind = "section" | "group" | "sub" | "total" | "grand" | "spacer";
@@ -47,8 +48,9 @@ export function fmtAmount(n: number | undefined | null): string {
return n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
export function renderReportPdf(report: StructuredReport, opts: RenderOpts): jsPDF {
const doc = new jsPDF({ unit: "pt", format: "letter" });
/** Draw the report body onto `doc`, starting on its current page. */
function buildReport(doc: jsPDF, report: StructuredReport, opts: RenderOpts): jsPDF {
const firstPage = doc.getNumberOfPages();
const W = doc.internal.pageSize.getWidth();
const H = doc.internal.pageSize.getHeight();
const ML = 40, MR = 40;
@@ -114,11 +116,12 @@ export function renderReportPdf(report: StructuredReport, opts: RenderOpts): jsP
const drawFooter = () => {
const total = doc.getNumberOfPages();
const created = new Date().toLocaleDateString("en-US");
for (let p = 1; p <= total; p++) {
// Footer only on content pages (skip any preceding cover page).
for (let p = firstPage; p <= total; p++) {
doc.setPage(p);
doc.setFont("helvetica", "normal"); doc.setFontSize(8); doc.setTextColor(...MUTED);
doc.text(`Created on ${created}`, ML, H - 28);
doc.text(`Page ${p}`, contentR, H - 28, { align: "right" });
doc.text(`Page ${p - firstPage + 1}`, contentR, H - 28, { align: "right" });
}
};
@@ -233,3 +236,24 @@ export function renderReportPdf(report: StructuredReport, opts: RenderOpts): jsP
drawFooter();
return doc;
}
/** Render a financial report PDF (no cover page). */
export function renderReportPdf(report: StructuredReport, opts: RenderOpts): jsPDF {
const doc = new jsPDF({ unit: "pt", format: "letter" });
return buildReport(doc, report, opts);
}
/**
* Render a financial report PDF that opens with the shared branded cover page
* (same look as the general Report Generator), followed by the report body.
*/
export async function renderReportPdfWithCover(
report: StructuredReport,
opts: RenderOpts,
cover: ReportCoverData,
): Promise<jsPDF> {
const doc = new jsPDF({ unit: "pt", format: "letter" });
await drawReportCoverPage(doc, doc.internal.pageSize.getWidth(), doc.internal.pageSize.getHeight(), cover);
doc.addPage();
return buildReport(doc, report, opts);
}