'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 { clearActiveSprintInSettings, setActiveSelectionInSettings, setActiveSprintInSettings, } from '@/lib/active-sprint' async function getSession() { return getIronSession(await cookies(), sessionOptions) } const setSchema = z.object({ productId: z.string().min(1), sprintId: z.string().min(1), }) const clearSchema = z.object({ productId: z.string().min(1), }) export async function setActiveSprintAction(productId: string, sprintId: string) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const parsed = setSchema.safeParse({ productId, sprintId }) if (!parsed.success) return { error: 'Ongeldig product- of sprint-id' } const sprint = await prisma.sprint.findFirst({ where: { id: parsed.data.sprintId, product_id: parsed.data.productId, product: productAccessFilter(session.userId), }, select: { id: true }, }) if (!sprint) return { error: 'Sprint niet gevonden of niet toegankelijk' } await setActiveSprintInSettings(session.userId, parsed.data.productId, parsed.data.sprintId) revalidatePath('/', 'layout') return { success: true, sprintId: parsed.data.sprintId } } export async function clearActiveSprintAction(productId: string) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const parsed = clearSchema.safeParse({ productId }) if (!parsed.success) return { error: 'Ongeldig product-id' } const product = await prisma.product.findFirst({ where: { id: parsed.data.productId, ...productAccessFilter(session.userId) }, select: { id: true }, }) if (!product) return { error: 'Product niet gevonden of niet toegankelijk' } await clearActiveSprintInSettings(session.userId, parsed.data.productId) revalidatePath('/', 'layout') return { success: true } } const selectionSchema = z.object({ productId: z.string().min(1), sprintId: z.string().min(1), }) /** * PBI-79: kies een sprint en auto-select zijn enige PBI/story (indien * singleton). Resultaat wordt server-side bepaald + atomair in user-settings * weggeschreven (sprint+pbi+story) zodat cross-device-restore klopt. */ export async function switchActiveSprintAction( productId: string, sprintId: string, ): Promise< | { success: true sprintId: string pbiId: string | null storyId: string | null } | { error: string } > { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } const parsed = selectionSchema.safeParse({ productId, sprintId }) if (!parsed.success) return { error: 'Ongeldig product- of sprint-id' } const sprint = await prisma.sprint.findFirst({ where: { id: parsed.data.sprintId, product_id: parsed.data.productId, product: productAccessFilter(session.userId), }, select: { id: true }, }) if (!sprint) return { error: 'Sprint niet gevonden of niet toegankelijk' } // Auto-select: alleen wanneer sprint exact één PBI heeft. Story-auto-select // alleen wanneer die PBI exact één story binnen deze sprint heeft. const sprintStories = await prisma.story.findMany({ where: { sprint_id: parsed.data.sprintId, product_id: parsed.data.productId, }, select: { id: true, pbi_id: true }, }) const uniquePbiIds = Array.from(new Set(sprintStories.map((s) => s.pbi_id))) let autoPbiId: string | null = null let autoStoryId: string | null = null if (uniquePbiIds.length === 1) { autoPbiId = uniquePbiIds[0] const storiesForPbi = sprintStories.filter((s) => s.pbi_id === autoPbiId) if (storiesForPbi.length === 1) { autoStoryId = storiesForPbi[0].id } } await setActiveSelectionInSettings(session.userId, parsed.data.productId, { sprintId: parsed.data.sprintId, pbiId: autoPbiId, storyId: autoStoryId, }) revalidatePath('/', 'layout') return { success: true, sprintId: parsed.data.sprintId, pbiId: autoPbiId, storyId: autoStoryId, } } export async function syncActiveSprintCookieAction(productId: string, sprintId: string) { const session = await getSession() if (!session.userId) return if (session.isDemo) return const parsed = setSchema.safeParse({ productId, sprintId }) if (!parsed.success) return const sprint = await prisma.sprint.findFirst({ where: { id: parsed.data.sprintId, product_id: parsed.data.productId, product: productAccessFilter(session.userId), }, select: { id: true }, }) if (!sprint) return await setActiveSprintInSettings(session.userId, parsed.data.productId, parsed.data.sprintId) }