BMHQ Upgrade: Add Auto-Run, Execution Logs, Change Log, Brainown panels
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface AgentOutput {
|
||||
agent: string;
|
||||
date: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
const AGENT_NAMES: Record<string, string> = {
|
||||
thoth: "Thoth",
|
||||
"thoth-trading": "Thoth Trading",
|
||||
ptah: "Ptah",
|
||||
seshat: "Seshat",
|
||||
anubis: "Anubis",
|
||||
sekhmet: "Sekhmet",
|
||||
horus: "Horus"
|
||||
};
|
||||
|
||||
const AGENT_COLORS: Record<string, string> = {
|
||||
thoth: "#8b5cf6",
|
||||
"thoth-trading": "#f59e0b",
|
||||
ptah: "#10b981",
|
||||
seshat: "#ec4899",
|
||||
anubis: "#6366f1",
|
||||
sekhmet: "#ef4444",
|
||||
horus: "#ff7bc0"
|
||||
};
|
||||
|
||||
export default function BrainownPanel() {
|
||||
const [agents, setAgents] = useState<string[]>([]);
|
||||
const [selectedAgent, setSelectedAgent] = useState<string | null>(null);
|
||||
const [dates, setDates] = useState<string[]>([]);
|
||||
const [selectedDate, setSelectedDate] = useState<string | null>(null);
|
||||
const [content, setContent] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAgents();
|
||||
}, []);
|
||||
|
||||
const fetchAgents = async () => {
|
||||
const res = await fetch("/api/agent-outputs");
|
||||
const data = await res.json();
|
||||
setAgents(data);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const fetchDates = async (agent: string) => {
|
||||
const res = await fetch(`/api/agent-outputs?agent=${agent}`);
|
||||
const data = await res.json();
|
||||
if (Array.isArray(data)) {
|
||||
setDates(data);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchContent = async (agent: string, date: string) => {
|
||||
const res = await fetch(`/api/agent-outputs?agent=${agent}&date=${date}`);
|
||||
const data = await res.json();
|
||||
if (data.content) {
|
||||
setContent(data.content);
|
||||
} else {
|
||||
setContent(`# ${AGENT_NAMES[agent] || agent} - ${date}\n\nStart writing your notes here...`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAgentSelect = (agent: string) => {
|
||||
setSelectedAgent(agent);
|
||||
setSelectedDate(null);
|
||||
setContent("");
|
||||
fetchDates(agent);
|
||||
};
|
||||
|
||||
const handleDateSelect = (date: string) => {
|
||||
setSelectedDate(date);
|
||||
if (selectedAgent) {
|
||||
fetchContent(selectedAgent, date);
|
||||
}
|
||||
};
|
||||
|
||||
const saveContent = async () => {
|
||||
if (!selectedAgent || !selectedDate) return;
|
||||
|
||||
await fetch("/api/agent-outputs", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
action: "save",
|
||||
agent: selectedAgent,
|
||||
date: selectedDate,
|
||||
content
|
||||
})
|
||||
});
|
||||
|
||||
setEditing(false);
|
||||
};
|
||||
|
||||
const createNewNote = () => {
|
||||
if (!selectedAgent) return;
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
setSelectedDate(today);
|
||||
setContent(`# ${AGENT_NAMES[selectedAgent]} - ${today}\n\n`);
|
||||
setEditing(true);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div className="p-4 text-white/50">Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h3 className="text-lg font-semibold">🧠 Brainown</h3>
|
||||
{selectedAgent && (
|
||||
<button
|
||||
onClick={createNewNote}
|
||||
className="px-3 py-1 bg-brand-pink hover:bg-[#ff7bc0] rounded text-sm font-medium"
|
||||
>
|
||||
+ New Note
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{/* Agent List */}
|
||||
<div className="col-span-1 space-y-2">
|
||||
<h4 className="text-sm text-white/50 font-medium">Agents</h4>
|
||||
{agents.map((agent) => (
|
||||
<button
|
||||
key={agent}
|
||||
onClick={() => handleAgentSelect(agent)}
|
||||
className={`w-full p-2 rounded text-left text-sm transition ${
|
||||
selectedAgent === agent
|
||||
? "bg-brand-pink/20 border border-brand-pink"
|
||||
: "bg-white/5 hover:bg-white/10 border border-transparent"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="inline-block w-2 h-2 rounded-full mr-2"
|
||||
style={{ backgroundColor: AGENT_COLORS[agent] || "#666" }}
|
||||
/>
|
||||
{AGENT_NAMES[agent] || agent}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{agents.length === 0 && (
|
||||
<p className="text-xs text-white/50">No agent outputs yet</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Date List */}
|
||||
<div className="col-span-1 space-y-2">
|
||||
<h4 className="text-sm text-white/50 font-medium">Notes</h4>
|
||||
{dates.map((date) => (
|
||||
<button
|
||||
key={date}
|
||||
onClick={() => handleDateSelect(date)}
|
||||
className={`w-full p-2 rounded text-left text-sm transition ${
|
||||
selectedDate === date
|
||||
? "bg-brand-pink/20 border border-brand-pink"
|
||||
: "bg-white/5 hover:bg-white/10 border border-transparent"
|
||||
}`}
|
||||
>
|
||||
📝 {date}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{selectedAgent && dates.length === 0 && (
|
||||
<p className="text-xs text-white/50">No notes yet</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content Editor */}
|
||||
<div className="col-span-2">
|
||||
{selectedDate ? (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<h4 className="text-sm text-white/50">
|
||||
{AGENT_NAMES[selectedAgent]} - {selectedDate}
|
||||
</h4>
|
||||
<div className="flex gap-2">
|
||||
{editing ? (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setEditing(false)}
|
||||
className="px-2 py-1 bg-white/10 rounded text-xs"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={saveContent}
|
||||
className="px-2 py-1 bg-green-600 rounded text-xs"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setEditing(true)}
|
||||
className="px-2 py-1 bg-white/10 rounded text-xs"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{editing ? (
|
||||
<textarea
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
className="flex-1 w-full bg-black/30 border border-white/20 rounded p-3 text-white text-sm font-mono resize-none"
|
||||
placeholder="Write your notes in Markdown..."
|
||||
/>
|
||||
) : (
|
||||
<div className="flex-1 bg-black/30 border border-white/20 rounded p-3 text-white text-sm overflow-y-auto prose prose-invert prose-sm max-w-none">
|
||||
<pre className="whitespace-pre-wrap font-sans">{content}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-64 flex items-center justify-center text-white/50">
|
||||
Select an agent and note to view
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user