import { motion } from 'framer-motion'; import { Calendar, User, Pencil, Trash2 } from 'lucide-react'; import { Checkbox } from '@/components/ui/checkbox'; import { Badge } from '@/components/ui/badge'; import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, } from '@/components/ui/context-menu'; import { PriorityBadge } from './PriorityBadge'; export type TaskItem = { id: string; projectId: string | null; title: string; description: string | null; status: string | null; priority: string | null; assignee: string | null; dueDate: number | null; isAiSuggested: number; isApproved: number; projectName: string | null; clientName: string | null; subClientName: string | null; }; export function parseAssignees(raw: string | null): string[] { if (!raw) return []; try { const parsed = JSON.parse(raw) as unknown; if (Array.isArray(parsed)) return parsed.filter((n): n is string => typeof n === 'string'); } catch { /* plain string fallback */ } return [raw]; } function formatDueDate(timestamp: number): string { const d = new Date(timestamp); const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const date = `Due ${months[d.getMonth()]} ${d.getDate()}`; if (d.getHours() === 0 && d.getMinutes() === 0) return date; const h = String(d.getHours()).padStart(2, '0'); const m = String(d.getMinutes()).padStart(2, '0'); return `${date}, ${h}:${m}`; } export function TaskRow({ task, onToggle, onEdit, onDelete, onClick, hideBreadcrumb, layoutId, }: { task: TaskItem; onToggle: (id: string, status: string | null) => void; onEdit?: (task: TaskItem) => void; onDelete?: (id: string) => void; onClick?: (task: TaskItem) => void; hideBreadcrumb?: boolean; layoutId?: string; }) { const isDone = task.status === 'done'; const checkboxState: boolean | 'indeterminate' = task.status === 'done' ? true : task.status === 'in_progress' ? 'indeterminate' : false; const breadcrumb: string[] = []; if (!hideBreadcrumb) { if (task.clientName) breadcrumb.push(task.clientName); if (task.subClientName) breadcrumb.push(task.subClientName); if (task.projectName) breadcrumb.push(task.projectName); } const hasMetadata = task.priority || task.dueDate || breadcrumb.length > 0 || task.assignee; const Wrapper = layoutId ? motion.div : 'div'; const wrapperProps = layoutId ? { layoutId, layout: true as const } : {}; return ( onClick?.(task)} > {/* Row 1: checkbox + title + description */}
onToggle(task.id, task.status)} onClick={(e) => e.stopPropagation()} className="mt-0.5 shrink-0" />
{task.title}
{task.description && (
{task.description}
)}
{/* Row 2: metadata, indented to align with title text */} {hasMetadata && (
{task.dueDate && ( {formatDueDate(task.dueDate)} )} {breadcrumb.length > 0 && ( {breadcrumb.map((part, i) => ( {i > 0 && } {part} ))} )} {task.assignee && (
{parseAssignees(task.assignee).join(', ')}
)}
)}
onEdit?.(task)}> Edit Task onDelete?.(task.id)} className="text-destructive focus:text-destructive" > Delete Task
); }