feat(20-01): extend POST /api/documents with documentTemplateId branch
- Parse documentTemplateId from JSON body alongside formTemplateId - Fetch document template with formTemplate relation for PDF filename - Copy signatureFields with fresh crypto.randomUUID per field (snapshot independence) - Map template signer role labels to client email + contacts array - Return early with inserted doc; existing form-library and upload paths unchanged
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { auth } from '@/lib/auth';
|
||||
import { db } from '@/lib/db';
|
||||
import { documents, formTemplates } from '@/lib/db/schema';
|
||||
import { documents, formTemplates, documentTemplates, clients } from '@/lib/db/schema';
|
||||
import type { SignatureFieldData, ClientContact, DocumentSigner } from '@/lib/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { copyFile, mkdir, writeFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
@@ -8,6 +9,8 @@ import path from 'node:path';
|
||||
const SEEDS_DIR = path.join(process.cwd(), 'seeds', 'forms');
|
||||
const UPLOADS_DIR = path.join(process.cwd(), 'uploads');
|
||||
|
||||
const SIGNER_COLORS = ['#6366f1', '#f43f5e', '#10b981', '#f59e0b'];
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const session = await auth();
|
||||
if (!session) return new Response('Unauthorized', { status: 401 });
|
||||
@@ -17,6 +20,7 @@ export async function POST(req: Request) {
|
||||
let clientId: string;
|
||||
let name: string;
|
||||
let formTemplateId: string | undefined;
|
||||
let documentTemplateId: string | undefined;
|
||||
let fileBuffer: ArrayBuffer | undefined;
|
||||
|
||||
if (contentType.includes('multipart/form-data')) {
|
||||
@@ -32,6 +36,7 @@ export async function POST(req: Request) {
|
||||
clientId = body.clientId;
|
||||
name = body.name;
|
||||
formTemplateId = body.formTemplateId;
|
||||
documentTemplateId = body.documentTemplateId;
|
||||
}
|
||||
|
||||
if (!clientId || !name) {
|
||||
@@ -50,6 +55,66 @@ export async function POST(req: Request) {
|
||||
|
||||
await mkdir(destDir, { recursive: true });
|
||||
|
||||
// Document template branch — apply saved template with fresh field UUIDs
|
||||
if (documentTemplateId) {
|
||||
const docTemplate = await db.query.documentTemplates.findFirst({
|
||||
where: eq(documentTemplates.id, documentTemplateId),
|
||||
with: { formTemplate: true },
|
||||
});
|
||||
if (!docTemplate) return Response.json({ error: 'Document template not found' }, { status: 404 });
|
||||
|
||||
// Copy PDF from seeds dir using the form template's filename
|
||||
const srcPath = path.join(SEEDS_DIR, docTemplate.formTemplate.filename);
|
||||
await copyFile(srcPath, destPath);
|
||||
|
||||
// Copy fields with fresh UUIDs — hints preserved verbatim
|
||||
const rawFields: SignatureFieldData[] = (docTemplate.signatureFields as SignatureFieldData[] | null) ?? [];
|
||||
const copiedFields: SignatureFieldData[] = rawFields.map(f => ({
|
||||
...f,
|
||||
id: crypto.randomUUID(),
|
||||
}));
|
||||
|
||||
// Collect unique role labels in order of first appearance
|
||||
const seenRoles = new Set<string>();
|
||||
const uniqueRoles: string[] = [];
|
||||
for (const f of copiedFields) {
|
||||
if (f.signerEmail && !seenRoles.has(f.signerEmail)) {
|
||||
seenRoles.add(f.signerEmail);
|
||||
uniqueRoles.push(f.signerEmail);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch client for email + contacts for role-to-email mapping
|
||||
const client = await db.query.clients.findFirst({
|
||||
where: eq(clients.id, clientId),
|
||||
});
|
||||
const clientEmails = [
|
||||
client?.email,
|
||||
...((client?.contacts as ClientContact[] | null) ?? []).map(c => c.email),
|
||||
].filter(Boolean) as string[];
|
||||
|
||||
const mappedSigners: DocumentSigner[] = uniqueRoles.map((role, i) => ({
|
||||
email: clientEmails[i] ?? role,
|
||||
color: SIGNER_COLORS[i % SIGNER_COLORS.length],
|
||||
}));
|
||||
|
||||
// Use the form template's ID for the DB insert
|
||||
const resolvedFormTemplateId = docTemplate.formTemplateId;
|
||||
|
||||
const [doc] = await db.insert(documents).values({
|
||||
id: docId,
|
||||
clientId,
|
||||
name,
|
||||
formTemplateId: resolvedFormTemplateId,
|
||||
filePath: relPath,
|
||||
status: 'Draft',
|
||||
signatureFields: copiedFields,
|
||||
signers: mappedSigners.length > 0 ? mappedSigners : null,
|
||||
}).returning();
|
||||
|
||||
return Response.json(doc, { status: 201 });
|
||||
}
|
||||
|
||||
if (fileBuffer !== undefined) {
|
||||
// Custom upload
|
||||
await writeFile(destPath, Buffer.from(fileBuffer));
|
||||
|
||||
Reference in New Issue
Block a user