feat(ST-abeu63oz): admin products-actions (create, update, archive, delete, addMember, removeMember)

- Zod-schema adminProductSchema (name, description, repo_url, definition_of_done, auto_pr, owner_user_id)
- adminCreateProductAction: owner-validatie, prisma.product.create
- adminUpdateProductAction: zelfde schema zonder owner_user_id
- adminArchiveProductAction: toggle archived-vlag
- adminDeleteProductAction: hard delete (cascade)
- adminAddMemberAction: upsert ProductMember
- adminRemoveMemberAction: deleteMany ProductMember
This commit is contained in:
Scrum4Me Agent 2026-05-05 14:54:08 +02:00
parent 9861495dbd
commit b9e6e725b6

86
actions/admin/products.ts Normal file
View file

@ -0,0 +1,86 @@
'use server'
import { revalidatePath } from 'next/cache'
import { z } from 'zod'
import { prisma } from '@/lib/prisma'
import { requireAdmin } from '@/lib/auth-guard'
const adminProductSchema = z.object({
name: z.string().min(1).max(100),
description: z.string().optional(),
repo_url: z.string().url().optional().or(z.literal('')),
definition_of_done: z.string().min(1),
auto_pr: z.boolean().default(false),
owner_user_id: z.string().cuid(),
})
const adminProductUpdateSchema = adminProductSchema.omit({ owner_user_id: true })
export async function adminCreateProductAction(data: unknown) {
await requireAdmin()
const parsed = adminProductSchema.safeParse(data)
if (!parsed.success) throw new Error(parsed.error.message)
const owner = await prisma.user.findUnique({ where: { id: parsed.data.owner_user_id } })
if (!owner) throw new Error('Eigenaar niet gevonden')
await prisma.product.create({
data: {
user_id: parsed.data.owner_user_id,
name: parsed.data.name,
description: parsed.data.description,
repo_url: parsed.data.repo_url || null,
definition_of_done: parsed.data.definition_of_done,
auto_pr: parsed.data.auto_pr,
},
})
revalidatePath('/admin/products')
}
export async function adminUpdateProductAction(productId: string, data: unknown) {
await requireAdmin()
const parsed = adminProductUpdateSchema.safeParse(data)
if (!parsed.success) throw new Error(parsed.error.message)
await prisma.product.update({
where: { id: productId },
data: {
name: parsed.data.name,
description: parsed.data.description,
repo_url: parsed.data.repo_url || null,
definition_of_done: parsed.data.definition_of_done,
auto_pr: parsed.data.auto_pr,
},
})
revalidatePath('/admin/products')
}
export async function adminArchiveProductAction(productId: string, archived: boolean) {
await requireAdmin()
await prisma.product.update({ where: { id: productId }, data: { archived } })
revalidatePath('/admin/products')
}
export async function adminDeleteProductAction(productId: string) {
await requireAdmin()
await prisma.product.delete({ where: { id: productId } })
revalidatePath('/admin/products')
}
export async function adminAddMemberAction(productId: string, userId: string) {
await requireAdmin()
await prisma.productMember.upsert({
where: { product_id_user_id: { product_id: productId, user_id: userId } },
create: { product_id: productId, user_id: userId },
update: {},
})
revalidatePath(`/admin/products/${productId}`)
}
export async function adminRemoveMemberAction(productId: string, userId: string) {
await requireAdmin()
await prisma.productMember.deleteMany({ where: { product_id: productId, user_id: userId } })
revalidatePath(`/admin/products/${productId}`)
}