import { useState, useEffect, useRef } from 'react'; import { UsageProgressBar } from './UsageProgressBar'; interface ClaudeAccount { id: string; name: string; status: string; accountType: string; lastUsedAt: string | null; schedulable?: boolean; stoppedReason?: string; usage?: { daily?: { tokens: number; requests: number; cost: number; }; }; claudeUsage?: { fiveHour?: { utilization: number; resetsAt: string; remainingSeconds: number; }; sevenDay?: { utilization: number; resetsAt: string; remainingSeconds: number; }; sevenDayOpus?: { utilization: number; resetsAt: string; remainingSeconds: number; }; }; } interface OpenAIAccount { id: string; name: string; status: string; accountType: string; lastUsedAt: string | null; schedulable?: boolean; stoppedReason?: string; usage?: { daily?: { tokens: number; requests: number; cost: number; }; }; codexUsage?: { primary?: { usedPercent: number; resetAfterSeconds: number; resetAt: string; }; secondary?: { usedPercent: number; resetAfterSeconds: number; resetAt: string; }; }; } type UnifiedAccount = (ClaudeAccount | OpenAIAccount) & { platform: 'claude' | 'openai'; }; interface AIAccountsProps { apiKey: string; } export function AIAccounts({ apiKey }: AIAccountsProps) { const [claudeAccounts, setClaudeAccounts] = useState([]); const [openaiAccounts, setOpenaiAccounts] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); const [refreshError, setRefreshError] = useState(''); const [initialError, setInitialError] = useState(''); const hasLoadedDataRef = useRef(false); useEffect(() => { fetchAccounts('initial'); const interval = setInterval(() => fetchAccounts('auto'), 30000); return () => clearInterval(interval); }, [apiKey]); const fetchAccounts = async (type: 'initial' | 'manual' | 'auto' = 'auto') => { if (type === 'manual') { setIsRefreshing(true); setRefreshError(''); } try { const response = await fetch('/api/ai-accounts', { headers: { 'X-API-Key': apiKey, }, }); if (!response.ok) { throw new Error('Failed to fetch AI accounts'); } const data = await response.json(); setClaudeAccounts(data.claude || []); setOpenaiAccounts(data.openai || []); setRefreshError(''); setInitialError(''); hasLoadedDataRef.current = true; } catch (err) { console.error('Error fetching AI accounts:', err); const errorMessage = err instanceof Error ? err.message : 'Failed to load AI accounts'; if (type === 'initial') { setInitialError(errorMessage); } else if (hasLoadedDataRef.current) { // Only set refresh error if we have previously loaded data successfully setRefreshError(errorMessage); } } finally { if (type === 'initial') { setIsLoading(false); } if (type === 'manual') { setIsRefreshing(false); } } }; const handleRetry = () => { setRefreshError(''); fetchAccounts('manual'); }; const formatLastUsed = (lastUsedAt: string | null) => { if (!lastUsedAt) return 'Never'; const date = new Date(lastUsedAt); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); if (diffMins < 1) return 'Just now'; if (diffMins < 60) return `${diffMins}m ago`; const diffHours = Math.floor(diffMins / 60); if (diffHours < 24) return `${diffHours}h ago`; const diffDays = Math.floor(diffHours / 24); return `${diffDays}d ago`; }; const getStatusColor = (status: string) => { switch (status) { case 'active': return 'bg-chart-1 text-white'; case 'blocked': return 'bg-chart-2 text-white'; case 'unauthorized': return 'bg-chart-3 text-white'; case 'temp_error': return 'bg-chart-4 text-white'; default: return 'bg-chart-5 text-white'; } }; const getAccountName = (account: UnifiedAccount, totalCount: number) => { if (totalCount === 1) { return account.platform === 'claude' ? 'Claude' : 'OpenAI'; } return `${account.platform === 'claude' ? 'Claude' : 'OpenAI'} (${account.name})`; }; // Merge accounts const allAccounts: UnifiedAccount[] = [ ...claudeAccounts.map(acc => ({ ...acc, platform: 'claude' as const })), ...openaiAccounts.map(acc => ({ ...acc, platform: 'openai' as const })), ]; if (isLoading) { return (

AI Accounts Status

Loading accounts...
); } if (initialError) { return (

AI Accounts Status

{initialError}

); } return (

AI Accounts Status

{refreshError && (
{refreshError}
)}
{allAccounts.length > 0 ? (
{allAccounts.map((account) => { const platformCount = account.platform === 'claude' ? claudeAccounts.length : openaiAccounts.length; const isClaudeAccount = account.platform === 'claude'; const claudeAcc = isClaudeAccount ? (account as ClaudeAccount) : null; const openaiAcc = !isClaudeAccount ? (account as OpenAIAccount) : null; return ( ); })}
Account Status Daily Usage Usage Windows Last Used
{getAccountName(account, platformCount)}
{account.status} {account.schedulable === false && ( Not schedulable {account.stoppedReason || 'Not schedulable'} )}
{account.usage?.daily && (isClaudeAccount || account.usage.daily.requests > 0) ? (
{account.usage.daily.requests} reqs
${account.usage.daily.cost.toFixed(2)} cost
) : ( {isClaudeAccount ? 'No data' : 'No usage today'} )}
{claudeAcc?.claudeUsage ? (
{claudeAcc.claudeUsage.fiveHour && ( )} {claudeAcc.claudeUsage.sevenDay && ( )} {claudeAcc.claudeUsage.sevenDayOpus && ( )}
) : openaiAcc?.codexUsage ? (
{openaiAcc.codexUsage.primary && ( )} {openaiAcc.codexUsage.secondary && ( )}
) : ( No data )}
{formatLastUsed(account.lastUsedAt)}
) : (
No shared AI accounts found
)}
); }