first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,901 @@
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<LogsHistogramResponse | null>(null);
const [tokenData, setTokenData] = useState<TokenHistogramResponse | null>(null);
const [costData, setCostData] = useState<CostHistogramResponse | null>(null);
const [modelData, setModelData] = useState<ModelHistogramResponse | null>(null);
const [latencyData, setLatencyData] = useState<LatencyHistogramResponse | null>(null);
const [providerCostData, setProviderCostData] = useState<ProviderCostHistogramResponse | null>(null);
const [providerTokenData, setProviderTokenData] = useState<ProviderTokenHistogramResponse | null>(null);
const [providerLatencyData, setProviderLatencyData] = useState<ProviderLatencyHistogramResponse | null>(null);
// Data states - MCP
const [mcpHistogramData, setMcpHistogramData] = useState<MCPHistogramResponse | null>(null);
const [mcpCostData, setMcpCostData] = useState<MCPCostHistogramResponse | null>(null);
const [mcpTopToolsData, setMcpTopToolsData] = useState<MCPTopToolsResponse | null>(null);
// Data states - Rankings
const [rankingsData, setRankingsData] = useState<ModelRankingsResponse | null>(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<string, unknown>)?.start_time && (search as Record<string, unknown>)?.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<string, string>;
} 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<Promise<void> | null>(null);
const providerFetchedRef = useRef(false);
const providerLoadingRef = useRef(false);
const providerGenRef = useRef(0);
const providerPromiseRef = useRef<Promise<void> | null>(null);
const mcpFetchedRef = useRef(false);
const mcpLoadingRef = useRef(false);
const mcpGenRef = useRef(0);
const mcpPromiseRef = useRef<Promise<void> | null>(null);
const rankingsFetchedRef = useRef(false);
const rankingsLoadingRef = useRef(false);
const rankingsGenRef = useRef(0);
const rankingsPromiseRef = useRef<Promise<void> | 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<string>("");
const hiddenTabsRef = useRef<HTMLElement[]>([]);
// 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<HTMLElement[]> => {
// Ensure every tab's data is loaded
await handlePreloadData();
setPdfMode(true);
// Wait for React to render the force-mounted tabs
await new Promise<void>((resolve) => {
requestAnimationFrame(() => {
requestAnimationFrame(() => resolve());
});
});
// Radix sets `hidden` on inactive force-mounted TabsContent.
// Temporarily remove it so html2canvas can capture them.
const hiddenTabs = document.querySelectorAll<HTMLElement>('[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<void>((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 (
<div id="dashboard-root" className="no-padding-parent no-border-parent bg-background flex h-[calc(100vh_-_16px)] w-full gap-3">
{/* Sidebar Filters */}
<LogsFilterSidebar filters={filters} onFiltersChange={setFilters} />
{/* Main Content */}
<ScrollArea className="bg-card flex min-w-0 flex-1 flex-col gap-4 rounded-l-md">
{/* Header */}
<div className="flex items-center justify-between p-4">
<div className="flex items-center gap-2">
<h1 className="text-lg font-semibold">Dashboard</h1>
</div>
<div className="flex items-center gap-2">
<ExportPopover
getData={getDashboardData}
onPreloadData={handlePreloadData}
onPdfExport={handlePdfExport}
onPdfExportDone={handlePdfExportDone}
/>
{urlState.tab === "mcp" && mcpFilterData && (
<div className="flex items-center gap-1">
{mcpFilterData.tool_names?.length > 0 && (
<ModelFilterSelect
models={mcpFilterData.tool_names}
selectedModel={selectedMcpToolNames.length === 1 ? selectedMcpToolNames[0] : "all"}
onModelChange={(value) => {
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 && (
<ModelFilterSelect
models={mcpFilterData.server_labels}
selectedModel={selectedMcpServerLabels.length === 1 ? selectedMcpServerLabels[0] : "all"}
onModelChange={(value) => {
if (value === "all") {
setUrlState({ mcp_server_labels: "" });
} else {
setUrlState({ mcp_server_labels: value });
}
}}
placeholder="All Servers"
data-testid="dashboard-mcp-server-filter"
/>
)}
</div>
)}
<DateTimePickerWithRange
dateTime={dateRange}
onDateTimeUpdate={handleDateRangeChange}
preDefinedPeriods={TIME_PERIODS}
predefinedPeriod={urlState.period || undefined}
onPredefinedPeriodChange={handlePeriodChange}
triggerTestId="dashboard-filter-daterange"
popupAlignment="end"
/>
</div>
</div>
<div className="p-4">
{/* Tabs */}
<Tabs value={urlState.tab} onValueChange={handleTabChange}>
<TabsList className="mb-2">
<TabsTrigger value="overview" data-testid="dashboard-tab-overview">
Overview
</TabsTrigger>
<TabsTrigger value="provider-usage" data-testid="dashboard-tab-provider-usage">
Provider Usage
</TabsTrigger>
<TabsTrigger value="rankings" data-testid="dashboard-tab-rankings">
Model Rankings
</TabsTrigger>
<TabsTrigger value="mcp" data-testid="dashboard-tab-mcp">
MCP usage
</TabsTrigger>
<TabsTrigger value="user-rankings" data-testid="dashboard-tab-user-rankings">
User Rankings
</TabsTrigger>
</TabsList>
{/* Overview Tab */}
<TabsContent value="overview" {...(pdfMode && { forceMount: true })}>
<div id="dashboard-section-overview">
<OverviewTab
histogramData={histogramData}
tokenData={tokenData}
costData={costData}
modelData={modelData}
latencyData={latencyData}
loadingHistogram={loadingHistogram}
loadingTokens={loadingTokens}
loadingCost={loadingCost}
loadingModels={loadingModels}
loadingLatency={loadingLatency}
startTime={urlState.start_time}
endTime={urlState.end_time}
volumeChartType={toChartType(urlState.volume_chart)}
tokenChartType={toChartType(urlState.token_chart)}
costChartType={toChartType(urlState.cost_chart)}
modelChartType={toChartType(urlState.model_chart)}
latencyChartType={toChartType(urlState.latency_chart)}
costModel={urlState.cost_model}
usageModel={urlState.usage_model}
costModels={costModels}
usageModels={usageModels}
availableModels={availableModels}
onVolumeChartToggle={handleVolumeChartToggle}
onTokenChartToggle={handleTokenChartToggle}
onCostChartToggle={handleCostChartToggle}
onModelChartToggle={handleModelChartToggle}
onLatencyChartToggle={handleLatencyChartToggle}
onCostModelChange={handleCostModelChange}
onUsageModelChange={handleUsageModelChange}
/>
</div>
</TabsContent>
{/* Provider Usage Tab */}
<TabsContent value="provider-usage" {...(pdfMode && { forceMount: true })}>
<div id="dashboard-section-provider-usage">
<ProviderUsageTab
providerCostData={providerCostData}
providerTokenData={providerTokenData}
providerLatencyData={providerLatencyData}
loadingProviderCost={loadingProviderCost}
loadingProviderTokens={loadingProviderTokens}
loadingProviderLatency={loadingProviderLatency}
startTime={urlState.start_time}
endTime={urlState.end_time}
providerCostChartType={toChartType(urlState.provider_cost_chart)}
providerTokenChartType={toChartType(urlState.provider_token_chart)}
providerLatencyChartType={toChartType(urlState.provider_latency_chart)}
providerCostProvider={urlState.provider_cost_provider}
providerTokenProvider={urlState.provider_token_provider}
providerLatencyProvider={urlState.provider_latency_provider}
availableProviders={availableProviders}
providerCostProviders={providerCostProviders}
providerTokenProviders={providerTokenProviders}
providerLatencyProviders={providerLatencyProviders}
onProviderCostChartToggle={handleProviderCostChartToggle}
onProviderTokenChartToggle={handleProviderTokenChartToggle}
onProviderLatencyChartToggle={handleProviderLatencyChartToggle}
onProviderCostProviderChange={handleProviderCostProviderChange}
onProviderTokenProviderChange={handleProviderTokenProviderChange}
onProviderLatencyProviderChange={handleProviderLatencyProviderChange}
/>
</div>
</TabsContent>
{/* Model Rankings Tab */}
<TabsContent value="rankings" {...(pdfMode && { forceMount: true })}>
<div id="dashboard-section-rankings">
<ModelRankingsTab
rankingsData={rankingsData}
loading={loadingRankings}
modelData={modelData}
loadingModels={loadingModels}
startTime={urlState.start_time}
endTime={urlState.end_time}
/>
</div>
</TabsContent>
{/* MCP Tab */}
<TabsContent value="mcp" {...(pdfMode && { forceMount: true })}>
<div id="dashboard-section-mcp">
<MCPTab
mcpHistogramData={mcpHistogramData}
mcpCostData={mcpCostData}
mcpTopToolsData={mcpTopToolsData}
loadingMcpHistogram={loadingMcpHistogram}
loadingMcpCost={loadingMcpCost}
loadingMcpTopTools={loadingMcpTopTools}
startTime={urlState.start_time}
endTime={urlState.end_time}
mcpVolumeChartType={toChartType(urlState.mcp_volume_chart)}
mcpCostChartType={toChartType(urlState.mcp_cost_chart)}
onMcpVolumeChartToggle={handleMcpVolumeChartToggle}
onMcpCostChartToggle={handleMcpCostChartToggle}
/>
</div>
</TabsContent>
{/* User Rankings Tab (Enterprise) */}
<TabsContent value="user-rankings">
<UserRankingsTab />
</TabsContent>
</Tabs>
</div>
</ScrollArea>
</div>
);
}