Rebuild the Classic template on the engine with A4/Letter and a template harness
Classic now renders through buildRenderModel, computeFrame, the template and renderCore, with S.No and HSN/SAC columns, GST slots only through checked Slot components, taxable value, reverse-charge line, cancelled tag, continuation band and Page n of N. Letter absorbs its height deficit in elastic gaps. A harness runs 15 fixtures on A4 and Letter plus an item sweep and asserts zero audit errors, repeated headers, no split blocks, rupee extraction and deterministic output. The legacy InvoiceDocument is removed.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { Page, View } from "@react-pdf/renderer";
|
||||
import type { ReactNode } from "react";
|
||||
import type { PageFrame } from "./engine/geometry";
|
||||
import { chromeHeight } from "./engine/metrics";
|
||||
import type { RenderModel } from "./model/build";
|
||||
import { AvailableWidthProvider } from "./blocks/AvailableWidth";
|
||||
import { PageChrome } from "./blocks/PageChrome";
|
||||
|
||||
/** Height the footer strip takes inside the bottom padding: the text line, a 6 pt gap and the rule. */
|
||||
export const FOOTER_RESERVE = 24;
|
||||
/** Room above the content on pages 2+ for the continuation band, inside the top margin. */
|
||||
export const CONTINUATION_RESERVE = 18;
|
||||
|
||||
const CHROME_INK = "#6f6f6f";
|
||||
const RULE = "#e0e0e0";
|
||||
const FOOTER_LEFT_WIDTH = 170;
|
||||
|
||||
export interface InvoicePageProps {
|
||||
frame: PageFrame;
|
||||
model: RenderModel;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only `<Page>` in the app: size and padding come from the frame (no page-size or margin literal
|
||||
* anywhere else), the flow is the template's, and the chrome (footer, continuation band) is added after
|
||||
* it as fixed page-level boxes. No lineHeight here: it would reach the render-prop page-number text.
|
||||
*/
|
||||
export function InvoicePage({ frame, model, children }: InvoicePageProps) {
|
||||
const { pad } = frame;
|
||||
const marginBottom = pad.bottom - frame.footerReserve;
|
||||
const ruleY = frame.page.height - marginBottom - chromeHeight("footer") - 6.6;
|
||||
const bandLeft = `${model.docTitle} ${model.meta.number} · ${model.meta.date}`;
|
||||
return (
|
||||
<Page
|
||||
size={frame.page.size}
|
||||
style={{ paddingTop: pad.top, paddingRight: pad.right, paddingBottom: pad.bottom, paddingLeft: pad.left, color: "#161616" }}
|
||||
>
|
||||
<AvailableWidthProvider width={frame.contentWidth}>{children}</AvailableWidthProvider>
|
||||
<PageChrome
|
||||
frame={frame}
|
||||
footerLeft={model.supplier.name}
|
||||
footerLeftWidth={FOOTER_LEFT_WIDTH}
|
||||
footerCenter={model.footerText}
|
||||
color={CHROME_INK}
|
||||
band={{ left: bandLeft, right: "continued" }}
|
||||
decor={[{ x: pad.left, y: ruleY, width: frame.contentWidth, height: 0.6, children: <View style={{ height: 0.6, backgroundColor: RULE }} /> }]}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user