import { authenticateApiRequest } from '@/lib/api-auth' import { prisma } from '@/lib/prisma' import { z } from 'zod' import { TASK_STATUS_API_VALUES, taskStatusFromApi, taskStatusToApi } from '@/lib/task-status' import { propagateStatusUpwards } from '@/lib/tasks-status-update' // `review` is a valid TaskStatus in the DB and the kanban-board UI, but the // sprint task list (components/sprint/task-list.tsx) does not yet render it. // Reject it here until the sprint UI handles REVIEW so external clients don't // drive tasks into a state the shared UI can't display. const PATCHABLE_TASK_STATUS = TASK_STATUS_API_VALUES.filter((s) => s !== 'review') const VERIFY_REQUIRED_VALUES = ['ALIGNED', 'ALIGNED_OR_PARTIAL', 'ANY'] as const const patchSchema = z .object({ status: z.enum(PATCHABLE_TASK_STATUS as [string, ...string[]]).optional(), implementation_plan: z.string().optional(), verify_only: z.boolean().optional(), verify_required: z.enum(VERIFY_REQUIRED_VALUES).optional(), }) .refine( (data) => data.status !== undefined || data.implementation_plan !== undefined || data.verify_only !== undefined || data.verify_required !== undefined, { message: 'Geef minimaal status, implementation_plan, verify_only of verify_required mee' }, ) export async function PATCH( request: Request, { params }: { params: Promise<{ id: string }> } ) { const auth = await authenticateApiRequest(request) if ('error' in auth) { return Response.json({ error: auth.error }, { status: auth.status }) } if (auth.isDemo) { return Response.json({ error: 'Niet beschikbaar in demo-modus' }, { status: 403 }) } const { id } = await params const task = await prisma.task.findFirst({ where: { id }, include: { story: { include: { product: { include: { members: { where: { user_id: auth.userId }, select: { id: true }, }, }, }, }, }, }, }) if (!task) { return Response.json({ error: 'Taak niet gevonden' }, { status: 404 }) } const hasAccess = task.story.product.user_id === auth.userId || (task.story.product.members?.length ?? 0) > 0 if (!hasAccess) { return Response.json({ error: 'Geen toegang' }, { status: 403 }) } let body: unknown try { body = await request.json() } catch { return Response.json({ error: 'Malformed JSON' }, { status: 400 }) } const parsed = patchSchema.safeParse(body) if (!parsed.success) { return Response.json({ error: parsed.error.flatten() }, { status: 422 }) } let dbStatus: ReturnType | undefined if (parsed.data.status !== undefined) { dbStatus = taskStatusFromApi(parsed.data.status) if (dbStatus === null) { return Response.json( { error: { fieldErrors: { status: ['Onbekende status'] } } }, { status: 422 }, ) } } // Combine simple field writes (plan, verify_only, verify_required) into one update call const simpleData: { implementation_plan?: string; verify_only?: boolean; verify_required?: typeof VERIFY_REQUIRED_VALUES[number] } = {} if (parsed.data.implementation_plan !== undefined) simpleData.implementation_plan = parsed.data.implementation_plan if (parsed.data.verify_only !== undefined) simpleData.verify_only = parsed.data.verify_only if (parsed.data.verify_required !== undefined) simpleData.verify_required = parsed.data.verify_required const updated = await prisma.$transaction(async (tx) => { const simpleUpdate = Object.keys(simpleData).length > 0 ? await tx.task.update({ where: { id }, data: simpleData, select: { id: true, status: true, implementation_plan: true, verify_only: true, verify_required: true }, }) : null if (dbStatus !== undefined && dbStatus !== null) { const result = await propagateStatusUpwards(id, dbStatus, tx) return { id: result.task.id, status: result.task.status, implementation_plan: result.task.implementation_plan, verify_only: simpleUpdate?.verify_only, verify_required: simpleUpdate?.verify_required, } } if (simpleUpdate) return simpleUpdate // Should not reach here — patchSchema rejects bodies without recognized fields. throw new Error('Geen wijzigingen') }) return Response.json({ id: updated.id, status: taskStatusToApi(updated.status), implementation_plan: updated.implementation_plan, ...(updated.verify_only !== undefined && { verify_only: updated.verify_only }), ...(updated.verify_required !== undefined && { verify_required: updated.verify_required }), }) }