"use client"; 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"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { cn } from "@/lib/utils"; import { getInitials, formatRelativeDate } from "@/lib/utils"; import { MoreHorizontal, Film, MessageSquare, Clock, 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< string, { label: string; color: string; icon: React.ElementType } > = { WAITING: { label: "Waiting", color: "bg-zinc-500/10 text-zinc-400 border-zinc-500/20", icon: Clock }, IN_PROGRESS: { label: "In Progress", color: "bg-blue-500/10 text-blue-400 border-blue-500/20", icon: Film }, IN_REVIEW: { label: "In Review", color: "bg-purple-500/10 text-purple-400 border-purple-500/20", icon: AlertCircle }, REVISIONS: { label: "Revisions", color: "bg-orange-500/10 text-orange-400 border-orange-500/20", icon: AlertCircle }, COMPLETE: { label: "Complete", color: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20", icon: CheckCircle2 }, }; const PRIORITY_DOT: Record = { LOW: "bg-zinc-500", NORMAL: "bg-blue-500", HIGH: "bg-amber-500", URGENT: "bg-red-500", }; 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 (
{shot.shotCode} {shot.sequence && ( {shot.sequence} )}
{shot.description && (

{shot.description}

)}
{statusCfg.label}
); } return ( {/* Thumbnail with cinema scope aspect ratio (2.39:1) */}
{shot.thumbnailUrl || latestVersion?.thumbnailUrl || latestVersion?.posterUrl ? ( {shot.shotCode} ) : (

{shot.shotCode}

)}
{/* Header info */}
{shot.shotCode} {shot.sequence && ( {shot.sequence} )}
{statusCfg.label} View shot {canManage && ( <> {isDuplicating ? "Duplicating…" : "Duplicate shot"} )}
{shot.description && (

{shot.description}

)} {/* Stats row */}
{shot.versions?.length ?? 0} version{(shot.versions?.length ?? 0) !== 1 ? "s" : ""} {openComments > 0 && ( {openComments} open )} {shot.dueDate && ( {formatRelativeDate(shot.dueDate)} )}
{/* Artist avatar */} {shot.artist ? (
{shot.artist.image && } {getInitials(shot.artist.name)} {shot.artist.name ?? shot.artist.email}
) : ( Unassigned )}
); }