/** * @file dashboard/KanbanCard.tsx * @description Client component rendering a single kanban card with support for priority indicators, due date alerts, team avatars, and a 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, ); /** * 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) --- */}
{/* Title */}

{task.title}

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

{task.description}

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