WORKFLOW-APP/app/api/tasks/[id]/claim/route.ts
Dave 2fb979b98a Director appointment acceptance — full feature
- DirectorAppointmentTab: table + 'Acceptance Letter' modal with company/director dropdowns
- GET/POST /api/appointments: create record, generate HTML letter, convert to PDF via LibreOffice, email to director via himalaya SMTP
- PATCH /api/appointments/[id]: save signature PNG, embed in signed letter PDF, mail to all company directors
- GET /api/appointments/sign/[token]: public token lookup
- /sign/[token]/page.tsx: public signature canvas (mouse + touch)
- ysterbaard_director_appointments PB collection (requires manual schema setup)
- Section 3 'Director Appointment' now active in ResolutionExplorer
- NEXT_PUBLIC_APP_URL added to .env.local
2026-08-17 18:39:14 +02:00

51 lines
1.7 KiB
TypeScript

// app/api/tasks/[id]/claim/route.ts
// POST /api/tasks/[id]/claim
// Atomic state transition: ready → in_progress. Sets claimed_by and
// claimed_at. Uses PB's filter to ensure only one agent can claim.
import { NextRequest, NextResponse } from "next/server";
import { BASE_URL, PBRecord } from "@/lib/pb";
export const dynamic = "force-dynamic";
const COLLECTION = "tasks";
export async function POST(req: NextRequest, { params }: { params: { id: string } }) {
let body: any = {};
try {
body = await req.json();
} catch {
// empty body is fine
}
const claimedBy = (body?.claimed_by ?? "agent").toString().slice(0, 100);
// Atomic PATCH: only succeeds if status is currently "ready".
const url = `${BASE_URL}/api/collections/${COLLECTION}/records/${encodeURIComponent(params.id)}?filter=status="ready"`;
const res = await fetch(url, {
method: "PATCH",
headers: {
Authorization: process.env.PB_TOKEN as string,
"Content-Type": "application/json",
},
body: JSON.stringify({
status: "in_progress",
claimed_by: claimedBy,
claimed_at: new Date().toISOString().replace("T", " ").replace("Z", ""),
started_at: new Date().toISOString().replace("T", " ").replace("Z", ""),
}),
cache: "no-store",
});
if (res.status === 404) {
return NextResponse.json(
{ ok: false, error: "task not found or already claimed" },
{ status: 409 },
);
}
if (!res.ok) {
const text = await res.text();
return NextResponse.json({ ok: false, error: `PB ${res.status}: ${text.slice(0, 200)}` }, { status: 500 });
}
const data: PBRecord = await res.json();
return NextResponse.json({ ok: true, task: data });
}