"use client"; import { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { formatRelativeDate, formatFileSize } from "@/lib/utils"; import { Film, Clock, MoreHorizontal, ExternalLink, CheckCircle2, XCircle, AlertCircle, Star, ChevronDown, ChevronUp, MessageSquare, } from "lucide-react"; import type { VersionWithDetails } from "@/types"; import { submitApproval } from "@/actions/approvals"; import { useToast } from "@/components/ui/use-toast"; interface VersionListProps { shotId: string; versions: VersionWithDetails[]; currentVersionId?: string; onVersionSelect?: (versionId: string) => void; canApprove?: boolean; } const APPROVAL_STATUS_STYLES: Record = { PENDING_REVIEW: "bg-amber-500/10 text-amber-400 border-amber-500/20", APPROVED: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20", REJECTED: "bg-red-500/10 text-red-400 border-red-500/20", NEEDS_CHANGES: "bg-orange-500/10 text-orange-400 border-orange-500/20", }; const APPROVAL_ICONS: Record = { PENDING_REVIEW: Clock, APPROVED: CheckCircle2, REJECTED: XCircle, NEEDS_CHANGES: AlertCircle, }; function getApprovalLabel(status: string) { switch (status) { case "PENDING_REVIEW": return "Pending"; case "APPROVED": return "Approved"; case "REJECTED": return "Rejected"; case "NEEDS_CHANGES": return "Needs Changes"; default: return status; } } export function VersionList({ shotId, versions, currentVersionId, onVersionSelect, canApprove = false, }: VersionListProps) { const [expanded, setExpanded] = useState(currentVersionId ?? null); const { toast } = useToast(); const router = useRouter(); if (versions.length === 0) { return (

No versions uploaded yet

); } const handleApprove = async (versionId: string) => { try { await submitApproval({ versionId, status: "APPROVED", notes: "" }); toast({ title: "Version approved" }); router.refresh(); } catch { toast({ title: "Failed to approve", variant: "destructive" }); } }; const handleReject = async (versionId: string) => { try { await submitApproval({ versionId, status: "REJECTED", notes: "Rejected from version list" }); toast({ title: "Version rejected", variant: "destructive" }); router.refresh(); } catch { toast({ title: "Failed to reject", variant: "destructive" }); } }; return (
{versions.map((version) => { const isActive = version.id === currentVersionId; const isExpanded = expanded === version.id; const StatusIcon = APPROVAL_ICONS[version.approvalStatus] ?? Clock; return (
{/* Row */}
{/* Version label + latest badge */}
v{String(version.versionNumber).padStart(3, "0")} {version.isLatest && ( Latest version )}
{/* Approval status */} {getApprovalLabel(version.approvalStatus)} {/* Meta */}
{version.fps} fps {version.duration && ( {Math.round(version.duration)}s )} {formatRelativeDate(version.createdAt)}
{/* Actions */}
Open review page {canApprove && ( <> handleApprove(version.id)} > Approve handleReject(version.id)} > Reject )}
{/* Expanded detail */} {isExpanded && (
{version.notes && (

“{version.notes}”

)}
Uploaded by{" "} {version.artist?.name ?? "Unknown"} {version.frameCount && ( {version.frameCount} frames )} {version.fileSize && ( {formatFileSize(version.fileSize)} )}
{/* Approval history */} {version.approvals && version.approvals.length > 0 && (

Review history

{version.approvals.map((approval) => { const statusStyles: Record = { APPROVED: "text-emerald-400", REJECTED: "text-red-400", NEEDS_CHANGES: "text-orange-400", PENDING_REVIEW: "text-amber-400", }; const StatusIcon = APPROVAL_ICONS[approval.status] ?? Clock; return (
{getApprovalLabel(approval.status)} by {approval.user.name ?? "Reviewer"} {approval.notes && (

“{approval.notes}”

)}
); })}
)}
)}
); })}
); }