feat(plant-hours): GET /api/plant-hours/template route
Emits the canonical xlsx with a sample row derived from the first vehicle in PocketBase. Falls back to a hardcoded sample if the vehicles collection is empty.
This commit is contained in:
parent
c36eaeed51
commit
7b8fe71db3
1 changed files with 57 additions and 0 deletions
57
src/app/api/plant-hours/template/route.ts
Normal file
57
src/app/api/plant-hours/template/route.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { pbAdmin } from "@/lib/pb";
|
||||
import { buildPlantHoursTemplateWorkbook } from "@/lib/plant-hours-template";
|
||||
|
||||
/**
|
||||
* GET /api/plant-hours/template
|
||||
*
|
||||
* Emits the canonical Plant_Hours_Template_2.xlsx workbook. The
|
||||
* sample row's vehicle QR / description / cost-per-hour are pulled
|
||||
* from the first vehicle in PocketBase (plant or fuel — doesn't
|
||||
* matter, the template just needs plausible real data).
|
||||
*/
|
||||
export async function GET() {
|
||||
const start = Date.now();
|
||||
try {
|
||||
const pb = await pbAdmin();
|
||||
const first = await pb
|
||||
.collection("vehicles")
|
||||
.getList(1, 1, { sort: "description" })
|
||||
.then((r) => r.items[0])
|
||||
.catch(() => null);
|
||||
|
||||
const sample = first
|
||||
? {
|
||||
qr_code_id: first.qr_code_id || "QR-A",
|
||||
description: first.description || "Generator X",
|
||||
cost_per_hour: first.cost_per_hour || 185,
|
||||
}
|
||||
: { qr_code_id: "QR-A", description: "Generator X", cost_per_hour: 185 };
|
||||
|
||||
// SheetJS returns a Uint8Array whose underlying buffer TS can't
|
||||
// narrow to ArrayBuffer (the lib.dom Uint8Array<ArrayBuffer>
|
||||
// generic is stricter than what XLSX.write emits). The xlsx bytes
|
||||
// are unambiguously an ArrayBuffer-backed view at runtime, so a
|
||||
// single cast is enough to satisfy NextResponse's BodyInit type.
|
||||
const bytes = buildPlantHoursTemplateWorkbook(sample) as Uint8Array<ArrayBuffer>;
|
||||
console.log(
|
||||
`[plant-hours-template] size=${bytes.byteLength} duration_ms=${Date.now() - start}`,
|
||||
);
|
||||
return new NextResponse(bytes, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type":
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"Content-Disposition":
|
||||
'attachment; filename="Plant_Hours_Template_2.xlsx"',
|
||||
"Cache-Control": "no-store",
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`[plant-hours-template] error:`, err);
|
||||
return NextResponse.json(
|
||||
{ error: err instanceof Error ? err.message : "template_failed" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue