Initial commit
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
"use server";
|
||||
|
||||
import { auth } from "@/auth";
|
||||
import { db } from "@/lib/db";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
import { ProjectStatus } from "@prisma/client";
|
||||
|
||||
const createProjectSchema = z.object({
|
||||
name: z.string().min(1).max(100),
|
||||
code: z.string().min(1).max(20).toUpperCase(),
|
||||
showId: z.string().min(1).max(10).regex(/^[A-Z0-9_]+$/i, "1–10 chars, letters/numbers/underscores").toUpperCase(),
|
||||
projectType: z.enum(["STANDARD", "EPISODIC"]).default("STANDARD"),
|
||||
description: z.string().optional(),
|
||||
clientId: z.string().cuid().optional(),
|
||||
dueDate: z.string().optional(),
|
||||
deadline: z.union([z.string(), z.date()]).optional(),
|
||||
startDate: z.string().optional(),
|
||||
slackWebhook: z.string().url().optional().or(z.literal("")),
|
||||
slackChannel: z.string().optional(),
|
||||
});
|
||||
|
||||
export async function createProject(data: z.infer<typeof createProjectSchema>) {
|
||||
const session = await auth();
|
||||
if (!session?.user) throw new Error("Unauthorized");
|
||||
if (!["ADMIN", "PRODUCER"].includes(session.user.role)) {
|
||||
throw new Error("Insufficient permissions");
|
||||
}
|
||||
|
||||
const parsed = createProjectSchema.parse(data);
|
||||
|
||||
// Verify the session user still exists in the DB (guards against stale JWT after a DB reset)
|
||||
const dbUser = await db.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!dbUser) throw new Error("Session expired — please sign out and sign back in.");
|
||||
|
||||
const project = await db.project.create({
|
||||
data: {
|
||||
name: parsed.name,
|
||||
code: parsed.code,
|
||||
showId: parsed.showId,
|
||||
projectType: parsed.projectType,
|
||||
description: parsed.description,
|
||||
clientId: parsed.clientId || undefined,
|
||||
producerId: session.user.id,
|
||||
dueDate: parsed.dueDate
|
||||
? new Date(parsed.dueDate)
|
||||
: parsed.deadline
|
||||
? new Date(parsed.deadline)
|
||||
: undefined,
|
||||
startDate: parsed.startDate ? new Date(parsed.startDate) : undefined,
|
||||
slackWebhook: parsed.slackWebhook || undefined,
|
||||
slackChannel: parsed.slackChannel || undefined,
|
||||
},
|
||||
include: { client: true },
|
||||
});
|
||||
|
||||
revalidatePath("/projects");
|
||||
return { success: true, project };
|
||||
}
|
||||
|
||||
const updateProjectSchema = z.object({
|
||||
id: z.string().cuid(),
|
||||
name: z.string().min(1).max(100).optional(),
|
||||
code: z.string().min(1).max(20).optional(),
|
||||
showId: z.string().min(1).max(10).regex(/^[A-Z0-9_]+$/i).toUpperCase().optional(),
|
||||
projectType: z.enum(["STANDARD", "EPISODIC"]).optional(),
|
||||
description: z.string().optional().nullable(),
|
||||
status: z.nativeEnum(ProjectStatus).optional(),
|
||||
clientId: z.string().cuid().optional().nullable(),
|
||||
producerId: z.string().cuid().optional().nullable(),
|
||||
supervisorId: z.string().cuid().optional().nullable(),
|
||||
dueDate: z.string().optional().nullable(),
|
||||
startDate: z.string().optional().nullable(),
|
||||
slackWebhook: z.string().url().optional().or(z.literal("")).nullable(),
|
||||
slackChannel: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export async function updateProject(data: z.infer<typeof updateProjectSchema>) {
|
||||
const session = await auth();
|
||||
if (!session?.user) throw new Error("Unauthorized");
|
||||
if (!["ADMIN", "PRODUCER", "SUPERVISOR"].includes(session.user.role)) {
|
||||
throw new Error("Insufficient permissions");
|
||||
}
|
||||
|
||||
const { id, ...rest } = updateProjectSchema.parse(data);
|
||||
|
||||
const updated = await db.project.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(rest.name !== undefined && { name: rest.name }),
|
||||
...(rest.code !== undefined && { code: rest.code.toUpperCase() }),
|
||||
...(rest.showId !== undefined && { showId: rest.showId }),
|
||||
...(rest.projectType !== undefined && { projectType: rest.projectType }),
|
||||
...(rest.description !== undefined && { description: rest.description ?? undefined }),
|
||||
...(rest.status !== undefined && { status: rest.status }),
|
||||
...(rest.clientId !== undefined && { clientId: rest.clientId }),
|
||||
...(rest.producerId !== undefined && { producerId: rest.producerId }),
|
||||
...(rest.supervisorId !== undefined && { supervisorId: rest.supervisorId }),
|
||||
...(rest.dueDate !== undefined && { dueDate: rest.dueDate ? new Date(rest.dueDate) : null }),
|
||||
...(rest.startDate !== undefined && { startDate: rest.startDate ? new Date(rest.startDate) : null }),
|
||||
...(rest.slackWebhook !== undefined && { slackWebhook: rest.slackWebhook || null }),
|
||||
...(rest.slackChannel !== undefined && { slackChannel: rest.slackChannel || null }),
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/projects/${id}`);
|
||||
revalidatePath("/projects");
|
||||
return { success: true, project: updated };
|
||||
}
|
||||
|
||||
export async function updateProjectStatus(
|
||||
projectId: string,
|
||||
status: ProjectStatus
|
||||
) {
|
||||
const session = await auth();
|
||||
if (!session?.user) throw new Error("Unauthorized");
|
||||
if (!["ADMIN", "PRODUCER"].includes(session.user.role)) {
|
||||
throw new Error("Insufficient permissions");
|
||||
}
|
||||
|
||||
await db.project.update({ where: { id: projectId }, data: { status } });
|
||||
revalidatePath(`/projects/${projectId}`);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function getProjects() {
|
||||
const session = await auth();
|
||||
if (!session?.user) throw new Error("Unauthorized");
|
||||
|
||||
// Clients only see their assigned projects
|
||||
if (session.user.role === "CLIENT") {
|
||||
const access = await db.clientAccess.findMany({
|
||||
where: { userId: session.user.id },
|
||||
select: { clientId: true },
|
||||
});
|
||||
const clientIds = access.map((a) => a.clientId);
|
||||
return db.project.findMany({
|
||||
where: { clientId: { in: clientIds }, status: { not: "ARCHIVED" } },
|
||||
include: {
|
||||
client: true,
|
||||
_count: { select: { shots: true } },
|
||||
},
|
||||
orderBy: { updatedAt: "desc" },
|
||||
});
|
||||
}
|
||||
|
||||
return db.project.findMany({
|
||||
include: {
|
||||
client: true,
|
||||
producer: { select: { id: true, name: true } },
|
||||
_count: { select: { shots: true } },
|
||||
},
|
||||
orderBy: { updatedAt: "desc" },
|
||||
});
|
||||
}
|
||||
|
||||
export async function getProjectById(id: string) {
|
||||
const session = await auth();
|
||||
if (!session?.user) throw new Error("Unauthorized");
|
||||
|
||||
return db.project.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
client: true,
|
||||
producer: { select: { id: true, name: true, email: true } },
|
||||
supervisor: { select: { id: true, name: true, email: true } },
|
||||
shots: {
|
||||
include: {
|
||||
artist: { select: { id: true, name: true, image: true } },
|
||||
_count: { select: { versions: true } },
|
||||
},
|
||||
orderBy: { shotCode: "asc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user