synapse / frontend /src /components /AnalyticsView.jsx
adwitiyashukla's picture
pushed Synapse, an agentic AI assistant with hybrid RAG
b2c1c67
Raw
History Blame Contribute Delete
6.54 kB
import { useEffect, useState } from "react";
import {
Area,
AreaChart,
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { Coins, Gauge, MessageSquare, Zap } from "lucide-react";
import { api } from "../lib/api.js";
const tooltipStyle = {
background: "#131827",
border: "1px solid #232b41",
borderRadius: 10,
fontSize: 13,
color: "#e7eaf3",
};
export default function AnalyticsView() {
const [data, setData] = useState(null);
const [error, setError] = useState("");
useEffect(() => {
api("/api/analytics/overview")
.then(setData)
.catch((err) => setError(err.message));
}, []);
if (error) {
return (
<div className="analytics-scroll">
<div className="analytics-inner">
<div className="error-banner">{error}</div>
</div>
</div>
);
}
if (!data) {
return (
<div className="analytics-scroll" style={{ display: "grid", placeItems: "center" }}>
<div className="spinner" />
</div>
);
}
const totalTokens = data.input_tokens + data.output_tokens;
const maxTool = Math.max(1, ...data.tools.map((t) => t.count));
return (
<div className="analytics-scroll">
<div className="analytics-inner fade-in">
<h2>Usage analytics</h2>
<p className="analytics-sub">
Token consumption, spend and agent activity for your account (last 30 days shown in charts).
</p>
<div className="stat-grid">
<div className="stat-card">
<div className="stat-label"><Coins size={14} /> Total spend</div>
<div className="stat-value">${data.cost_usd.toFixed(4)}</div>
<div className="stat-hint">estimated from provider pricing</div>
</div>
<div className="stat-card">
<div className="stat-label"><Zap size={14} /> Tokens</div>
<div className="stat-value">{totalTokens.toLocaleString()}</div>
<div className="stat-hint">
{data.input_tokens.toLocaleString()} in / {data.output_tokens.toLocaleString()} out
</div>
</div>
<div className="stat-card">
<div className="stat-label"><MessageSquare size={14} /> Messages</div>
<div className="stat-value">{data.total_messages.toLocaleString()}</div>
<div className="stat-hint">
across {data.total_sessions} session{data.total_sessions === 1 ? "" : "s"}
</div>
</div>
<div className="stat-card">
<div className="stat-label"><Gauge size={14} /> Avg response</div>
<div className="stat-value">{(data.avg_latency_ms / 1000).toFixed(1)}s</div>
<div className="stat-hint">end-to-end, tools included</div>
</div>
</div>
<div className="chart-card">
<h3>Daily spend (USD)</h3>
<ResponsiveContainer width="100%" height={220}>
<AreaChart data={data.daily}>
<defs>
<linearGradient id="cost" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#6c8cff" stopOpacity={0.45} />
<stop offset="100%" stopColor="#6c8cff" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid stroke="#232b41" strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="date"
stroke="#8e97ad"
fontSize={11}
tickFormatter={(d) => d.slice(5)}
interval="preserveStartEnd"
/>
<YAxis stroke="#8e97ad" fontSize={11} width={54} />
<Tooltip contentStyle={tooltipStyle} />
<Area type="monotone" dataKey="cost_usd" stroke="#6c8cff" fill="url(#cost)" name="cost (USD)" />
</AreaChart>
</ResponsiveContainer>
</div>
<div className="chart-row">
<div className="chart-card">
<h3>Messages per day</h3>
<ResponsiveContainer width="100%" height={200}>
<BarChart data={data.daily}>
<CartesianGrid stroke="#232b41" strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="date"
stroke="#8e97ad"
fontSize={11}
tickFormatter={(d) => d.slice(5)}
interval="preserveStartEnd"
/>
<YAxis stroke="#8e97ad" fontSize={11} width={30} allowDecimals={false} />
<Tooltip contentStyle={tooltipStyle} />
<Bar dataKey="messages" fill="#22d3aa" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
<div className="chart-card">
<h3>Cost by model</h3>
{data.models.length === 0 ? (
<div className="centered-note">No model usage yet.</div>
) : (
<ResponsiveContainer width="100%" height={200}>
<BarChart data={data.models} layout="vertical">
<CartesianGrid stroke="#232b41" strokeDasharray="3 3" horizontal={false} />
<XAxis type="number" stroke="#8e97ad" fontSize={11} />
<YAxis
type="category"
dataKey="model"
stroke="#8e97ad"
fontSize={11}
width={110}
/>
<Tooltip contentStyle={tooltipStyle} />
<Bar dataKey="cost_usd" fill="#6c8cff" radius={[0, 4, 4, 0]} name="cost (USD)" />
</BarChart>
</ResponsiveContainer>
)}
</div>
</div>
<div className="chart-card">
<h3>Agent tool activity</h3>
{data.tools.length === 0 ? (
<div className="centered-note">
The agent has not used any tools yet. Ask about the weather or the web.
</div>
) : (
data.tools.map((tool) => (
<div className="tool-usage-row" key={tool.name}>
<span style={{ width: 170, color: "var(--muted)" }}>{tool.name}</span>
<div
className="tool-usage-bar"
style={{ width: `${(tool.count / maxTool) * 60}%` }}
/>
<span>{tool.count}</span>
</div>
))
)}
</div>
</div>
</div>
);
}