feat(10-02): type-branched field rendering in preparePdf()

- Add getFieldType import from @/lib/db/schema
- Replace single-variant blue loop with branched rendering per field type
- client-signature: unchanged blue rectangle + "Sign Here"
- initials: purple rectangle + "Initials" label
- checkbox: gray rectangle + X diagonal lines (embedded at prepare time)
- date: amber rectangle + "Date" label (actual date stamped at POST time)
- text: light gray rectangle, no label (visual marker only)
- agent-signature: skipped (no placeholder drawn)
This commit is contained in:
Chandler Copeland
2026-03-21 12:49:20 -06:00
parent 2205a7bce5
commit 7510c8ee08

View File

@@ -1,6 +1,7 @@
import { PDFDocument, StandardFonts, rgb } from '@cantoo/pdf-lib'; import { PDFDocument, StandardFonts, rgb } from '@cantoo/pdf-lib';
import { readFile, writeFile, rename } from 'node:fs/promises'; import { readFile, writeFile, rename } from 'node:fs/promises';
import type { SignatureFieldData } from '@/lib/db/schema'; import type { SignatureFieldData } from '@/lib/db/schema';
import { getFieldType } from '@/lib/db/schema';
/** /**
* Fills AcroForm text fields and draws signature rectangles on a PDF. * Fills AcroForm text fields and draws signature rectangles on a PDF.
@@ -87,26 +88,79 @@ export async function preparePdf(
} }
} }
// Draw signature field placeholders (blue rectangle + "Sign Here" label) // Draw field placeholders — rendering varies by field type
for (const field of sigFields) { for (const field of sigFields) {
const page = pages[field.page - 1]; // page is 1-indexed const page = pages[field.page - 1]; // page is 1-indexed
if (!page) continue; if (!page) continue;
page.drawRectangle({ const fieldType = getFieldType(field);
x: field.x,
y: field.y, if (fieldType === 'client-signature') {
width: field.width, // Blue "Sign Here" placeholder — client will sign at signing time
height: field.height, page.drawRectangle({
borderColor: rgb(0.15, 0.39, 0.92), x: field.x, y: field.y, width: field.width, height: field.height,
borderWidth: 1.5, borderColor: rgb(0.15, 0.39, 0.92), borderWidth: 1.5,
color: rgb(0.90, 0.94, 0.99), color: rgb(0.90, 0.94, 0.99),
}); });
page.drawText('Sign Here', { page.drawText('Sign Here', {
x: field.x + 4, x: field.x + 4, y: field.y + 4, size: 8, font: helvetica,
y: field.y + 4, color: rgb(0.15, 0.39, 0.92),
size: 8, });
font: helvetica,
color: rgb(0.15, 0.39, 0.92), } else if (fieldType === 'initials') {
}); // Purple "Initials" placeholder — client will initial at signing time
page.drawRectangle({
x: field.x, y: field.y, width: field.width, height: field.height,
borderColor: rgb(0.49, 0.23, 0.93), borderWidth: 1.5,
color: rgb(0.95, 0.92, 1.0),
});
page.drawText('Initials', {
x: field.x + 4, y: field.y + 4, size: 8, font: helvetica,
color: rgb(0.49, 0.23, 0.93),
});
} else if (fieldType === 'checkbox') {
// Checked box: light gray background + X crossing diagonals (embedded at prepare time)
page.drawRectangle({
x: field.x, y: field.y, width: field.width, height: field.height,
borderColor: rgb(0.1, 0.1, 0.1), borderWidth: 1.5,
color: rgb(0.95, 0.95, 0.95),
});
// X mark: two diagonals
page.drawLine({
start: { x: field.x + 2, y: field.y + 2 },
end: { x: field.x + field.width - 2, y: field.y + field.height - 2 },
thickness: 1.5, color: rgb(0.1, 0.1, 0.1),
});
page.drawLine({
start: { x: field.x + field.width - 2, y: field.y + 2 },
end: { x: field.x + 2, y: field.y + field.height - 2 },
thickness: 1.5, color: rgb(0.1, 0.1, 0.1),
});
} else if (fieldType === 'date') {
// Light placeholder rectangle — actual signing date stamped at POST time in route.ts
page.drawRectangle({
x: field.x, y: field.y, width: field.width, height: field.height,
borderColor: rgb(0.85, 0.47, 0.04), borderWidth: 1,
color: rgb(1.0, 0.97, 0.90),
});
page.drawText('Date', {
x: field.x + 4, y: field.y + 4, size: 8, font: helvetica,
color: rgb(0.85, 0.47, 0.04),
});
} else if (fieldType === 'text') {
// Light background rectangle — text content is provided via textFillData (separate pipeline)
// type='text' SignatureFieldData fields are visual position markers only
page.drawRectangle({
x: field.x, y: field.y, width: field.width, height: field.height,
borderColor: rgb(0.39, 0.45, 0.55), borderWidth: 1,
color: rgb(0.96, 0.97, 0.98),
});
} else if (fieldType === 'agent-signature') {
// Skip — agent signature handled by Phase 11; no placeholder drawn here
}
} }
const modifiedBytes = await pdfDoc.save(); const modifiedBytes = await pdfDoc.save();