'use client'; import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { ArrowLeft, Save, Loader2, Settings2, Sparkles, MessageSquare, Check, Clock } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; import { Badge } from '@/components/ui/badge'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useAgent, useUpdateAgent } from '@/hooks/react-query/agents/use-agents'; import { AgentMCPConfiguration } from '../../_components/agent-mcp-configuration'; import { toast } from 'sonner'; import { AgentToolsConfiguration } from '../../_components/agent-tools-configuration'; import { AgentPreview } from '../../_components/agent-preview'; import { cn } from '@/lib/utils'; import { getAgentAvatar } from '../../_utils/get-agent-style'; import { EditableText } from '@/components/ui/editable'; import { StylePicker } from '../../_components/style-picker'; interface AgentWithStyling { agent_id: string; name?: string; description?: string; system_prompt?: string; agentpress_tools?: Record; configured_mcps?: Array<{ name: string; qualifiedName: string; config: any; enabledTools?: string[] }>; is_default?: boolean; avatar?: string; avatar_color?: string; created_at?: string; updated_at?: string; } type SaveStatus = 'idle' | 'saving' | 'saved' | 'error'; export default function AgentConfigurationPage() { const params = useParams(); const router = useRouter(); const agentId = params.agentId as string; const { data: agent, isLoading, error } = useAgent(agentId); const updateAgentMutation = useUpdateAgent(); const { avatar, color } = getAgentAvatar(agentId); const [formData, setFormData] = useState({ name: '', description: '', system_prompt: '', agentpress_tools: {}, configured_mcps: [], is_default: false, avatar: '', avatar_color: '', }); const originalDataRef = useRef(null); const [saveStatus, setSaveStatus] = useState('idle'); const [debounceTimer, setDebounceTimer] = useState(null); const accordionRef = useRef(null); useEffect(() => { if (agent) { const agentData = agent as any; // Safe casting for extended properties const initialData = { name: agentData.name || '', description: agentData.description || '', system_prompt: agentData.system_prompt || '', agentpress_tools: agentData.agentpress_tools || {}, configured_mcps: agentData.configured_mcps || [], is_default: agentData.is_default || false, avatar: agentData.avatar || '', avatar_color: agentData.avatar_color || '', }; setFormData(initialData); originalDataRef.current = { ...initialData }; } }, [agent]); useEffect(() => { if (error) { const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes('Access denied') || errorMessage.includes('403')) { toast.error('You don\'t have permission to edit this agent'); router.push('/agents'); return; } } }, [error, router]); const hasDataChanged = useCallback((newData: typeof formData, originalData: typeof formData | null): boolean => { if (!originalData) return true; if (newData.name !== originalData.name || newData.description !== originalData.description || newData.system_prompt !== originalData.system_prompt || newData.is_default !== originalData.is_default || newData.avatar !== originalData.avatar || newData.avatar_color !== originalData.avatar_color) { return true; } if (JSON.stringify(newData.agentpress_tools) !== JSON.stringify(originalData.agentpress_tools) || JSON.stringify(newData.configured_mcps) !== JSON.stringify(originalData.configured_mcps)) { return true; } return false; }, []); const saveAgent = useCallback(async (data: typeof formData) => { try { setSaveStatus('saving'); await updateAgentMutation.mutateAsync({ agentId, ...data }); originalDataRef.current = { ...data }; setSaveStatus('saved'); setTimeout(() => setSaveStatus('idle'), 2000); } catch (error) { console.error('Error updating agent:', error); setSaveStatus('error'); toast.error('Failed to update agent'); setTimeout(() => setSaveStatus('idle'), 3000); } }, [agentId, updateAgentMutation]); const debouncedSave = useCallback((data: typeof formData) => { if (debounceTimer) { clearTimeout(debounceTimer); } if (!hasDataChanged(data, originalDataRef.current)) { return; } const timer = setTimeout(() => { if (hasDataChanged(data, originalDataRef.current)) { saveAgent(data); } }, 500); setDebounceTimer(timer); }, [debounceTimer, saveAgent, hasDataChanged]); const handleFieldChange = (field: string, value: any) => { const newFormData = { ...formData, [field]: value }; setFormData(newFormData); debouncedSave(newFormData); }; const handleBack = () => { router.push('/agents'); }; // Auto-scroll to accordion when it opens const scrollToAccordion = useCallback(() => { if (accordionRef.current) { accordionRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' }); } }, []); const handleStyleChange = useCallback((emoji: string, color: string) => { const newFormData = { ...formData, avatar: emoji, avatar_color: color, }; setFormData(newFormData); debouncedSave(newFormData); }, [formData, debouncedSave]); // Get current style with fallback to generated defaults const getCurrentStyle = useCallback(() => { if (formData.avatar && formData.avatar_color) { return { avatar: formData.avatar, color: formData.avatar_color, }; } return getAgentAvatar(agentId); }, [formData.avatar, formData.avatar_color, agentId]); const currentStyle = getCurrentStyle(); const getSaveStatusBadge = () => { const showSaved = saveStatus === 'idle' && !hasDataChanged(formData, originalDataRef.current); switch (saveStatus) { case 'saving': return ( Saving... ); case 'saved': return ( Saved ); case 'error': return ( Error saving ); default: return showSaved ? ( Saved ) : ( Error saving ); } }; useEffect(() => { return () => { if (debounceTimer) { clearTimeout(debounceTimer); } }; }, [debounceTimer]); if (isLoading) { return (
Loading agent...
); } if (error || !agent) { const errorMessage = error instanceof Error ? error.message : String(error); const isAccessDenied = errorMessage.includes('Access denied') || errorMessage.includes('403'); return (
{isAccessDenied ? ( You don't have permission to edit this agent. You can only edit agents that you created. ) : ( <>

Agent not found

The agent you're looking for doesn't exist.

)}
); } return (
Custom Agent Builder
{getSaveStatusBadge()}
{currentStyle.avatar}
handleFieldChange('name', value)} className="text-xl font-semibold bg-transparent" placeholder="Click to add agent name..." /> handleFieldChange('description', value)} className="text-muted-foreground" placeholder="Click to add description..." />
Instructions
handleFieldChange('system_prompt', value)} className='bg-transparent hover:bg-transparent border-none focus-visible:ring-0 shadow-none flex-1' placeholder='Click to set system instructions...' multiline={true} minHeight="300px" />
AgentPress Tools
handleFieldChange('agentpress_tools', tools)} />
MCP Servers New
handleFieldChange('configured_mcps', mcps)} />
🚧

Agent Builder Coming Soon

We're working on an intuitive agent builder interface.

); }