added duplicate shot
Deploy / deploy (push) Successful in 2m28s

This commit is contained in:
twotalesanimation
2026-05-20 18:20:45 +02:00
parent c801d929af
commit 05475a6c19
4 changed files with 164 additions and 3 deletions
+94
View File
@@ -90,6 +90,100 @@ export async function createShot(data: z.infer<typeof createShotSchema>) {
return { success: true, shot };
}
// ── Duplicate Shot ────────────────────────────────────────────────────────────
export async function duplicateShot(sourceShotId: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
if (!["ADMIN", "PRODUCER", "SUPERVISOR"].includes(session.user.role)) {
throw new Error("Insufficient permissions");
}
// Load source shot with tasks
const source = await db.shot.findUnique({
where: { id: sourceShotId },
include: {
tasks: {
select: {
title: true,
description: true,
type: true,
priority: true,
estimatedHours: true,
sortOrder: true,
dueDate: true,
assignedArtistId: true,
},
},
},
});
if (!source) throw new Error("Shot not found");
const project = await db.project.findUnique({
where: { id: source.projectId },
select: { showId: true, projectType: true },
});
if (!project?.showId) throw new Error("Project has no Show ID");
// Determine next shot number in same scene/episode scope
const scopeWhere = {
projectId: source.projectId,
scene: source.scene,
...(project.projectType === "EPISODIC" ? { episode: source.episode } : {}),
};
const maxShot = await db.shot.findFirst({
where: scopeWhere,
orderBy: { shotNumber: "desc" },
select: { shotNumber: true },
});
const shotNumber = (maxShot?.shotNumber ?? 0) + 10;
const paddedNumber = shotNumber.toString().padStart(4, "0");
const shotCode =
project.projectType === "EPISODIC" && source.episode
? `${project.showId}_${source.episode}_${source.scene}_${paddedNumber}`
: `${project.showId}_${source.scene}_${paddedNumber}`;
const newShot = await db.shot.create({
data: {
shotCode,
scene: source.scene,
episode: source.episode,
shotNumber,
sequence: source.sequence,
description: source.description,
projectId: source.projectId,
artistId: source.artistId,
priority: source.priority,
fps: source.fps,
frameStart: source.frameStart,
frameEnd: source.frameEnd,
dueDate: source.dueDate,
thumbnailUrl: source.thumbnailUrl,
// Duplicate tasks — reset status and schedule fields
tasks: {
create: source.tasks.map((t) => ({
title: t.title,
description: t.description,
type: t.type,
priority: t.priority,
estimatedHours: t.estimatedHours,
sortOrder: t.sortOrder,
dueDate: t.dueDate,
assignedArtistId: t.assignedArtistId,
projectId: source.projectId,
status: "TODO" as const,
})),
},
},
});
revalidatePath(`/projects/${source.projectId}`);
return { success: true, shot: newShot };
}
// ── Update Shot ───────────────────────────────────────────────────────────────
const updateShotSchema = z.object({