IntegraChat / frontend /components /analytics-panel.tsx
nothingworry's picture
update the redflag resopnse
d25df55
raw
history blame
4.73 kB
"use client";
import { useState } from "react";
import { useTenant } from "@/contexts/TenantContext";
type ToolUsageStats = {
count: number;
avg_latency_ms: number;
total_tokens: number;
success_count: number;
error_count: number;
};
type AnalyticsOverview = {
overview: {
total_queries: number;
tool_usage: Record<string, ToolUsageStats>;
redflag_count: number;
active_users: number;
};
};
const API_BASE =
process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "") || "http://localhost:8000";
export function AnalyticsPanel() {
const { tenantId } = useTenant();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<AnalyticsOverview["overview"] | null>(null);
async function fetchAnalytics() {
setLoading(true);
setError(null);
try {
const res = await fetch(`${API_BASE}/analytics/overview`, {
headers: {
"x-tenant-id": tenantId,
},
});
if (!res.ok) {
throw new Error(`Analytics endpoint returned ${res.status}`);
}
const payload: AnalyticsOverview = await res.json();
setData(payload.overview);
} catch (err) {
console.error(err);
setError(
err instanceof Error
? err.message
: "Unable to reach analytics API. Is the FastAPI service running?",
);
} finally {
setLoading(false);
}
}
return (
<section
id="analytics"
className="glass-panel border border-white/10 p-6 text-white"
>
<div className="flex flex-wrap items-center justify-between gap-4">
<div>
<p className="text-sm uppercase tracking-[0.5em] text-cyan-200/70">
Compliance Pulse
</p>
<h2 className="mt-2 text-3xl font-semibold">Analytics snapshot</h2>
</div>
<button
onClick={fetchAnalytics}
disabled={loading}
className="rounded-full bg-white/90 px-5 py-2.5 text-sm font-semibold text-slate-900 shadow-lg shadow-cyan-500/30 transition hover:-translate-y-0.5 disabled:opacity-60"
>
{loading ? "Loading…" : "Refresh metrics"}
</button>
</div>
<div className="mt-6 grid gap-4 md:grid-cols-4">
{["total_queries", "active_users", "redflag_count"].map((key) => (
<div
key={key}
className="rounded-2xl border border-white/5 bg-slate-900/40 p-4 text-center"
>
<p className="text-sm uppercase tracking-widest text-slate-400">
{key.replace("_", " ")}
</p>
<p className="mt-2 text-3xl font-semibold">
{data ? data[key as keyof typeof data] : "—"}
</p>
</div>
))}
<div className="rounded-2xl border border-white/5 bg-slate-900/40 p-4 text-center">
<p className="text-sm uppercase tracking-widest text-slate-400">
Tool usage (top)
</p>
<p className="mt-2 text-3xl font-semibold">
{data
? Object.entries(data.tool_usage)
.sort((a, b) => b[1].count - a[1].count)[0]?.[0] ?? "—"
: "—"}
</p>
</div>
</div>
<div className="mt-6 rounded-2xl border border-white/5 bg-slate-950/50 p-4">
<p className="text-sm uppercase tracking-[0.5em] text-slate-400">
Raw tool usage
</p>
<div className="mt-4 grid gap-3 sm:grid-cols-3">
{data
? Object.entries(data.tool_usage).map(([tool, stats]) => (
<div
key={tool}
className="rounded-xl border border-white/10 bg-white/5 px-4 py-3"
>
<p className="text-sm uppercase tracking-widest text-slate-400">
{tool}
</p>
<p className="text-2xl font-semibold text-white">{stats.count}</p>
</div>
))
: Array.from({ length: 3 }).map((_, idx) => (
<div
key={idx}
className="rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-slate-500"
>
<p className="text-sm uppercase tracking-widest text-slate-500">
Tool {idx + 1}
</p>
<p className="text-2xl font-semibold text-slate-500"></p>
</div>
))}
</div>
</div>
{error && (
<p className="mt-4 rounded-2xl border border-red-500/40 bg-red-500/10 px-4 py-3 text-sm text-red-200">
{error}
</p>
)}
</section>
);
}