import { Badge } from '@renderer/components/ui/badge'; import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip'; import { getTeamColorSet } from '@renderer/constants/teamColors'; import { useTheme } from '@renderer/hooks/useTheme'; import { formatAgentRole } from '@renderer/utils/formatAgentRole'; import { agentAvatarUrl, buildMemberLaunchPresentation, displayMemberName, } from '@renderer/utils/memberHelpers'; import { deriveTaskDisplayId } from '@shared/utils/taskIdentity'; import { AlertTriangle, GitBranch, Loader2, MessageSquare, Plus } from 'lucide-react'; import { CurrentTaskIndicator } from './CurrentTaskIndicator'; import type { TaskStatusCounts } from '@renderer/utils/pathNormalize'; import type { LeadActivityState, MemberLaunchState, MemberSpawnLivenessSource, MemberSpawnStatus, ResolvedTeamMember, TeamTaskWithKanban, } from '@shared/types'; interface MemberCardProps { member: ResolvedTeamMember; memberColor: string; runtimeSummary?: string; taskCounts?: TaskStatusCounts | null; isTeamAlive?: boolean; isTeamProvisioning?: boolean; leadActivity?: LeadActivityState; currentTask?: TeamTaskWithKanban | null; reviewTask?: TeamTaskWithKanban | null; isAwaitingReply?: boolean; isRemoved?: boolean; spawnStatus?: MemberSpawnStatus; spawnError?: string; spawnLivenessSource?: MemberSpawnLivenessSource; spawnLaunchState?: MemberLaunchState; spawnRuntimeAlive?: boolean; isLaunchSettling?: boolean; onOpenTask?: () => void; onOpenReviewTask?: () => void; onClick?: () => void; onSendMessage?: () => void; onAssignTask?: () => void; } export const MemberCard = ({ member, memberColor, runtimeSummary, taskCounts, isTeamAlive, isTeamProvisioning, leadActivity, currentTask, reviewTask, isAwaitingReply, isRemoved, spawnStatus, spawnError, spawnLivenessSource, spawnLaunchState, spawnRuntimeAlive, isLaunchSettling, onOpenTask, onOpenReviewTask, onClick, onSendMessage, onAssignTask, }: MemberCardProps): React.JSX.Element => { // NOTE: lead context display disabled — usage formula is inaccurate // const teamName = useStore((s) => s.selectedTeamName); // const leadContext = useStore((s) => // member.agentType === 'team-lead' && teamName ? s.leadContextByTeam[teamName] : undefined // ); const launchPresentation = buildMemberLaunchPresentation({ member, spawnStatus, spawnLaunchState, spawnLivenessSource, spawnRuntimeAlive, runtimeAdvisory: member.runtimeAdvisory, isLaunchSettling, isTeamAlive, isTeamProvisioning, leadActivity, }); const dotClass = launchPresentation.dotClass; const runtimeAdvisoryLabel = launchPresentation.runtimeAdvisoryLabel; const runtimeAdvisoryTitle = launchPresentation.runtimeAdvisoryTitle; const presenceLabel = launchPresentation.presenceLabel; const spawnCardClass = launchPresentation.cardClass; const colors = getTeamColorSet(memberColor); const { isLight } = useTheme(); const pending = taskCounts?.pending ?? 0; const inProgress = taskCounts?.inProgress ?? 0; const completed = taskCounts?.completed ?? 0; const totalTasks = pending + inProgress + completed; const progressPercent = totalTasks > 0 ? Math.round((completed / totalTasks) * 100) : 0; const roleLabel = formatAgentRole(member.role) ?? formatAgentRole(member.agentType); const activityTask = currentTask ?? reviewTask ?? null; const activityTitle = currentTask ? `Current task: #${deriveTaskDisplayId(currentTask.id)}` : reviewTask ? `Reviewing task: #${deriveTaskDisplayId(reviewTask.id)}` : undefined; const showStartingSkeleton = !isRemoved && presenceLabel === 'starting' && spawnLaunchState !== 'failed_to_start' && !activityTask && !runtimeSummary; const showStartingBadge = !isRemoved && presenceLabel === 'starting' && !activityTask; const showRuntimeAdvisoryBadge = !isRemoved && Boolean(runtimeAdvisoryLabel) && !showStartingBadge && spawnStatus !== 'error' && (Boolean(activityTask) || !isAwaitingReply); return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick?.(); } }} >
{member.name}
{displayMemberName(member.name)} {member.gitBranch ? ( {member.gitBranch} ) : null} {currentTask ? ( ) : null} {reviewTask ? ( ) : null} {!activityTask && isAwaitingReply ? ( <> {runtimeAdvisoryLabel ?? 'awaiting reply'} ) : null}
{showStartingSkeleton ? (