|
|
|
import { useState, useEffect } from "react"; |
|
import { useLocalStorage } from "react-use"; |
|
import { toast } from "react-toastify"; |
|
import ChatInterface from "./ChatInterface"; |
|
import { defaultHTML } from "../../utils/consts"; |
|
import { v4 as uuidv4 } from "uuid"; |
|
import { AgentTask, ExecutionPlan } from "./AgentOrchestrator"; |
|
import { FiCpu, FiAlertCircle, FiCheckCircle, FiLoader } from "react-icons/fi"; |
|
|
|
export type AskAIProps = { |
|
agentId: string; |
|
html: string; |
|
setHtml: (h: string) => void; |
|
isAiWorking: boolean; |
|
setisAiWorking: (b: boolean) => void; |
|
onNewPrompt: (p: string) => void; |
|
onScrollToBottom: () => void; |
|
}; |
|
|
|
export default function AskAI(props: AskAIProps) { |
|
const { |
|
agentId, |
|
html, |
|
setHtml, |
|
isAiWorking, |
|
setisAiWorking, |
|
onNewPrompt, |
|
onScrollToBottom, |
|
} = props; |
|
|
|
|
|
const [geminiKey] = useLocalStorage<string>("geminiKey", ""); |
|
const [chatgptKey] = useLocalStorage<string>("chatgptKey", ""); |
|
const [hfToken] = useLocalStorage<string>("hfToken", ""); |
|
const [activeTasks, setActiveTasks] = useState<AgentTask[]>([]); |
|
const [currentPlan, setCurrentPlan] = useState<ExecutionPlan | null>(null); |
|
|
|
|
|
const handleTaskCreated = (task: AgentTask) => { |
|
setActiveTasks(prev => [...prev, task]); |
|
toast.info(`Nova tarefa criada: ${task.description.substring(0, 40)}...`); |
|
}; |
|
|
|
const handlePlanCreated = (plan: ExecutionPlan) => { |
|
setCurrentPlan(plan); |
|
toast.success(`Plano criado com ${plan.steps.length} passos`); |
|
}; |
|
|
|
const handleTaskUpdated = (taskId: string, status: AgentTask['status'], result?: string) => { |
|
setActiveTasks(prev => |
|
prev.map(task => |
|
task.id === taskId |
|
? { |
|
...task, |
|
status, |
|
result, |
|
completedAt: (status === 'completed' || status === 'failed') ? new Date() : undefined |
|
} |
|
: task |
|
) |
|
); |
|
}; |
|
|
|
|
|
useEffect(() => { |
|
const interval = setInterval(() => { |
|
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000); |
|
setActiveTasks(prev => |
|
prev.filter(task => |
|
task.status !== 'completed' || |
|
!task.completedAt || |
|
task.completedAt > oneHourAgo |
|
) |
|
); |
|
}, 15 * 60 * 1000); |
|
|
|
return () => clearInterval(interval); |
|
}, []); |
|
|
|
|
|
const getAgentStatusColor = () => { |
|
switch (agentId) { |
|
case 'orchestrator': |
|
return 'bg-purple-600'; |
|
case 'mike': |
|
return 'bg-blue-600'; |
|
case 'alex': |
|
return 'bg-green-600'; |
|
case 'david': |
|
return 'bg-amber-600'; |
|
default: |
|
return 'bg-indigo-600'; |
|
} |
|
}; |
|
|
|
return ( |
|
<div className="flex flex-col h-full"> |
|
<div className="bg-gray-900 text-white py-3 px-4 border-b border-gray-800"> |
|
<div className="flex items-center"> |
|
<div className={`w-3 h-3 rounded-full mr-2 ${getAgentStatusColor()}`}></div> |
|
<h2 className="text-lg font-medium"> |
|
Agente: {agentId} |
|
</h2> |
|
{isAiWorking && ( |
|
<div className="ml-2 animate-pulse text-amber-400"> |
|
<FiLoader className="animate-spin" /> |
|
</div> |
|
)} |
|
</div> |
|
|
|
{currentPlan && ( |
|
<div className="mt-2 text-sm bg-gray-800 rounded p-2"> |
|
<div className="flex items-center text-indigo-400 mb-1"> |
|
<FiCpu className="mr-1" /> Plano: {currentPlan.name} |
|
</div> |
|
<div className="text-xs text-gray-400"> |
|
{currentPlan.steps.length} passos planejados |
|
</div> |
|
</div> |
|
)} |
|
|
|
{activeTasks.length > 0 && ( |
|
<div className="mt-2"> |
|
<div className="text-xs text-gray-400 mb-1">Tarefas:</div> |
|
<div className="flex flex-wrap gap-2"> |
|
{activeTasks |
|
.filter(t => t.status === 'in-progress') |
|
.slice(0, 2) |
|
.map(task => ( |
|
<div key={task.id} className="text-xs bg-gray-800 rounded px-2 py-1 flex items-center"> |
|
<FiLoader className="animate-spin mr-1 text-amber-400" /> |
|
<span className="truncate max-w-[180px]">{task.description}</span> |
|
</div> |
|
))} |
|
|
|
{activeTasks.filter(t => t.status === 'completed').length > 0 && ( |
|
<div className="text-xs bg-gray-800 rounded px-2 py-1 flex items-center"> |
|
<FiCheckCircle className="mr-1 text-green-400" /> |
|
<span>{activeTasks.filter(t => t.status === 'completed').length} concluídas</span> |
|
</div> |
|
)} |
|
</div> |
|
</div> |
|
)} |
|
</div> |
|
|
|
<ChatInterface |
|
agentId={agentId} |
|
html={html} |
|
setHtml={setHtml} |
|
isAiWorking={isAiWorking} |
|
setisAiWorking={setisAiWorking} |
|
onNewPrompt={onNewPrompt} |
|
onScrollToBottom={onScrollToBottom} |
|
geminiKey={geminiKey} |
|
chatgptKey={chatgptKey} |
|
hfToken={hfToken} |
|
/> |
|
</div> |
|
); |
|
} |
|
|