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
+35 -1
View File
@@ -3,6 +3,7 @@
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";
@@ -11,6 +12,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
@@ -23,13 +25,17 @@ import {
CheckCircle2,
AlertCircle,
ArrowUpRight,
Copy,
} from "lucide-react";
import type { ShotWithDetails } from "@/types";
import { duplicateShot } from "@/actions/shots";
import { useToast } from "@/components/ui/use-toast";
interface ShotCardProps {
shot: ShotWithDetails;
projectId: string;
compact?: boolean;
canManage?: boolean;
}
const STATUS_CONFIG: Record<
@@ -50,14 +56,29 @@ const PRIORITY_DOT: Record<string, string> = {
URGENT: "bg-red-500",
};
export function ShotCard({ shot, projectId, compact = false }: ShotCardProps) {
export function ShotCard({ shot, projectId, compact = false, canManage = false }: ShotCardProps) {
const router = useRouter();
const { toast } = useToast();
const [isDuplicating, setIsDuplicating] = useState(false);
const statusCfg = STATUS_CONFIG[shot.status] ?? STATUS_CONFIG.WAITING;
const StatusIcon = statusCfg.icon;
const latestVersion = shot.versions?.[0];
const openComments = shot.versions
?.reduce((sum, v) => sum + (v._count?.comments ?? 0), 0) ?? 0;
const handleDuplicate = async () => {
setIsDuplicating(true);
try {
const { shot: newShot } = await duplicateShot(shot.id);
toast({ title: "Shot duplicated", description: `Created ${newShot.shotCode}` });
router.push(`/projects/${projectId}/shots/${newShot.id}`);
} catch (e) {
toast({ title: "Duplicate failed", description: e instanceof Error ? e.message : undefined, variant: "destructive" });
} finally {
setIsDuplicating(false);
}
};
if (compact) {
return (
<div className="flex items-center gap-3 px-3 py-2.5 rounded-lg border border-border hover:border-border/80 transition-colors group">
@@ -145,6 +166,19 @@ export function ShotCard({ shot, projectId, compact = false }: ShotCardProps) {
View shot
</Link>
</DropdownMenuItem>
{canManage && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={handleDuplicate}
disabled={isDuplicating}
className="gap-2"
>
<Copy className="h-3.5 w-3.5" />
{isDuplicating ? "Duplicating…" : "Duplicate shot"}
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>