/** * TeamOrchestrator - Multi-Agent Team Orchestration UI * * Provides an interface for creating teams, assigning agents, * managing tasks, and monitoring collaboration workflows. * * @module components/TeamOrchestrator */ import { useState, useEffect } from 'react'; import { useTeamStore } from '../store/teamStore'; import { useGatewayStore } from '../store/gatewayStore'; import type { TeamMember, TeamTask, TeamMemberRole, TaskPriority, CollaborationPattern, } from '../types/team'; import { Users, Plus, Trash2, X, Bot, Clock, AlertTriangle, CheckCircle, Play, UserPlus, FileText, } from 'lucide-react'; // === Sub-Components === interface MemberCardProps { member: TeamMember; isSelected: boolean; onSelect: () => void; onRoleChange: (role: TeamMemberRole) => void; onRemove: () => void; } function MemberCard({ member, isSelected, onSelect, onRoleChange, onRemove }: MemberCardProps) { const [showRoleMenu, setShowRoleMenu] = useState(false); const roleColors: Record = { orchestrator: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300', developer: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', reviewer: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300', tester: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300', architect: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300', specialist: 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300', }; const statusColors = { idle: 'bg-gray-400', running: 'bg-green-500 animate-pulse', paused: 'bg-yellow-500', error: 'bg-red-500', }; return (
{member.name}
{showRoleMenu && (
{(['orchestrator', 'developer', 'reviewer', 'tester', 'architect', 'specialist'] as TeamMemberRole[]).map(role => ( ))}
)}
Workload: {member.workload}% Tasks: {member.currentTasks.length}
); } interface TaskCardProps { task: TeamTask; members: TeamMember[]; isSelected: boolean; onSelect: () => void; onAssign: (memberId: string) => void; onStatusChange: (status: TeamTask['status']) => void; } function TaskCard({ task, members, isSelected, onSelect, onAssign, onStatusChange: _onStatusChange }: TaskCardProps) { const [showAssignMenu, setShowAssignMenu] = useState(false); const priorityColors: Record = { critical: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300', high: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300', medium: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300', low: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300', }; const statusIcons: Record = { pending: , assigned: , in_progress: , review: , blocked: , completed: , failed: , }; const assignee = members.find(m => m.id === task.assigneeId); return (
{statusIcons[task.status]} {task.title}
{task.description && (

{task.description}

)}
{task.priority}
{task.type}
{task.estimate && ( {task.estimate}pts )}
{showAssignMenu && (
{members.map(member => ( ))}
)}
); } // === Main Component === interface TeamOrchestratorProps { isOpen: boolean; onClose: () => void; } export function TeamOrchestrator({ isOpen, onClose }: TeamOrchestratorProps) { const [view, setView] = useState<'teams' | 'tasks' | 'members'>('teams'); const [isCreating, setIsCreating] = useState(false); const [newTeamName, setNewTeamName] = useState(''); const [newTeamPattern, setNewTeamPattern] = useState('sequential'); const { teams, activeTeam, metrics, error, selectedTaskId, selectedMemberId, loadTeams, createTeam, deleteTeam, setActiveTeam, addTask, updateTaskStatus, assignTask, addMember, removeMember, updateMemberRole, setSelectedTask, setSelectedMember, } = useTeamStore(); const { clones } = useGatewayStore(); useEffect(() => { if (isOpen) { loadTeams(); } }, [isOpen, loadTeams]); const handleCreateTeam = async () => { if (!newTeamName.trim()) return; const team = await createTeam({ name: newTeamName.trim(), pattern: newTeamPattern, memberAgents: [], }); if (team) { setActiveTeam(team); setNewTeamName(''); setIsCreating(false); } }; const handleAddMember = async (agentId: string) => { if (!activeTeam) return; await addMember(activeTeam.id, agentId, 'developer'); }; const handleAddTask = async () => { if (!activeTeam) return; await addTask({ teamId: activeTeam.id, title: `Task ${activeTeam.tasks.length + 1}`, priority: 'medium', type: 'implementation', }); }; if (!isOpen) return null; return (
{/* Header */}

Team Orchestrator

{metrics && (
Completed: {metrics.tasksCompleted} Pass Rate: {metrics.passRate.toFixed(0)}% Efficiency: {metrics.efficiency.toFixed(0)}%
)}
{/* Content */}
{/* Sidebar - Team List */}

Teams

{isCreating && (
setNewTeamName(e.target.value)} placeholder="Team name..." className="w-full px-2 py-1 text-sm border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white" />
)}
{teams.map(team => (
setActiveTeam(team)} className={`p-3 rounded-lg cursor-pointer transition-all ${ activeTeam?.id === team.id ? 'bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800' : 'hover:bg-gray-50 dark:hover:bg-gray-800' }`} >
{team.name}
{team.members.length} members · {team.tasks.length} tasks
{team.pattern}
))}
{/* Main Content */} {activeTeam ? (
{/* View Tabs */}
{/* Tasks View */} {view === 'tasks' && (

Tasks

{activeTeam.tasks.length === 0 ? (
No tasks yet. Click "Add Task" to create one.
) : ( activeTeam.tasks.map(task => ( setSelectedTask(task.id)} onAssign={(memberId) => assignTask(activeTeam.id, task.id, memberId)} onStatusChange={(status) => updateTaskStatus(activeTeam.id, task.id, status)} /> )) )}
)} {/* Members View */} {view === 'members' && (

Members

{activeTeam.members.length === 0 ? (
No members yet. Select an agent to add.
) : ( activeTeam.members.map(member => ( setSelectedMember(member.id)} onRoleChange={(role) => updateMemberRole(activeTeam.id, member.id, role)} onRemove={() => removeMember(activeTeam.id, member.id)} /> )) )}
)}
) : (

Select or create a team to get started

)}
{/* Footer */}
{teams.length} teams total {error && {error}}
); }