/** * Search Analytics Tab * * Shows search request stats from call_logs (request_type = 'search'), * provider breakdown, cache hit rate, and cost summary. */ "use client"; import { useTranslations } from "next-intl"; import { useEffect, useState } from "react"; interface SearchStats { total: number; today: number; cached: number; errors: number; totalCostUsd: number; byProvider: Record; last24h: Array<{ hour: string; count: number }>; cacheHitRate: number; avgDurationMs: number; } function StatCard({ icon, label, value, sub, }: { icon: string; label: string; value: string | number; sub?: string; }) { return (
{icon} {label}
{value}
{sub &&
{sub}
}
); } function ProviderBar({ provider, count, total, costUsd, }: { provider: string; count: number; total: number; costUsd: number; }) { const pct = total > 0 ? Math.round((count / total) * 100) : 0; return (
{provider} {count} queries · ${costUsd.toFixed(4)}
{pct}%
); } export default function SearchAnalyticsTab() { const t = useTranslations("analytics"); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch("/api/v1/search/analytics") .then((r) => r.json()) .then((d) => { setStats(d); setLoading(false); }) .catch((e) => { setError(e.message); setLoading(false); }); }, []); if (loading) { return (
progress_activity Loading search analytics…
); } if (error || !stats) { return (
search_off {error || "No search data available yet."}

Search requests will appear here after the first search via /v1/search.

); } const providers = Object.entries(stats.byProvider).sort(([, a], [, b]) => b.count - a.count); return (
{/* KPI Cards */}
0 ? `${stats.errors} errors` : "No errors"} />
{/* Provider Breakdown */} {providers.length > 0 && (

hub Provider Breakdown

{providers.map(([prov, data]) => ( ))}
)} {/* Empty state */} {stats.total === 0 && (
travel_explore

{t("searchAnalyticsNoSearchesYet")}

Use POST /v1/search to start routing web searches.

)} {/* Free tier note */}
check_circle Free tier available: Serper (2,500/mo), Brave (2,000/mo), Exa (1,000/mo), Tavily (1,000/mo) — total 6,500+ free searches/month with automatic failover.
); }