import React, { useState } from 'react'; import { useTelemetryQuery } from '@/hooks/use-query/useTelemetryQuery'; import { Header } from '@/components/layout/Header'; import { Sidebar } from '@/components/layout/Sidebar'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, Legend, Area, AreaChart } from 'recharts'; import { useAppsQuery } from '@/hooks/use-query/useAppsQuery'; import { useRolloutVersions, useRolloutAdoption } from '@/hooks/use-query/useRolloutQuery'; import { useChannelQuery } from '@/hooks/use-query/useChannelQuery'; import { usePlatformQuery } from '@/hooks/use-query/usePlatformQuery'; import { useArchitectureQuery } from '@/hooks/use-query/useArchitectureQuery'; import { useTheme } from '@/providers/themeProvider'; import { AppListItem } from '@/hooks/use-query/useAppsQuery'; import { Channel } from '@/hooks/use-query/useChannelQuery'; import { Platform } from '@/hooks/use-query/usePlatformQuery'; import { Architecture } from '@/hooks/use-query/useArchitectureQuery'; import DatePicker from 'react-datepicker'; import "react-datepicker/dist/react-datepicker.css"; import { Dropdown } from '@/components/common/Dropdown'; import { FIELD_LABEL, STATUS_BADGE } from '@/components/common/ui'; import { getPlatformIcon } from '@/utils/platformIcon'; const PANEL_CLASS = 'bg-theme-card rounded-lg border border-theme-card-hover shadow-md backdrop-blur-lg'; const CHART_GRID_STROKE = 'rgba(148, 163, 184, 0.25)'; const CHART_AXIS_STROKE = 'rgba(148, 163, 184, 0.7)'; const TOOLTIP_STYLE = { backgroundColor: 'var(--theme-card)', border: '1px solid var(--theme-card-hover)', color: 'var(--theme-primary)', borderRadius: '12px', boxShadow: '0 10px 30px rgba(15, 23, 42, 0.25)', }; const DATE_PICKER_POPUP_STYLE = { backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)', boxShadow: '0 16px 40px rgba(15, 23, 42, 0.35)', }; const DARK_CHART_COLORS = ['#5AA9FF', '#34D399', '#F59E0B', '#F97316', '#A78BFA', '#2DD4BF', '#FB7185', '#22D3EE']; const LIGHT_CHART_COLORS = ['#2563EB', '#059669', '#D97706', '#EA580C', '#7C3AED', '#0D9488', '#E11D48', '#0891B2']; type Filters = { apps: string[]; channels: string[]; platforms: string[]; architectures: string[]; range?: 'today' | 'week' | 'month'; date?: string; }; const StatCard = ({ title, value, icon }: { title: string; value: number; icon: React.ReactNode }) => (

{title}

{value}

