/** * @file dashboard/KanbanCard.tsx * @description Client component rendering a single kanban card with native drag-and-drop source handlers, dynamic priority styling, and mobile status dropdown. */ "use client"; import { AlertCircle, Crown, MoreHorizontal, CornerDownRight, CalendarDays, } from "lucide-react"; import { KANBAN_COLUMNS, KanbanCardProps, TaskStatus } from "@/types/tasks"; import { useState, useEffect, useRef } from "react"; /** * Renders an interactive kanban card featuring title, description, priority levels, * deadline alerts, team avatars, and a mobile-friendly status-shifting dropdown menu. * * @param {KanbanCardProps} props - The component props containing the task object and status change handler. * @returns {JSX.Element} The rendered kanban card component. */ export default function KanbanCard({ task, onStatusChange }: KanbanCardProps) { const [showMobileActions, setShowMobileActions] = useState(false); const dropdownRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( dropdownRef.current && !dropdownRef.current.contains(event.target as Node) ) { setShowMobileActions(false); } }; if (showMobileActions) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [showMobileActions]); const isOverdue = task.dueDate && new Date(task.dueDate) < new Date() && task.status !== "done"; const filteredAssignees = (task.assignees || []).filter( (a) => a !== task.creator, ); /** * Initiates the drag action on a task card, storing its ID and current status in the data transfer payload. * * @param {React.DragEvent} e - The drag event object. */ const handleDragStart = (e: React.DragEvent) => { e.dataTransfer.setData("text/plain", task.id); e.dataTransfer.setData("sourceStatus", task.status); e.dataTransfer.effectAllowed = "move"; }; /** * Handles shifting the task to a new status category. * * @param {React.MouseEvent} e - The mouse event triggered by clicking a column destination. * @param {TaskStatus} newStatus - The target task status to transition to. */ const handleMove = (e: React.MouseEvent, newStatus: TaskStatus) => { e.stopPropagation(); if (onStatusChange) { onStatusChange(task.id, newStatus); } setShowMobileActions(false); }; return (
{/* --- Card Header (Title & Priority) --- */}

{task.title}

{task.priority && ( {task.priority.charAt(0).toUpperCase() + task.priority.slice(1)} )}
{/* Card Description */}

{task.description}

{/* --- Card Footer --- */}
{/* Date Badge */} {task.dueDate && (
{isOverdue ? : } {new Date(task.dueDate).toLocaleDateString("de-DE", { day: "2-digit", month: "short", })} {new Date(task.dueDate).toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit", })}
)} {/* Avatars & Mobile Switcher */}
{(task.creator || filteredAssignees.length > 0) && (
{filteredAssignees.map((assignee, index) => (
{assignee.substring(0, 2).toUpperCase()}
))} {task.creator && (
{task.creator.substring(0, 2).toUpperCase()}
)}
)} {/* Mobile Switcher */}
{KANBAN_COLUMNS.map((col) => { if (col.id === task.status) return null; return ( ); })}
); }