import React, { useState, useEffect } from 'react'; import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom'; import { Box, CircularProgress, Typography } from '@mui/material'; import { CopilotKit } from "@copilotkit/react-core"; import { ClerkProvider, useAuth } from '@clerk/clerk-react'; import "@copilotkit/react-ui/styles.css"; import Wizard from './components/OnboardingWizard/Wizard'; import MainDashboard from './components/MainDashboard/MainDashboard'; import SEODashboard from './components/SEODashboard/SEODashboard'; import ContentPlanningDashboard from './components/ContentPlanningDashboard/ContentPlanningDashboard'; import FacebookWriter from './components/FacebookWriter/FacebookWriter'; import LinkedInWriter from './components/LinkedInWriter/LinkedInWriter'; import BlogWriter from './components/BlogWriter/BlogWriter'; import WixTestPage from './components/WixTestPage/WixTestPage'; import WixCallbackPage from './components/WixCallbackPage/WixCallbackPage'; import WordPressCallbackPage from './components/WordPressCallbackPage/WordPressCallbackPage'; import ProtectedRoute from './components/shared/ProtectedRoute'; import GSCAuthCallback from './components/SEODashboard/components/GSCAuthCallback'; import Landing from './components/Landing/Landing'; import ErrorBoundary from './components/shared/ErrorBoundary'; import ErrorBoundaryTest from './components/shared/ErrorBoundaryTest'; import { OnboardingProvider } from './contexts/OnboardingContext'; import { apiClient, setAuthTokenGetter } from './api/client'; import { useOnboarding } from './contexts/OnboardingContext'; // interface OnboardingStatus { // onboarding_required: boolean; // onboarding_complete: boolean; // current_step?: number; // total_steps?: number; // completion_percentage?: number; // } // Conditional CopilotKit wrapper that only shows sidebar on content-planning route const ConditionalCopilotKit: React.FC<{ children: React.ReactNode }> = ({ children }) => { const location = useLocation(); // const isContentPlanningRoute = location.pathname === '/content-planning'; // Do not render CopilotSidebar here. Let specific pages/components control it. return <>{children}; }; // Component to handle initial routing based on onboarding status // Now uses OnboardingContext instead of making its own API calls const InitialRouteHandler: React.FC = () => { const { loading, error, isOnboardingComplete } = useOnboarding(); // Loading state if (loading) { return ( Checking onboarding status... ); } // Error state if (error) { return ( Error {error} ); } // Redirect based on onboarding status from context if (isOnboardingComplete) { console.log('InitialRouteHandler: Onboarding complete (from context), redirecting to dashboard'); return ; } else { console.log('InitialRouteHandler: Onboarding not complete (from context), redirecting to onboarding'); return ; } }; // Root route that chooses Landing (signed out) or InitialRouteHandler (signed in) const RootRoute: React.FC = () => { const { isSignedIn } = useAuth(); if (isSignedIn) { return ; } return ; }; // Installs Clerk auth token getter into axios clients; must render under ClerkProvider const TokenInstaller: React.FC = () => { const { getToken } = useAuth(); useEffect(() => { setAuthTokenGetter(async () => { try { const template = process.env.REACT_APP_CLERK_JWT_TEMPLATE; // If a template is provided, request a template-specific JWT if (template) { // @ts-ignore Clerk types allow options object return await getToken({ template }); } return await getToken(); } catch { return null; } }); }, [getToken]); return null; }; const App: React.FC = () => { // React Hooks MUST be at the top before any conditionals const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Get CopilotKit key from localStorage or .env const [copilotApiKey, setCopilotApiKey] = useState(() => { const savedKey = localStorage.getItem('copilotkit_api_key'); return savedKey || process.env.REACT_APP_COPILOTKIT_API_KEY || ''; }); useEffect(() => { const checkBackendHealth = async () => { try { await apiClient.get('/health'); setLoading(false); } catch (err) { setError('Backend service is not available. Please check if the server is running.'); setLoading(false); } }; checkBackendHealth(); }, []); // Listen for CopilotKit key updates useEffect(() => { const handleKeyUpdate = (event: CustomEvent) => { const newKey = event.detail?.apiKey; if (newKey) { console.log('App: CopilotKit key updated, reloading...'); setCopilotApiKey(newKey); setTimeout(() => window.location.reload(), 500); } }; window.addEventListener('copilotkit-key-updated', handleKeyUpdate as EventListener); return () => window.removeEventListener('copilotkit-key-updated', handleKeyUpdate as EventListener); }, []); // Token installer must be inside ClerkProvider; see TokenInstaller below if (loading) { return ( Connecting to ALwrity... ); } if (error) { return ( Connection Error {error} Please ensure the backend server is running and try refreshing the page. ); } // Get environment variables with fallbacks const clerkPublishableKey = process.env.REACT_APP_CLERK_PUBLISHABLE_KEY || ''; // Show error if required keys are missing if (!clerkPublishableKey) { return ( Missing Clerk Publishable Key Please add REACT_APP_CLERK_PUBLISHABLE_KEY to your .env file ); } return ( { // Custom error handler - send to analytics/monitoring console.error('Global error caught:', { error, errorInfo }); // TODO: Send to error tracking service (Sentry, LogRocket, etc.) }} > console.error("CopilotKit Error:", e)} > } /> } /> {/* Error Boundary Testing - Development Only */} {process.env.NODE_ENV === 'development' && ( } /> )} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> ); }; export default App;