79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
"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<ProjectDTO> {
|
|
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<void> {
|
|
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<void> {
|
|
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<DeleteProjectResult> {
|
|
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 {};
|
|
}
|