feat(frontend): add initial frontend structure with components, services, and assets
This commit is contained in:
801
frontend/app/agents/config/TaskAgentConfig.tsx
Normal file
801
frontend/app/agents/config/TaskAgentConfig.tsx
Normal file
@@ -0,0 +1,801 @@
|
||||
/*
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ @author: Davidson Gomes │
|
||||
│ @file: /app/agents/config/TaskAgentConfig.tsx │
|
||||
│ Developed by: Davidson Gomes │
|
||||
│ Creation date: May 13, 2025 │
|
||||
│ Contact: contato@evolution-api.com │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @copyright © Evolution API 2025. All rights reserved. │
|
||||
│ Licensed under the Apache License, Version 2.0 │
|
||||
│ │
|
||||
│ You may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ @important │
|
||||
│ For any future changes to the code in this file, it is recommended to │
|
||||
│ include, together with the modification, the information of the developer │
|
||||
│ who changed it and the date of modification. │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { Agent, TaskConfig } from "@/types/agent";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
Maximize2,
|
||||
Save,
|
||||
X,
|
||||
ArrowDown,
|
||||
List,
|
||||
Search,
|
||||
Edit,
|
||||
PenTool,
|
||||
} from "lucide-react";
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
interface TaskAgentConfigProps {
|
||||
values: Partial<Agent>;
|
||||
onChange: (values: Partial<Agent>) => void;
|
||||
agents: Agent[];
|
||||
getAgentNameById: (id: string) => string;
|
||||
singleTask?: boolean;
|
||||
}
|
||||
|
||||
const getAgentTypeLabel = (type: string): string => {
|
||||
const typeMap: Record<string, string> = {
|
||||
llm: "LLM",
|
||||
a2a: "A2A",
|
||||
sequential: "Sequential",
|
||||
parallel: "Parallel",
|
||||
loop: "Loop",
|
||||
workflow: "Workflow",
|
||||
task: "Task",
|
||||
};
|
||||
return typeMap[type] || type;
|
||||
};
|
||||
|
||||
const getAgentTypeColor = (type: string): string => {
|
||||
const colorMap: Record<string, string> = {
|
||||
llm: "bg-blue-800 text-white",
|
||||
a2a: "bg-purple-800 text-white",
|
||||
sequential: "bg-orange-800 text-white",
|
||||
parallel: "bg-green-800 text-white",
|
||||
loop: "bg-pink-800 text-white",
|
||||
workflow: "bg-yellow-800 text-black",
|
||||
task: "bg-green-800 text-white",
|
||||
};
|
||||
return colorMap[type] || "bg-neutral-800 text-white";
|
||||
};
|
||||
|
||||
export function TaskAgentConfig({
|
||||
values,
|
||||
onChange,
|
||||
agents,
|
||||
getAgentNameById,
|
||||
singleTask = false,
|
||||
}: TaskAgentConfigProps) {
|
||||
const [newTask, setNewTask] = useState<TaskConfig>({
|
||||
agent_id: "",
|
||||
description: "",
|
||||
expected_output: "",
|
||||
enabled_tools: [],
|
||||
});
|
||||
|
||||
const [taskAgentSearchQuery, setTaskAgentSearchQuery] = useState<string>("");
|
||||
const [filteredTaskAgents, setFilteredTaskAgents] = useState<Agent[]>([]);
|
||||
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState(false);
|
||||
const [expandedDescription, setExpandedDescription] = useState("");
|
||||
const [editingTaskIndex, setEditingTaskIndex] = useState<number | null>(null);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
const [toolSearchQuery, setToolSearchQuery] = useState<string>("");
|
||||
const [filteredTools, setFilteredTools] = useState<{id: string, name: string}[]>([]);
|
||||
const [isToolsModalOpen, setIsToolsModalOpen] = useState(false);
|
||||
const [tempSelectedTools, setTempSelectedTools] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isToolsModalOpen) {
|
||||
if (isEditing && editingTaskIndex !== null && values.config?.tasks) {
|
||||
setTempSelectedTools(
|
||||
values.config.tasks[editingTaskIndex]?.enabled_tools || []
|
||||
);
|
||||
} else {
|
||||
setTempSelectedTools([...newTask.enabled_tools || []]);
|
||||
}
|
||||
}
|
||||
}, [isToolsModalOpen]);
|
||||
|
||||
const getAvailableTaskAgents = (currentTaskAgentId?: string) =>
|
||||
agents.filter(
|
||||
(agent) =>
|
||||
agent.id !== values.id &&
|
||||
(!values.config?.tasks?.some((task) => task.agent_id === agent.id) ||
|
||||
agent.id === currentTaskAgentId)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const currentTaskAgentId =
|
||||
isEditing && editingTaskIndex !== null && values.config?.tasks
|
||||
? values.config.tasks[editingTaskIndex].agent_id
|
||||
: undefined;
|
||||
|
||||
const availableAgents = getAvailableTaskAgents(currentTaskAgentId);
|
||||
|
||||
if (taskAgentSearchQuery.trim() === "") {
|
||||
setFilteredTaskAgents(availableAgents);
|
||||
} else {
|
||||
const query = taskAgentSearchQuery.toLowerCase();
|
||||
setFilteredTaskAgents(
|
||||
availableAgents.filter(
|
||||
(agent) =>
|
||||
agent.name.toLowerCase().includes(query) ||
|
||||
(agent.description?.toLowerCase() || "").includes(query)
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [
|
||||
taskAgentSearchQuery,
|
||||
agents,
|
||||
values.config?.tasks,
|
||||
isEditing,
|
||||
editingTaskIndex,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
// Reset editing state when values change externally
|
||||
if (!isEditing) {
|
||||
const currentTaskAgentId =
|
||||
editingTaskIndex !== null && values.config?.tasks
|
||||
? values.config.tasks[editingTaskIndex]?.agent_id
|
||||
: undefined;
|
||||
setFilteredTaskAgents(getAvailableTaskAgents(currentTaskAgentId));
|
||||
}
|
||||
}, [agents, values.config?.tasks]);
|
||||
|
||||
const getAvailableTools = () => {
|
||||
if (!values.config?.tasks || values.config.tasks.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const taskAgentIds = values.config.tasks.map(task => task.agent_id);
|
||||
|
||||
const toolsList: {id: string, name: string}[] = [];
|
||||
const toolsMap: Record<string, boolean> = {};
|
||||
|
||||
taskAgentIds.forEach(agentId => {
|
||||
const agent = agents.find(a => a.id === agentId);
|
||||
|
||||
if (agent?.type === "llm" && agent.config?.tools) {
|
||||
agent.config.tools.forEach(tool => {
|
||||
if (!toolsMap[tool.id]) {
|
||||
toolsList.push({ id: tool.id, name: tool.id });
|
||||
toolsMap[tool.id] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (agent?.type === "llm" && agent.config?.mcp_servers) {
|
||||
agent.config.mcp_servers.forEach(mcp => {
|
||||
if (mcp.tools) {
|
||||
mcp.tools.forEach(toolId => {
|
||||
if (!toolsMap[toolId]) {
|
||||
toolsList.push({ id: toolId, name: toolId });
|
||||
toolsMap[toolId] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return toolsList;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const availableTools = getAvailableTools();
|
||||
|
||||
if (toolSearchQuery.trim() === "") {
|
||||
setFilteredTools(availableTools);
|
||||
} else {
|
||||
const query = toolSearchQuery.toLowerCase();
|
||||
setFilteredTools(
|
||||
availableTools.filter(
|
||||
(tool) =>
|
||||
tool.name.toLowerCase().includes(query) ||
|
||||
tool.id.toLowerCase().includes(query)
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [toolSearchQuery, values.config?.tasks, agents]);
|
||||
|
||||
const handleAddTask = () => {
|
||||
if (!newTask.agent_id || !newTask.description) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEditing && editingTaskIndex !== null) {
|
||||
const tasks = [...(values.config?.tasks || [])];
|
||||
tasks[editingTaskIndex] = { ...newTask };
|
||||
|
||||
onChange({
|
||||
...values,
|
||||
config: {
|
||||
...(values.config || {}),
|
||||
tasks,
|
||||
},
|
||||
});
|
||||
|
||||
setIsEditing(false);
|
||||
setEditingTaskIndex(null);
|
||||
} else {
|
||||
const tasks = [...(values.config?.tasks || [])];
|
||||
|
||||
if (singleTask) {
|
||||
tasks.splice(0, tasks.length, newTask);
|
||||
} else {
|
||||
tasks.push(newTask);
|
||||
}
|
||||
|
||||
onChange({
|
||||
...values,
|
||||
config: {
|
||||
...(values.config || {}),
|
||||
tasks,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setNewTask({
|
||||
agent_id: "",
|
||||
description: "",
|
||||
expected_output: "",
|
||||
enabled_tools: [],
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditTask = (index: number) => {
|
||||
const task = values.config?.tasks?.[index];
|
||||
if (task) {
|
||||
setNewTask({ ...task });
|
||||
setIsEditing(true);
|
||||
setEditingTaskIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setNewTask({
|
||||
agent_id: "",
|
||||
description: "",
|
||||
expected_output: "",
|
||||
enabled_tools: [],
|
||||
});
|
||||
setIsEditing(false);
|
||||
setEditingTaskIndex(null);
|
||||
};
|
||||
|
||||
const handleRemoveTask = (index: number) => {
|
||||
if (editingTaskIndex === index) {
|
||||
handleCancelEdit();
|
||||
}
|
||||
|
||||
const tasks = [...(values.config?.tasks || [])];
|
||||
tasks.splice(index, 1);
|
||||
|
||||
onChange({
|
||||
...values,
|
||||
config: {
|
||||
...(values.config || {}),
|
||||
tasks,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement>
|
||||
) => {
|
||||
const newValue = e.target.value;
|
||||
setNewTask({
|
||||
...newTask,
|
||||
description: newValue,
|
||||
});
|
||||
};
|
||||
|
||||
const handleExpandDescription = () => {
|
||||
setExpandedDescription(newTask.description);
|
||||
setIsDescriptionModalOpen(true);
|
||||
};
|
||||
|
||||
const handleSaveExpandedDescription = () => {
|
||||
setNewTask({
|
||||
...newTask,
|
||||
description: expandedDescription,
|
||||
});
|
||||
setIsDescriptionModalOpen(false);
|
||||
};
|
||||
|
||||
const handleToggleTool = (toolId: string) => {
|
||||
const index = tempSelectedTools.indexOf(toolId);
|
||||
|
||||
if (index > -1) {
|
||||
setTempSelectedTools(tempSelectedTools.filter(id => id !== toolId));
|
||||
} else {
|
||||
setTempSelectedTools([...tempSelectedTools, toolId]);
|
||||
}
|
||||
};
|
||||
|
||||
const isToolEnabled = (toolId: string) => {
|
||||
return tempSelectedTools.includes(toolId);
|
||||
};
|
||||
|
||||
const handleSaveTools = () => {
|
||||
if (isEditing && editingTaskIndex !== null && values.config?.tasks) {
|
||||
const tasks = [...(values.config?.tasks || [])];
|
||||
|
||||
const updatedTask = {
|
||||
...tasks[editingTaskIndex],
|
||||
enabled_tools: [...tempSelectedTools]
|
||||
};
|
||||
|
||||
tasks[editingTaskIndex] = updatedTask;
|
||||
|
||||
const newConfig = {
|
||||
...(values.config || {}),
|
||||
tasks: tasks
|
||||
};
|
||||
|
||||
onChange({
|
||||
...values,
|
||||
config: newConfig
|
||||
});
|
||||
|
||||
} else if (newTask.agent_id) {
|
||||
const updatedNewTask = {
|
||||
...newTask,
|
||||
enabled_tools: [...tempSelectedTools]
|
||||
};
|
||||
|
||||
setNewTask(updatedNewTask);
|
||||
}
|
||||
|
||||
setIsToolsModalOpen(false);
|
||||
};
|
||||
|
||||
const renderAgentTypeBadge = (agentId: string) => {
|
||||
const agent = agents.find((a) => a.id === agentId);
|
||||
if (!agent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge className={`ml-2 ${getAgentTypeColor(agent.type)} text-xs`}>
|
||||
{getAgentTypeLabel(agent.type)}
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-medium text-white flex items-center">
|
||||
<List className="mr-2 h-5 w-5 text-emerald-400" />
|
||||
{singleTask ? "Task" : "Tasks"}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="border border-[#444] rounded-md p-4 bg-[#222]">
|
||||
<p className="text-sm text-neutral-400 mb-4">
|
||||
{singleTask
|
||||
? "Configure the task that will be executed by the agent."
|
||||
: "Configure the sequential tasks that will be executed by the team of agents."}
|
||||
</p>
|
||||
|
||||
{values.config?.tasks && values.config.tasks.length > 0 ? (
|
||||
<div className="space-y-4 mb-4">
|
||||
{values.config.tasks.map((task, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`border border-[#333] rounded-md p-3 ${
|
||||
editingTaskIndex === index ? "bg-[#1e3a3a]" : "bg-[#2a2a2a]"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center">
|
||||
<span className="inline-flex items-center justify-center rounded-full bg-[#333] px-2 py-1 text-xs text-white mr-2">
|
||||
{index + 1}
|
||||
</span>
|
||||
<h4 className="font-medium text-white flex items-center">
|
||||
{getAgentNameById(task.agent_id)}
|
||||
{renderAgentTypeBadge(task.agent_id)}
|
||||
</h4>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-300 mt-1">
|
||||
{task.description}
|
||||
</p>
|
||||
{task.expected_output && (
|
||||
<div className="mt-2">
|
||||
<span className="text-xs text-neutral-400">
|
||||
Expected output:
|
||||
</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="ml-2 bg-[#333] text-emerald-400 border-emerald-400/30"
|
||||
>
|
||||
{task.expected_output}
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
{task.enabled_tools && task.enabled_tools.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<span className="text-xs text-neutral-400">
|
||||
Enabled tools:
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{task.enabled_tools.map((toolId) => (
|
||||
<Badge
|
||||
key={toolId}
|
||||
className="bg-[#333] text-emerald-400 border border-emerald-400/30 text-xs"
|
||||
>
|
||||
{toolId}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleEditTask(index)}
|
||||
className="text-neutral-400 hover:text-emerald-400 hover:bg-[#333] mr-1"
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleRemoveTask(index)}
|
||||
className="text-red-500 hover:text-red-400 hover:bg-[#333]"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{!singleTask &&
|
||||
index < (values.config?.tasks?.length || 0) - 1 && (
|
||||
<div className="flex justify-center my-2">
|
||||
<ArrowDown className="h-4 w-4 text-neutral-400" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-4 mb-4 bg-[#2a2a2a] rounded-md">
|
||||
<p className="text-neutral-400">No tasks configured</p>
|
||||
<p className="text-xs text-neutral-500">
|
||||
{singleTask
|
||||
? "Add a task to define the agent's behavior"
|
||||
: "Add tasks to define the workflow of the team"}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(!singleTask ||
|
||||
!values.config?.tasks ||
|
||||
values.config.tasks.length === 0 ||
|
||||
isEditing) && (
|
||||
<div className="space-y-3 border-t border-[#333] pt-4">
|
||||
<h4 className="text-sm font-medium text-white flex items-center justify-between">
|
||||
<span>
|
||||
{isEditing
|
||||
? "Edit task"
|
||||
: `Add ${singleTask ? "one" : "new"} task`}
|
||||
</span>
|
||||
{isEditing && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleCancelEdit}
|
||||
className="text-neutral-400 hover:text-emerald-400 hover:bg-[#333]"
|
||||
>
|
||||
<X className="h-4 w-4 mr-1" /> Cancel
|
||||
</Button>
|
||||
)}
|
||||
</h4>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="agent_id"
|
||||
className="text-xs text-neutral-400 mb-1 block"
|
||||
>
|
||||
Agent
|
||||
</Label>
|
||||
<Select
|
||||
value={newTask.agent_id}
|
||||
onValueChange={(value) =>
|
||||
setNewTask({ ...newTask, agent_id: value })
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="bg-[#2a2a2a] border-[#444] text-white">
|
||||
<SelectValue placeholder="Select agent" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-[#2a2a2a] border-[#444] text-white p-0">
|
||||
<div className="sticky top-0 z-10 p-2 bg-[#2a2a2a] border-b border-[#444]">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-neutral-400" />
|
||||
<Input
|
||||
placeholder="Search agents..."
|
||||
className="bg-[#333] border-[#444] text-white h-8 pl-8"
|
||||
value={taskAgentSearchQuery}
|
||||
onChange={(e) =>
|
||||
setTaskAgentSearchQuery(e.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-h-[200px] overflow-y-auto py-1">
|
||||
{filteredTaskAgents.length > 0 ? (
|
||||
filteredTaskAgents.map((agent) => (
|
||||
<SelectItem
|
||||
key={agent.id}
|
||||
value={agent.id}
|
||||
className="hover:bg-[#333] focus:bg-[#333] flex items-center justify-between px-2"
|
||||
data-agent-item="true"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="mr-2">{agent.name}</span>
|
||||
<Badge
|
||||
className={`${getAgentTypeColor(
|
||||
agent.type
|
||||
)} text-xs`}
|
||||
>
|
||||
{getAgentTypeLabel(agent.type)}
|
||||
</Badge>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))
|
||||
) : (
|
||||
<div className="text-neutral-500 px-4 py-2 text-center">
|
||||
No agents found
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2">
|
||||
<Label
|
||||
htmlFor="description"
|
||||
className="text-xs text-neutral-400 mb-1 block"
|
||||
>
|
||||
Task description
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Textarea
|
||||
id="description"
|
||||
value={newTask.description}
|
||||
onChange={handleDescriptionChange}
|
||||
className="w-full bg-[#2a2a2a] border-[#444] text-white pr-10"
|
||||
rows={3}
|
||||
onClick={handleExpandDescription}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-3 right-5 text-neutral-400 hover:text-emerald-400 focus:outline-none"
|
||||
onClick={handleExpandDescription}
|
||||
>
|
||||
<Maximize2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-neutral-400">
|
||||
<span className="inline-block h-3 w-3 mr-1">ℹ️</span>
|
||||
<span>
|
||||
Use {"{"}content{"}"} to insert the user's input.
|
||||
<span className="ml-2 text-emerald-400">
|
||||
Click to expand editor.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="expected_output"
|
||||
className="text-xs text-neutral-400 mb-1 block"
|
||||
>
|
||||
Expected output (optional)
|
||||
</Label>
|
||||
<Input
|
||||
id="expected_output"
|
||||
placeholder="Ex: JSON report, List of recommendations, etc."
|
||||
value={newTask.expected_output}
|
||||
onChange={(e) =>
|
||||
setNewTask({ ...newTask, expected_output: e.target.value })
|
||||
}
|
||||
className="bg-[#2a2a2a] border-[#444] text-white"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{newTask.enabled_tools && newTask.enabled_tools.length > 0 && (
|
||||
<div className="mt-3">
|
||||
<Label className="text-xs text-neutral-400 mb-1 block">
|
||||
Selected tools:
|
||||
</Label>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{newTask.enabled_tools.map((toolId) => (
|
||||
<Badge
|
||||
key={toolId}
|
||||
className="bg-[#333] text-emerald-400 border border-emerald-400/30"
|
||||
>
|
||||
{toolId}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between mt-3">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (newTask.agent_id) setIsToolsModalOpen(true);
|
||||
}}
|
||||
disabled={!newTask.agent_id}
|
||||
className="border-emerald-400 text-emerald-400 hover:bg-emerald-400/10 px-3"
|
||||
>
|
||||
<PenTool className="h-4 w-4 mr-2" />
|
||||
Configure tools
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleAddTask}
|
||||
disabled={!newTask.agent_id || !newTask.description}
|
||||
className="bg-[#222] text-emerald-400 border border-emerald-400 hover:bg-emerald-400/10"
|
||||
>
|
||||
<Save className="h-4 w-4 mr-1" />{" "}
|
||||
{isEditing ? "Update task" : "Add task"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
open={isDescriptionModalOpen}
|
||||
onOpenChange={setIsDescriptionModalOpen}
|
||||
>
|
||||
<DialogContent className="sm:max-w-[1200px] max-h-[90vh] bg-[#1a1a1a] border-[#333] overflow-hidden flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white">Task Description</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex-1 overflow-hidden flex flex-col min-h-[60vh]">
|
||||
<Textarea
|
||||
value={expandedDescription}
|
||||
onChange={(e) => setExpandedDescription(e.target.value)}
|
||||
className="flex-1 min-h-full bg-[#222] border-[#444] text-white p-4 focus:border-emerald-400 focus:ring-emerald-400 focus:ring-opacity-50 resize-none"
|
||||
placeholder="Enter detailed description for the task..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsDescriptionModalOpen(false)}
|
||||
className="bg-[#222] border-[#444] text-neutral-300 hover:bg-[#333] hover:text-white"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSaveExpandedDescription}
|
||||
className="bg-emerald-400 text-black hover:bg-[#00cc7d]"
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Save description
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={isToolsModalOpen} onOpenChange={setIsToolsModalOpen}>
|
||||
<DialogContent className="sm:max-w-[600px] max-h-[90vh] bg-[#1a1a1a] border-[#333] overflow-hidden flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white">
|
||||
Available tools
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="relative mb-4">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-neutral-400" />
|
||||
<Input
|
||||
placeholder="Search tools..."
|
||||
className="bg-[#222] border-[#444] text-white pl-9"
|
||||
value={toolSearchQuery}
|
||||
onChange={(e) => setToolSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto space-y-2 pr-1">
|
||||
{filteredTools.length > 0 ? (
|
||||
filteredTools.map((tool) => (
|
||||
<div
|
||||
key={tool.id}
|
||||
className="flex items-center space-x-2 p-2 rounded-md hover:bg-[#333] transition duration-150"
|
||||
>
|
||||
<Checkbox
|
||||
id={tool.id}
|
||||
checked={isToolEnabled(tool.id)}
|
||||
onCheckedChange={() => handleToggleTool(tool.id)}
|
||||
className="border-[#444] data-[state=checked]:bg-emerald-400 data-[state=checked]:text-black"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={tool.id}
|
||||
className="cursor-pointer text-white flex-1"
|
||||
>
|
||||
{tool.name}
|
||||
</Label>
|
||||
<Badge className="bg-[#333] text-emerald-400">{tool.id}</Badge>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-neutral-400">No tools available</p>
|
||||
<p className="text-xs text-neutral-500">
|
||||
The tools are obtained from the selected agents in the tasks.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
onClick={handleSaveTools}
|
||||
className="bg-emerald-400 text-black hover:bg-[#00cc7d]"
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Save settings
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user