181 lines
6.0 KiB
TypeScript
181 lines
6.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { RecurringTemplate, defaultRecurringTemplates } from "@/lib/mission-control/recurring";
|
|
|
|
const AGENT_COLORS: Record<string, string> = {
|
|
thoth: "#8b5cf6",
|
|
"thoth-trading": "#f59e0b",
|
|
ptah: "#10b981",
|
|
seshat: "#ec4899",
|
|
anubis: "#6366f1",
|
|
sekhmet: "#ef4444"
|
|
};
|
|
|
|
const AGENT_NAMES: Record<string, string> = {
|
|
thoth: "Thoth",
|
|
"thoth-trading": "Thoth Trading",
|
|
ptah: "Ptah",
|
|
seshat: "Seshat",
|
|
anubis: "Anubis",
|
|
sekhmet: "Sekhmet"
|
|
};
|
|
|
|
export default function AutoRunPanel() {
|
|
const [templates, setTemplates] = useState<RecurringTemplate[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [running, setRunning] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
fetchTemplates();
|
|
}, []);
|
|
|
|
const fetchTemplates = async () => {
|
|
const res = await fetch("/api/recurring");
|
|
const data = await res.json();
|
|
|
|
// Merge with defaults if empty
|
|
if (data.length === 0) {
|
|
await fetch("/api/recurring", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
action: "save",
|
|
template: defaultRecurringTemplates[0]
|
|
})
|
|
});
|
|
setTemplates(defaultRecurringTemplates);
|
|
} else {
|
|
setTemplates(data);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
const toggleTemplate = async (templateId: string, enabled: boolean) => {
|
|
await fetch("/api/recurring", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ action: "toggle", templateId, enabled })
|
|
});
|
|
fetchTemplates();
|
|
};
|
|
|
|
const runNow = async (templateId: string) => {
|
|
setRunning(templateId);
|
|
await fetch("/api/recurring", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ action: "run-now", templateId })
|
|
});
|
|
setRunning(null);
|
|
fetchTemplates();
|
|
};
|
|
|
|
const formatSchedule = (template: RecurringTemplate) => {
|
|
const { schedule } = template;
|
|
switch (schedule.type) {
|
|
case "hourly": return "Hourly";
|
|
case "daily": return `Daily @ ${schedule.time}`;
|
|
case "weekly":
|
|
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
return `Weekly: ${days[schedule.dayOfWeek || 0]} @ ${schedule.time}`;
|
|
case "monthly": return `Monthly: Day ${schedule.dayOfMonth}`;
|
|
default: return schedule.type;
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return <div className="p-4 text-white/50">Loading templates...</div>;
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h3 className="text-lg font-semibold">🔄 Auto-Run Templates</h3>
|
|
<span className="text-sm text-white/50">
|
|
{templates.filter(t => t.enabled).length} active
|
|
</span>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
{templates.map((template) => (
|
|
<div
|
|
key={template.id}
|
|
className={`p-4 rounded-lg border ${
|
|
template.enabled
|
|
? "bg-white/10 border-green-500/30"
|
|
: "bg-white/5 border-white/10"
|
|
}`}
|
|
>
|
|
<div className="flex justify-between items-start">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<span
|
|
className="px-2 py-0.5 rounded text-xs font-medium"
|
|
style={{ backgroundColor: AGENT_COLORS[template.agent] || "#666" }}
|
|
>
|
|
{AGENT_NAMES[template.agent] || template.agent}
|
|
</span>
|
|
<span className="text-white/70 text-sm">
|
|
{formatSchedule(template)}
|
|
</span>
|
|
</div>
|
|
<h4 className="font-medium">{template.name}</h4>
|
|
<p className="text-sm text-white/50">{template.description}</p>
|
|
|
|
{template.preInstructions && (
|
|
<details className="mt-2">
|
|
<summary className="text-xs text-white/40 cursor-pointer">
|
|
View pre-instructions
|
|
</summary>
|
|
<p className="text-xs text-white/60 mt-1 p-2 bg-black/20 rounded">
|
|
{template.preInstructions}
|
|
</p>
|
|
</details>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2 ml-4">
|
|
<button
|
|
onClick={() => toggleTemplate(template.id, !template.enabled)}
|
|
className={`px-3 py-1 rounded text-sm font-medium transition ${
|
|
template.enabled
|
|
? "bg-green-600 hover:bg-green-700"
|
|
: "bg-white/10 hover:bg-white/20"
|
|
}`}
|
|
>
|
|
{template.enabled ? "✓ ON" : "○ OFF"}
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => runNow(template.id)}
|
|
disabled={running === template.id}
|
|
className="px-3 py-1 bg-brand-pink/80 hover:bg-brand-pink rounded text-sm font-medium disabled:opacity-50"
|
|
>
|
|
{running === template.id ? "⏳" : "▶ Run"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex gap-4 mt-3 text-xs text-white/40">
|
|
<span>Run count: {template.runCount || 0}</span>
|
|
{template.lastRun && (
|
|
<span>Last: {new Date(template.lastRun).toLocaleString()}</span>
|
|
)}
|
|
{template.nextRun && (
|
|
<span>Next: {new Date(template.nextRun).toLocaleString()}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{templates.length === 0 && (
|
|
<div className="text-center py-8 text-white/50">
|
|
No recurring templates. Create one to automate your agents.
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|