import { useEffect, useState } from "react"; import { api, type Overview } from "../api"; import { ContributionCalendarChart } from "../components/charts/ContributionCalendarChart"; import { DailyTokenUsageChart } from "../components/charts/DailyTokenUsageChart"; import { HomeMetrics } from "../components/metrics/HomeMetrics"; import { PageContent } from "../components/layout/PageContent"; import type { VirtualPageSection } from "../components/layout/VirtualPage"; import { OverviewTimeRangeFilter, type OverviewRangePreset } from "../components/overview/OverviewTimeRangeFilter"; import { PageActions } from "../layouts/PageActions"; import { appStore, useAppStore } from "../store/appStore"; import { formatTimeInput, parseTimeInput } from "../utils/parseTimeInput"; import { claudeIcon, openAiIcon } from "../components/ui/icons"; type TimeRange = { startMs: number; endMs: number }; function presetRange(preset: Exclude, now = new Date()): TimeRange { const endMs = now.getTime(); if (preset === "today") { const start = new Date(now); start.setHours(0, 0, 0, 0); return { startMs: start.getTime(), endMs }; } if (preset === "month") { const start = new Date(now); start.setMonth(start.getMonth() - 1); return { startMs: start.getTime(), endMs }; } const duration = preset === "ten-minutes" ? 10 * 60_000 : preset === "hour" ? 60 * 60_000 : 7 * 24 * 60 * 60_000; return { startMs: now.getTime() - duration, endMs }; } export function HomePage() { const { overview, busy, models, providers } = useAppStore(); const [preset, setPreset] = useState("month"); const [customRange, setCustomRange] = useState(null); const [customOpen, setCustomOpen] = useState(false); const [customStart, setCustomStart] = useState(""); const [customEnd, setCustomEnd] = useState(""); const [selectedModels, setSelectedModels] = useState([]); const [selectedProviders, setSelectedProviders] = useState([]); const [appliedModels, setAppliedModels] = useState([]); const [appliedProviders, setAppliedProviders] = useState([]); const [rangeOverview, setRangeOverview] = useState(null); const [rangeBusy, setRangeBusy] = useState(false); const [refreshVersion, setRefreshVersion] = useState(0); const selectedRange = preset === "custom" ? customRange : presetRange(preset); useEffect(() => { if (!selectedRange) return; let active = true; setRangeBusy(true); void api.overview({ ...selectedRange, modelHashes: appliedModels, providerIds: appliedProviders.map(Number), }).then((next) => { if (active) setRangeOverview(next); }).finally(() => { if (active) setRangeBusy(false); }); return () => { active = false; }; }, [preset, customRange, overview, refreshVersion, appliedModels, appliedProviders]); const filteredOverview = rangeOverview ?? overview; const dailyTokenUsage = filteredOverview.token_usage_series.map((bucket) => ({ bucketStartMs: bucket.bucket_start_ms, inputTokens: bucket.input_tokens, cacheReadTokens: bucket.cache_read_tokens, cacheWriteTokens: bucket.cache_write_tokens, outputTokens: bucket.output_tokens, })); const contribution = overview.token_usage_series.map((bucket) => ({ date: new Date(bucket.bucket_start_ms).toISOString().slice(0, 10), tokens: bucket.input_tokens + bucket.cache_read_tokens + bucket.cache_write_tokens + bucket.output_tokens, })); const metrics = { llmCalls: filteredOverview.metrics.llm_calls, successfulCalls: filteredOverview.metrics.successful_calls, failedCalls: filteredOverview.metrics.failed_calls, tokenUsage: filteredOverview.metrics.token_usage, promptTokens: filteredOverview.metrics.prompt_tokens, cacheReadTokens: filteredOverview.metrics.cache_read_tokens, cacheWriteTokens: filteredOverview.metrics.cache_write_tokens, }; const openCustom = (open: boolean) => { if (open && !customStart && !customEnd) { const now = new Date(); setCustomEnd(formatTimeInput(now)); setCustomStart(formatTimeInput(new Date(now.getTime() - 60 * 60_000))); } setCustomOpen(open); }; const applyCustom = () => { const startMs = parseTimeInput(customStart); const endMs = parseTimeInput(customEnd); if (startMs === null || endMs === null || startMs >= endMs) return; setCustomRange({ startMs, endMs }); setAppliedModels(selectedModels); setAppliedProviders(selectedProviders); setPreset("custom"); setCustomOpen(false); }; const refresh = async () => { await appStore.refresh(); setRefreshVersion((version) => version + 1); }; const iconFor = (type: string) => type === "anthropic" ? claudeIcon : openAiIcon; const modelOptions = models.map((model) => ({ value: model.model_hash, label: model.display_name, icon: iconFor(model.endpoint_type), })); const providerOptions = providers.map((provider) => ({ value: String(provider.provider_id), label: provider.name, icon: iconFor(provider.provider_type), })); const sections: VirtualPageSection[] = [ { key: "daily-token-usage", estimatedHeight: 240, content: , }, { key: "metrics", estimatedHeight: 130, content: , }, { key: "activity", estimatedHeight: 106, content: , }, ]; return <> { setPreset(value); setCustomOpen(false); }} onCustomOpenChange={openCustom} onCustomStartChange={setCustomStart} onCustomEndChange={setCustomEnd} onSelectedModelsChange={setSelectedModels} onSelectedProvidersChange={setSelectedProviders} onCustomApply={applyCustom} onRefresh={() => void refresh()} /> ; }