"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { hasPermission } from "@/lib/permissions"; import { revalidatePath } from "next/cache"; import { z } from "zod"; const schema = z.object({ // A category NAME — picked from the existing list or typed to create a new one. categoryName: z.string().trim().min(1, "Category is required"), text: z.string().trim().min(1, "Clause text is required"), isDefault: z.boolean().default(false), }); type Result = { ok: true } | { error: string }; async function guard(): Promise<{ ok: true } | { error: string }> { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "manage_terms")) { return { error: "Forbidden" }; } return { ok: true }; } function parse(formData: FormData) { return schema.safeParse({ categoryName: formData.get("categoryName"), text: formData.get("text"), isDefault: formData.get("isDefault") === "on" || formData.get("isDefault") === "true", }); } // Find a category by name (case-insensitive), creating it (appended to the end) // if it doesn't exist — this is how new categories are added "along with clauses". async function ensureCategory(name: string): Promise { const existing = await db.termsCategory.findFirst({ where: { name: { equals: name, mode: "insensitive" } }, select: { id: true }, }); if (existing) return existing.id; const max = await db.termsCategory.aggregate({ _max: { sortOrder: true } }); const created = await db.termsCategory.create({ data: { name, sortOrder: (max._max.sortOrder ?? 0) + 1 }, }); return created.id; } export async function createTerm(formData: FormData): Promise { const g = await guard(); if ("error" in g) return g; const parsed = parse(formData); if (!parsed.success) return { error: parsed.error.errors[0].message }; const categoryId = await ensureCategory(parsed.data.categoryName); await db.termsCondition.create({ data: { categoryId, text: parsed.data.text, isDefault: parsed.data.isDefault }, }); revalidatePath("/admin/terms"); return { ok: true }; } export async function updateTerm(id: string, formData: FormData): Promise { const g = await guard(); if ("error" in g) return g; const parsed = parse(formData); if (!parsed.success) return { error: parsed.error.errors[0].message }; const categoryId = await ensureCategory(parsed.data.categoryName); await db.termsCondition.update({ where: { id }, data: { categoryId, text: parsed.data.text, isDefault: parsed.data.isDefault }, }); revalidatePath("/admin/terms"); return { ok: true }; } export async function toggleTermActive(id: string): Promise { const g = await guard(); if ("error" in g) return g; const term = await db.termsCondition.findUnique({ where: { id }, select: { isActive: true } }); if (!term) return { error: "Not found" }; await db.termsCondition.update({ where: { id }, data: { isActive: !term.isActive } }); revalidatePath("/admin/terms"); return { ok: true }; } export async function deleteTerm(id: string): Promise { const g = await guard(); if ("error" in g) return g; // Safe to delete: POs keep their T&C as a JSON snapshot, so no PO references this row. await db.termsCondition.delete({ where: { id } }); revalidatePath("/admin/terms"); return { ok: true }; }