|
|
@@ -2,23 +2,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect, useRef } from "react";
|
|
|
|
import { useState, useEffect, useRef } from "react";
|
|
|
|
|
|
|
|
|
|
|
|
const AGENT_CONFIG: Record<string, { name: string; avatar: string; color: string; role: string; description: string; skills: string[] }> = {
|
|
|
|
const AGENT_CONFIG: Record<string, { name: string; avatar: string; color: string; role: string; description: string; skills: string[]; project: string }> = {
|
|
|
|
horus: { name: "Horus", avatar: "👁️", color: "#ff7bc0", role: "Master Orchestrator", description: "Command center, delegates tasks, manages all agents", skills: ["Task delegation", "System orchestration", "Strategic planning"] },
|
|
|
|
horus: { name: "Horus", avatar: "👁️", color: "#ff7bc0", role: "Master Orchestrator", description: "Command center, delegates tasks, manages all agents", skills: ["Task delegation", "System orchestration", "Strategic planning"], project: "horus" },
|
|
|
|
thoth: { name: "Thoth", avatar: "🧠", color: "#8b5cf6", role: "Strategy & Research", description: "SiteMente planning, research, analysis", skills: ["Market research", "Competitor analysis", "AI trends", "Lead research"] },
|
|
|
|
thoth: { name: "Thoth", avatar: "🧠", color: "#8b5cf6", role: "Strategy & Research", description: "SiteMente planning, research, analysis", skills: ["Market research", "Competitor analysis", "AI trends", "Lead research"], project: "sitemente" },
|
|
|
|
"thoth-trading": { name: "Thoth Trading", avatar: "📈", color: "#f59e0b", role: "Market Research", description: "Crypto market analysis and research", skills: ["Technical analysis", "Price alerts", "Portfolio tracking"] },
|
|
|
|
"thoth-trading": { name: "Thoth Trading", avatar: "📈", color: "#f59e0b", role: "Market Research", description: "Crypto market analysis and research", skills: ["Technical analysis", "Price alerts", "Portfolio tracking"], project: "trading" },
|
|
|
|
ptah: { name: "Ptah", avatar: "🏗️", color: "#10b981", role: "Dev & Ops", description: "Development, deployment, technical implementation", skills: ["Code review", "Deployments", "Infra management", "Bug fixes"] },
|
|
|
|
ptah: { name: "Ptah", avatar: "🏗️", color: "#10b981", role: "Dev & Ops", description: "Development, deployment, technical implementation", skills: ["Code review", "Deployments", "Infra management", "Bug fixes"], project: "infrastructure" },
|
|
|
|
seshat: { name: "Seshat", avatar: "📜", color: "#ec4899", role: "Content & SEO", description: "Content strategy, SEO, marketing copy", skills: ["SEO optimization", "Copywriting", "Blog posts", "Social media"] },
|
|
|
|
seshat: { name: "Seshat", avatar: "📜", color: "#ec4899", role: "Content & SEO", description: "Content strategy, SEO, marketing copy", skills: ["SEO optimization", "Copywriting", "Blog posts", "Social media"], project: "sitemente" },
|
|
|
|
anubis: { name: "Anubis", avatar: "🐺", color: "#6366f1", role: "Outreach & Growth", description: "Lead generation, client acquisition", skills: ["Lead research", "Cold outreach", "Follow-ups", "Deal closing"] },
|
|
|
|
anubis: { name: "Anubis", avatar: "🐺", color: "#6366f1", role: "Outreach & Growth", description: "Lead generation, client acquisition", skills: ["Lead research", "Cold outreach", "Follow-ups", "Deal closing"], project: "sitemente" },
|
|
|
|
sekhmet: { name: "Sekhmet", avatar: "⚔️", color: "#ef4444", role: "Risk Management", description: "Trade execution and risk", skills: ["Risk assessment", "Trade execution", "Stop-loss management"] },
|
|
|
|
sekhmet: { name: "Sekhmet", avatar: "⚔️", color: "#ef4444", role: "Risk Management", description: "Trade execution and risk", skills: ["Risk assessment", "Trade execution", "Stop-loss management"], project: "trading" },
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface TaskModalProps {
|
|
|
|
|
|
|
|
agentId: string;
|
|
|
|
|
|
|
|
task?: any;
|
|
|
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
|
|
|
onSave: (task: any) => void;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function TaskModal({ agentId, task, onClose, onSave }: TaskModalProps) {
|
|
|
|
|
|
|
|
const agent = AGENT_CONFIG[agentId];
|
|
|
|
|
|
|
|
const [title, setTitle] = useState(task?.title || "");
|
|
|
|
|
|
|
|
const [description, setDescription] = useState(task?.description || "");
|
|
|
|
|
|
|
|
const [priority, setPriority] = useState(task?.priority || "medium");
|
|
|
|
|
|
|
|
const [isRecurring, setIsRecurring] = useState(false);
|
|
|
|
|
|
|
|
const [scheduleType, setScheduleType] = useState<"hourly" | "daily" | "weekly" | "monthly">("daily");
|
|
|
|
|
|
|
|
const [scheduleTime, setScheduleTime] = useState("09:00");
|
|
|
|
|
|
|
|
const [scheduleDay, setScheduleDay] = useState(1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleSave = () => {
|
|
|
|
|
|
|
|
if (!title.trim()) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const newTask = {
|
|
|
|
|
|
|
|
id: task?.id || `task-${Date.now()}`,
|
|
|
|
|
|
|
|
title,
|
|
|
|
|
|
|
|
description,
|
|
|
|
|
|
|
|
priority,
|
|
|
|
|
|
|
|
assignee: agentId,
|
|
|
|
|
|
|
|
project: agent?.project || "sitemente",
|
|
|
|
|
|
|
|
status: "todo",
|
|
|
|
|
|
|
|
order: Date.now(),
|
|
|
|
|
|
|
|
...(isRecurring && {
|
|
|
|
|
|
|
|
recurring: {
|
|
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
|
|
type: scheduleType,
|
|
|
|
|
|
|
|
time: scheduleType !== "hourly" ? scheduleTime : undefined,
|
|
|
|
|
|
|
|
dayOfWeek: scheduleType === "weekly" ? scheduleDay : undefined,
|
|
|
|
|
|
|
|
dayOfMonth: scheduleType === "monthly" ? scheduleDay : undefined,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onSave(newTask);
|
|
|
|
|
|
|
|
onClose();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-[60] p-4">
|
|
|
|
|
|
|
|
<div className="bg-gradient-to-br from-[#1a1a2e] to-[#16213e] rounded-xl w-full max-w-md border border-white/20 p-6">
|
|
|
|
|
|
|
|
<h3 className="text-lg font-bold mb-4">{task ? "Edit Task" : "Add New Task"}</h3>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Task Title *</label>
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
|
|
value={title}
|
|
|
|
|
|
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
|
|
|
|
|
|
placeholder="What needs to be done?"
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
autoFocus
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Description</label>
|
|
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
|
|
value={description}
|
|
|
|
|
|
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
|
|
|
|
|
|
placeholder="More details..."
|
|
|
|
|
|
|
|
rows={2}
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Priority</label>
|
|
|
|
|
|
|
|
<select
|
|
|
|
|
|
|
|
value={priority}
|
|
|
|
|
|
|
|
onChange={(e) => setPriority(e.target.value)}
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<option value="low">Low</option>
|
|
|
|
|
|
|
|
<option value="medium">Medium</option>
|
|
|
|
|
|
|
|
<option value="high">High</option>
|
|
|
|
|
|
|
|
<option value="critical">Critical</option>
|
|
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Recurring Option */}
|
|
|
|
|
|
|
|
<div className="border-t border-white/10 pt-4">
|
|
|
|
|
|
|
|
<label className="flex items-center gap-2 cursor-pointer">
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
|
|
|
type="checkbox"
|
|
|
|
|
|
|
|
checked={isRecurring}
|
|
|
|
|
|
|
|
onChange={(e) => setIsRecurring(e.target.checked)}
|
|
|
|
|
|
|
|
className="w-4 h-4 rounded accent-brand-pink"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<span className="text-sm">Make this a recurring task</span>
|
|
|
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{isRecurring && (
|
|
|
|
|
|
|
|
<div className="mt-3 space-y-3 pl-6">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Schedule</label>
|
|
|
|
|
|
|
|
<select
|
|
|
|
|
|
|
|
value={scheduleType}
|
|
|
|
|
|
|
|
onChange={(e) => setScheduleType(e.target.value as any)}
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<option value="hourly">Hourly</option>
|
|
|
|
|
|
|
|
<option value="daily">Daily</option>
|
|
|
|
|
|
|
|
<option value="weekly">Weekly</option>
|
|
|
|
|
|
|
|
<option value="monthly">Monthly</option>
|
|
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{scheduleType !== "hourly" && (
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Time (CET)</label>
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
|
|
|
type="time"
|
|
|
|
|
|
|
|
value={scheduleTime}
|
|
|
|
|
|
|
|
onChange={(e) => setScheduleTime(e.target.value)}
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{scheduleType === "weekly" && (
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Day of Week</label>
|
|
|
|
|
|
|
|
<select
|
|
|
|
|
|
|
|
value={scheduleDay}
|
|
|
|
|
|
|
|
onChange={(e) => setScheduleDay(parseInt(e.target.value))}
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<option value={0}>Sunday</option>
|
|
|
|
|
|
|
|
<option value={1}>Monday</option>
|
|
|
|
|
|
|
|
<option value={2}>Tuesday</option>
|
|
|
|
|
|
|
|
<option value={3}>Wednesday</option>
|
|
|
|
|
|
|
|
<option value={4}>Thursday</option>
|
|
|
|
|
|
|
|
<option value={5}>Friday</option>
|
|
|
|
|
|
|
|
<option value={6}>Saturday</option>
|
|
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{scheduleType === "monthly" && (
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<label className="text-xs text-white/50">Day of Month</label>
|
|
|
|
|
|
|
|
<input
|
|
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
|
|
min={1}
|
|
|
|
|
|
|
|
max={31}
|
|
|
|
|
|
|
|
value={scheduleDay}
|
|
|
|
|
|
|
|
onChange={(e) => setScheduleDay(parseInt(e.target.value))}
|
|
|
|
|
|
|
|
className="w-full mt-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-sm"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex gap-2 mt-6">
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
|
|
onClick={onClose}
|
|
|
|
|
|
|
|
className="flex-1 py-2 bg-white/10 rounded-lg text-sm hover:bg-white/20"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
Cancel
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
|
|
onClick={handleSave}
|
|
|
|
|
|
|
|
disabled={!title.trim()}
|
|
|
|
|
|
|
|
className="flex-1 py-2 bg-brand-pink rounded-lg text-sm font-medium hover:bg-[#ff7bc0] disabled:opacity-50"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
{task ? "Update" : "Add Task"}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface AgentModalProps {
|
|
|
|
interface AgentModalProps {
|
|
|
|
agentId: string;
|
|
|
|
agentId: string;
|
|
|
|
onClose: () => void;
|
|
|
|
onClose: () => void;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
const agent = AGENT_CONFIG[agentId] || { name: agentId, avatar: "🤖", color: "#666", role: "Agent", description: "", skills: [] };
|
|
|
|
const agent = AGENT_CONFIG[agentId] || { name: agentId, avatar: "🤖", color: "#666", role: "Agent", description: "", skills: [], project: "sitemente" };
|
|
|
|
|
|
|
|
|
|
|
|
const [tasks, setTasks] = useState<any[]>([]);
|
|
|
|
const [tasks, setTasks] = useState<any[]>([]);
|
|
|
|
const [templates, setTemplates] = useState<any[]>([]);
|
|
|
|
const [templates, setTemplates] = useState<any[]>([]);
|
|
|
@@ -30,6 +214,10 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
const [tokens, setTokens] = useState("0");
|
|
|
|
const [tokens, setTokens] = useState("0");
|
|
|
|
const [lastRun, setLastRun] = useState<string | null>(null);
|
|
|
|
const [lastRun, setLastRun] = useState<string | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Task Modal
|
|
|
|
|
|
|
|
const [showTaskModal, setShowTaskModal] = useState(false);
|
|
|
|
|
|
|
|
const [editingTask, setEditingTask] = useState<any>(null);
|
|
|
|
|
|
|
|
|
|
|
|
// Command input
|
|
|
|
// Command input
|
|
|
|
const [command, setCommand] = useState("");
|
|
|
|
const [command, setCommand] = useState("");
|
|
|
|
const [conversation, setConversation] = useState<{role: string, content: string}[]>([]);
|
|
|
|
const [conversation, setConversation] = useState<{role: string, content: string}[]>([]);
|
|
|
@@ -60,7 +248,7 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
if (tRes.ok) {
|
|
|
|
if (tRes.ok) {
|
|
|
|
const allTasks = await tRes.json();
|
|
|
|
const allTasks = await tRes.json();
|
|
|
|
const agentTasks = allTasks.filter((t: any) =>
|
|
|
|
const agentTasks = allTasks.filter((t: any) =>
|
|
|
|
t.assignee === agentId || t.project === getAgentProject(agentId)
|
|
|
|
t.assignee === agentId || t.project === agent.project
|
|
|
|
);
|
|
|
|
);
|
|
|
|
setTasks(agentTasks);
|
|
|
|
setTasks(agentTasks);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -118,14 +306,6 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
setLoading(false);
|
|
|
|
setLoading(false);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getAgentProject = (id: string): string => {
|
|
|
|
|
|
|
|
const map: Record<string, string> = {
|
|
|
|
|
|
|
|
thoth: "sitemente", "thoth-trading": "trading", ptah: "infrastructure",
|
|
|
|
|
|
|
|
seshat: "sitemente", anubis: "sitemente", sekhmet: "trading",
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
return map[id] || "sitemente";
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const toggleSection = (section: string) => {
|
|
|
|
const toggleSection = (section: string) => {
|
|
|
|
setExpanded(prev => ({ ...prev, [section]: !prev[section] }));
|
|
|
|
setExpanded(prev => ({ ...prev, [section]: !prev[section] }));
|
|
|
|
};
|
|
|
|
};
|
|
|
@@ -149,6 +329,48 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
setTimeout(fetchAllData, 2000);
|
|
|
|
setTimeout(fetchAllData, 2000);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Task Management
|
|
|
|
|
|
|
|
const handleSaveTask = async (task: any) => {
|
|
|
|
|
|
|
|
// Save to local state (in real app, would save to API)
|
|
|
|
|
|
|
|
const existing = tasks.find(t => t.id === task.id);
|
|
|
|
|
|
|
|
if (existing) {
|
|
|
|
|
|
|
|
setTasks(tasks.map(t => t.id === task.id ? task : t));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
setTasks([task, ...tasks]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If recurring, also create/update template
|
|
|
|
|
|
|
|
if (task.recurring) {
|
|
|
|
|
|
|
|
const template = {
|
|
|
|
|
|
|
|
id: `template-${task.id}`,
|
|
|
|
|
|
|
|
agent: agentId,
|
|
|
|
|
|
|
|
project: task.project,
|
|
|
|
|
|
|
|
schedule: task.recurring,
|
|
|
|
|
|
|
|
taskTemplate: { title: task.title, description: task.description, priority: task.priority },
|
|
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
|
|
runCount: 0
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
await fetch("/api/recurring", {
|
|
|
|
|
|
|
|
method: "POST",
|
|
|
|
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
|
|
|
|
body: JSON.stringify({ action: "save", template })
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setShowTaskModal(false);
|
|
|
|
|
|
|
|
setEditingTask(null);
|
|
|
|
|
|
|
|
fetchAllData();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDeleteTask = (taskId: string) => {
|
|
|
|
|
|
|
|
if (!confirm("Delete this task?")) return;
|
|
|
|
|
|
|
|
setTasks(tasks.filter(t => t.id !== taskId));
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleTaskStatusChange = (taskId: string, status: string) => {
|
|
|
|
|
|
|
|
setTasks(tasks.map(t => t.id === taskId ? { ...t, status } : t));
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const sendCommand = async () => {
|
|
|
|
const sendCommand = async () => {
|
|
|
|
if (!command.trim() || processing) return;
|
|
|
|
if (!command.trim() || processing) return;
|
|
|
|
|
|
|
|
|
|
|
@@ -157,7 +379,6 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
setConversation(prev => [...prev, { role: "user", content: userCommand }]);
|
|
|
|
setConversation(prev => [...prev, { role: "user", content: userCommand }]);
|
|
|
|
setProcessing(true);
|
|
|
|
setProcessing(true);
|
|
|
|
|
|
|
|
|
|
|
|
// Simulate agent response
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
const responses: Record<string, string> = {
|
|
|
|
const responses: Record<string, string> = {
|
|
|
|
thoth: `I've researched "${userCommand}" for SiteMente. Here are my findings:\n\n1. Market trends show increasing demand for AI chatbots\n2. Local competitors: 3 agencies in Málaga\n3. Recommended approach: Focus on restaurants first`,
|
|
|
|
thoth: `I've researched "${userCommand}" for SiteMente. Here are my findings:\n\n1. Market trends show increasing demand for AI chatbots\n2. Local competitors: 3 agencies in Málaga\n3. Recommended approach: Focus on restaurants first`,
|
|
|
@@ -218,7 +439,7 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
<span className="text-white/50">Tokens: {tokens}k</span>
|
|
|
|
<span className="text-white/50">Tokens: {tokens}k</span>
|
|
|
|
{lastRun && <span className="text-white/40">Last: {new Date(lastRun).toLocaleTimeString()}</span>}
|
|
|
|
{lastRun && <span className="text-white/40">Last: {new Date(lastRun).toLocaleTimeString()}</span>}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="text-xs text-white/40">{agent.project || getAgentProject(agentId)}</div>
|
|
|
|
<div className="text-xs text-white/40">{agent.project}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Skills */}
|
|
|
|
{/* Skills */}
|
|
|
@@ -238,7 +459,7 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
<div className="p-3 border-b border-white/10">
|
|
|
|
<div className="p-3 border-b border-white/10">
|
|
|
|
<span className="font-semibold">💬 Chat with {agent.name}</span>
|
|
|
|
<span className="font-semibold">💬 Chat with {agent.name}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="p-3 max-h-48 overflow-y-auto space-y-2">
|
|
|
|
<div className="p-3 max-h-40 overflow-y-auto space-y-2">
|
|
|
|
{conversation.length === 0 ? (
|
|
|
|
{conversation.length === 0 ? (
|
|
|
|
<p className="text-sm text-white/50 text-center py-4">
|
|
|
|
<p className="text-sm text-white/50 text-center py-4">
|
|
|
|
Ask {agent.name} anything about {agent.role}...
|
|
|
|
Ask {agent.name} anything about {agent.role}...
|
|
|
@@ -247,9 +468,7 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
conversation.map((msg, i) => (
|
|
|
|
conversation.map((msg, i) => (
|
|
|
|
<div key={i} className={`text-sm ${msg.role === "user" ? "text-right" : "text-left"}`}>
|
|
|
|
<div key={i} className={`text-sm ${msg.role === "user" ? "text-right" : "text-left"}`}>
|
|
|
|
<span className={`inline-block px-3 py-2 rounded-lg max-w-[85%] ${
|
|
|
|
<span className={`inline-block px-3 py-2 rounded-lg max-w-[85%] ${
|
|
|
|
msg.role === "user"
|
|
|
|
msg.role === "user" ? "bg-brand-pink/20 text-white" : "bg-white/10 text-white/90"
|
|
|
|
? "bg-brand-pink/20 text-white"
|
|
|
|
|
|
|
|
: "bg-white/10 text-white/90"
|
|
|
|
|
|
|
|
}`}>
|
|
|
|
}`}>
|
|
|
|
{msg.content}
|
|
|
|
{msg.content}
|
|
|
|
</span>
|
|
|
|
</span>
|
|
|
@@ -258,9 +477,7 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
{processing && (
|
|
|
|
{processing && (
|
|
|
|
<div className="text-left">
|
|
|
|
<div className="text-left">
|
|
|
|
<span className="inline-block px-3 py-2 rounded-lg bg-white/10 text-white/50">
|
|
|
|
<span className="inline-block px-3 py-2 rounded-lg bg-white/10 text-white/50">⏳ Processing...</span>
|
|
|
|
⏳ Processing...
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
<div ref={conversationEndRef} />
|
|
|
|
<div ref={conversationEndRef} />
|
|
|
@@ -289,33 +506,61 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
|
|
|
|
|
|
|
|
{/* 📋 TASKS */}
|
|
|
|
{/* 📋 TASKS */}
|
|
|
|
<div className="bg-white/5 rounded-xl border border-white/10 overflow-hidden">
|
|
|
|
<div className="bg-white/5 rounded-xl border border-white/10 overflow-hidden">
|
|
|
|
<button onClick={() => toggleSection("tasks")} className="w-full p-3 flex justify-between items-center hover:bg-white/5">
|
|
|
|
<div className="p-3 border-b border-white/10 flex justify-between items-center">
|
|
|
|
|
|
|
|
<button onClick={() => toggleSection("tasks")} className="flex items-center gap-2">
|
|
|
|
<span className="font-semibold">📋 TASKS ({tasks.length})</span>
|
|
|
|
<span className="font-semibold">📋 TASKS ({tasks.length})</span>
|
|
|
|
<span className="text-white/50">{expanded.tasks ? "▲" : "▼"}</span>
|
|
|
|
<span className="text-white/50 text-xs">{expanded.tasks ? "▲" : "▼"}</span>
|
|
|
|
</button>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
|
|
onClick={() => { setEditingTask(null); setShowTaskModal(true); }}
|
|
|
|
|
|
|
|
className="px-2 py-1 bg-brand-pink rounded text-xs hover:bg-[#ff7bc0]"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
+ Add
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
{expanded.tasks && (
|
|
|
|
{expanded.tasks && (
|
|
|
|
<div className="p-3 pt-0 grid grid-cols-3 gap-2">
|
|
|
|
<div className="p-3 pt-0 grid grid-cols-3 gap-2">
|
|
|
|
<div className="bg-white/5 rounded-lg p-2">
|
|
|
|
<div className="bg-white/5 rounded-lg p-2">
|
|
|
|
<h4 className="text-xs text-white/50 mb-2 text-center">To Do ({todoTasks.length})</h4>
|
|
|
|
<h4 className="text-xs text-white/50 mb-2 text-center">To Do ({todoTasks.length})</h4>
|
|
|
|
<div className="space-y-1">
|
|
|
|
<div className="space-y-1 max-h-32 overflow-y-auto">
|
|
|
|
{todoTasks.slice(0, 3).map((t: any) => (
|
|
|
|
{todoTasks.slice(0, 5).map((t: any) => (
|
|
|
|
<div key={t.id} className="p-1.5 bg-white/5 rounded text-xs truncate">{t.title}</div>
|
|
|
|
<div key={t.id} className="p-1.5 bg-white/5 rounded text-xs flex items-center justify-between group">
|
|
|
|
|
|
|
|
<span className="truncate flex-1">{t.title}</span>
|
|
|
|
|
|
|
|
<div className="hidden group-hover:flex gap-1">
|
|
|
|
|
|
|
|
<button onClick={() => handleTaskStatusChange(t.id, "in_progress")} className="text-blue-400">→</button>
|
|
|
|
|
|
|
|
<button onClick={() => { setEditingTask(t); setShowTaskModal(true); }} className="text-white/50">✎</button>
|
|
|
|
|
|
|
|
<button onClick={() => handleDeleteTask(t.id)} className="text-red-400">✕</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="bg-blue-500/10 rounded-lg p-2">
|
|
|
|
<div className="bg-blue-500/10 rounded-lg p-2">
|
|
|
|
<h4 className="text-xs text-blue-400 mb-2 text-center">In Progress ({inProgressTasks.length})</h4>
|
|
|
|
<h4 className="text-xs text-blue-400 mb-2 text-center">In Progress ({inProgressTasks.length})</h4>
|
|
|
|
<div className="space-y-1">
|
|
|
|
<div className="space-y-1 max-h-32 overflow-y-auto">
|
|
|
|
{inProgressTasks.slice(0, 3).map((t: any) => (
|
|
|
|
{inProgressTasks.slice(0, 5).map((t: any) => (
|
|
|
|
<div key={t.id} className="p-1.5 bg-white/5 rounded text-xs truncate">{t.title}</div>
|
|
|
|
<div key={t.id} className="p-1.5 bg-white/5 rounded text-xs flex items-center justify-between group">
|
|
|
|
|
|
|
|
<span className="truncate flex-1">{t.title}</span>
|
|
|
|
|
|
|
|
<div className="hidden group-hover:flex gap-1">
|
|
|
|
|
|
|
|
<button onClick={() => handleTaskStatusChange(t.id, "done")} className="text-green-400">✓</button>
|
|
|
|
|
|
|
|
<button onClick={() => { setEditingTask(t); setShowTaskModal(true); }} className="text-white/50">✎</button>
|
|
|
|
|
|
|
|
<button onClick={() => handleDeleteTask(t.id)} className="text-red-400">✕</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="bg-green-500/10 rounded-lg p-2">
|
|
|
|
<div className="bg-green-500/10 rounded-lg p-2">
|
|
|
|
<h4 className="text-xs text-green-400 mb-2 text-center">Done ({doneTasks.length})</h4>
|
|
|
|
<h4 className="text-xs text-green-400 mb-2 text-center">Done ({doneTasks.length})</h4>
|
|
|
|
<div className="space-y-1">
|
|
|
|
<div className="space-y-1 max-h-32 overflow-y-auto">
|
|
|
|
{doneTasks.slice(0, 3).map((t: any) => (
|
|
|
|
{doneTasks.slice(0, 5).map((t: any) => (
|
|
|
|
<div key={t.id} className="p-1.5 bg-white/5 rounded text-xs truncate opacity-50">{t.title}</div>
|
|
|
|
<div key={t.id} className="p-1.5 bg-white/5 rounded text-xs flex items-center justify-between group opacity-50">
|
|
|
|
|
|
|
|
<span className="truncate flex-1 line-through">{t.title}</span>
|
|
|
|
|
|
|
|
<div className="hidden group-hover:flex gap-1">
|
|
|
|
|
|
|
|
<button onClick={() => handleTaskStatusChange(t.id, "todo")} className="text-white/50">↩</button>
|
|
|
|
|
|
|
|
<button onClick={() => handleDeleteTask(t.id)} className="text-red-400">✕</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
@@ -421,6 +666,16 @@ export default function AgentModal({ agentId, onClose }: AgentModalProps) {
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Task Modal */}
|
|
|
|
|
|
|
|
{showTaskModal && (
|
|
|
|
|
|
|
|
<TaskModal
|
|
|
|
|
|
|
|
agentId={agentId}
|
|
|
|
|
|
|
|
task={editingTask}
|
|
|
|
|
|
|
|
onClose={() => { setShowTaskModal(false); setEditingTask(null); }}
|
|
|
|
|
|
|
|
onSave={handleSaveTask}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|