/** * CLIProxy Header Component * Fixed header with OAuth login buttons, status indicator, and refresh */ import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { RefreshCw, Loader2 } from 'lucide-react'; import { useCliproxyAuth } from '@/hooks/use-cliproxy'; import { useCliproxyAuthFlow } from '@/hooks/use-cliproxy-auth-flow'; import { cn } from '@/lib/utils'; interface LoginButtonProps { provider: string; displayName: string; isAuthenticated: boolean; accountCount: number; isAuthenticating: boolean; onLogin: () => void; } function LoginButton({ displayName, isAuthenticated, accountCount, isAuthenticating, onLogin, }: LoginButtonProps) { if (isAuthenticating) { return ( ); } if (isAuthenticated) { return ( ); } return ( ); } // Helper to format relative time function formatRelativeTime(date: Date): string { const diff = Date.now() - date.getTime(); const minutes = Math.floor(diff / 60000); if (minutes < 1) return 'just now'; if (minutes === 1) return '1m ago'; return `${minutes}m ago`; } // Hook for relative time display that updates periodically function useRelativeTime(date: Date | undefined): string | null { const [text, setText] = useState(() => (date ? formatRelativeTime(date) : null)); useEffect(() => { if (!date) return; // Update every 30 seconds via interval only const interval = setInterval(() => { setText(formatRelativeTime(date)); }, 30000); return () => clearInterval(interval); }, [date]); // Compute current value on each render if date changes // This is the pure computation part const currentText = date ? formatRelativeTime(date) : null; // Return the more recent of computed or state-based value // State value will be updated by interval return date ? currentText : text; } interface CliproxyHeaderProps { onRefresh: () => void; isRefreshing: boolean; lastUpdated?: Date; isRunning?: boolean; } export function CliproxyHeader({ onRefresh, isRefreshing, lastUpdated, isRunning = true, }: CliproxyHeaderProps) { const { data: authData } = useCliproxyAuth(); const { provider: authProvider, isAuthenticating, startAuth } = useCliproxyAuthFlow(); const lastUpdatedText = useRelativeTime(lastUpdated); const providers = [ { id: 'claude', displayName: 'Claude' }, { id: 'gemini', displayName: 'Gemini' }, { id: 'codex', displayName: 'Codex' }, { id: 'agy', displayName: 'Agy' }, { id: 'kiro', displayName: 'Kiro' }, ]; const getProviderStatus = (providerId: string) => { const status = authData?.authStatus.find((s) => s.provider === providerId); return { isAuthenticated: status?.authenticated ?? false, accountCount: status?.accounts?.length ?? 0, }; }; return (
{/* Top row: Title and Login Buttons */}

CLIProxy Plus

CCS-level account management

{/* Login Buttons - Wrap on mobile */}
{providers.map((p) => { const status = getProviderStatus(p.id); return ( startAuth(p.id)} /> ); })}
{/* Bottom row: Status and Refresh */}
{isRunning ? 'Running' : 'Offline'} {lastUpdatedText && ( {lastUpdatedText} )}
); }