@@ -17,11 +17,13 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getInitials, formatRelativeDate, formatFileSize } from "@/lib/utils";
|
||||
import { updateTask, updateTaskStatus } from "@/actions/tasks";
|
||||
import { submitApproval, unapproveVersion } from "@/actions/approvals";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { TaskStatus, TaskType } from "@prisma/client";
|
||||
import { formatDistanceToNow, format } from "date-fns";
|
||||
@@ -42,6 +44,8 @@ import {
|
||||
User,
|
||||
ExternalLink,
|
||||
Trash2,
|
||||
XCircle,
|
||||
MoreHorizontal,
|
||||
} from "lucide-react";
|
||||
import { TASK_STATUS_CONFIG, TASK_TYPE_LABELS } from "@/components/tasks/TaskCard";
|
||||
import { VersionUpload } from "@/components/versions/VersionUpload";
|
||||
@@ -195,6 +199,29 @@ export function TaskDetailClient({
|
||||
}
|
||||
};
|
||||
|
||||
const handleApproveVersion = async (versionId: string, status: "APPROVED" | "REJECTED" | "NEEDS_CHANGES") => {
|
||||
try {
|
||||
await submitApproval({ versionId, status, notes: "" });
|
||||
toast({
|
||||
title: status === "APPROVED" ? "Version approved" : status === "REJECTED" ? "Version rejected" : "Changes requested",
|
||||
});
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
toast({ title: "Failed", description: e instanceof Error ? e.message : undefined, variant: "destructive" });
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnapproveVersion = async (versionId: string) => {
|
||||
if (!confirm("Undo approval? The version will return to Pending Review and the task will return to Internal Review.")) return;
|
||||
try {
|
||||
await unapproveVersion(versionId);
|
||||
toast({ title: "Approval undone", description: "Version reset to Pending Review" });
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
toast({ title: "Failed to unapprove", description: e instanceof Error ? e.message : undefined, variant: "destructive" });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<div className="max-w-5xl mx-auto p-6 space-y-6">
|
||||
@@ -339,19 +366,58 @@ export function TaskDetailClient({
|
||||
</Button>
|
||||
</Link>
|
||||
{canManage && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-red-500"
|
||||
onClick={() =>
|
||||
handleDeleteVersion(
|
||||
v.id,
|
||||
`v${String(v.versionNumber).padStart(3, "0")}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 text-muted-foreground">
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
className="text-emerald-500"
|
||||
onClick={() => handleApproveVersion(v.id, "APPROVED")}
|
||||
>
|
||||
<CheckCircle2 className="h-3.5 w-3.5 mr-2" />
|
||||
Approve
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="text-orange-400"
|
||||
onClick={() => handleApproveVersion(v.id, "NEEDS_CHANGES")}
|
||||
>
|
||||
<AlertCircle className="h-3.5 w-3.5 mr-2" />
|
||||
Needs Changes
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="text-red-500"
|
||||
onClick={() => handleApproveVersion(v.id, "REJECTED")}
|
||||
>
|
||||
<XCircle className="h-3.5 w-3.5 mr-2" />
|
||||
Reject
|
||||
</DropdownMenuItem>
|
||||
{v.approvalStatus === "APPROVED" && (
|
||||
<DropdownMenuItem
|
||||
className="text-amber-500"
|
||||
onClick={() => handleUnapproveVersion(v.id)}
|
||||
>
|
||||
<AlertCircle className="h-3.5 w-3.5 mr-2" />
|
||||
Undo Approval
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="text-red-500 focus:text-red-500"
|
||||
onClick={() =>
|
||||
handleDeleteVersion(
|
||||
v.id,
|
||||
`v${String(v.versionNumber).padStart(3, "0")}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5 mr-2" />
|
||||
Delete version
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user