'use client'; import { memo, useState } from 'react'; import { IconArchive, IconLoader2 } from '@tabler/icons-react'; import { cn } from '@/lib/utils'; import type { TaskState } from '@/lib/types/http'; import { TaskItemMenu } from './task-item-menu'; type DiffStats = { additions: number; deletions: number; }; type TaskItemProps = { title: string; description?: string; state?: TaskState; isSelected?: boolean; onClick?: () => void; diffStats?: DiffStats; updatedAt?: string; onArchive?: () => void; onRename?: () => void; onDuplicate?: () => void; onReview?: () => void; onDelete?: () => void; isDeleting?: boolean; }; // Helper to format relative time function formatRelativeTime(dateString: string): string { const date = new Date(dateString); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffSecs = Math.floor(diffMs / 1000); const diffMins = Math.floor(diffSecs / 60); const diffHours = Math.floor(diffMins / 60); const diffDays = Math.floor(diffHours / 24); if (diffSecs < 60) return 'just now'; if (diffMins < 60) return `${diffMins}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays < 7) return `${diffDays}d ago`; return date.toLocaleDateString(); } export const TaskItem = memo(function TaskItem({ title, description, state, isSelected = false, onClick, diffStats, updatedAt, onArchive, onRename, onDuplicate, onReview, onDelete, isDeleting, }: TaskItemProps) { const [menuOpen, setMenuOpen] = useState(false); // Effective open state: keep menu open while deleting const effectiveMenuOpen = menuOpen || isDeleting; const isInProgress = state === 'IN_PROGRESS' || state === 'SCHEDULING'; const hasDiffStats = diffStats && (diffStats.additions > 0 || diffStats.deletions > 0); // Determine what to show on the right side const renderRightContent = () => { if (isInProgress) { return ( ); } if (hasDiffStats) { return (
+{diffStats.additions} -{diffStats.deletions}
); } if (updatedAt) { return ( {formatRelativeTime(updatedAt)} ); } return null; }; return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick?.(); } }} className={cn( 'group relative flex w-full items-start gap-2 rounded-md border p-2 text-left text-sm outline-none cursor-pointer', 'transition-all duration-100', isSelected ? 'bg-primary/10 border-primary/30' : 'bg-background/50 border-border hover:bg-background/80 hover:border-border' )} > {/* Content */}
{title} {description && ( {description} )}
{/* Right side: Spinner / Diff stats / Time (default) or Action buttons (on hover) */}
{/* Default content - visible by default, hidden on hover */}
{renderRightContent()}
{/* Action buttons - hidden by default, visible on hover */}
{/* 3-dot menu button */} { // Prevent closing while deleting if (!open && isDeleting) return; setMenuOpen(open); }} onRename={onRename} onDuplicate={onDuplicate} onReview={onReview} onDelete={onDelete} isDeleting={isDeleting} /> {/* Archive button */}
); });