Building an Agent Admin Dashboard: React Components for Monitoring and Configuration
By Sagar Shankaran, Founder of CallSphere
Design and build an admin dashboard for AI agents with metric cards, real-time charts, configuration panels, and activity logs using React, TypeScript, and TanStack Query.
Key takeaways
What an Agent Dashboard Needs
An agent admin dashboard serves two audiences: operations teams monitoring agent health and behavior, and product teams configuring agent behavior. The dashboard must display key metrics at a glance, show conversation activity in real-time, surface errors and escalations, and provide configuration controls for agent parameters.
Dashboard Layout
Use a grid-based layout with a sidebar for navigation and a main content area divided into metric cards at the top and detailed panels below.
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
function AgentDashboard() {
return (
<div className="flex h-screen bg-gray-50">
<Sidebar />
<main className="flex-1 overflow-y-auto p-6">
<h1 className="text-2xl font-bold mb-6">Agent Overview</h1>
<MetricCardsRow />
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6">
<ConversationChart />
<RecentActivity />
</div>
<AgentConfigPanel />
</main>
</div>
);
}
Metric Cards with Real-Time Data
Metric cards show the most important numbers: total conversations, average response time, error rate, and user satisfaction score. Fetch these with TanStack Query for automatic background refetching.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
import { useQuery } from "@tanstack/react-query";
interface AgentMetrics {
totalConversations: number;
avgResponseTimeMs: number;
errorRate: number;
satisfactionScore: number;
}
function MetricCardsRow() {
const { data, isLoading } = useQuery<AgentMetrics>({
queryKey: ["agent-metrics"],
queryFn: () =>
fetch("/api/admin/metrics").then((r) => r.json()),
refetchInterval: 30_000, // Refresh every 30 seconds
});
if (isLoading) return <MetricCardsSkeleton />;
return (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
<MetricCard
label="Conversations"
value={data!.totalConversations.toLocaleString()}
trend="+12%"
trendUp={true}
/>
<MetricCard
label="Avg Response"
value={`${(data!.avgResponseTimeMs / 1000).toFixed(1)}s`}
trend="-8%"
trendUp={true}
/>
<MetricCard
label="Error Rate"
value={`${(data!.errorRate * 100).toFixed(2)}%`}
trend="+0.3%"
trendUp={false}
/>
<MetricCard
label="Satisfaction"
value={`${data!.satisfactionScore.toFixed(1)}/5`}
trend="+0.2"
trendUp={true}
/>
</div>
);
}
The Metric Card Component
Each card displays a label, value, and trend indicator. The trend arrow and color change based on whether the direction is positive or negative.
interface MetricCardProps {
label: string;
value: string;
trend: string;
trendUp: boolean;
}
function MetricCard({ label, value, trend, trendUp }: MetricCardProps) {
return (
<div className="bg-white rounded-xl border p-5">
<p className="text-sm text-gray-500 mb-1">{label}</p>
<p className="text-2xl font-bold text-gray-900">{value}</p>
<p
className={`text-sm mt-2 ${
trendUp ? "text-green-600" : "text-red-600"
}`}
>
{trendUp ? "^" : "v"} {trend} vs last week
</p>
</div>
);
}
Skeleton Loading States
Dashboard components should show skeleton placeholders during data loading instead of blank space. This prevents layout shift and communicates that data is on the way.
function MetricCardsSkeleton() {
return (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} className="bg-white rounded-xl border p-5">
<div className="h-4 w-20 bg-gray-200 rounded animate-pulse mb-2" />
<div className="h-8 w-24 bg-gray-200 rounded animate-pulse mb-2" />
<div className="h-3 w-28 bg-gray-200 rounded animate-pulse" />
</div>
))}
</div>
);
}
Recent Activity Feed
An activity log shows recent conversations, errors, and escalations in chronological order. Use a polling query to keep it up-to-date.
interface ActivityItem {
id: string;
type: "conversation" | "error" | "escalation";
summary: string;
timestamp: string;
}
function RecentActivity() {
const { data } = useQuery<ActivityItem[]>({
queryKey: ["agent-activity"],
queryFn: () =>
fetch("/api/admin/activity?limit=20").then((r) => r.json()),
refetchInterval: 10_000,
});
const typeStyles: Record<ActivityItem["type"], string> = {
conversation: "bg-blue-100 text-blue-700",
error: "bg-red-100 text-red-700",
escalation: "bg-yellow-100 text-yellow-700",
};
return (
<div className="bg-white rounded-xl border p-5">
<h2 className="font-semibold text-lg mb-4">Recent Activity</h2>
<div className="space-y-3 max-h-80 overflow-y-auto">
{data?.map((item) => (
<div key={item.id} className="flex items-start gap-3">
<span
className={`text-xs px-2 py-0.5 rounded-full
font-medium ${typeStyles[item.type]}`}
>
{item.type}
</span>
<div className="flex-1 min-w-0">
<p className="text-sm text-gray-700 truncate">
{item.summary}
</p>
<p className="text-xs text-gray-400">{item.timestamp}</p>
</div>
</div>
))}
</div>
</div>
);
}
Agent Configuration Panel
Allow admins to tweak agent parameters like system prompt, temperature, max tokens, and enabled tools without deploying code.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
import { useMutation, useQueryClient } from "@tanstack/react-query";
interface AgentConfig {
systemPrompt: string;
temperature: number;
maxTokens: number;
enabledTools: string[];
}
function AgentConfigPanel() {
const queryClient = useQueryClient();
const { data: config } = useQuery<AgentConfig>({
queryKey: ["agent-config"],
queryFn: () =>
fetch("/api/admin/config").then((r) => r.json()),
});
const mutation = useMutation({
mutationFn: (updated: AgentConfig) =>
fetch("/api/admin/config", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(updated),
}),
onSuccess: () =>
queryClient.invalidateQueries({ queryKey: ["agent-config"] }),
});
if (!config) return null;
return (
<div className="bg-white rounded-xl border p-6 mt-6">
<h2 className="font-semibold text-lg mb-4">
Agent Configuration
</h2>
<label className="block text-sm font-medium mb-1">
System Prompt
</label>
<textarea
defaultValue={config.systemPrompt}
rows={4}
className="w-full border rounded-lg p-3 text-sm mb-4"
/>
<button
onClick={() => mutation.mutate(config)}
className="bg-blue-600 text-white px-4 py-2 rounded-lg
text-sm disabled:opacity-50"
disabled={mutation.isPending}
>
{mutation.isPending ? "Saving..." : "Save Changes"}
</button>
</div>
);
}
FAQ
How do I add charts to the dashboard?
Use a charting library like Recharts or Chart.js with a React wrapper. Fetch time-series data from your API (grouped by hour or day) and pass it to a line or bar chart component. Recharts integrates naturally with React because its charts are composed from React components like <LineChart>, <Line>, and <XAxis>.
Should I use WebSockets or polling for real-time dashboard updates?
Polling with TanStack Query's refetchInterval is simpler and works well for dashboards where 10-30 second latency is acceptable. Use WebSockets only if you need sub-second updates, such as live conversation transcripts or real-time error alerts that require immediate operator attention.
How do I restrict dashboard access to admin users?
Wrap your dashboard routes in an authentication guard that checks the user's role. In Next.js, use middleware to redirect non-admin users before the page loads. On the API side, every admin endpoint should verify the JWT token contains an admin role claim and return 403 if it does not.
#AdminDashboard #React #Monitoring #TypeScript #AIAgentManagement #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.