import React from 'react'; import { UsageCard } from './UsageCard'; import { QuotaCredentials } from './QuotaCredentials'; import { QUOTA_PROVIDERS } from '@/lib/quota'; import { useQuotaAutoRefresh, useQuotaStore } from '@/stores/useQuotaStore'; import { updateDesktopSettings } from '@/lib/persistence'; import { ProviderLogo } from '@/components/ui/ProviderLogo'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import type { UsageWindows, QuotaProviderId } from '@/types'; import { getAllModelFamilies, getDisplayModelName, sortModelFamilies, groupModelsByFamilyWithGetter } from '@/lib/quota/model-families'; import { Icon } from "@/components/icon/Icon"; import { useI18n } from '@/lib/i18n'; import { formatTimeForPreference } from '@/lib/timeFormat'; import { useUIStore, type TimeFormatPreference } from '@/stores/useUIStore'; import { SettingsPageLayout } from '@/components/sections/shared/SettingsPageLayout'; import { SettingsSection, SettingsCheckboxRow, } from '@/components/sections/shared/SettingsSection'; const formatTime = (timestamp: number | null, timeFormatPreference: TimeFormatPreference) => { if (!timestamp) return '-'; try { return formatTimeForPreference(timestamp, timeFormatPreference, { fallback: '-' }); } catch { return '-'; } }; interface ModelInfo { name: string; windows: UsageWindows; } export const UsagePage: React.FC = () => { const { t } = useI18n(); const timeFormatPreference = useUIStore((state) => state.timeFormatPreference); const results = useQuotaStore((state) => state.results); const selectedProviderId = useQuotaStore((state) => state.selectedProviderId); const setSelectedProvider = useQuotaStore((state) => state.setSelectedProvider); const loadSettings = useQuotaStore((state) => state.loadSettings); const fetchAllQuotas = useQuotaStore((state) => state.fetchAllQuotas); const isLoading = useQuotaStore((state) => state.isLoading); const lastUpdated = useQuotaStore((state) => state.lastUpdated); const error = useQuotaStore((state) => state.error); const dropdownProviderIds = useQuotaStore((state) => state.dropdownProviderIds); const setDropdownProviderIds = useQuotaStore((state) => state.setDropdownProviderIds); const selectedModels = useQuotaStore((state) => state.selectedModels); const toggleModelSelected = useQuotaStore((state) => state.toggleModelSelected); const applyDefaultSelections = useQuotaStore((state) => state.applyDefaultSelections); useQuotaAutoRefresh(); React.useEffect(() => { void loadSettings(); void fetchAllQuotas(); }, [loadSettings, fetchAllQuotas]); React.useEffect(() => { if (selectedProviderId) { return; } if (results.length === 0) { return; } const firstConfigured = results.find((entry) => entry.configured)?.providerId; setSelectedProvider(firstConfigured ?? QUOTA_PROVIDERS[0]?.id ?? null); }, [results, selectedProviderId, setSelectedProvider]); const selectedResult = results.find((entry) => entry.providerId === selectedProviderId) ?? null; const providerMeta = QUOTA_PROVIDERS.find((provider) => provider.id === selectedProviderId); const providerName = providerMeta?.name ?? selectedProviderId ?? t('settings.usage.sidebar.title'); const usage = selectedResult?.usage; const selectedProviderError = selectedResult?.configured && !selectedResult.ok ? selectedResult.error : null; const showInDropdown = selectedProviderId ? dropdownProviderIds.includes(selectedProviderId) : false; const hasCredentialsForm = selectedProviderId === 'opencode-go' || selectedProviderId === 'ollama-cloud' || selectedProviderId === 'cursor'; const handleDropdownToggle = React.useCallback((enabled: boolean) => { if (!selectedProviderId) { return; } const next = enabled ? Array.from(new Set([...dropdownProviderIds, selectedProviderId])) : dropdownProviderIds.filter((id) => id !== selectedProviderId); setDropdownProviderIds(next); void updateDesktopSettings({ usageDropdownProviders: next }); }, [dropdownProviderIds, selectedProviderId, setDropdownProviderIds]); const providerModels = React.useMemo((): ModelInfo[] => { if (!usage?.models) return []; return Object.entries(usage.models) .map(([name, modelUsage]) => ({ name, windows: modelUsage })) .filter((model) => Object.keys(model.windows.windows).length > 0); }, [usage?.models]); React.useEffect(() => { if (selectedProviderId && providerModels.length > 0) { applyDefaultSelections(selectedProviderId, providerModels.map((m) => m.name)); } }, [selectedProviderId, providerModels, applyDefaultSelections]); const modelsByFamily = React.useMemo(() => { if (!selectedProviderId || providerModels.length === 0) { return new Map(); } return groupModelsByFamilyWithGetter( providerModels, (model) => model.name, selectedProviderId as QuotaProviderId ); }, [providerModels, selectedProviderId]); const sortedFamilies = React.useMemo(() => { if (!selectedProviderId) return []; const families = getAllModelFamilies(selectedProviderId as QuotaProviderId); return sortModelFamilies(families); }, [selectedProviderId]); const [collapsedFamilies, setCollapsedFamilies] = React.useState>(() => { return {}; }); const toggleFamilyCollapsed = React.useCallback((familyId: string) => { setCollapsedFamilies((prev) => ({ ...prev, [familyId]: !prev[familyId], })); }, []); const handleModelToggle = React.useCallback((modelName: string) => { if (!selectedProviderId) return; toggleModelSelected(selectedProviderId, modelName); const currentSelected = selectedModels[selectedProviderId] ?? []; const isSelected = currentSelected.includes(modelName); const nextSelected = isSelected ? currentSelected.filter((m) => m !== modelName) : [...currentSelected, modelName]; const nextSettings: Record = { ...selectedModels, [selectedProviderId]: nextSelected }; void updateDesktopSettings({ usageSelectedModels: nextSettings }); }, [selectedProviderId, selectedModels, toggleModelSelected]); const providerSelectedModels = selectedProviderId ? (selectedModels[selectedProviderId] ?? []) : []; if (!selectedProviderId) { return (

{t('settings.usage.page.empty.selectProvider')}

); } return ( } description={ isLoading ? ( {t('settings.usage.page.header.refreshing')} ) : ( t('settings.usage.page.header.lastUpdated', { time: formatTime(lastUpdated, timeFormatPreference) }) ) } showSaveStatus > {!selectedResult && (

{t('settings.usage.page.state.noData')}

)} {(error || selectedProviderError) && (

{t('settings.usage.page.state.refreshFailedTitle')}

{error ?? selectedProviderError}

)} {/* Providers with an inline credentials form don't need the "go to Providers" banner — the form IS the fix. */} {selectedResult && !selectedResult.configured && !hasCredentialsForm && (

{t('settings.usage.page.state.providerNotConfiguredTitle')}

{t('settings.usage.page.state.providerNotConfiguredDescription')}

)} {(selectedProviderId === 'opencode-go' || selectedProviderId === 'ollama-cloud' || selectedProviderId === 'cursor') && ( )} {usage?.windows && Object.keys(usage.windows).length > 0 && (
{Object.entries(usage.windows).map(([label, window]) => ( ))}
)} {providerModels.length > 0 && ( {sortedFamilies.map((family) => { const familyModels = modelsByFamily.get(family.id) ?? []; if (familyModels.length === 0) return null; const isCollapsed = collapsedFamilies[family.id] ?? false; return (
toggleFamilyCollapsed(family.id)} >
{family.label} ({familyModels.length})
{isCollapsed ? ( ) : ( )}
{familyModels.map((model) => { const entries = Object.entries(model.windows.windows); if (entries.length === 0) return null; const [label, window] = entries[0]; const isSelected = providerSelectedModels.includes(model.name); return ( handleModelToggle(model.name)} /> ); })}
); })} {(() => { const otherModels = modelsByFamily.get(null) ?? []; if (otherModels.length === 0) return null; const isCollapsed = collapsedFamilies['other'] ?? false; return (
toggleFamilyCollapsed('other')} >
{t('settings.usage.page.section.otherModels')} ({otherModels.length})
{isCollapsed ? ( ) : ( )}
{otherModels.map((model) => { const entries = Object.entries(model.windows.windows); if (entries.length === 0) return null; const [label, window] = entries[0]; const isSelected = providerSelectedModels.includes(model.name); return ( handleModelToggle(model.name)} /> ); })}
); })()}
)} {selectedResult?.configured && usage && Object.keys(usage.windows ?? {}).length === 0 && providerModels.length === 0 && (

{t('settings.usage.page.state.noQuotaWindowsTitle')}

{t('settings.usage.page.state.noQuotaWindowsDescription')}

)}
); };