- 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>
66 lines
No EOL
2.5 KiB
TypeScript
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 });
|
|
}
|
|
} |