355 lines
19 KiB
TypeScript
355 lines
19 KiB
TypeScript
import { useState } from 'react';
|
|
import { Link } from '@tanstack/react-router';
|
|
import { Info, ChevronDown } from 'lucide-react';
|
|
import { APP_PATHS } from '@/routes';
|
|
import { AddToolDialog } from '@/components/ui/add-tool-dialog';
|
|
import { ConfigureMemoryDialog } from '@/components/ui/configure-memory-dialog';
|
|
import { ToolChip } from '@/components/ui/tool-chip';
|
|
import type { Tool } from '@/types';
|
|
|
|
/**
|
|
* Toggle Switch Component
|
|
* @description A toggle switch that visually represents on/off state
|
|
*/
|
|
function ToggleSwitch({ enabled }: { enabled: boolean }) {
|
|
return (
|
|
<div
|
|
className={`relative h-5 w-[35px] rounded-full transition-colors duration-200 ${
|
|
enabled ? 'bg-[#0033FF]' : 'bg-gray-300'
|
|
}`}
|
|
>
|
|
<div
|
|
className={`absolute top-0.5 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 ${
|
|
enabled ? 'translate-x-[15px]' : 'translate-x-0.5'
|
|
}`}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* AgentCreatePage component
|
|
* @description Page for creating a new agent with configuration options
|
|
* @returns Agent creation form page
|
|
*/
|
|
export function AgentCreatePage() {
|
|
const [enableKb, setEnableKb] = useState(false);
|
|
const [enableMemory, setEnableMemory] = useState(true);
|
|
const [isToolDialogOpen, setIsToolDialogOpen] = useState(false);
|
|
const [isMemoryDialogOpen, setIsMemoryDialogOpen] = useState(false);
|
|
const [memoryValue, setMemoryValue] = useState(2);
|
|
const [editingTool, setEditingTool] = useState<Tool | null>(null);
|
|
const [tools, setTools] = useState<Tool[]>([
|
|
{ id: '1', name: 'Slack', type: 'API', color: '#ff0080' },
|
|
{ id: '2', name: 'GitHub', type: 'MCP', color: '#8fbc24' },
|
|
]);
|
|
|
|
const handleAddTool = (toolData: Omit<Tool, 'id'>) => {
|
|
const newTool: Tool = {
|
|
...toolData,
|
|
id: Date.now().toString(), // Simple ID generation
|
|
};
|
|
setTools((prev) => [...prev, newTool]);
|
|
};
|
|
|
|
const handleUpdateTool = (toolId: string, toolData: Omit<Tool, 'id'>) => {
|
|
setTools((prev) =>
|
|
prev.map((tool) => (tool.id === toolId ? { ...tool, ...toolData } : tool))
|
|
);
|
|
setEditingTool(null);
|
|
};
|
|
|
|
const handleDeleteTool = (id: string) => {
|
|
setTools((prev) => prev.filter((tool) => tool.id !== id));
|
|
};
|
|
|
|
const handleRedirectTool = (id: string) => {
|
|
// Find the tool and open configuration/update dialog
|
|
const tool = tools.find((t) => t.id === id);
|
|
if (tool) {
|
|
setEditingTool(tool);
|
|
setIsToolDialogOpen(true);
|
|
}
|
|
};
|
|
|
|
const handleDialogClose = (open: boolean) => {
|
|
setIsToolDialogOpen(open);
|
|
if (!open) {
|
|
setEditingTool(null);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen px-[50px] py-6">
|
|
<div className="mb-6">
|
|
<h1 className="font-semibold text-[20px] text-black leading-normal">
|
|
Add Agent
|
|
</h1>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-6">
|
|
{/* Main Form Card */}
|
|
<div className="bg-white border border-[rgba(0,0,0,0.1)] rounded-[24px] p-8 shadow-md">
|
|
<div className="grid gap-6 lg:grid-cols-2">
|
|
{/* Left Column */}
|
|
<div className="flex flex-col gap-6">
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
Name<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<input
|
|
className="w-full h-[46px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 text-sm text-[rgba(0,0,0,0.75)] shadow-[0px_2px_4px_0px_#EEF1F7] outline-none focus:border-[#0033FF] tracking-[0.2px]"
|
|
placeholder="Input here"
|
|
type="text"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
Agent Description
|
|
<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<textarea
|
|
rows={4}
|
|
className="w-full h-[135px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 text-sm text-[rgba(0,0,0,0.75)] shadow-[0px_2px_4px_0px_#EEF1F7] outline-none focus:border-[#0033FF] tracking-[0.2px] resize-none"
|
|
placeholder="Lorem Ipsum is simply dummy text"
|
|
name="agentDescription"
|
|
/>
|
|
</div>
|
|
<div className="grid gap-8 md:grid-cols-2">
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
LLM Provider
|
|
<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<div className="flex items-center justify-between h-[46px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 shadow-[0px_2px_4px_0px_#EEF1F7]">
|
|
<span className="text-sm text-[rgba(0,0,0,0.75)] tracking-[0.2px]">
|
|
Select
|
|
</span>
|
|
<ChevronDown className="shrink-0 size-5 text-[rgba(0,0,0,0.75)]" />
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
LLM Model
|
|
<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<div className="flex items-center justify-between h-[46px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 shadow-[0px_2px_4px_0px_#EEF1F7]">
|
|
<span className="text-sm text-[rgba(0,0,0,0.75)] tracking-[0.2px]">
|
|
Select
|
|
</span>
|
|
<ChevronDown className="shrink-0 size-5 text-[rgba(0,0,0,0.75)]" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right Column */}
|
|
<div className="flex flex-col gap-6 border border-[rgba(0,0,0,0.15)] rounded-[24px] p-8">
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
Agent Role<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<input
|
|
className="w-full h-[46px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 text-sm text-[rgba(0,0,0,0.75)] shadow-[0px_2px_4px_0px_#EEF1F7] outline-none focus:border-[#0033FF] tracking-[0.2px]"
|
|
placeholder="Input here"
|
|
type="text"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
Prompt<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<input
|
|
className="w-full h-[46px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 text-sm text-[rgba(0,0,0,0.75)] shadow-[0px_2px_4px_0px_#EEF1F7] outline-none focus:border-[#0033FF] tracking-[0.2px]"
|
|
placeholder="Your Prompt"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-3">
|
|
<div className="flex items-center justify-between">
|
|
<label className="font-medium text-sm text-black leading-[1.6]">
|
|
Instruction & Suggestion
|
|
<span className="text-[#ef3125]">*</span>
|
|
</label>
|
|
<button
|
|
className="bg-[rgba(0,51,255,0.06)] border border-[#333] rounded-md px-3 py-1.5 text-xs font-medium text-[#333] hover:bg-[rgba(0,51,255,0.1)] transition-colors"
|
|
>
|
|
Improve Instruction
|
|
</button>
|
|
</div>
|
|
<textarea
|
|
rows={3}
|
|
className="w-full h-[78px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-3 text-sm text-[rgba(0,0,0,0.75)] shadow-[0px_2px_4px_0px_#EEF1F7] outline-none focus:border-[#0033FF] tracking-[0.2px] resize-none"
|
|
placeholder="Lorem Ipsum is simply dummy text"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Features Card */}
|
|
<div className="bg-white border border-[rgba(0,0,0,0.1)] rounded-[24px] p-8 shadow-md">
|
|
<div className="flex flex-col gap-6">
|
|
<h2 className="font-semibold text-base text-black leading-[1.6]">
|
|
Features
|
|
</h2>
|
|
<div className="grid gap-6 md:grid-cols-3">
|
|
{/* Knowledge Base */}
|
|
<div className="flex flex-col gap-2">
|
|
<div
|
|
className={`flex items-center justify-between h-[56px] rounded-lg px-4 py-[13px] transition-all ${
|
|
enableKb
|
|
? 'border border-[rgba(0,51,255,0.25)] shadow-[0px_2px_12px_0px_rgba(0,51,255,0.5)]'
|
|
: 'border border-[#E5E8F4] bg-white shadow-[0px_2px_4px_0px_#EEF1F7]'
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-0.5">
|
|
<span className="font-medium text-sm text-black leading-[1.5]">
|
|
Knowledge Base
|
|
</span>
|
|
<Info className="size-4 text-black" />
|
|
</div>
|
|
<button
|
|
onClick={() => setEnableKb((v) => !v)}
|
|
className="relative shrink-0"
|
|
aria-pressed={enableKb}
|
|
>
|
|
<ToggleSwitch enabled={enableKb} />
|
|
</button>
|
|
</div>
|
|
{enableKb && (
|
|
<div className="flex items-center justify-between">
|
|
<button className="flex items-center gap-0.5 text-xs font-medium text-[#03f] leading-[1.5] hover:underline">
|
|
Configure
|
|
<img src="/Agent/redirect.svg" alt="Redirect" className="size-3.5 text-[#03f]" />
|
|
</button>
|
|
<div className="bg-white h-[22px] rounded px-1.5 py-0.5" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Memory */}
|
|
<div className="flex flex-col gap-2">
|
|
<div
|
|
className={`flex items-center justify-between h-[56px] rounded-lg px-4 py-[13px] transition-all ${
|
|
enableMemory
|
|
? 'border border-[rgba(0,51,255,0.25)] shadow-[0px_2px_12px_0px_rgba(0,51,255,0.5)]'
|
|
: 'border border-[#E5E8F4] bg-white shadow-[0px_2px_4px_0px_#EEF1F7]'
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-0.5">
|
|
<span className="font-medium text-sm text-black leading-[1.5]">
|
|
Memory
|
|
</span>
|
|
<Info className="size-4 text-black" />
|
|
</div>
|
|
<button
|
|
onClick={() => setEnableMemory((v) => !v)}
|
|
className="relative shrink-0"
|
|
aria-pressed={enableMemory}
|
|
>
|
|
<ToggleSwitch enabled={enableMemory} />
|
|
</button>
|
|
</div>
|
|
{enableMemory && (
|
|
<div className="flex items-center justify-between">
|
|
<button
|
|
onClick={() => setIsMemoryDialogOpen(true)}
|
|
className="flex items-center gap-0.5 text-xs font-medium text-[#03f] leading-[1.5] hover:underline"
|
|
>
|
|
Configure
|
|
<img src="/Agent/redirect.svg" alt="Redirect" className="size-3.5 text-[#03f]" />
|
|
</button>
|
|
<span className="bg-white border border-[#e5e8f4] border-solid flex items-center px-1.5 py-0.5 rounded text-xs font-normal text-black text-center">
|
|
{memoryValue} Messages
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Tool Configuration */}
|
|
<div className="flex items-center justify-between h-[56px] rounded-lg border border-[#E5E8F4] bg-white px-4 py-[13px] shadow-[0px_2px_8px_0px_#EEF1F7]">
|
|
<div className="flex flex-1 gap-0.5 items-center">
|
|
<span className="font-medium text-sm text-black leading-[1.6]">
|
|
Tool Configuration
|
|
</span>
|
|
<Info className="size-4 text-black" />
|
|
</div>
|
|
<button
|
|
onClick={() => {
|
|
setEditingTool(null);
|
|
setIsToolDialogOpen(true);
|
|
}}
|
|
className="bg-white border border-black border-solid flex items-center justify-center gap-1.5 px-3 py-[3px] rounded-[6px] hover:bg-gray-50 transition-colors"
|
|
>
|
|
<div className="relative size-6 flex items-center justify-center">
|
|
<img
|
|
src="/Agent/add.svg"
|
|
alt="Add"
|
|
className="size-3.5 filter brightness-0"
|
|
/> </div>
|
|
<span className="font-medium text-sm text-black leading-[1.5] text-left">
|
|
Add
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Divider */}
|
|
<div className="h-px bg-[#E5E8F4] w-full" />
|
|
|
|
{/* Tools Display Section */}
|
|
<div className="bg-[rgba(229,232,244,0.25)] border border-[#e5e8f4] border-solid flex flex-col items-start justify-center p-6 relative rounded-[12px] shrink-0 w-full">
|
|
<div className="flex gap-3 items-start relative shrink-0 flex-wrap">
|
|
{tools.map((tool) => (
|
|
<ToolChip
|
|
key={tool.id}
|
|
tool={tool}
|
|
onDelete={handleDeleteTool}
|
|
onRedirect={handleRedirectTool}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex gap-2 justify-center font-semibold">
|
|
<Link
|
|
to={APP_PATHS.agent}
|
|
className="bg-white border border-[rgba(0,0,0,0.5)] rounded-xl px-[42px] py-[19px] w-[182px] flex items-center justify-center hover:bg-gray-50 transition-colors"
|
|
>
|
|
<span className="font-medium text-sm text-black text-center leading-[18px]">
|
|
Cancel
|
|
</span>
|
|
</Link>
|
|
<button
|
|
className="bg-[#03f] border border-[#03f] rounded-xl px-[42px] py-[19px] w-[182px] flex items-center justify-center hover:bg-[#002BCC] transition-colors"
|
|
>
|
|
<span className="font-medium text-sm text-white text-center leading-[18px]">
|
|
Create
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Add Tool Dialog */}
|
|
<AddToolDialog
|
|
open={isToolDialogOpen}
|
|
onOpenChange={handleDialogClose}
|
|
onAddTool={handleAddTool}
|
|
onUpdateTool={handleUpdateTool}
|
|
editingTool={editingTool}
|
|
/>
|
|
|
|
{/* Configure Memory Dialog */}
|
|
<ConfigureMemoryDialog
|
|
open={isMemoryDialogOpen}
|
|
onOpenChange={setIsMemoryDialogOpen}
|
|
initialValue={memoryValue}
|
|
onSave={setMemoryValue}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|