mega-plant/app/api/mechanics/route.ts
David c269cd00b4 Add Job Cards (vehicle-QR start/finish) and retire Operators/Mechanics pages
- Plant Clock "Job cards" tab: scan a vehicle QR to start a job, scan again to finish
  with odometer (or no meter), kind of work and a note
- /running/job-cards table with period, work, vehicle and mechanic filters
- Remove /running/operators and /running/mechanics; mechanic badge PDF moves to Employees

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 12:54:59 +02:00

66 lines
No EOL
2.5 KiB
TypeScript

import { revalidatePath } from "next/cache";
import { serverAuth } from "@/lib/pb";
export const dynamic = "force-dynamic";
// POST /api/mechanics — add a person to the badge register from the app.
//
// Mechanics turn up mid-season and a badge should not have to wait for the next rate
// sheet. The row is marked "Added in app" and the seed reconciles the collection by
// employee code rather than clearing it, so it survives every future re-seed.
//
// The employee number is REQUIRED here, not merely recommended: it is what the badge QR
// encodes. A mechanic without one cannot be issued a badge — the code would carry
// nothing and scan as a dead code — so the row would be a person you can never print.
export async function POST(req: Request) {
let body: { name?: string; employeeCode?: string; role?: string } = {};
try {
body = await req.json();
} catch {
return Response.json({ ok: false, error: "Invalid JSON body" }, { status: 400 });
}
const name = (body.name || "").trim();
const employeeCode = (body.employeeCode || "").trim();
const role = (body.role || "Mechanic").trim();
if (!name) return Response.json({ ok: false, error: "Name is required" }, { status: 400 });
if (!employeeCode) {
return Response.json(
{ ok: false, error: "Employee number is required — the badge QR encodes it" },
{ status: 400 },
);
}
try {
const pb = await serverAuth();
// The employee code IS the identity, and it is what the QR encodes. Two people
// sharing one would be indistinguishable to a scanner, so this is refused outright.
const clash = (await pb.collection("mechanics").getFullList())
.find((m) => (m.employee_code || "").trim().toUpperCase() === employeeCode.toUpperCase());
if (clash) {
return Response.json(
{ ok: false, error: `${employeeCode} is already on ${clash.name}'s badge` },
{ status: 409 },
);
}
const created = await pb.collection("mechanics").create({
name,
employee_code: employeeCode,
role,
source: "Added in app",
});
revalidatePath("/employees", "layout");
revalidatePath("/running");
revalidatePath("/employees", "layout");
return Response.json({
ok: true,
mechanic: { id: created.id, name: created.name, employee_code: created.employee_code, role: created.role },
});
} catch (e: any) {
return Response.json({ ok: false, error: e?.message || "Could not add the mechanic" }, { status: 500 });
}
}