import { type ReactNode, useState } from 'react'; import { CARD_BG, CARD_BORDER_STYLE, CARD_ICON_MUTED } from '@renderer/constants/cssVariables'; import { getTeamColorSet, getThemedBadge } from '@renderer/constants/teamColors'; import { useTheme } from '@renderer/hooks/useTheme'; import { formatAgentRole } from '@renderer/utils/formatAgentRole'; import { agentAvatarUrl, buildMemberAvatarMap, buildMemberColorMap, displayMemberName, } from '@renderer/utils/memberHelpers'; import { formatTaskDisplayLabel } from '@shared/utils/taskIdentity'; import { ChevronRight } from 'lucide-react'; import type { ResolvedTeamMember, TeamTaskWithKanban } from '@shared/types'; interface ActiveTasksBlockProps { members: ResolvedTeamMember[]; tasks: TeamTaskWithKanban[]; /** Start collapsed (e.g. when rendered inside the sidebar where MemberList already shows status). */ defaultCollapsed?: boolean; headerRight?: ReactNode; onMemberClick?: (member: ResolvedTeamMember) => void; onTaskClick?: (task: TeamTaskWithKanban) => void; } interface ActivityEntry { member: ResolvedTeamMember; task: TeamTaskWithKanban | undefined; taskId: string; kind: 'working' | 'reviewing'; } export const ActiveTasksBlock = ({ members, tasks, defaultCollapsed = false, headerRight, onMemberClick, onTaskClick, }: ActiveTasksBlockProps): React.JSX.Element | null => { const { isLight } = useTheme(); const [collapsed, setCollapsed] = useState(defaultCollapsed); const colorMap = buildMemberColorMap(members); const avatarMap = buildMemberAvatarMap(members); const taskMap = new Map(tasks.map((t) => [t.id, t])); const entries: ActivityEntry[] = []; // Members working on tasks const workingMemberNames = new Set(); for (const m of members) { if (!m.currentTaskId) continue; const task = taskMap.get(m.currentTaskId); // Defense-in-depth: hide banner for approved/completed tasks even if currentTaskId is stale if (task && (task.reviewState === 'approved' || task.status === 'completed')) continue; workingMemberNames.add(m.name); entries.push({ member: m, task, taskId: m.currentTaskId, kind: 'working' }); } // Members reviewing tasks (only if not already shown as working) for (const m of members) { if (workingMemberNames.has(m.name)) continue; const reviewTask = tasks.find( (t) => t.reviewer === m.name && (t.reviewState === 'review' || t.kanbanColumn === 'review') ); if (reviewTask) { entries.push({ member: m, task: reviewTask, taskId: reviewTask.id, kind: 'reviewing' }); } } if (entries.length === 0) return null; return (
{headerRight ?
{headerRight}
: null}
{!collapsed && entries.map(({ member, task, taskId, kind }) => { const colors = getTeamColorSet(colorMap.get(member.name) ?? ''); const roleLabel = formatAgentRole( member.role ?? (member.agentType !== 'general-purpose' ? member.agentType : undefined) ); const dotPing = kind === 'reviewing' ? 'bg-amber-400' : 'bg-emerald-400'; const dotSolid = kind === 'reviewing' ? 'bg-amber-500' : 'bg-emerald-500'; const activityLabel = kind === 'reviewing' ? 'reviewing' : 'working on'; return (
{onMemberClick ? ( ) : ( {displayMemberName(member.name)} )} {roleLabel ? ( {roleLabel} ) : null} {activityLabel} {task && (onTaskClick ? ( ) : ( {formatTaskDisplayLabel(task)} {task.subject} ))}
); })}
); };