"use client"; import { useDraggable } from "@dnd-kit/core"; import { CSS } from "@dnd-kit/utilities"; import Link from "next/link"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { cn, getInitials } from "@/lib/utils"; import { CalendarDays, Layers, MessageSquare, CheckCircle2, XCircle, Clock, GripVertical, } from "lucide-react"; import { TaskStatus, TaskType } from "@prisma/client"; import { TASK_STATUS_CONFIG, TASK_TYPE_LABELS } from "./TaskCard"; import { formatDistanceToNow } from "date-fns"; const PRIORITY_DOT: Record = { LOW: "bg-zinc-500", NORMAL: "bg-blue-500", HIGH: "bg-amber-500", URGENT: "bg-red-500", }; interface KanbanTask { id: string; title: string; type: TaskType; status: TaskStatus; priority: string; dueDate: Date | null; shot?: { shotCode: string } | null; asset?: { assetCode: string; name: string } | null; assignedArtist?: { id: string; name: string | null; email: string; image: string | null } | null; _count?: { versions: number }; versions?: { id: string; versionNumber: number; approvalStatus: string; createdAt: Date; }[]; } interface KanbanCardProps { task: KanbanTask; isDragOverlay?: boolean; } export function KanbanCard({ task, isDragOverlay = false }: KanbanCardProps) { const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ id: task.id, data: { task }, }); const style = transform ? { transform: CSS.Translate.toString(transform) } : undefined; const latestVersion = task.versions?.[0]; const isOverdue = task.dueDate && new Date(task.dueDate) < new Date() && task.status !== "DONE"; const contextCode = task.shot?.shotCode ?? task.asset?.assetCode; return (
{/* Header: type badge + context code */}
{TASK_TYPE_LABELS[task.type]} {contextCode && ( {contextCode} )}
{/* Title */} e.stopPropagation()} > {task.title} {/* Latest version + approval */} {latestVersion && (
v{latestVersion.versionNumber} {latestVersion.approvalStatus === "APPROVED" ? ( ) : latestVersion.approvalStatus === "CHANGES_REQUESTED" ? ( ) : ( )}
)} {/* Footer: due date + priority dot + artist */}
{/* Priority dot */} {/* Due date */} {task.dueDate && ( {formatDistanceToNow(new Date(task.dueDate), { addSuffix: true })} )} {/* Version count */} {(task._count?.versions ?? 0) > 0 && ( {task._count!.versions} )}
{/* Assignee avatar */} {task.assignedArtist && ( {getInitials(task.assignedArtist.name ?? task.assignedArtist.email)} )}
); }