MEGAINFRA-WEBAPP/src/app/api/vehicles/route.ts

94 lines
2.9 KiB
TypeScript

import { authorizeOperation } from "@/lib/auth/operation";
import { NextResponse } from "next/server";
import { pbAdmin } from "@/lib/pb";
import type { UnitType } from "@/lib/pb-types";
/**
* POST /api/vehicles
*
* Creates a new vehicle row (used by the "Add plant" modal on the
* Plant dashboard). Required: `description`, `site` (relation id) — a
* vehicle's project attribution comes entirely from `site`, never a
* denormalized copy on this row. Everything else is optional and may
* be backfilled later via PATCH /api/vehicles/[id] or in PB admin.
*/
export async function POST(request: Request) {
const denied = await authorizeOperation(request);
if (denied) return denied;
let body: Record<string, unknown>;
try {
body = await request.json();
} catch {
return NextResponse.json({ error: "Body must be JSON" }, { status: 400 });
}
const description =
typeof body.description === "string" ? body.description.trim() : "";
if (!description) {
return NextResponse.json(
{ error: "description is required" },
{ status: 400 },
);
}
const site = typeof body.site === "string" ? body.site.trim() : "";
if (!site) {
return NextResponse.json(
{ error: "site is required" },
{ status: 400 },
);
}
// Optional fields — only include the key in the payload when the caller
// actually supplied a value, so PB doesn't reject "" for non-text fields
// (cost_per_hour / standard_work_hours are numeric in the schema).
const payload: Record<string, unknown> = {
description,
site,
};
if (typeof body.qr_code_id === "string") {
const qr = body.qr_code_id.trim();
if (qr) payload.qr_code_id = qr;
}
if (typeof body.fuel_limits === "string") {
const fl = body.fuel_limits.trim();
if (fl) payload.fuel_limits = fl;
}
if (body.unit_type === "fuel" || body.unit_type === "plant") {
payload.unit_type = body.unit_type as UnitType;
}
const numField = (v: unknown): number | undefined => {
if (v === undefined || v === null || v === "") return undefined;
const n = Number(v);
return Number.isFinite(n) ? n : undefined;
};
const standardWorkHours = numField(body.standard_work_hours);
if (standardWorkHours !== undefined) {
payload.standard_work_hours = standardWorkHours;
}
const costPerHour = numField(body.cost_per_hour);
if (costPerHour !== undefined) {
payload.cost_per_hour = costPerHour;
}
const pb = await pbAdmin();
try {
const created = await pb.collection("vehicles").create(payload);
return NextResponse.json({ vehicle: created }, { status: 201 });
} catch (err) {
const msg = err instanceof Error ? err.message : "Insert failed";
if (/UNIQUE|duplicate|idx_/i.test(msg)) {
return NextResponse.json(
{ error: msg },
{ status: 409 },
);
}
return NextResponse.json({ error: msg }, { status: 500 });
}
}