@@ -128,3 +128,68 @@ export async function getApprovalHistory(versionId: string) {
|
||||
orderBy: { createdAt: "desc" },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets an approved version back to PENDING_REVIEW and the associated task
|
||||
* back to INTERNAL_REVIEW. Recalculates shot status.
|
||||
* Only ADMIN, PRODUCER, SUPERVISOR can unapprove.
|
||||
*/
|
||||
export async function unapproveVersion(versionId: string) {
|
||||
const session = await auth();
|
||||
if (!session?.user) throw new Error("Unauthorized");
|
||||
|
||||
const allowedRoles = ["ADMIN", "PRODUCER", "SUPERVISOR"];
|
||||
if (!allowedRoles.includes(session.user.role)) {
|
||||
throw new Error("Insufficient permissions to unapprove versions");
|
||||
}
|
||||
|
||||
const version = await db.version.findUnique({
|
||||
where: { id: versionId },
|
||||
include: {
|
||||
task: {
|
||||
include: { shot: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!version) throw new Error("Version not found");
|
||||
if (version.approvalStatus !== "APPROVED") {
|
||||
throw new Error("Only approved versions can be unapproved");
|
||||
}
|
||||
|
||||
// Create an audit record for the unapproval
|
||||
await db.approval.create({
|
||||
data: {
|
||||
versionId,
|
||||
userId: session.user.id,
|
||||
status: "PENDING_REVIEW",
|
||||
notes: "Approval undone",
|
||||
},
|
||||
});
|
||||
|
||||
// Reset version approval status
|
||||
await db.version.update({
|
||||
where: { id: versionId },
|
||||
data: { approvalStatus: "PENDING_REVIEW" },
|
||||
});
|
||||
|
||||
// Reset task back to INTERNAL_REVIEW if it was moved to DONE
|
||||
if (version.task && version.task.status === "DONE") {
|
||||
await db.task.update({
|
||||
where: { id: version.task.id },
|
||||
data: { status: "INTERNAL_REVIEW" },
|
||||
});
|
||||
}
|
||||
|
||||
// Recalculate shot status
|
||||
if (version.task?.shot) {
|
||||
await recalcShotStatus(version.task.shot.id).catch(() => {});
|
||||
}
|
||||
|
||||
revalidatePath(`/review/${versionId}`);
|
||||
if (version.task) {
|
||||
revalidatePath(`/tasks/${version.task.id}`);
|
||||
revalidatePath(`/projects/${version.task.projectId}`);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -744,6 +744,43 @@ export async function clientApproveShot(shotId: string) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin/Producer/Supervisor: undo a client approval on a shot.
|
||||
* Resets shotApprovalStatus from CLIENT_APPROVED → INTERNALLY_APPROVED.
|
||||
* Shot status reverts to CLIENT_REVIEW (if still shared) or READY_FOR_CLIENT.
|
||||
*/
|
||||
export async function unapproveShot(shotId: 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");
|
||||
}
|
||||
|
||||
const shot = await db.shot.findUnique({
|
||||
where: { id: shotId },
|
||||
select: { projectId: true, shotApprovalStatus: true, sharedWithClient: true },
|
||||
});
|
||||
if (!shot) throw new Error("Shot not found");
|
||||
if (shot.shotApprovalStatus !== "CLIENT_APPROVED") {
|
||||
throw new Error("Shot is not client-approved");
|
||||
}
|
||||
|
||||
const newStatus = shot.sharedWithClient ? "CLIENT_REVIEW" : "READY_FOR_CLIENT";
|
||||
|
||||
await db.shot.update({
|
||||
where: { id: shotId },
|
||||
data: {
|
||||
shotApprovalStatus: "INTERNALLY_APPROVED",
|
||||
status: newStatus,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/projects/${shot.projectId}`);
|
||||
revalidatePath(`/projects/${shot.projectId}/shots/${shotId}`);
|
||||
revalidatePath(`/shot-status`);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal: handle client requesting changes on a shot.
|
||||
* Resets shotApprovalStatus = PENDING, sharedWithClient = false.
|
||||
|
||||
Reference in New Issue
Block a user