'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 { getAccessibleProduct, productAccessFilter } from '@/lib/product-access' async function getSession() { return getIronSession(await cookies(), sessionOptions) } export async function createSprintAction(_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 parsed = z.object({ productId: z.string(), sprint_goal: z.string().min(1, 'Sprint Goal is verplicht').max(500), }).safeParse({ productId: formData.get('productId'), sprint_goal: formData.get('sprint_goal'), }) if (!parsed.success) return { error: parsed.error.flatten().fieldErrors } const product = await getAccessibleProduct(parsed.data.productId, session.userId) if (!product) return { error: 'Product niet gevonden' } const existing = await prisma.sprint.findFirst({ where: { product_id: parsed.data.productId, status: 'ACTIVE' }, }) if (existing) return { error: 'Er is al een actieve Sprint voor dit product', sprintId: existing.id } const sprint = await prisma.sprint.create({ data: { product_id: parsed.data.productId, sprint_goal: parsed.data.sprint_goal, status: 'ACTIVE', }, }) revalidatePath(`/products/${parsed.data.productId}`) return { success: true, sprintId: sprint.id } } export async function updateSprintGoalAction(_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 sprint_goal = formData.get('sprint_goal') as string if (!sprint_goal?.trim()) return { error: 'Sprint Goal is verplicht' } const sprint = await prisma.sprint.findFirst({ where: { id, product: productAccessFilter(session.userId) }, }) if (!sprint) return { error: 'Sprint niet gevonden' } await prisma.sprint.update({ where: { id }, data: { sprint_goal } }) revalidatePath(`/products/${sprint.product_id}/sprint`) return { success: true } } export async function addStoryToSprintAction(sprintId: string, storyId: string) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const sprint = await prisma.sprint.findFirst({ where: { id: sprintId, product: productAccessFilter(session.userId) }, }) if (!sprint) return { error: 'Sprint niet gevonden' } const story = await prisma.story.findFirst({ where: { id: storyId, product: productAccessFilter(session.userId) }, }) if (!story) return { error: 'Story niet gevonden' } const last = await prisma.story.findFirst({ where: { sprint_id: sprintId }, orderBy: { sort_order: 'desc' }, }) await prisma.story.update({ where: { id: storyId }, data: { sprint_id: sprintId, status: 'IN_SPRINT', sort_order: (last?.sort_order ?? 0) + 1.0 }, }) revalidatePath(`/products/${sprint.product_id}/sprint`) revalidatePath(`/products/${sprint.product_id}`) return { success: true } } export async function removeStoryFromSprintAction(storyId: 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) }, include: { sprint: true }, }) if (!story) return { error: 'Story niet gevonden' } await prisma.story.update({ where: { id: storyId }, data: { sprint_id: null, status: 'OPEN' }, }) revalidatePath(`/products/${story.product_id}/sprint`) revalidatePath(`/products/${story.product_id}`) return { success: true } } export async function reorderSprintStoriesAction(sprintId: 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 sprint = await prisma.sprint.findFirst({ where: { id: sprintId, product: productAccessFilter(session.userId) }, }) if (!sprint) return { error: 'Sprint niet gevonden' } await prisma.$transaction( orderedIds.map((id, i) => prisma.story.update({ where: { id }, data: { sort_order: i + 1.0 } }) ) ) revalidatePath(`/products/${sprint.product_id}/sprint`) return { success: true } } export async function completeSprintAction( sprintId: string, decisions: Record ) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const sprint = await prisma.sprint.findFirst({ where: { id: sprintId, product: productAccessFilter(session.userId) }, }) if (!sprint) return { error: 'Sprint niet gevonden' } await prisma.$transaction([ ...Object.entries(decisions).map(([storyId, status]) => prisma.story.update({ where: { id: storyId }, data: { status, sprint_id: status === 'OPEN' ? null : undefined, }, }) ), prisma.sprint.update({ where: { id: sprintId }, data: { status: 'COMPLETED', completed_at: new Date() }, }), ]) revalidatePath(`/products/${sprint.product_id}`) revalidatePath(`/products/${sprint.product_id}/sprint`) return { success: true } }