@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user