import React, { useState, useEffect, useCallback } from 'react'; import { SessionCreationForm } from './components/SessionCreationForm'; import { SessionProgressMonitor } from './components/SessionProgressMonitor'; import { LoginPrompt } from './components/LoginPrompt'; import { BrowserModeToggle } from './components/BrowserModeToggle'; import { CheckoutConfirmationDialog } from './components/CheckoutConfirmationDialog'; import { RaceCreationSuccessScreen } from './components/RaceCreationSuccessScreen'; import type { HostedSessionConfig } from '../../../packages/automation/domain/types/HostedSessionConfig'; interface SessionProgress { sessionId: string; currentStep: number; state: string; completedSteps: number[]; hasError: boolean; errorMessage: string | null; } type AuthState = 'UNKNOWN' | 'AUTHENTICATED' | 'EXPIRED' | 'LOGGED_OUT' | 'CHECKING'; type LoginStatus = 'idle' | 'waiting' | 'success' | 'error'; export function App() { const [authState, setAuthState] = useState('CHECKING'); const [authError, setAuthError] = useState(undefined); const [sessionId, setSessionId] = useState(null); const [progress, setProgress] = useState(null); const [isRunning, setIsRunning] = useState(false); const [loginStatus, setLoginStatus] = useState('idle'); const [checkoutRequest, setCheckoutRequest] = useState<{ price: string; state: 'ready' | 'insufficient_funds'; sessionMetadata: { sessionName: string; trackId: string; carIds: string[]; }; timeoutMs: number; } | null>(null); const [raceCreationResult, setRaceCreationResult] = useState<{ sessionId: string; sessionName: string; trackId: string; carIds: string[]; finalPrice: string; createdAt: Date; } | null>(null); const handleLogin = useCallback(async () => { if (!window.electronAPI) return; setAuthError(undefined); setLoginStatus('waiting'); try { // This now waits for login to complete (auto-detects and closes browser) const result = await window.electronAPI.initiateLogin(); if (result.success) { // Login completed successfully - browser closed automatically setLoginStatus('success'); // Show success message for 2 seconds before transitioning setTimeout(() => { setAuthState('AUTHENTICATED'); }, 2000); } else { setLoginStatus('error'); setAuthError(result.error); } } catch (error) { console.error('Login failed:', error); setLoginStatus('error'); setAuthError(error instanceof Error ? error.message : 'Login failed'); } }, []); const handleRetryAuth = useCallback(async () => { if (!window.electronAPI) return; setAuthState('CHECKING'); setAuthError(undefined); setLoginStatus('idle'); try { const result = await window.electronAPI.checkAuth(); if (result.success && result.state) { setAuthState(result.state as AuthState); } else { setAuthError(result.error); setAuthState('UNKNOWN'); } } catch (error) { console.error('Auth check failed:', error); setAuthError(error instanceof Error ? error.message : 'Connection failed'); setAuthState('UNKNOWN'); } }, []); useEffect(() => { if (!window.electronAPI) return; const checkAuth = async () => { try { const result = await window.electronAPI.checkAuth(); if (result.success && result.state) { setAuthState(result.state as AuthState); } else { setAuthError(result.error || 'Failed to check authentication'); setAuthState('UNKNOWN'); } } catch (error) { setAuthError(error instanceof Error ? error.message : 'Failed to check authentication'); setAuthState('UNKNOWN'); } }; // Subscribe to checkout confirmation requests const unsubscribeCheckout = window.electronAPI.onCheckoutConfirmationRequest((request) => { setCheckoutRequest(request); }); checkAuth(); window.electronAPI.onSessionProgress((newProgress: SessionProgress) => { setProgress(newProgress); if (newProgress.state === 'COMPLETED' || newProgress.state === 'FAILED' || newProgress.state === 'STOPPED_AT_STEP_18') { setIsRunning(false); } }); // Cleanup subscription on unmount return () => { unsubscribeCheckout?.(); }; }, []); const handleStartAutomation = async (config: HostedSessionConfig) => { setIsRunning(true); const result = await window.electronAPI.startAutomation(config) as { success: boolean; sessionId?: string; error?: string; authRequired?: boolean; authState?: AuthState; }; if (result.success && result.sessionId) { setSessionId(result.sessionId); return; } setIsRunning(false); if ('authRequired' in result && result.authRequired) { const nextAuthState = result.authState as AuthState | undefined; setAuthState(nextAuthState ?? 'EXPIRED'); setAuthError(result.error ?? 'Authentication required before starting automation.'); return; } alert(`Failed to start automation: ${result.error}`); }; const handleStopAutomation = async () => { if (sessionId) { const result = await window.electronAPI.stopAutomation(sessionId); if (result.success) { setIsRunning(false); setProgress(prev => prev ? { ...prev, state: 'STOPPED', hasError: false, errorMessage: 'User stopped automation' } : null); } else { alert(`Failed to stop automation: ${result.error}`); } } }; if (authState === 'CHECKING') { return (

Checking authentication...

); } // Show checkout confirmation dialog if requested if (checkoutRequest) { return ; } // Show race creation success screen if completed if (raceCreationResult) { return ; } if (authState !== 'AUTHENTICATED') { return ( ); } return (

GridPilot Companion

Hosted Session Automation POC

{isRunning && ( )}
); }