import { LogsFilterSidebar } from "@/components/filters/logsFilterSidebar"; import { DateTimePickerWithRange } from "@/components/ui/datePickerWithRange"; import { ScrollArea } from "@/components/ui/scrollArea"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useGetMCPAvailableFilterDataQuery, useLazyGetLogsCostHistogramQuery, useLazyGetLogsHistogramQuery, useLazyGetLogsLatencyHistogramQuery, useLazyGetLogsModelHistogramQuery, useLazyGetLogsProviderCostHistogramQuery, useLazyGetLogsProviderLatencyHistogramQuery, useLazyGetLogsProviderTokenHistogramQuery, useLazyGetLogsTokenHistogramQuery, useLazyGetMCPCostHistogramQuery, useLazyGetMCPHistogramQuery, useLazyGetMCPTopToolsQuery, useLazyGetModelRankingsQuery, } from "@/lib/store"; import type { CostHistogramResponse, LatencyHistogramResponse, LogFilters, LogsHistogramResponse, MCPCostHistogramResponse, MCPHistogramResponse, MCPToolLogFilters, MCPTopToolsResponse, ModelHistogramResponse, ModelRankingsResponse, ProviderCostHistogramResponse, ProviderLatencyHistogramResponse, ProviderTokenHistogramResponse, TokenHistogramResponse, } from "@/lib/types/logs"; import { dateUtils } from "@/lib/types/logs"; import { getRangeForPeriod, TIME_PERIODS } from "@/lib/utils/timeRange"; import UserRankingsTab from "@enterprise/components/user-rankings/userRankingsTab"; import { useLocation } from "@tanstack/react-router"; import { parseAsInteger, parseAsString, useQueryStates } from "nuqs"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { type ChartType } from "./components/charts/chartTypeToggle"; import { ModelFilterSelect } from "./components/charts/modelFilterSelect"; import { ExportPopover } from "./components/exportPopover"; import { MCPTab } from "./components/mcpTab"; import { ModelRankingsTab } from "./components/modelRankingsTab"; import { OverviewTab } from "./components/overviewTab"; import { ProviderUsageTab } from "./components/providerUsageTab"; // Type-safe parser for chart type URL state const toChartType = (value: string): ChartType => (value === "line" ? "line" : "bar"); const parseCsvParam = (value: string): string[] => (value ? value.split(",").filter(Boolean) : []); const sanitizeSeriesLabels = (values?: string[]): string[] => { if (!values) return []; const trimmedValues = values.map((value) => value.trim()).filter((value) => value.length > 0); return [...new Set(trimmedValues)]; }; export default function DashboardPage() { // Data states - Overview const [histogramData, setHistogramData] = useState(null); const [tokenData, setTokenData] = useState(null); const [costData, setCostData] = useState(null); const [modelData, setModelData] = useState(null); const [latencyData, setLatencyData] = useState(null); const [providerCostData, setProviderCostData] = useState(null); const [providerTokenData, setProviderTokenData] = useState(null); const [providerLatencyData, setProviderLatencyData] = useState(null); // Data states - MCP const [mcpHistogramData, setMcpHistogramData] = useState(null); const [mcpCostData, setMcpCostData] = useState(null); const [mcpTopToolsData, setMcpTopToolsData] = useState(null); // Data states - Rankings const [rankingsData, setRankingsData] = useState(null); // Loading states - Overview const [loadingHistogram, setLoadingHistogram] = useState(true); const [loadingTokens, setLoadingTokens] = useState(true); const [loadingCost, setLoadingCost] = useState(true); const [loadingModels, setLoadingModels] = useState(true); const [loadingLatency, setLoadingLatency] = useState(true); const [loadingProviderCost, setLoadingProviderCost] = useState(true); const [loadingProviderTokens, setLoadingProviderTokens] = useState(true); const [loadingProviderLatency, setLoadingProviderLatency] = useState(true); // Loading states - MCP const [loadingMcpHistogram, setLoadingMcpHistogram] = useState(true); const [loadingMcpCost, setLoadingMcpCost] = useState(true); const [loadingMcpTopTools, setLoadingMcpTopTools] = useState(true); // Loading states - Rankings const [loadingRankings, setLoadingRankings] = useState(true); // RTK Query lazy hooks - Overview const [triggerHistogram] = useLazyGetLogsHistogramQuery({}); const [triggerTokens] = useLazyGetLogsTokenHistogramQuery(); const [triggerCost] = useLazyGetLogsCostHistogramQuery(); const [triggerModels] = useLazyGetLogsModelHistogramQuery(); const [triggerLatency] = useLazyGetLogsLatencyHistogramQuery(); const [triggerProviderCost] = useLazyGetLogsProviderCostHistogramQuery(); const [triggerProviderTokens] = useLazyGetLogsProviderTokenHistogramQuery(); const [triggerProviderLatency] = useLazyGetLogsProviderLatencyHistogramQuery(); // RTK Query lazy hooks - MCP const [triggerMcpHistogram] = useLazyGetMCPHistogramQuery(); const [triggerMcpCost] = useLazyGetMCPCostHistogramQuery(); const [triggerMcpTopTools] = useLazyGetMCPTopToolsQuery(); // RTK Query lazy hooks - Rankings const [triggerRankings] = useLazyGetModelRankingsQuery(); // MCP filter data const { data: mcpFilterData } = useGetMCPAvailableFilterDataQuery(); // Memoize default time range to prevent recalculation on every render // This is crucial to avoid triggering refetches when the sheet opens/closes const defaultTimeRange = useMemo(() => dateUtils.getDefaultTimeRange(), []); const { search } = useLocation(); const hasExplicitTimeRange = (search as Record)?.start_time && (search as Record)?.end_time; // URL state management const [urlState, setUrlState] = useQueryStates( { period: parseAsString.withDefault(hasExplicitTimeRange ? "" : "1h").withOptions({ clearOnDefault: false }), start_time: parseAsInteger.withDefault(defaultTimeRange.startTime), end_time: parseAsInteger.withDefault(defaultTimeRange.endTime), tab: parseAsString.withDefault("overview"), virtual_key_ids: parseAsString.withDefault(""), providers: parseAsString.withDefault(""), models: parseAsString.withDefault(""), selected_key_ids: parseAsString.withDefault(""), objects: parseAsString.withDefault(""), status: parseAsString.withDefault(""), routing_rule_ids: parseAsString.withDefault(""), routing_engine_used: parseAsString.withDefault(""), missing_cost_only: parseAsString.withDefault("false"), metadata_filters: parseAsString.withDefault(""), volume_chart: parseAsString.withDefault("bar"), token_chart: parseAsString.withDefault("bar"), cost_chart: parseAsString.withDefault("bar"), model_chart: parseAsString.withDefault("bar"), latency_chart: parseAsString.withDefault("bar"), cost_model: parseAsString.withDefault("all"), usage_model: parseAsString.withDefault("all"), provider_cost_chart: parseAsString.withDefault("bar"), provider_token_chart: parseAsString.withDefault("bar"), provider_latency_chart: parseAsString.withDefault("bar"), provider_cost_provider: parseAsString.withDefault("all"), provider_token_provider: parseAsString.withDefault("all"), provider_latency_provider: parseAsString.withDefault("all"), mcp_volume_chart: parseAsString.withDefault("bar"), mcp_cost_chart: parseAsString.withDefault("bar"), mcp_tool_names: parseAsString.withDefault(""), mcp_server_labels: parseAsString.withDefault(""), }, { history: "push", shallow: false, }, ); // Parse filter arrays from URL state const selectedProviders = useMemo(() => parseCsvParam(urlState.providers), [urlState.providers]); const selectedModels = useMemo(() => parseCsvParam(urlState.models), [urlState.models]); const selectedKeyIds = useMemo(() => parseCsvParam(urlState.selected_key_ids), [urlState.selected_key_ids]); const selectedVirtualKeyIds = useMemo(() => parseCsvParam(urlState.virtual_key_ids), [urlState.virtual_key_ids]); const selectedTypes = useMemo(() => parseCsvParam(urlState.objects), [urlState.objects]); const selectedStatuses = useMemo(() => parseCsvParam(urlState.status), [urlState.status]); const selectedRoutingRuleIds = useMemo(() => parseCsvParam(urlState.routing_rule_ids), [urlState.routing_rule_ids]); const selectedRoutingEngines = useMemo(() => parseCsvParam(urlState.routing_engine_used), [urlState.routing_engine_used]); const missingCostOnly = useMemo(() => urlState.missing_cost_only === "true", [urlState.missing_cost_only]); const metadataFilters = useMemo(() => { if (!urlState.metadata_filters) return undefined; try { return JSON.parse(urlState.metadata_filters) as Record; } catch { return undefined; } }, [urlState.metadata_filters]); // MCP filter arrays const selectedMcpToolNames = useMemo(() => parseCsvParam(urlState.mcp_tool_names), [urlState.mcp_tool_names]); const selectedMcpServerLabels = useMemo(() => parseCsvParam(urlState.mcp_server_labels), [urlState.mcp_server_labels]); // Derived filter for API calls const filters: LogFilters = useMemo( () => ({ start_time: dateUtils.toISOString(urlState.start_time), end_time: dateUtils.toISOString(urlState.end_time), ...(selectedProviders.length > 0 && { providers: selectedProviders }), ...(selectedModels.length > 0 && { models: selectedModels }), ...(selectedKeyIds.length > 0 && { selected_key_ids: selectedKeyIds }), ...(selectedVirtualKeyIds.length > 0 && { virtual_key_ids: selectedVirtualKeyIds, }), ...(selectedTypes.length > 0 && { objects: selectedTypes }), ...(selectedStatuses.length > 0 && { status: selectedStatuses }), ...(selectedRoutingRuleIds.length > 0 && { routing_rule_ids: selectedRoutingRuleIds, }), ...(selectedRoutingEngines.length > 0 && { routing_engine_used: selectedRoutingEngines, }), ...(missingCostOnly && { missing_cost_only: true }), ...(metadataFilters && Object.keys(metadataFilters).length > 0 && { metadata_filters: metadataFilters, }), }), [ urlState.start_time, urlState.end_time, selectedProviders, selectedModels, selectedKeyIds, selectedVirtualKeyIds, selectedTypes, selectedStatuses, selectedRoutingRuleIds, selectedRoutingEngines, missingCostOnly, metadataFilters, ], ); // MCP filters const mcpFilters: MCPToolLogFilters = useMemo( () => ({ start_time: dateUtils.toISOString(urlState.start_time), end_time: dateUtils.toISOString(urlState.end_time), ...(selectedMcpToolNames.length > 0 && { tool_names: selectedMcpToolNames, }), ...(selectedMcpServerLabels.length > 0 && { server_labels: selectedMcpServerLabels, }), }), [urlState.start_time, urlState.end_time, selectedMcpToolNames, selectedMcpServerLabels], ); // Model lists for each chart's legend (must match what the chart component actually renders) const costModels = useMemo(() => sanitizeSeriesLabels(costData?.models), [costData?.models]); const usageModels = useMemo(() => sanitizeSeriesLabels(modelData?.models), [modelData?.models]); // Available models for filter dropdowns (union of both sources) const availableModels = useMemo(() => { return sanitizeSeriesLabels([...(costData?.models ?? []), ...(modelData?.models ?? [])]); }, [costData?.models, modelData?.models]); // Available providers for provider chart filter dropdowns const availableProviders = useMemo(() => { return sanitizeSeriesLabels([ ...(providerCostData?.providers ?? []), ...(providerTokenData?.providers ?? []), ...(providerLatencyData?.providers ?? []), ]); }, [providerCostData?.providers, providerTokenData?.providers, providerLatencyData?.providers]); // Provider lists for each chart's legend const providerCostProviders = useMemo(() => sanitizeSeriesLabels(providerCostData?.providers), [providerCostData?.providers]); const providerTokenProviders = useMemo(() => sanitizeSeriesLabels(providerTokenData?.providers), [providerTokenData?.providers]); const providerLatencyProviders = useMemo(() => sanitizeSeriesLabels(providerLatencyData?.providers), [providerLatencyData?.providers]); // Fetch Overview tab data (5 calls) const fetchOverviewData = useCallback(async () => { setLoadingHistogram(true); setLoadingTokens(true); setLoadingCost(true); setLoadingModels(true); setLoadingLatency(true); const fetchFilters = { filters }; const [histogramResult, tokenResult, costResult, modelResult, latencyResult] = await Promise.all([ triggerHistogram(fetchFilters, false), triggerTokens(fetchFilters, false), triggerCost(fetchFilters, false), triggerModels(fetchFilters, false), triggerLatency(fetchFilters, false), ]); setHistogramData(histogramResult.data ?? null); setLoadingHistogram(false); setTokenData(tokenResult.data ?? null); setLoadingTokens(false); setCostData(costResult.data ?? null); setLoadingCost(false); setModelData(modelResult.data ?? null); setLoadingModels(false); setLatencyData(latencyResult.data ?? null); setLoadingLatency(false); }, [filters, triggerHistogram, triggerTokens, triggerCost, triggerModels, triggerLatency]); // Fetch Provider Usage tab data (3 calls) const fetchProviderData = useCallback(async () => { setLoadingProviderCost(true); setLoadingProviderTokens(true); setLoadingProviderLatency(true); const fetchFilters = { filters }; const [providerCostResult, providerTokenResult, providerLatencyResult] = await Promise.all([ triggerProviderCost(fetchFilters, false), triggerProviderTokens(fetchFilters, false), triggerProviderLatency(fetchFilters, false), ]); setProviderCostData(providerCostResult.data ?? null); setLoadingProviderCost(false); setProviderTokenData(providerTokenResult.data ?? null); setLoadingProviderTokens(false); setProviderLatencyData(providerLatencyResult.data ?? null); setLoadingProviderLatency(false); }, [filters, triggerProviderCost, triggerProviderTokens, triggerProviderLatency]); // Fetch MCP data const fetchMcpData = useCallback(async () => { setLoadingMcpHistogram(true); setLoadingMcpCost(true); setLoadingMcpTopTools(true); const fetchFilters = { filters: mcpFilters }; const [mcpHistResult, mcpCostResult, mcpTopToolsResult] = await Promise.all([ triggerMcpHistogram(fetchFilters, false), triggerMcpCost(fetchFilters, false), triggerMcpTopTools(fetchFilters, false), ]); setMcpHistogramData(mcpHistResult.data ?? null); setLoadingMcpHistogram(false); setMcpCostData(mcpCostResult.data ?? null); setLoadingMcpCost(false); setMcpTopToolsData(mcpTopToolsResult.data ?? null); setLoadingMcpTopTools(false); }, [mcpFilters, triggerMcpHistogram, triggerMcpCost, triggerMcpTopTools]); // Fetch Rankings data const fetchRankingsData = useCallback(async () => { setLoadingRankings(true); const result = await triggerRankings({ filters }, false); setRankingsData(result.data ?? null); setLoadingRankings(false); }, [filters, triggerRankings]); // --- Lazy-load refs: each tab fetches only once per filter change --- const overviewFetchedRef = useRef(false); const overviewLoadingRef = useRef(false); const overviewGenRef = useRef(0); const overviewPromiseRef = useRef | null>(null); const providerFetchedRef = useRef(false); const providerLoadingRef = useRef(false); const providerGenRef = useRef(0); const providerPromiseRef = useRef | null>(null); const mcpFetchedRef = useRef(false); const mcpLoadingRef = useRef(false); const mcpGenRef = useRef(0); const mcpPromiseRef = useRef | null>(null); const rankingsFetchedRef = useRef(false); const rankingsLoadingRef = useRef(false); const rankingsGenRef = useRef(0); const rankingsPromiseRef = useRef | null>(null); const ensureOverviewDataLoaded = useCallback(async () => { if (overviewFetchedRef.current) return; if (overviewLoadingRef.current) return overviewPromiseRef.current ?? undefined; const gen = overviewGenRef.current; overviewLoadingRef.current = true; const promise = fetchOverviewData() .then(() => { if (gen === overviewGenRef.current) overviewFetchedRef.current = true; }) .finally(() => { if (gen === overviewGenRef.current) { overviewLoadingRef.current = false; overviewPromiseRef.current = null; } }); overviewPromiseRef.current = promise; return promise; }, [fetchOverviewData]); const ensureProviderDataLoaded = useCallback(async () => { if (providerFetchedRef.current) return; if (providerLoadingRef.current) return providerPromiseRef.current ?? undefined; const gen = providerGenRef.current; providerLoadingRef.current = true; const promise = fetchProviderData() .then(() => { if (gen === providerGenRef.current) providerFetchedRef.current = true; }) .finally(() => { if (gen === providerGenRef.current) { providerLoadingRef.current = false; providerPromiseRef.current = null; } }); providerPromiseRef.current = promise; return promise; }, [fetchProviderData]); const ensureMcpDataLoaded = useCallback(async () => { if (mcpFetchedRef.current) return; if (mcpLoadingRef.current) return mcpPromiseRef.current ?? undefined; const gen = mcpGenRef.current; mcpLoadingRef.current = true; const promise = fetchMcpData() .then(() => { if (gen === mcpGenRef.current) mcpFetchedRef.current = true; }) .finally(() => { if (gen === mcpGenRef.current) { mcpLoadingRef.current = false; mcpPromiseRef.current = null; } }); mcpPromiseRef.current = promise; return promise; }, [fetchMcpData]); const ensureRankingsDataLoaded = useCallback(async () => { if (rankingsFetchedRef.current) return; if (rankingsLoadingRef.current) return rankingsPromiseRef.current ?? undefined; const gen = rankingsGenRef.current; rankingsLoadingRef.current = true; const promise = fetchRankingsData() .then(() => { if (gen === rankingsGenRef.current) rankingsFetchedRef.current = true; }) .finally(() => { if (gen === rankingsGenRef.current) { rankingsLoadingRef.current = false; rankingsPromiseRef.current = null; } }); rankingsPromiseRef.current = promise; return promise; }, [fetchRankingsData]); // Reset all lazy-load flags when filters change (not on tab switch) useEffect(() => { overviewFetchedRef.current = false; overviewLoadingRef.current = false; overviewGenRef.current += 1; providerFetchedRef.current = false; providerLoadingRef.current = false; providerGenRef.current += 1; rankingsFetchedRef.current = false; rankingsLoadingRef.current = false; rankingsGenRef.current += 1; }, [filters]); useEffect(() => { mcpFetchedRef.current = false; mcpLoadingRef.current = false; mcpGenRef.current += 1; }, [mcpFilters]); // Fetch current tab's data when filters change or tab switches // The ensure* functions are no-ops if data is already loaded for the current filters useEffect(() => { const tab = urlState.tab || "overview"; if (tab === "overview") void ensureOverviewDataLoaded(); else if (tab === "provider-usage") void ensureProviderDataLoaded(); else if (tab === "rankings") void ensureRankingsDataLoaded(); else if (tab === "mcp") void ensureMcpDataLoaded(); }, [urlState.tab, ensureOverviewDataLoaded, ensureProviderDataLoaded, ensureRankingsDataLoaded, ensureMcpDataLoaded]); // Warm other tabs in the background after 150ms useEffect(() => { const tab = urlState.tab || "overview"; const timeoutId = window.setTimeout(() => { if (tab !== "overview") void ensureOverviewDataLoaded(); if (tab !== "provider-usage") void ensureProviderDataLoaded(); if (tab !== "mcp") void ensureMcpDataLoaded(); if (tab !== "rankings") void ensureRankingsDataLoaded(); }, 150); return () => window.clearTimeout(timeoutId); }, [urlState.tab, ensureOverviewDataLoaded, ensureProviderDataLoaded, ensureMcpDataLoaded, ensureRankingsDataLoaded]); // Tab change handler const handleTabChange = useCallback( (tab: string) => { setUrlState({ tab }); }, [setUrlState], ); // Chart type toggles const handleVolumeChartToggle = useCallback((type: ChartType) => setUrlState({ volume_chart: type }), [setUrlState]); const handleTokenChartToggle = useCallback((type: ChartType) => setUrlState({ token_chart: type }), [setUrlState]); const handleCostChartToggle = useCallback((type: ChartType) => setUrlState({ cost_chart: type }), [setUrlState]); const handleModelChartToggle = useCallback((type: ChartType) => setUrlState({ model_chart: type }), [setUrlState]); const handleLatencyChartToggle = useCallback((type: ChartType) => setUrlState({ latency_chart: type }), [setUrlState]); // Adapter: converts a full LogFilters object to dashboard's CSV-based URL state const setFilters = useCallback( (newFilters: LogFilters) => { const newStartTime = newFilters.start_time ? dateUtils.toUnixTimestamp(new Date(newFilters.start_time)) : undefined; const newEndTime = newFilters.end_time ? dateUtils.toUnixTimestamp(new Date(newFilters.end_time)) : undefined; const timeChanged = newStartTime !== urlState.start_time || newEndTime !== urlState.end_time; setUrlState({ ...(timeChanged && { period: "" }), start_time: newStartTime, end_time: newEndTime, providers: (newFilters.providers || []).join(","), models: (newFilters.models || []).join(","), selected_key_ids: (newFilters.selected_key_ids || []).join(","), virtual_key_ids: (newFilters.virtual_key_ids || []).join(","), objects: (newFilters.objects || []).join(","), status: (newFilters.status || []).join(","), routing_rule_ids: (newFilters.routing_rule_ids || []).join(","), routing_engine_used: (newFilters.routing_engine_used || []).join(","), missing_cost_only: String(newFilters.missing_cost_only ?? false), metadata_filters: newFilters.metadata_filters && Object.keys(newFilters.metadata_filters).length > 0 ? JSON.stringify(newFilters.metadata_filters) : "", }); }, [setUrlState, urlState.start_time, urlState.end_time], ); // Date range for picker const dateRange = useMemo( () => ({ from: dateUtils.fromUnixTimestamp(urlState.start_time), to: dateUtils.fromUnixTimestamp(urlState.end_time), }), [urlState.start_time, urlState.end_time], ); const handlePeriodChange = useCallback( (period: string | undefined) => { if (!period) return; const { from, to } = getRangeForPeriod(period); setUrlState({ period, start_time: Math.floor(from.getTime() / 1000), end_time: Math.floor(to.getTime() / 1000), }); }, [setUrlState], ); const handleDateRangeChange = useCallback( (range: { from?: Date; to?: Date }) => { if (!range.from || !range.to) return; setUrlState({ period: "", start_time: dateUtils.toUnixTimestamp(range.from), end_time: dateUtils.toUnixTimestamp(range.to), }); }, [setUrlState], ); const handleProviderCostChartToggle = useCallback((type: ChartType) => setUrlState({ provider_cost_chart: type }), [setUrlState]); const handleProviderTokenChartToggle = useCallback((type: ChartType) => setUrlState({ provider_token_chart: type }), [setUrlState]); const handleProviderLatencyChartToggle = useCallback((type: ChartType) => setUrlState({ provider_latency_chart: type }), [setUrlState]); // MCP chart type toggles const handleMcpVolumeChartToggle = useCallback((type: ChartType) => setUrlState({ mcp_volume_chart: type }), [setUrlState]); const handleMcpCostChartToggle = useCallback((type: ChartType) => setUrlState({ mcp_cost_chart: type }), [setUrlState]); // Model filter changes const handleCostModelChange = useCallback((model: string) => setUrlState({ cost_model: model }), [setUrlState]); const handleUsageModelChange = useCallback((model: string) => setUrlState({ usage_model: model }), [setUrlState]); // Provider filter changes const handleProviderCostProviderChange = useCallback( (provider: string) => setUrlState({ provider_cost_provider: provider }), [setUrlState], ); const handleProviderTokenProviderChange = useCallback( (provider: string) => setUrlState({ provider_token_provider: provider }), [setUrlState], ); const handleProviderLatencyProviderChange = useCallback( (provider: string) => setUrlState({ provider_latency_provider: provider }), [setUrlState], ); // Aggregate data object for export const dashboardData = useMemo( () => ({ histogramData, tokenData, costData, modelData, latencyData, providerCostData, providerTokenData, providerLatencyData, rankingsData, mcpHistogramData, mcpCostData, mcpTopToolsData, }), [ histogramData, tokenData, costData, modelData, latencyData, providerCostData, providerTokenData, providerLatencyData, rankingsData, mcpHistogramData, mcpCostData, mcpTopToolsData, ], ); // Keep a ref in sync so export callbacks always read the latest data const dashboardDataRef = useRef(dashboardData); dashboardDataRef.current = dashboardData; const getDashboardData = useCallback(() => dashboardDataRef.current, []); // Preload all tab data (used by CSV and PDF export) const handlePreloadData = useCallback(async () => { await Promise.all([ensureOverviewDataLoaded(), ensureProviderDataLoaded(), ensureRankingsDataLoaded(), ensureMcpDataLoaded()]); }, [ensureOverviewDataLoaded, ensureProviderDataLoaded, ensureRankingsDataLoaded, ensureMcpDataLoaded]); // PDF export mode — when true, all TabsContent are force-mounted so // html2canvas can capture every tab. const [pdfMode, setPdfMode] = useState(false); const dashboardMinHeightRef = useRef(""); const hiddenTabsRef = useRef([]); // Called by ExportPopover. Loads all data, force-mounts all tabs, // unhides inactive tabs so html2canvas can capture them, then returns // the 4 section DOM elements. Caller must invoke the returned cleanup // function when done capturing. const handlePdfExport = useCallback(async (): Promise => { // Ensure every tab's data is loaded await handlePreloadData(); setPdfMode(true); // Wait for React to render the force-mounted tabs await new Promise((resolve) => { requestAnimationFrame(() => { requestAnimationFrame(() => resolve()); }); }); // Radix sets `hidden` on inactive force-mounted TabsContent. // Temporarily remove it so html2canvas can capture them. const hiddenTabs = document.querySelectorAll('[data-slot="tabs-content"][hidden]'); hiddenTabsRef.current = Array.from(hiddenTabs); for (const tab of hiddenTabs) { tab.removeAttribute("hidden"); tab.style.display = "block"; } // Collapse min-height on the dashboard container so captured // sections wrap tightly around their content (no extra whitespace). const dashboardEl = document.getElementById("dashboard-root"); if (dashboardEl) { dashboardMinHeightRef.current = dashboardEl.style.minHeight; dashboardEl.style.minHeight = "0"; } // Let ResizeObserver-based charts (meter gauge) re-measure window.dispatchEvent(new Event("resize")); await new Promise((resolve) => { requestAnimationFrame(() => { requestAnimationFrame(() => resolve()); }); }); const ids = ["dashboard-section-overview", "dashboard-section-provider-usage", "dashboard-section-rankings", "dashboard-section-mcp"]; return ids.map((id) => document.getElementById(id)).filter(Boolean) as HTMLElement[]; }, [handlePreloadData]); // Cleanup after PDF capture is complete const handlePdfExportDone = useCallback(() => { // Restore minHeight on dashboard container const dashboardEl = document.getElementById("dashboard-root"); if (dashboardEl) { dashboardEl.style.minHeight = dashboardMinHeightRef.current; } // Re-hide tabs that were temporarily shown for capture for (const tab of hiddenTabsRef.current) { tab.setAttribute("hidden", ""); tab.style.display = ""; } hiddenTabsRef.current = []; setPdfMode(false); }, []); return (
{/* Sidebar Filters */} {/* Main Content */} {/* Header */}

Dashboard

{urlState.tab === "mcp" && mcpFilterData && (
{mcpFilterData.tool_names?.length > 0 && ( { if (value === "all") { setUrlState({ mcp_tool_names: "" }); } else { setUrlState({ mcp_tool_names: value }); } }} placeholder="All Tools" data-testid="dashboard-mcp-tool-filter" /> )} {mcpFilterData.server_labels?.length > 0 && ( { if (value === "all") { setUrlState({ mcp_server_labels: "" }); } else { setUrlState({ mcp_server_labels: value }); } }} placeholder="All Servers" data-testid="dashboard-mcp-server-filter" /> )}
)}
{/* Tabs */} Overview Provider Usage Model Rankings MCP usage User Rankings {/* Overview Tab */}
{/* Provider Usage Tab */}
{/* Model Rankings Tab */}
{/* MCP Tab */}
{/* User Rankings Tab (Enterprise) */}
); }