"use server"; import { revalidatePath } from "next/cache"; import { prisma } from "@/lib/db"; import { requireUserId } from "@/lib/auth-helpers"; import { projectAccessFilter } from "@/lib/access"; import { ProjectTitleSchema, ProjectThemeSchema } from "@/lib/validation/project"; import type { ProjectDTO } from "@/types/project"; export async function createProject(title: string): Promise { const userId = await requireUserId(); const parsedTitle = ProjectTitleSchema.parse(title); const project = await prisma.project.create({ data: { title: parsedTitle, ownerId: userId }, }); revalidatePath("/projects"); return { id: project.id, title: project.title, theme: null }; } /** * Assigns a theme to a project (or clears it back to "Default Theme" with * null, which follows the user's global theme preference). Only the * project's own page is themed -- see components/theme/project-theme-scope.tsx. */ export async function setProjectTheme( projectId: string, theme: string | null ): Promise { const userId = await requireUserId(); const parsed = ProjectThemeSchema.parse(theme); const { count } = await prisma.project.updateMany({ where: { id: projectId, ...projectAccessFilter(userId) }, data: { theme: parsed }, }); if (count === 0) throw new Error("Project not found"); revalidatePath("/projects"); revalidatePath(`/projects/${projectId}`); } export async function renameProject(projectId: string, title: string): Promise { const userId = await requireUserId(); const parsedTitle = ProjectTitleSchema.parse(title); const { count } = await prisma.project.updateMany({ where: { id: projectId, ...projectAccessFilter(userId) }, data: { title: parsedTitle }, }); if (count === 0) throw new Error("Project not found"); revalidatePath("/projects"); revalidatePath(`/projects/${projectId}`); } export type DeleteProjectResult = { error?: string }; /** * Deletes a project and everything in it -- its categories, groups, and * to-dos all cascade away with it (unlike deleting a single Category, * which refuses if it still has groups). The confirmation dialog on the * client carries the "this can't be undone" warning; this action doesn't * re-check emptiness. */ export async function deleteProject(projectId: string): Promise { const userId = await requireUserId(); const { count } = await prisma.project.deleteMany({ where: { id: projectId, ...projectAccessFilter(userId) }, }); if (count === 0) return { error: "Project not found." }; revalidatePath("/projects"); return {}; }