File size: 5,355 Bytes
20ec4ad 969b550 20ec4ad 969b550 20ec4ad 969b550 20ec4ad 969b550 20ec4ad |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
/* src/components/ask-ai/ask-ai.tsx */
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;
/* lê as três chaves do localStorage ---------------------------- */
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);
// Funções para o orquestrador
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
)
);
};
// Limpa tarefas concluídas após 1 hora
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); // Checa a cada 15 minutos
return () => clearInterval(interval);
}, []);
// Determina a cor do status baseado no agentId
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>
);
}
|