{icon}
); const RolloutHealthRow = ({ appName, version, channel, configuredPercent, observedPercent, observedClients, eligibleClients, barColor, }: { appName: string; version: string; channel: string; configuredPercent: number; observedPercent: number | null; observedClients: number | null; eligibleClients: number | null; barColor: string; }) => { const clampedObserved = observedPercent === null ? 0 : Math.min(Math.max(observedPercent, 0), 100); const clampedTarget = Math.min(Math.max(configuredPercent, 0), 100); return (
{appName} v{version} {channel}
target {configuredPercent}% observed{' '} {observedPercent === null ? '—' : `${observedPercent.toFixed(1)}%`}

{observedClients === null || eligibleClients === null ? 'No telemetry for this version in the selected period.' : `${formatNumber(observedClients)} of ${formatNumber(eligibleClients)} eligible clients on this version or newer.`}

); }; const numberFormatter = new Intl.NumberFormat('en-US'); const formatNumber = (value: number | string) => { const normalizedValue = typeof value === 'number' ? value : Number(value); if (Number.isNaN(normalizedValue)) { return String(value); } return numberFormatter.format(normalizedValue); }; const formatChartDate = (value: string) => { const parsedDate = new Date(value); if (Number.isNaN(parsedDate.getTime())) { return value; } return parsedDate.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); }; const formatChartDateLong = (value: string) => { const parsedDate = new Date(value); if (Number.isNaN(parsedDate.getTime())) { return value; } return parsedDate.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }); }; const compareVersions = (a: string, b: string) => { const partsA = a.replace(/^v/i, '').split('.').map(Number); const partsB = b.replace(/^v/i, '').split('.').map(Number); const len = Math.max(partsA.length, partsB.length); for (let i = 0; i < len; i++) { const numA = partsA[i] ?? 0; const numB = partsB[i] ?? 0; if (Number.isNaN(numA) || Number.isNaN(numB)) { const cmp = a.localeCompare(b); if (cmp !== 0) { return cmp; } continue; } if (numA !== numB) { return numA - numB; } } return 0; }; const SinglePointTrendView = ({ value, color, dateLabel, }: { value: number; color: string; dateLabel: string; }) => (

Single Day Snapshot

{formatNumber(value)}

{dateLabel}

); const MultiFilter = ({ label, placeholder, values, options, onToggle, panelClassName, }: { label: string; placeholder: string; values: string[]; options: { value: string; label: string; icon?: string }[]; onToggle: (value: string) => void; panelClassName: string; }) => (
{values.length > 0 && (
{values.map((value) => ( {value} ))}
)}
); export const StatisticsPage = () => { const [isSidebarOpen, setIsSidebarOpen] = useState(false); const { theme } = useTheme(); const [filters, setFilters] = useState({ apps: [], channels: [], platforms: [], architectures: [], range: 'today' }); const [selectedDate, setSelectedDate] = useState(null); const [showDatePicker, setShowDatePicker] = useState(false); const tooltipTextColor = theme === 'dark' ? '#FFFFFF' : '#000000'; const tooltipContentStyle = React.useMemo( () => ({ ...TOOLTIP_STYLE, color: tooltipTextColor }), [tooltipTextColor] ); const tooltipLabelStyle = React.useMemo( () => ({ color: tooltipTextColor, fontWeight: 'bold', marginBottom: '4px', }), [tooltipTextColor] ); const tooltipItemStyle = React.useMemo( () => ({ color: tooltipTextColor, padding: '4px 0', }), [tooltipTextColor] ); const chartColors = React.useMemo( () => (theme === 'dark' ? DARK_CHART_COLORS : LIGHT_CHART_COLORS), [theme] ); const lineChartConfig = React.useMemo( () => ({ requests: chartColors[0], uniqueClients: chartColors[1], latestUsers: chartColors[2], outdatedClients: chartColors[3], }), [chartColors] ); const sharedTooltipProps = React.useMemo( () => ({ contentStyle: tooltipContentStyle, labelStyle: tooltipLabelStyle, itemStyle: tooltipItemStyle, formatter: (value: number | string) => formatNumber(value), }), [tooltipContentStyle, tooltipLabelStyle, tooltipItemStyle] ); const { apps = [] } = useAppsQuery(); const { channels = [] } = useChannelQuery(); const { platforms = [] } = usePlatformQuery(); const { architectures = [] } = useArchitectureQuery(); const { data, isLoading, refetch } = useTelemetryQuery(filters); const rolloutAppNames = React.useMemo( () => (filters.apps.length ? filters.apps : (apps as AppListItem[]).map((a) => a.AppName)), [filters.apps, apps] ); const rolloutScope = React.useMemo( () => ({ channels: filters.channels, platforms: filters.platforms, architectures: filters.architectures }), [filters.channels, filters.platforms, filters.architectures] ); const { rolloutVersions } = useRolloutVersions(rolloutAppNames, rolloutScope); const rolloutAppList = React.useMemo( () => [...new Set(rolloutVersions.map((r) => r.appName))], [rolloutVersions] ); const adoptionFilters = React.useMemo( () => ({ channels: filters.channels, platforms: filters.platforms, architectures: filters.architectures, range: filters.range, date: filters.date, }), [filters.channels, filters.platforms, filters.architectures, filters.range, filters.date] ); const { byApp: adoptionByApp } = useRolloutAdoption(rolloutAppList, adoptionFilters); const rolloutRows = React.useMemo(() => { return rolloutVersions .map((r) => { const app = adoptionByApp[r.appName]; // Hide rollouts for apps with no telemetry in view (idle/other apps), // but keep apps that have traffic even if this version is at 0% adoption. if (!app || app.uniqueClients <= 0) { return null; } const usageList = app.usage; const hasUsage = usageList.some((u) => u.version === r.version); const geCount = (version: string) => usageList.reduce( (acc, u) => acc + (compareVersions(u.version, version) >= 0 ? u.client_count : 0), 0 ); const predecessor = [...usageList] .map((u) => u.version) .sort(compareVersions) .reverse() .find((v) => compareVersions(v, r.version) < 0); const observedClients = hasUsage ? geCount(r.version) : null; const eligibleClients = predecessor ? geCount(predecessor) : app.uniqueClients; const observedPercent = hasUsage && eligibleClients > 0 ? ((observedClients as number) / eligibleClients) * 100 : null; return { r, observedClients, eligibleClients, observedPercent }; }) .filter((row): row is NonNullable => row !== null); }, [rolloutVersions, adoptionByApp]); const dailyStats = React.useMemo(() => (Array.isArray(data?.daily_stats) ? data.daily_stats : []), [data?.daily_stats]); const hasTrendData = dailyStats.length > 0; const showSinglePointTrend = dailyStats.length === 1; const singlePointDateLabel = showSinglePointTrend ? formatChartDateLong(String(dailyStats[0].date)) : ''; const handleOptionClick = (dropdownName: keyof Filters, value: string) => { setFilters(prev => { const currentValues = prev[dropdownName] as string[]; const newValues = currentValues.includes(value) ? currentValues.filter(v => v !== value) : [...currentValues, value]; return { ...prev, [dropdownName]: newValues }; }); }; const handleTimeRangeChange = (range: 'today' | 'week' | 'month' | 'custom') => { if (range === 'custom') { setShowDatePicker(true); setFilters(prev => ({ ...prev, range: undefined, date: undefined })); } else { setShowDatePicker(false); setSelectedDate(null); setFilters(prev => ({ ...prev, range, date: undefined })); } }; const handleDateChange = (date: Date | null) => { setSelectedDate(date); if (date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const formattedDate = `${year}-${month}-${day}`; setFilters(prev => ({ ...prev, date: formattedDate, range: undefined })); setShowDatePicker(false); } }; React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as HTMLElement; if (!target.closest('.react-datepicker') && !target.closest('.date-picker-container')) { setShowDatePicker(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, []); if (isLoading) { return (
setIsSidebarOpen(false)} />
setIsSidebarOpen(true)} onCreateClick={() => {}} createButtonText="" hideSearch={true} />
); } if (!data) { return (
setIsSidebarOpen(false)} />
setIsSidebarOpen(true)} onCreateClick={() => {}} createButtonText="" hideSearch={true} />

No Data Available

There are no statistics available for the selected period.

); } const NoDataMessage = () => (

No data available for this section

); return (
setIsSidebarOpen(false)} />
setIsSidebarOpen(true)} onCreateClick={() => {}} createButtonText="" hideSearch={true} />

