import { useMemo } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { TrendingUp, Clock, Calendar, CalendarDays } from 'lucide-react'; interface UsageBucket { totalTokens: number; requests: number; } interface ForecastCardProps { /** Hourly buckets for the selected period (always fetched for all ranges). */ hourlyData?: UsageBucket[]; /** Daily buckets for the selected period. */ dailyData?: UsageBucket[]; range: '24h' | '7d' | '30d'; } function formatNumber(n: number): string { if (n >= 1_000_000) { return `${(n / 1_000_000).toFixed(1)}M`; } if (n >= 1_000) { return `${(n / 1_000).toFixed(1)}K`; } return n.toLocaleString(undefined, { maximumFractionDigits: 0 }); } /** * Usage Averages Card. * * Calculates REAL averages over the time the user actually used the proxy, * not projections over the entire selected window. * * Logic: * - totalTokens = sum of all buckets in the period * - activeHours = number of hourly buckets with totalTokens > 0 * - activeDays = number of daily buckets with totalTokens > 0 * - avgPerHour = totalTokens / activeHours (real average, no padding) * - avgPerDay = totalTokens / activeDays (real average, no padding) * * For the 24h range, avgPerDay equals totalTokens (since it's a single day). * For 7d/30d, avgPerDay = totalTokens / activeDays. * * The card title says "Usage Averages" (not "Forecast") because these are * real historical averages, not projections. */ export function ForecastCard({ hourlyData, dailyData, range }: ForecastCardProps) { const stats = useMemo(() => { const hours = hourlyData ?? []; const days = dailyData ?? []; const totalTokens = hours.length > 0 ? hours.reduce((acc, d) => acc + d.totalTokens, 0) : days.reduce((acc, d) => acc + d.totalTokens, 0); const totalRequests = hours.length > 0 ? hours.reduce((acc, d) => acc + d.requests, 0) : days.reduce((acc, d) => acc + d.requests, 0); // Active hours = hourly buckets where there was actual usage const activeHours = hours.filter((d) => d.totalTokens > 0).length; // Active days = daily buckets where there was actual usage const activeDays = days.filter((d) => d.totalTokens > 0).length; // Real average per active hour const avgPerHour = activeHours > 0 ? totalTokens / activeHours : 0; // Real average per active day const avgPerDay = activeDays > 0 ? totalTokens / activeDays : 0; // Average tokens per request const avgPerRequest = totalRequests > 0 ? totalTokens / totalRequests : 0; return { totalTokens, totalRequests, activeHours, activeDays, avgPerHour, avgPerDay, avgPerRequest, }; }, [hourlyData, dailyData]); const rangeLabel = range === '24h' ? 'Last 24h' : range === '7d' ? 'Last 7 days' : 'Last 30 days'; return (
Usage Averages {rangeLabel}
{/* Total consumed in period */}
Total in period
{formatNumber(stats.totalTokens)}{' '} tokens
{stats.activeHours > 0 && `${stats.activeHours} active hour${stats.activeHours !== 1 ? 's' : ''}`} {stats.activeHours > 0 && stats.activeDays > 0 && ' · '} {stats.activeDays > 0 && `${stats.activeDays} active day${stats.activeDays !== 1 ? 's' : ''}`} {stats.totalRequests > 0 && ` · ${stats.totalRequests.toLocaleString()} requests`}
{/* Average per active hour */}
Avg / active hour
{formatNumber(stats.avgPerHour)} tokens/h
{/* Average per active day */}
Avg / active day
{formatNumber(stats.avgPerDay)} tokens/d
{/* Average per request */} {stats.totalRequests > 0 && (
Avg / request
{formatNumber(stats.avgPerRequest)} tokens/req
)}
); }