// ============================================================================
// public/screens/agents.jsx — Agent library (list + create modal)
// ----------------------------------------------------------------------------
// Cards-on-grid layout matching the mockup. Each card shows avatar gradient,
// name, role, language, LLM, status badge, and total calls.
// Phase 4 will add the full agent editor (prompt, voice, flow); for now we
// support: list, create with basic fields, activate/archive, delete.
// ============================================================================
function AgentsScreen({ go }) {
const [agents, setAgents] = React.useState(null);
const [error, setError] = React.useState(null);
const [showCreate, setShow] = React.useState(false);
const load = React.useCallback(() => {
setError(null);
VoaisAPI.get("/api/agents").then((r) => {
if (r.ok && r.data?.ok) setAgents(r.data.agents);
else setError(r.data?.msg || "Failed to load agents.");
});
}, []);
React.useEffect(load, [load]);
const handleStatus = async (id, status) => {
await VoaisAPI.patch("/api/agents/" + id, { status });
load();
};
const handleDelete = async (id) => {
if (!confirm("Delete this agent? This cannot be undone.")) return;
const r = await VoaisAPI.del("/api/agents/" + id);
if (!r.ok) { alert(r.data?.msg || "Delete failed."); return; }
load();
};
if (agents === null && !error) return ;
return (
{/* Header */}
{agents?.length || 0} agents
} onClick={() => setShow(true)}>New agent
{error && (
{error}
)}
{/* Empty state */}
{agents?.length === 0 && !error && (
No agents yet
Agents are the AI personas that handle your calls. Create your first one — give it a name, a role, and a voice — and you'll be ready to launch campaigns.
} onClick={() => setShow(true)}>Create your first agent
)}
{/* Grid */}
{agents && agents.length > 0 && (
)}
{showCreate &&
setShow(false)} onCreated={() => { setShow(false); load(); }}/>}
);
}
// ── Agent card ──────────────────────────────────────────────────────────
function AgentCard({ agent, onStatusChange, onDelete }) {
const grad = agent.avatar_gradient || "linear-gradient(135deg,#5B8FFF,#00D49F)";
const statusTone = agent.status === "active" ? "green" : agent.status === "draft" ? "gray" : "yellow";
return (
{/* Coloured header */}
{(agent.name || "?").charAt(0).toUpperCase()}
{agent.status}
{agent.name}
· {agent.code}
{agent.role || "—"}
{agent.language || "—"}
{agent.llm_provider} · {agent.llm_model}
Calls handled
{Number(agent.total_calls || 0).toLocaleString("en-IN")}
Success rate
{agent.success_rate ? `${Number(agent.success_rate)}%` : "—"}
{agent.status === "draft" && (
onStatusChange(agent.id, "active")}>Activate
)}
{agent.status === "active" && (
onStatusChange(agent.id, "archived")}>Archive
)}
{agent.status === "archived" && (
onStatusChange(agent.id, "active")}>Restore
)}
onDelete(agent.id)} title="Delete">
);
}
// ── Create agent modal ──────────────────────────────────────────────────
function CreateAgentModal({ onClose, onCreated }) {
const [form, setForm] = React.useState({
name: "",
role: "",
description: "",
language: "hinglish",
llm_provider: "gemini",
llm_model: "gemini-1.5-pro",
system_prompt: "",
opening_script: "",
});
const [submitting, setSubmitting] = React.useState(false);
const [err, setErr] = React.useState(null);
const submit = async (e) => {
e.preventDefault();
setErr(null);
if (!form.name.trim()) return setErr("Name is required.");
setSubmitting(true);
const r = await VoaisAPI.post("/api/agents", form);
setSubmitting(false);
if (r.ok) onCreated();
else setErr(r.data?.msg || "Failed to create agent.");
};
return (
);
}
// ── Modal primitive ─────────────────────────────────────────────────────
function Modal({ title, onClose, children, width = 520 }) {
React.useEffect(() => {
const onKey = (e) => { if (e.key === "Escape") onClose(); };
document.addEventListener("keydown", onKey);
document.body.style.overflow = "hidden";
return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
}, [onClose]);
return (
{ if (e.target.classList.contains("modal-backdrop")) onClose(); }}>
);
}
window.AgentsScreen = AgentsScreen;
window.Modal = Modal; // re-export for campaigns + contacts screens