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:
@@ -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;
|
||||||
|
const fieldType = getFieldType(field);
|
||||||
|
|
||||||
|
if (fieldType === 'client-signature') {
|
||||||
|
// Blue "Sign Here" placeholder — client will sign at signing time
|
||||||
page.drawRectangle({
|
page.drawRectangle({
|
||||||
x: field.x,
|
x: field.x, y: field.y, width: field.width, height: field.height,
|
||||||
y: field.y,
|
borderColor: rgb(0.15, 0.39, 0.92), borderWidth: 1.5,
|
||||||
width: field.width,
|
|
||||||
height: field.height,
|
|
||||||
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,
|
|
||||||
size: 8,
|
|
||||||
font: helvetica,
|
|
||||||
color: rgb(0.15, 0.39, 0.92),
|
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();
|
||||||
|
|||||||
Reference in New Issue
Block a user