import { useEffect, useMemo, useState } from 'react'; import { MemberBadge } from '@renderer/components/team/MemberBadge'; import { UnreadCommentsBadge } from '@renderer/components/team/UnreadCommentsBadge'; import { Badge } from '@renderer/components/ui/badge'; import { Button } from '@renderer/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover'; import { useUnreadCommentCount } from '@renderer/hooks/useUnreadCommentCount'; import { useStore } from '@renderer/store'; import { buildMemberColorMap } from '@renderer/utils/memberHelpers'; import { ArrowLeftFromLine, ArrowRightFromLine, CheckCircle2, FileCode, HelpCircle, Play, Trash2, XCircle, } from 'lucide-react'; import type { KanbanColumnId, KanbanTaskState, ResolvedTeamMember, TeamTask } from '@shared/types'; interface KanbanTaskCardProps { task: TeamTask; teamName: string; columnId: KanbanColumnId; kanbanTaskState?: KanbanTaskState; hasReviewers: boolean; compact?: boolean; taskMap: Map; members: ResolvedTeamMember[]; onRequestReview: (taskId: string) => void; onApprove: (taskId: string) => void; onRequestChanges: (taskId: string) => void; onMoveBackToDone: (taskId: string) => void; onStartTask: (taskId: string) => void; onCompleteTask: (taskId: string) => void; onCancelTask: (taskId: string) => void; onScrollToTask?: (taskId: string) => void; onTaskClick?: (task: TeamTask) => void; onViewChanges?: (taskId: string) => void; onDeleteTask?: (taskId: string) => void; } interface DependencyBadgeProps { taskId: string; taskMap: Map; onScrollToTask?: (taskId: string) => void; } const DependencyBadge = ({ taskId, taskMap, onScrollToTask, }: DependencyBadgeProps): React.JSX.Element => { const depTask = taskMap.get(taskId); const isCompleted = depTask?.status === 'completed'; return ( ); }; const CancelTaskButton = ({ taskId, onConfirm, }: { taskId: string; onConfirm: (taskId: string) => void; }): React.JSX.Element => { const [open, setOpen] = useState(false); return ( e.stopPropagation()} >

Move this task back to TODO and notify the team?

); }; export const KanbanTaskCard = ({ task, teamName, columnId, kanbanTaskState: _kanbanTaskState, hasReviewers, compact, taskMap, members, onRequestReview, onApprove, onRequestChanges, onMoveBackToDone, onStartTask, onCompleteTask, onCancelTask, onScrollToTask, onTaskClick, onViewChanges, onDeleteTask, }: KanbanTaskCardProps): React.JSX.Element => { const colorMap = useMemo(() => buildMemberColorMap(members), [members]); const unreadCount = useUnreadCommentCount(teamName, task.id, task.comments); const blockedByIds = task.blockedBy?.filter((id) => id.length > 0) ?? []; const blocksIds = task.blocks?.filter((id) => id.length > 0) ?? []; const hasBlockedBy = blockedByIds.length > 0; const hasBlocks = blocksIds.length > 0; // Lazy-check if task has file changes (only for done/review/approved columns) const showChangesColumn = (columnId === 'done' || columnId === 'review' || columnId === 'approved') && !!onViewChanges; const cacheKey = `${teamName}:${task.id}`; const taskHasChanges = useStore((s) => s.taskHasChanges[cacheKey]); const checkTaskHasChanges = useStore((s) => s.checkTaskHasChanges); useEffect(() => { if (showChangesColumn && task.status === 'completed' && taskHasChanges == null) { void checkTaskHasChanges(teamName, task.id); } }, [showChangesColumn, task.status, task.id, teamName, taskHasChanges, checkTaskHasChanges]); return (
onTaskClick?.(task)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onTaskClick?.(task); } }} >
#{task.id} {task.owner ? : null} {task.needsClarification ? ( {task.needsClarification === 'user' ? 'Awaiting user' : 'Awaiting lead'} ) : null} {!compact && (
{task.subject}
)}
{compact && (
{task.subject}
)}
{hasBlockedBy ? (
Blocked by {blockedByIds.map((id) => ( ))}
) : null} {hasBlocks ? (
Blocks {blocksIds.map((id) => ( ))}
) : null}
{columnId === 'todo' ? ( <> ) : null} {columnId === 'in_progress' ? ( <> ) : null} {columnId === 'done' ? ( <> ) : null} {columnId === 'review' ? (
{!hasReviewers ? (

Manual review

) : null}
) : null} {columnId === 'approved' ? ( ) : null}
{showChangesColumn && taskHasChanges === true ? ( ) : null} {onDeleteTask ? ( ) : null}
); };