From 35d60cc43bfe047545aebb8697d0349ec444f197 Mon Sep 17 00:00:00 2001 From: janpeter visser Date: Mon, 27 Apr 2026 19:01:20 +0200 Subject: [PATCH] feat(ST-902): add setActiveProduct + clearActiveProduct server actions - actions/active-product.ts: setActiveProductAction validates access via productAccessFilter, rejects archived products and demo users - archiveProductAction: clears active_product_id for all affected users in transaction - removeProductMemberAction: clears active_product_id for removed member - leaveProductAction: clears active_product_id for leaving user Co-Authored-By: Claude Sonnet 4.6 --- actions/active-product.ts | 51 +++++++++++++++++++++++++++++++++++++++ actions/products.ts | 28 ++++++++++++++++++--- 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 actions/active-product.ts diff --git a/actions/active-product.ts b/actions/active-product.ts new file mode 100644 index 0000000..a3b026a --- /dev/null +++ b/actions/active-product.ts @@ -0,0 +1,51 @@ +'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' + +async function getSession() { + return getIronSession(await cookies(), sessionOptions) +} + +const setSchema = z.object({ productId: z.string().min(1) }) + +export async function setActiveProductAction(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 = setSchema.safeParse({ productId }) + if (!parsed.success) return { error: 'Ongeldig product-id' } + + const product = await prisma.product.findFirst({ + where: { id: parsed.data.productId, archived: false, ...productAccessFilter(session.userId) }, + }) + if (!product) return { error: 'Product niet gevonden of niet toegankelijk' } + + await prisma.user.update({ + where: { id: session.userId }, + data: { active_product_id: parsed.data.productId }, + }) + + revalidatePath('/', 'layout') + return { success: true, productId: parsed.data.productId } +} + +export async function clearActiveProductAction() { + const session = await getSession() + if (!session.userId) return { error: 'Niet ingelogd' } + if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } + + await prisma.user.update({ + where: { id: session.userId }, + data: { active_product_id: null }, + }) + + revalidatePath('/', 'layout') + return { success: true } +} diff --git a/actions/products.ts b/actions/products.ts index 6eae56a..08f7385 100644 --- a/actions/products.ts +++ b/actions/products.ts @@ -148,9 +148,16 @@ export async function archiveProductAction(id: string) { }) if (!product) return { error: 'Product niet gevonden' } - await prisma.product.update({ where: { id }, data: { archived: true } }) + await prisma.$transaction([ + // Clear active_product_id for all users who had this product active + prisma.user.updateMany({ + where: { active_product_id: id }, + data: { active_product_id: null }, + }), + prisma.product.update({ where: { id }, data: { archived: true } }), + ]) - revalidatePath('/dashboard') + revalidatePath('/', 'layout') redirect('/dashboard') } @@ -215,7 +222,13 @@ export async function removeProductMemberAction(productId: string, memberId: str const product = await prisma.product.findFirst({ where: { id: productId, user_id: session.userId } }) if (!product) return { error: 'Product niet gevonden of geen eigenaar' } - await prisma.productMember.deleteMany({ where: { product_id: productId, user_id: memberId } }) + await prisma.$transaction([ + prisma.user.updateMany({ + where: { id: memberId, active_product_id: productId }, + data: { active_product_id: null }, + }), + prisma.productMember.deleteMany({ where: { product_id: productId, user_id: memberId } }), + ]) revalidatePath(`/products/${productId}/settings`) return { success: true } @@ -225,8 +238,15 @@ export async function leaveProductAction(productId: string) { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd' } - await prisma.productMember.deleteMany({ where: { product_id: productId, user_id: session.userId } }) + await prisma.$transaction([ + prisma.user.updateMany({ + where: { id: session.userId, active_product_id: productId }, + data: { active_product_id: null }, + }), + prisma.productMember.deleteMany({ where: { product_id: productId, user_id: session.userId } }), + ]) + revalidatePath('/', 'layout') revalidatePath('/settings') return { success: true } }