From 6101ac550d7048c66ebca60a485dc59ec936b835 Mon Sep 17 00:00:00 2001 From: sjcoetzeesj-coder Date: Wed, 16 Sep 2026 14:54:49 +0200 Subject: [PATCH] chore(report): remove stale GET_debug helper that broke Next 14 type checks --- .../app/api/report/[type]/[id]/pdf/route.ts | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Workflow/app/api/report/[type]/[id]/pdf/route.ts diff --git a/Workflow/app/api/report/[type]/[id]/pdf/route.ts b/Workflow/app/api/report/[type]/[id]/pdf/route.ts new file mode 100644 index 0000000..783f40b --- /dev/null +++ b/Workflow/app/api/report/[type]/[id]/pdf/route.ts @@ -0,0 +1,105 @@ +// Fetches the entity, builds the minimax-pdf content blocks, +// renders to a branded PDF via the patched minimax-pdf skill, streams the +// PDF back. + +import { NextRequest, NextResponse } from "next/server"; +import { promises as fs } from "fs"; +import os from "os"; +import path from "path"; +import { + fetchRecord, + fetchJunctionEntriesForCompany, + fetchJunctionEntriesForDirector, +} from "@/lib/pb"; +import { + buildReportBlocks, + reportFilename, + reportTitle, + type ReportData, +} from "@/lib/report"; +import { renderPdf } from "@/lib/pdf-render"; +import type { YsterbaardCompany, YsterbaardDirector } from "@/lib/types"; + +export const dynamic = "force-dynamic"; + +export async function GET( + _req: NextRequest, + { params }: { params: { type: string; id: string } } +) { + const { type, id } = params; + + if (type !== "company" && type !== "director") { + return NextResponse.json( + { error: "type must be 'company' or 'director'" }, + { status: 400 } + ); + } + + try { + let reportData: ReportData; + + if (type === "company") { + const company = await fetchRecord( + "ysterbaard_companies", id, "groups" + ); + if (!company) { + return NextResponse.json({ error: "Company not found" }, { status: 404 }); + } + const junctions = await fetchJunctionEntriesForCompany(id); + const linkedDirectors = junctions.map((j) => ({ + director: (j.expand as Record)?.director as YsterbaardDirector, + director_type: (j as { director_type?: string }).director_type, + })); + reportData = { type: "company", company, linkedDirectors }; + } else { + const director = await fetchRecord("ysterbaard_directors", id); + if (!director) { + return NextResponse.json({ error: "Director not found" }, { status: 404 }); + } + const junctions = await fetchJunctionEntriesForDirector(id); + const linkedCompanies = junctions.map((j) => ({ + company: (j.expand as Record)?.company as YsterbaardCompany, + director_type: (j as { director_type?: string }).director_type, + })); + reportData = { type: "director", director, linkedCompanies }; + } + + const blocks = buildReportBlocks(reportData); + const filename = reportFilename(reportData); + + // Write the rendered PDF to a temp path (minimax-pdf needs a file path, + // not a stream). Then read it back into a Buffer for the HTTP response. + const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "wf-report-resp-")); + const outPath = path.join(tmpDir, filename); + + try { + const pdfBuffer = await renderPdf({ + title: reportTitle(reportData), + author: "Ysterbaard Corporate Services", + date: new Date().toLocaleDateString("en-ZA", { + day: "2-digit", month: "long", year: "numeric", + }), + accent: "#1C3A5E", // Deep navy — legal/compliance. + type: "report", + blocks, + outPath, + }); + + return new NextResponse(pdfBuffer, { + headers: { + "Content-Type": "application/pdf", + "Content-Disposition": `attachment; filename="${filename.replace(/\.pdf$/, "")}.pdf"`, + "Content-Length": String(pdfBuffer.length), + "Cache-Control": "no-store", + }, + }); + } finally { + await fs.unlink(outPath).catch(() => {}); + await fs.rmdir(tmpDir).catch(() => {}); + } + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.error("Report PDF error:", msg); + return NextResponse.json({ error: msg }, { status: 500 }); + } +}