Filters & Time Range

{data.summary?.total_requests || 0} requests in view
{/* Time Range Selector */}
{showDatePicker && (
)}
{/* Filters */}
handleOptionClick('apps', value)} options={(Array.isArray(apps) ? (apps as AppListItem[]) : []).map((app) => ({ value: app.AppName, label: app.AppName, }))} /> handleOptionClick('channels', value)} options={(Array.isArray(channels) ? (channels as Channel[]) : []).map((channel) => ({ value: channel.ChannelName, label: channel.ChannelName, }))} /> handleOptionClick('platforms', value)} options={(Array.isArray(platforms) ? (platforms as Platform[]) : []).map((platform) => ({ value: platform.PlatformName, label: platform.PlatformName, icon: getPlatformIcon(platform.PlatformName), }))} /> handleOptionClick('architectures', value)} options={(Array.isArray(architectures) ? (architectures as Architecture[]) : []).map((arch) => ({ value: arch.ArchID, label: arch.ArchID, }))} />
{/* Summary Cards */}
} /> } /> } /> } /> } />
{/* Rollout Health */} {rolloutRows.length > 0 && (

Rollout Health

{rolloutRows.length} staged {rolloutRows.length === 1 ? 'rollout' : 'rollouts'}
{rolloutRows.map(({ r, observedClients, eligibleClients, observedPercent }, index) => ( ))}

Observed is adoption within the eligible pool: clients on the rollout version or newer, over clients on the previous version or newer — the same base the rollout percent is defined against, so it lines up with the target. It trails and converges as eligible clients check in, with a ~±2% bucketing margin. Filter by the rollout's channel/platform to scope it.

)} {/* Trend Graphs */}
{/* Total Requests Trend */}

Total Requests Trend

{hasTrendData ? ( showSinglePointTrend ? ( ) : ( formatNumber(value)} /> formatChartDateLong(String(value))} /> ) ) : ( )}
{/* Unique Clients Trend */}

Unique Clients Trend

{hasTrendData ? ( showSinglePointTrend ? ( ) : ( formatNumber(value)} /> formatChartDateLong(String(value))} /> ) ) : ( )}
{/* Latest Version Users Trend */}

Latest Version Users Trend

{hasTrendData ? ( showSinglePointTrend ? ( ) : ( formatNumber(value)} /> formatChartDateLong(String(value))} /> ) ) : ( )}
{/* Outdated Clients Trend */}

Outdated Clients Trend

{hasTrendData ? ( showSinglePointTrend ? ( ) : ( formatNumber(value)} /> formatChartDateLong(String(value))} /> ) ) : ( )}
{/* Charts */}
{/* Version Usage Chart */}

Version Usage

{data.versions?.usage ? ( formatNumber(value)} /> ) : ( )}
{/* Platform Distribution Chart */}

Platform Distribution

{data.platforms ? ( {data.platforms.map((_, index) => ( ))} {value}} /> ) : ( )}
{/* Architecture Distribution Chart */}

Architecture Distribution

{data.architectures ? ( {data.architectures.map((_, index) => ( ))} {value}} /> ) : ( )}
{/* Channel Distribution Chart */}

Channel Distribution

{data.channels ? ( {data.channels.map((_, index) => ( ))} {value}} /> ) : ( )}
{/* Version Table */}

Version Details

{data.versions?.usage ? ( {data.versions.usage.map((version) => ( ))}
Version Client Count % of Total
{version.version} {version.client_count} {((version.client_count / (data.summary?.unique_clients || 1)) * 100).toFixed(1)}%
) : ( )}
); };