'use server' import { revalidatePath } from 'next/cache' import { cookies } from 'next/headers' import { getIronSession } from 'iron-session' import { z } from 'zod' import { prisma } from '@/lib/prisma' import { SessionData, sessionOptions } from '@/lib/session' import { productAccessFilter } from '@/lib/product-access' import { requireProductWriter } from '@/lib/auth' async function getSession() { return getIronSession(await cookies(), sessionOptions) } const taskSchema = z.object({ title: z.string().min(1, 'Titel is verplicht').max(200), description: z.string().max(1000).optional(), priority: z.coerce.number().int().min(1).max(4), }) export async function createTaskAction(_prevState: unknown, formData: FormData) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const storyId = formData.get('storyId') as string const sprintId = formData.get('sprintId') as string const parsed = taskSchema.safeParse({ title: formData.get('title'), description: formData.get('description') || undefined, priority: formData.get('priority') ?? 2, }) if (!parsed.success) return { error: parsed.error.flatten().fieldErrors } const story = await prisma.story.findFirst({ where: { id: storyId, product: productAccessFilter(session.userId) }, }) if (!story) return { error: 'Story niet gevonden' } const last = await prisma.task.findFirst({ where: { story_id: storyId }, orderBy: { sort_order: 'desc' }, }) const task = await prisma.task.create({ data: { story_id: storyId, sprint_id: sprintId || null, title: parsed.data.title, description: parsed.data.description ?? null, priority: parsed.data.priority, sort_order: (last?.sort_order ?? 0) + 1.0, status: 'TO_DO', }, }) revalidatePath(`/products/${story.product_id}/sprint/planning`) return { success: true, task } } export async function updateTaskAction(_prevState: unknown, formData: FormData) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const id = formData.get('id') as string const parsed = taskSchema.safeParse({ title: formData.get('title'), description: formData.get('description') || undefined, priority: formData.get('priority'), }) if (!parsed.success) return { error: parsed.error.flatten().fieldErrors } const task = await prisma.task.findFirst({ where: { id, story: { product: productAccessFilter(session.userId) } }, include: { story: true }, }) if (!task) return { error: 'Taak niet gevonden' } await prisma.task.update({ where: { id }, data: { title: parsed.data.title, description: parsed.data.description ?? null, priority: parsed.data.priority }, }) revalidatePath(`/products/${task.story.product_id}/sprint/planning`) return { success: true } } export async function updateTaskStatusAction(id: string, status: 'TO_DO' | 'IN_PROGRESS' | 'DONE') { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const task = await prisma.task.findFirst({ where: { id, story: { product: productAccessFilter(session.userId) } }, include: { story: true }, }) if (!task) return { error: 'Taak niet gevonden' } await prisma.task.update({ where: { id }, data: { status } }) revalidatePath(`/products/${task.story.product_id}/sprint/planning`) revalidatePath(`/products/${task.story.product_id}/solo`) return { success: true } } export async function deleteTaskAction(id: string) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const task = await prisma.task.findFirst({ where: { id, story: { product: productAccessFilter(session.userId) } }, include: { story: true }, }) if (!task) return { error: 'Taak niet gevonden' } await prisma.task.delete({ where: { id } }) revalidatePath(`/products/${task.story.product_id}/sprint/planning`) return { success: true } } const updateTaskPlanSchema = z.object({ taskId: z.string().min(1), productId: z.string().min(1), implementationPlan: z.string().max(10000), }) export async function updateTaskPlanAction(taskId: string, productId: string, implementationPlan: string) { try { await requireProductWriter(productId) } catch (e) { return { error: e instanceof Error ? e.message : 'Niet geautoriseerd' } } const parsed = updateTaskPlanSchema.safeParse({ taskId, productId, implementationPlan }) if (!parsed.success) return { error: 'Ongeldige invoer' } const task = await prisma.task.findFirst({ where: { id: taskId, story: { product_id: productId } }, include: { story: true }, }) if (!task) return { error: 'Taak niet gevonden' } await prisma.task.update({ where: { id: taskId }, data: { implementation_plan: implementationPlan || null }, }) revalidatePath(`/products/${productId}/solo`) revalidatePath(`/products/${productId}/sprint/planning`) return { success: true } } export async function reorderTasksAction(storyId: string, orderedIds: string[]) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const story = await prisma.story.findFirst({ where: { id: storyId, product: productAccessFilter(session.userId) }, }) if (!story) return { error: 'Story niet gevonden' } await prisma.$transaction( orderedIds.map((id, i) => prisma.task.update({ where: { id }, data: { sort_order: i + 1.0 } }) ) ) revalidatePath(`/products/${story.product_id}/sprint/planning`) return { success: true } }