From e4eb8fb3a9cfc222b53c4539b2e33bb3cc940d5e Mon Sep 17 00:00:00 2001 From: twotalesanimation <80506065+twotalesanimation@users.noreply.github.com> Date: Wed, 20 May 2026 21:53:18 +0200 Subject: [PATCH] Added delete version --- actions/versions.ts | 42 +++++++++++++++++++ .../tasks/[taskId]/TaskDetailClient.tsx | 28 +++++++++++++ components/versions/VersionList.tsx | 32 ++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/actions/versions.ts b/actions/versions.ts index e74f074..7a06721 100644 --- a/actions/versions.ts +++ b/actions/versions.ts @@ -165,6 +165,48 @@ export async function shareVersionWithClient(versionId: string) { return { success: true }; } +export async function deleteVersion(versionId: 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 version = await db.version.findUnique({ + where: { id: versionId }, + select: { + id: true, + isLatest: true, + taskId: true, + task: { select: { projectId: true, shotId: true } }, + }, + }); + if (!version) throw new Error("Version not found"); + + // If this was the latest version, promote the next most recent one + if (version.isLatest && version.taskId) { + const next = await db.version.findFirst({ + where: { taskId: version.taskId, id: { not: versionId } }, + orderBy: { versionNumber: "desc" }, + select: { id: true }, + }); + if (next) { + await db.version.update({ where: { id: next.id }, data: { isLatest: true } }); + } + } + + await db.version.delete({ where: { id: versionId } }); + + const projectId = version.task?.projectId; + const shotId = version.task?.shotId; + if (shotId) await recalcShotStatus(shotId).catch(() => {}); + if (projectId) revalidatePath(`/projects/${projectId}`); + if (version.taskId) revalidatePath(`/tasks/${version.taskId}`); + if (shotId && projectId) revalidatePath(`/projects/${projectId}/shots/${shotId}`); + + return { success: true }; +} + export async function getVersionById(versionId: string) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); diff --git a/app/(dashboard)/tasks/[taskId]/TaskDetailClient.tsx b/app/(dashboard)/tasks/[taskId]/TaskDetailClient.tsx index b2f498a..250d543 100644 --- a/app/(dashboard)/tasks/[taskId]/TaskDetailClient.tsx +++ b/app/(dashboard)/tasks/[taskId]/TaskDetailClient.tsx @@ -41,9 +41,11 @@ import { AlertCircle, User, ExternalLink, + Trash2, } from "lucide-react"; import { TASK_STATUS_CONFIG, TASK_TYPE_LABELS } from "@/components/tasks/TaskCard"; import { VersionUpload } from "@/components/versions/VersionUpload"; +import { deleteVersion } from "@/actions/versions"; import type { CommentWithReplies } from "@/types"; import { CommentPanel } from "@/components/comments/CommentPanel"; @@ -155,6 +157,17 @@ export function TaskDetailClient({ } }; + const handleDeleteVersion = async (versionId: string, label: string) => { + if (!confirm(`Delete ${label}? This will permanently remove the file and all comments.`)) return; + try { + await deleteVersion(versionId); + toast({ title: "Version deleted" }); + router.refresh(); + } catch (e) { + toast({ title: "Delete failed", description: e instanceof Error ? e.message : undefined, variant: "destructive" }); + } + }; + return (
@@ -298,6 +311,21 @@ export function TaskDetailClient({ Review + {canManage && ( + + )}
))} diff --git a/components/versions/VersionList.tsx b/components/versions/VersionList.tsx index 8a4d16d..f7fedc5 100644 --- a/components/versions/VersionList.tsx +++ b/components/versions/VersionList.tsx @@ -32,9 +32,11 @@ import { ChevronDown, ChevronUp, MessageSquare, + Trash2, } from "lucide-react"; import type { VersionWithDetails } from "@/types"; import { submitApproval } from "@/actions/approvals"; +import { deleteVersion } from "@/actions/versions"; import { useToast } from "@/components/ui/use-toast"; interface VersionListProps { @@ -43,6 +45,7 @@ interface VersionListProps { currentVersionId?: string; onVersionSelect?: (versionId: string) => void; canApprove?: boolean; + canManage?: boolean; } const APPROVAL_STATUS_STYLES: Record = { @@ -75,6 +78,7 @@ export function VersionList({ currentVersionId, onVersionSelect, canApprove = false, + canManage = false, }: VersionListProps) { const [expanded, setExpanded] = useState(currentVersionId ?? null); const { toast } = useToast(); @@ -109,6 +113,17 @@ export function VersionList({ } }; + const handleDelete = async (versionId: string, label: string) => { + if (!confirm(`Delete ${label}? This will permanently remove the file and all comments.`)) return; + try { + await deleteVersion(versionId); + toast({ title: "Version deleted" }); + router.refresh(); + } catch (e) { + toast({ title: "Delete failed", description: e instanceof Error ? e.message : undefined, variant: "destructive" }); + } + }; + return (
{versions.map((version) => { @@ -220,6 +235,23 @@ export function VersionList({ )} + {canManage && ( + <> + + + handleDelete( + version.id, + `v${String(version.versionNumber).padStart(3, "0")}` + ) + } + > + + Delete version + + + )}