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;