Files

14 lines
39 KiB
JSON

{
"name": "ai-agents",
"description": "Multi-agent system components",
"dependencies": [
"@hanzo/ui"
],
"files": [
{
"name": "ai-agents.tsx",
"content": "\"use client\"\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\"\nimport { Grid } from \"@hanzo/ui/grid\"\nimport {\n Activity,\n AlertCircle,\n ArrowRight,\n Bot,\n Brain,\n CheckCircle,\n CircleStop,\n Clock,\n Code,\n Copy,\n Edit,\n MessageSquare,\n Network,\n Palette,\n Pause,\n Play,\n Plus,\n Search,\n Settings,\n Trash2,\n Zap,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"@/registry/default/ui/badge\"\nimport { Button } from \"@/registry/default/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@/registry/default/ui/card\"\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/registry/default/ui/dialog\"\nimport { Input } from \"@/registry/default/ui/input\"\nimport { Label } from \"@/registry/default/ui/label\"\nimport { Progress } from \"@/registry/default/ui/progress\"\nimport { ScrollArea } from \"@/registry/default/ui/scroll-area\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/registry/default/ui/select\"\nimport { Separator } from \"@/registry/default/ui/separator\"\nimport {\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from \"@/registry/default/ui/tabs\"\nimport { Textarea } from \"@/registry/default/ui/textarea\"\n\n// Types and Interfaces\nexport type AgentType =\n \"research\" | \"code\" | \"analysis\" | \"creative\" | \"automation\"\nexport type AgentStatus = \"idle\" | \"working\" | \"completed\" | \"error\"\nexport type WorkflowMode = \"sequential\" | \"parallel\" | \"custom\"\n\nexport interface Task {\n id: string\n title: string\n description: string\n priority: \"low\" | \"medium\" | \"high\"\n status: \"pending\" | \"in-progress\" | \"completed\" | \"failed\"\n assignedTo?: string\n progress: number\n createdAt: Date\n estimatedTime?: number\n}\n\nexport interface Agent {\n id: string\n name: string\n type: AgentType\n capabilities: string[]\n status: AgentStatus\n currentTask?: Task\n position: { x: number; y: number }\n connections: string[]\n avatar?: string\n description?: string\n performance: {\n tasksCompleted: number\n successRate: number\n avgCompletionTime: number\n }\n}\n\nexport interface WorkflowConnection {\n id: string\n source: string\n target: string\n condition?: string\n animated?: boolean\n}\n\nexport interface AIAgentsProps {\n agents?: Agent[]\n onAgentCreate?: (\n agent: Omit<Agent, \"id\" | \"position\" | \"performance\">\n ) => void\n onTaskAssign?: (task: Task, agentId: string) => void\n onWorkflowSave?: (workflow: {\n agents: Agent[]\n connections: WorkflowConnection[]\n }) => void\n workflowMode?: WorkflowMode\n className?: string\n}\n\n// Agent type configurations\nconst agentTypeConfig = {\n research: {\n icon: Search,\n color: \"bg-blue-500\",\n capabilities: [\n \"web-search\",\n \"data-analysis\",\n \"fact-checking\",\n \"source-validation\",\n ],\n },\n code: {\n icon: Code,\n color: \"bg-green-500\",\n capabilities: [\"code-generation\", \"debugging\", \"testing\", \"optimization\"],\n },\n analysis: {\n icon: Brain,\n color: \"bg-purple-500\",\n capabilities: [\n \"data-processing\",\n \"pattern-recognition\",\n \"insights\",\n \"reporting\",\n ],\n },\n creative: {\n icon: Palette,\n color: \"bg-pink-500\",\n capabilities: [\n \"content-creation\",\n \"design\",\n \"brainstorming\",\n \"storytelling\",\n ],\n },\n automation: {\n icon: Zap,\n color: \"bg-orange-500\",\n capabilities: [\n \"workflow-execution\",\n \"api-integration\",\n \"scheduling\",\n \"monitoring\",\n ],\n },\n}\n\n// Communication animation component\nconst CommunicationFlow: React.FC<{\n connections: WorkflowConnection[]\n agents: Agent[]\n}> = ({ connections, agents }) => {\n const [activeFlows, setActiveFlows] = useState<string[]>([])\n\n useEffect(() => {\n const interval = setInterval(() => {\n if (connections.length > 0) {\n const randomConnection =\n connections[Math.floor(Math.random() * connections.length)]\n setActiveFlows((prev) => [...prev, randomConnection.id])\n\n setTimeout(() => {\n setActiveFlows((prev) =>\n prev.filter((id) => id !== randomConnection.id)\n )\n }, 2000)\n }\n }, 3000)\n\n return () => clearInterval(interval)\n }, [connections])\n\n return (\n <svg\n className=\"absolute inset-0 w-full h-full pointer-events-none z-10\"\n style={{ overflow: \"visible\" }}\n >\n {connections.map((connection) => {\n const sourceAgent = agents.find((a) => a.id === connection.source)\n const targetAgent = agents.find((a) => a.id === connection.target)\n\n if (!sourceAgent || !targetAgent) return null\n\n const isActive = activeFlows.includes(connection.id)\n\n return (\n <g key={connection.id}>\n <defs>\n <marker\n id={`arrowhead-${connection.id}`}\n markerWidth=\"10\"\n markerHeight=\"7\"\n refX=\"10\"\n refY=\"3.5\"\n orient=\"auto\"\n >\n <polygon\n points=\"0 0, 10 3.5, 0 7\"\n fill={isActive ? \"#3b82f6\" : \"#94a3b8\"}\n />\n </marker>\n </defs>\n <line\n x1={sourceAgent.position.x + 50}\n y1={sourceAgent.position.y + 50}\n x2={targetAgent.position.x + 50}\n y2={targetAgent.position.y + 50}\n stroke={isActive ? \"#3b82f6\" : \"#94a3b8\"}\n strokeWidth={isActive ? \"3\" : \"2\"}\n strokeDasharray={isActive ? \"5,5\" : \"0\"}\n markerEnd={`url(#arrowhead-${connection.id})`}\n className={isActive ? \"animate-pulse\" : \"\"}\n />\n {isActive && (\n <circle r=\"4\" fill=\"#3b82f6\" className=\"animate-pulse\">\n <animateMotion\n dur=\"2s\"\n repeatCount=\"1\"\n path={`M${sourceAgent.position.x + 50},${sourceAgent.position.y + 50} L${targetAgent.position.x + 50},${targetAgent.position.y + 50}`}\n />\n </circle>\n )}\n </g>\n )\n })}\n </svg>\n )\n}\n\n// Agent card component\nconst AgentCard: React.FC<{\n agent: Agent\n isSelected?: boolean\n onSelect?: () => void\n onEdit?: () => void\n onDelete?: () => void\n onDrag?: (position: { x: number; y: number }) => void\n isDragging?: boolean\n}> = ({\n agent,\n isSelected,\n onSelect,\n onEdit,\n onDelete,\n onDrag,\n isDragging,\n}) => {\n const dragRef = useRef<HTMLDivElement>(null)\n const [isDragActive, setIsDragActive] = useState(false)\n const config = agentTypeConfig[agent.type]\n const Icon = config.icon\n\n const handleMouseDown = useCallback(\n (e: React.MouseEvent) => {\n if (!onDrag) return\n\n setIsDragActive(true)\n const startX = e.clientX - agent.position.x\n const startY = e.clientY - agent.position.y\n\n const handleMouseMove = (e: MouseEvent) => {\n const newX = e.clientX - startX\n const newY = e.clientY - startY\n onDrag({ x: Math.max(0, newX), y: Math.max(0, newY) })\n }\n\n const handleMouseUp = () => {\n setIsDragActive(false)\n document.removeEventListener(\"mousemove\", handleMouseMove)\n document.removeEventListener(\"mouseup\", handleMouseUp)\n }\n\n document.addEventListener(\"mousemove\", handleMouseMove)\n document.addEventListener(\"mouseup\", handleMouseUp)\n },\n [agent.position, onDrag]\n )\n\n const getStatusColor = (status: AgentStatus) => {\n switch (status) {\n case \"idle\":\n return \"bg-gray-500\"\n case \"working\":\n return \"bg-blue-500\"\n case \"completed\":\n return \"bg-green-500\"\n case \"error\":\n return \"bg-red-500\"\n default:\n return \"bg-gray-500\"\n }\n }\n\n const getStatusIcon = (status: AgentStatus) => {\n switch (status) {\n case \"idle\":\n return Clock\n case \"working\":\n return Activity\n case \"completed\":\n return CheckCircle\n case \"error\":\n return AlertCircle\n default:\n return Clock\n }\n }\n\n const StatusIcon = getStatusIcon(agent.status)\n\n return (\n <Card\n ref={dragRef}\n className={cn(\n \"absolute w-64 transition-all duration-200 cursor-move select-none\",\n isSelected && \"ring-2 ring-blue-500\",\n isDragActive && \"shadow-lg scale-105\",\n isDragging && \"opacity-50\"\n )}\n style={{\n left: agent.position.x,\n top: agent.position.y,\n transform: isDragActive ? \"scale(1.05)\" : \"scale(1)\",\n }}\n onMouseDown={handleMouseDown}\n onClick={onSelect}\n >\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div className={cn(\"p-2 rounded-lg text-white\", config.color)}>\n <Icon className=\"h-4 w-4\" />\n </div>\n <div>\n <CardTitle className=\"text-sm\">{agent.name}</CardTitle>\n <CardDescription className=\"text-xs capitalize\">\n {agent.type} Agent\n </CardDescription>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n onEdit?.()\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation()\n onDelete?.()\n }}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent>\n <Grid columns={1} gap={8}>\n <div className=\"flex items-center gap-2\">\n <StatusIcon\n className={cn(\"h-3 w-3\", getStatusColor(agent.status))}\n />\n <span className=\"text-xs font-medium capitalize\">\n {agent.status}\n </span>\n </div>\n\n {agent.currentTask && (\n <div className=\"bg-muted p-2 rounded text-xs\">\n <div className=\"font-medium\">{agent.currentTask.title}</div>\n <Progress\n value={agent.currentTask.progress}\n className=\"mt-1 h-1\"\n />\n </div>\n )}\n\n <div className=\"flex flex-wrap gap-1\">\n {agent.capabilities.slice(0, 3).map((capability) => (\n <Badge key={capability} variant=\"secondary\" className=\"text-xs\">\n {capability}\n </Badge>\n ))}\n {agent.capabilities.length > 3 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{agent.capabilities.length - 3}\n </Badge>\n )}\n </div>\n\n <Grid\n columns=\"auto auto\"\n gap={8}\n className=\"text-xs text-muted-foreground\"\n style={{ justifyContent: \"space-between\" }}\n >\n <span>Tasks: {agent.performance.tasksCompleted}</span>\n <span>Success: {agent.performance.successRate}%</span>\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n )\n}\n\n// Agent creation dialog\nconst CreateAgentDialog: React.FC<{\n onAgentCreate: (agent: Omit<Agent, \"id\" | \"position\" | \"performance\">) => void\n}> = ({ onAgentCreate }) => {\n const [open, setOpen] = useState(false)\n const [formData, setFormData] = useState({\n name: \"\",\n type: \"research\" as AgentType,\n description: \"\",\n capabilities: [] as string[],\n })\n\n const handleSubmit = () => {\n if (!formData.name) return\n\n onAgentCreate({\n name: formData.name,\n type: formData.type,\n status: \"idle\",\n capabilities:\n formData.capabilities.length > 0\n ? formData.capabilities\n : agentTypeConfig[formData.type].capabilities,\n connections: [],\n description: formData.description,\n })\n\n setFormData({\n name: \"\",\n type: \"research\",\n description: \"\",\n capabilities: [],\n })\n setOpen(false)\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button>\n <Plus className=\"h-4 w-4 mr-2\" />\n Create Agent\n </Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Create New Agent</DialogTitle>\n <DialogDescription>\n Configure a new AI agent for your workflow\n </DialogDescription>\n </DialogHeader>\n <Grid columns={1} gap={16}>\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"name\">Agent Name</Label>\n <Input\n id=\"name\"\n value={formData.name}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, name: e.target.value }))\n }\n placeholder=\"Enter agent name\"\n />\n </Grid>\n\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"type\">Agent Type</Label>\n <Select\n value={formData.type}\n onValueChange={(value: AgentType) =>\n setFormData((prev) => ({ ...prev, type: value }))\n }\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(agentTypeConfig).map(([type, config]) => {\n const Icon = config.icon\n return (\n <SelectItem key={type} value={type}>\n <div className=\"flex items-center gap-2\">\n <Icon className=\"h-4 w-4\" />\n <span className=\"capitalize\">{type}</span>\n </div>\n </SelectItem>\n )\n })}\n </SelectContent>\n </Select>\n </Grid>\n\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n value={formData.description}\n onChange={(e) =>\n setFormData((prev) => ({\n ...prev,\n description: e.target.value,\n }))\n }\n placeholder=\"Describe the agent's purpose and role\"\n />\n </Grid>\n\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!formData.name}>\n Create Agent\n </Button>\n </div>\n </Grid>\n </DialogContent>\n </Dialog>\n )\n}\n\n// Task assignment dialog\nconst TaskAssignmentDialog: React.FC<{\n agents: Agent[]\n onTaskAssign: (task: Task, agentId: string) => void\n}> = ({ agents, onTaskAssign }) => {\n const [open, setOpen] = useState(false)\n const [taskData, setTaskData] = useState({\n title: \"\",\n description: \"\",\n priority: \"medium\" as Task[\"priority\"],\n assignedTo: \"\",\n estimatedTime: 0,\n })\n\n const handleSubmit = () => {\n if (!taskData.title || !taskData.assignedTo) return\n\n const task: Task = {\n id: crypto.randomUUID(),\n title: taskData.title,\n description: taskData.description,\n priority: taskData.priority,\n status: \"pending\",\n progress: 0,\n createdAt: new Date(),\n estimatedTime: taskData.estimatedTime || undefined,\n }\n\n onTaskAssign(task, taskData.assignedTo)\n\n setTaskData({\n title: \"\",\n description: \"\",\n priority: \"medium\",\n assignedTo: \"\",\n estimatedTime: 0,\n })\n setOpen(false)\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"outline\">\n <MessageSquare className=\"h-4 w-4 mr-2\" />\n Assign Task\n </Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Assign New Task</DialogTitle>\n <DialogDescription>\n Create and assign a task to an agent\n </DialogDescription>\n </DialogHeader>\n <Grid columns={1} gap={16}>\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"title\">Task Title</Label>\n <Input\n id=\"title\"\n value={taskData.title}\n onChange={(e) =>\n setTaskData((prev) => ({ ...prev, title: e.target.value }))\n }\n placeholder=\"Enter task title\"\n />\n </Grid>\n\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n value={taskData.description}\n onChange={(e) =>\n setTaskData((prev) => ({\n ...prev,\n description: e.target.value,\n }))\n }\n placeholder=\"Describe the task requirements\"\n />\n </Grid>\n\n <Grid columns={{ min: 180, max: 2 }} gap={16}>\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"priority\">Priority</Label>\n <Select\n value={taskData.priority}\n onValueChange={(value: Task[\"priority\"]) =>\n setTaskData((prev) => ({ ...prev, priority: value }))\n }\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"low\">Low</SelectItem>\n <SelectItem value=\"medium\">Medium</SelectItem>\n <SelectItem value=\"high\">High</SelectItem>\n </SelectContent>\n </Select>\n </Grid>\n\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"estimatedTime\">Estimated Time (hours)</Label>\n <Input\n id=\"estimatedTime\"\n type=\"number\"\n value={taskData.estimatedTime}\n onChange={(e) =>\n setTaskData((prev) => ({\n ...prev,\n estimatedTime: parseInt(e.target.value) || 0,\n }))\n }\n placeholder=\"0\"\n />\n </Grid>\n </Grid>\n\n <Grid columns={1} gap={8}>\n <Label htmlFor=\"assignedTo\">Assign to Agent</Label>\n <Select\n value={taskData.assignedTo}\n onValueChange={(value) =>\n setTaskData((prev) => ({ ...prev, assignedTo: value }))\n }\n >\n <SelectTrigger>\n <SelectValue placeholder=\"Select an agent\" />\n </SelectTrigger>\n <SelectContent>\n {agents\n .filter((agent) => agent.status === \"idle\")\n .map((agent) => (\n <SelectItem key={agent.id} value={agent.id}>\n <div className=\"flex items-center gap-2\">\n <div\n className={cn(\n \"w-2 h-2 rounded-full\",\n agentTypeConfig[agent.type].color\n )}\n />\n {agent.name}\n </div>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </Grid>\n\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button\n onClick={handleSubmit}\n disabled={!taskData.title || !taskData.assignedTo}\n >\n Assign Task\n </Button>\n </div>\n </Grid>\n </DialogContent>\n </Dialog>\n )\n}\n\n// Main component\nexport const AIAgents: React.FC<AIAgentsProps> = ({\n agents: initialAgents = [],\n onAgentCreate,\n onTaskAssign,\n onWorkflowSave,\n workflowMode = \"custom\",\n className,\n}) => {\n const [agents, setAgents] = useState<Agent[]>(initialAgents)\n const [connections, setConnections] = useState<WorkflowConnection[]>([])\n const [selectedAgent, setSelectedAgent] = useState<string | null>(null)\n const [isWorkflowRunning, setIsWorkflowRunning] = useState(false)\n const workflowRef = useRef<HTMLDivElement>(null)\n\n // Initialize default agents if none provided\n useEffect(() => {\n if (initialAgents.length === 0) {\n const defaultAgents: Agent[] = [\n {\n id: \"1\",\n name: \"Research Assistant\",\n type: \"research\",\n capabilities: [\"web-search\", \"data-analysis\", \"fact-checking\"],\n status: \"idle\",\n position: { x: 50, y: 50 },\n connections: [\"2\"],\n performance: {\n tasksCompleted: 12,\n successRate: 95,\n avgCompletionTime: 45,\n },\n },\n {\n id: \"2\",\n name: \"Code Generator\",\n type: \"code\",\n capabilities: [\"code-generation\", \"debugging\", \"testing\"],\n status: \"working\",\n position: { x: 400, y: 50 },\n connections: [\"3\"],\n currentTask: {\n id: \"t1\",\n title: \"Generate React Component\",\n description: \"Create a reusable button component\",\n priority: \"high\",\n status: \"in-progress\",\n progress: 65,\n createdAt: new Date(),\n },\n performance: {\n tasksCompleted: 8,\n successRate: 90,\n avgCompletionTime: 38,\n },\n },\n {\n id: \"3\",\n name: \"Quality Analyst\",\n type: \"analysis\",\n capabilities: [\"code-review\", \"performance-analysis\", \"testing\"],\n status: \"idle\",\n position: { x: 750, y: 50 },\n connections: [],\n performance: {\n tasksCompleted: 15,\n successRate: 98,\n avgCompletionTime: 25,\n },\n },\n ]\n setAgents(defaultAgents)\n\n const defaultConnections: WorkflowConnection[] = [\n { id: \"c1\", source: \"1\", target: \"2\", animated: true },\n { id: \"c2\", source: \"2\", target: \"3\", animated: true },\n ]\n setConnections(defaultConnections)\n }\n }, [initialAgents])\n\n const handleAgentCreate = (\n agentData: Omit<Agent, \"id\" | \"position\" | \"performance\">\n ) => {\n const newAgent: Agent = {\n ...agentData,\n id: crypto.randomUUID(),\n position: { x: 100 + agents.length * 300, y: 100 },\n performance: {\n tasksCompleted: 0,\n successRate: 100,\n avgCompletionTime: 0,\n },\n }\n\n setAgents((prev) => [...prev, newAgent])\n onAgentCreate?.(agentData)\n }\n\n const handleTaskAssign = (task: Task, agentId: string) => {\n setAgents((prev) =>\n prev.map((agent) =>\n agent.id === agentId\n ? { ...agent, currentTask: task, status: \"working\" as AgentStatus }\n : agent\n )\n )\n onTaskAssign?.(task, agentId)\n\n // Simulate task progress\n const progressInterval = setInterval(() => {\n setAgents((prev) =>\n prev.map((agent) => {\n if (agent.id === agentId && agent.currentTask) {\n const newProgress = Math.min(agent.currentTask.progress + 10, 100)\n const updatedTask = { ...agent.currentTask, progress: newProgress }\n\n if (newProgress === 100) {\n clearInterval(progressInterval)\n return {\n ...agent,\n currentTask: undefined,\n status: \"completed\" as AgentStatus,\n performance: {\n ...agent.performance,\n tasksCompleted: agent.performance.tasksCompleted + 1,\n },\n }\n }\n\n return { ...agent, currentTask: updatedTask }\n }\n return agent\n })\n )\n }, 2000)\n }\n\n const handleAgentDrag = (\n agentId: string,\n position: { x: number; y: number }\n ) => {\n setAgents((prev) =>\n prev.map((agent) =>\n agent.id === agentId ? { ...agent, position } : agent\n )\n )\n }\n\n const handleDeleteAgent = (agentId: string) => {\n setAgents((prev) => prev.filter((agent) => agent.id !== agentId))\n setConnections((prev) =>\n prev.filter((conn) => conn.source !== agentId && conn.target !== agentId)\n )\n if (selectedAgent === agentId) {\n setSelectedAgent(null)\n }\n }\n\n const handleWorkflowToggle = () => {\n setIsWorkflowRunning(!isWorkflowRunning)\n\n if (!isWorkflowRunning) {\n // Start workflow simulation\n agents.forEach((agent, index) => {\n setTimeout(() => {\n setAgents((prev) =>\n prev.map((a) =>\n a.id === agent.id ? { ...a, status: \"working\" as AgentStatus } : a\n )\n )\n }, index * 1000)\n })\n } else {\n // Stop workflow\n setAgents((prev) =>\n prev.map((agent) => ({ ...agent, status: \"idle\" as AgentStatus }))\n )\n }\n }\n\n const handleSaveWorkflow = () => {\n const workflow = { agents, connections }\n onWorkflowSave?.(workflow)\n }\n\n return (\n <div className={cn(\"w-full h-full\", className)}>\n <Tabs defaultValue=\"workflow\" className=\"w-full h-full\">\n <div className=\"flex items-center justify-between p-4 border-b\">\n <TabsList>\n <TabsTrigger value=\"workflow\">Workflow Designer</TabsTrigger>\n <TabsTrigger value=\"agents\">Agent Manager</TabsTrigger>\n <TabsTrigger value=\"monitoring\">Monitoring</TabsTrigger>\n </TabsList>\n\n <div className=\"flex items-center gap-2\">\n <CreateAgentDialog onAgentCreate={handleAgentCreate} />\n <TaskAssignmentDialog\n agents={agents}\n onTaskAssign={handleTaskAssign}\n />\n <Separator orientation=\"vertical\" className=\"h-6\" />\n <Button\n variant={isWorkflowRunning ? \"destructive\" : \"default\"}\n onClick={handleWorkflowToggle}\n >\n {isWorkflowRunning ? (\n <>\n <CircleStop className=\"h-4 w-4 mr-2\" />\n Stop Workflow\n </>\n ) : (\n <>\n <Play className=\"h-4 w-4 mr-2\" />\n Run Workflow\n </>\n )}\n </Button>\n <Button variant=\"outline\" onClick={handleSaveWorkflow}>\n Save Workflow\n </Button>\n </div>\n </div>\n\n <TabsContent value=\"workflow\" className=\"flex-1 p-0\">\n <div\n ref={workflowRef}\n className=\"relative w-full h-[600px] bg-muted/20 overflow-hidden\"\n style={{\n backgroundImage: `radial-gradient(circle, #e5e7eb 1px, transparent 1px)`,\n backgroundSize: \"20px 20px\",\n }}\n >\n <CommunicationFlow connections={connections} agents={agents} />\n\n {agents.map((agent) => (\n <AgentCard\n key={agent.id}\n agent={agent}\n isSelected={selectedAgent === agent.id}\n onSelect={() => setSelectedAgent(agent.id)}\n onEdit={() => {\n /* TODO: Implement edit */\n }}\n onDelete={() => handleDeleteAgent(agent.id)}\n onDrag={(position) => handleAgentDrag(agent.id, position)}\n />\n ))}\n\n {agents.length === 0 && (\n <Grid\n columns={1}\n gap={8}\n className=\"absolute inset-0 text-muted-foreground text-center\"\n style={{ placeContent: \"center\", justifyItems: \"center\" }}\n >\n <Bot className=\"h-12 w-12\" />\n <div>\n <h3 className=\"text-lg font-medium\">No Agents Created</h3>\n <p className=\"text-sm\">\n Create your first AI agent to get started\n </p>\n </div>\n </Grid>\n )}\n </div>\n </TabsContent>\n\n <TabsContent value=\"agents\" className=\"flex-1 p-4\">\n <Grid columns={{ min: 280, max: 3 }} gap={16}>\n {agents.map((agent) => {\n const config = agentTypeConfig[agent.type]\n const Icon = config.icon\n\n return (\n <Card key={agent.id}>\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div\n className={cn(\n \"p-3 rounded-lg text-white\",\n config.color\n )}\n >\n <Icon className=\"h-5 w-5\" />\n </div>\n <div>\n <CardTitle className=\"text-lg\">{agent.name}</CardTitle>\n <CardDescription className=\"capitalize\">\n {agent.type} Agent\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent>\n <Grid columns={1} gap={16}>\n <Grid\n columns=\"minmax(0, 1fr) auto\"\n gap={8}\n style={{ alignItems: \"center\" }}\n >\n <span className=\"text-sm font-medium\">Status</span>\n <Badge\n variant={\n agent.status === \"working\" ? \"default\" : \"secondary\"\n }\n className=\"capitalize\"\n >\n {agent.status}\n </Badge>\n </Grid>\n\n {agent.currentTask && (\n <Grid columns={1} gap={8}>\n <div className=\"text-sm font-medium\">\n Current Task\n </div>\n <div className=\"bg-muted p-3 rounded-lg\">\n <div className=\"font-medium text-sm\">\n {agent.currentTask.title}\n </div>\n <Progress\n value={agent.currentTask.progress}\n className=\"mt-2\"\n />\n <div className=\"text-xs text-muted-foreground mt-1\">\n {agent.currentTask.progress}% Complete\n </div>\n </div>\n </Grid>\n )}\n\n <Grid columns={1} gap={8}>\n <div className=\"text-sm font-medium\">Capabilities</div>\n <div className=\"flex flex-wrap gap-1\">\n {agent.capabilities.map((capability) => (\n <Badge\n key={capability}\n variant=\"outline\"\n className=\"text-xs\"\n >\n {capability}\n </Badge>\n ))}\n </div>\n </Grid>\n\n <Grid columns={3} gap={8} className=\"text-center\">\n <div>\n <div className=\"text-lg font-bold\">\n {agent.performance.tasksCompleted}\n </div>\n <div className=\"text-xs text-muted-foreground\">\n Tasks\n </div>\n </div>\n <div>\n <div className=\"text-lg font-bold\">\n {agent.performance.successRate}%\n </div>\n <div className=\"text-xs text-muted-foreground\">\n Success\n </div>\n </div>\n <div>\n <div className=\"text-lg font-bold\">\n {agent.performance.avgCompletionTime}m\n </div>\n <div className=\"text-xs text-muted-foreground\">\n Avg Time\n </div>\n </div>\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n )\n })}\n </Grid>\n </TabsContent>\n\n <TabsContent value=\"monitoring\" className=\"flex-1 p-4\">\n <Grid columns={{ min: 380, max: 2 }} gap={24}>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Activity className=\"h-5 w-5\" />\n System Overview\n </CardTitle>\n </CardHeader>\n <CardContent>\n <Grid columns={1} gap={16}>\n <Grid columns={{ min: 130, max: 2 }} gap={16}>\n <div className=\"text-center p-4 bg-muted rounded-lg\">\n <div className=\"text-2xl font-bold\">{agents.length}</div>\n <div className=\"text-sm text-muted-foreground\">\n Total Agents\n </div>\n </div>\n <div className=\"text-center p-4 bg-muted rounded-lg\">\n <div className=\"text-2xl font-bold\">\n {agents.filter((a) => a.status === \"working\").length}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n Active\n </div>\n </div>\n </Grid>\n\n <Grid columns={1} gap={8} className=\"text-sm\">\n <Grid\n columns=\"minmax(0, 1fr) auto\"\n gap={8}\n style={{ alignItems: \"center\" }}\n >\n <span>Workflow Status</span>\n <Badge\n variant={isWorkflowRunning ? \"default\" : \"secondary\"}\n >\n {isWorkflowRunning ? \"Running\" : \"Stopped\"}\n </Badge>\n </Grid>\n <Grid columns=\"minmax(0, 1fr) auto\" gap={8}>\n <span>Connected Agents</span>\n <span>{connections.length} connections</span>\n </Grid>\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Network className=\"h-5 w-5\" />\n Agent Performance\n </CardTitle>\n </CardHeader>\n <CardContent>\n <ScrollArea className=\"h-64\">\n <Grid columns={1} gap={12}>\n {agents.map((agent) => (\n <Grid\n key={agent.id}\n columns=\"minmax(0, 1fr) auto\"\n gap={12}\n className=\"p-3 bg-muted rounded-lg\"\n style={{ alignItems: \"center\" }}\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={cn(\n \"w-3 h-3 rounded-full\",\n agentTypeConfig[agent.type].color\n )}\n />\n <div>\n <div className=\"font-medium text-sm\">\n {agent.name}\n </div>\n <div className=\"text-xs text-muted-foreground capitalize\">\n {agent.type}\n </div>\n </div>\n </div>\n <div className=\"text-right\">\n <div className=\"text-sm font-medium\">\n {agent.performance.successRate}%\n </div>\n <div className=\"text-xs text-muted-foreground\">\n success rate\n </div>\n </div>\n </Grid>\n ))}\n </Grid>\n </ScrollArea>\n </CardContent>\n </Card>\n </Grid>\n </TabsContent>\n </Tabs>\n </div>\n )\n}\n\nexport default AIAgents\n"
}
],
"type": "components:ai"
}