Alwrity copilotkit integration - 0.5.7
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||
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 "@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 { apiClient } from './api/client';
|
||||
|
||||
interface OnboardingStatus {
|
||||
@@ -15,64 +18,61 @@ interface OnboardingStatus {
|
||||
completion_percentage?: number;
|
||||
}
|
||||
|
||||
const App: React.FC = () => {
|
||||
// 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
|
||||
const InitialRouteHandler: React.FC = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [onboardingStatus, setOnboardingStatus] = useState<OnboardingStatus | null>(null);
|
||||
const [onboardingComplete, setOnboardingComplete] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const checkOnboardingStatus = async () => {
|
||||
try {
|
||||
console.log('Checking onboarding status...');
|
||||
const response = await apiClient.get('/api/onboarding/status');
|
||||
const status = response.data;
|
||||
|
||||
console.log('Onboarding status:', status);
|
||||
|
||||
if (status.is_completed) {
|
||||
console.log('Onboarding is complete, redirecting to dashboard');
|
||||
setOnboardingComplete(true);
|
||||
} else {
|
||||
console.log('Onboarding not complete, staying on onboarding');
|
||||
setOnboardingComplete(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error checking onboarding status:', err);
|
||||
setError('Failed to check onboarding status');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
checkOnboardingStatus();
|
||||
}, []);
|
||||
|
||||
const checkOnboardingStatus = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// Use the correct endpoint that exists in our backend
|
||||
const response = await apiClient.get('/api/onboarding/status');
|
||||
const status: any = response.data;
|
||||
|
||||
// Transform the backend response to match frontend expectations
|
||||
const transformedStatus: OnboardingStatus = {
|
||||
onboarding_required: !status.is_completed,
|
||||
onboarding_complete: status.is_completed || false,
|
||||
current_step: status.current_step,
|
||||
total_steps: 6, // We know there are 6 steps
|
||||
completion_percentage: status.completion_percentage
|
||||
};
|
||||
|
||||
setOnboardingStatus(transformedStatus);
|
||||
} catch (err) {
|
||||
console.error('Error checking onboarding status:', err);
|
||||
// If the endpoint doesn't exist, assume onboarding is required
|
||||
setOnboardingStatus({
|
||||
onboarding_required: true,
|
||||
onboarding_complete: false,
|
||||
current_step: 1,
|
||||
total_steps: 6,
|
||||
completion_percentage: 0
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnboardingComplete = async () => {
|
||||
// Refresh onboarding status after completion
|
||||
await checkOnboardingStatus();
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minHeight="100vh"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
minHeight="100vh"
|
||||
gap={2}
|
||||
>
|
||||
<CircularProgress size={60} />
|
||||
<Typography variant="h6" sx={{ mt: 2 }}>
|
||||
Loading Alwrity...
|
||||
<Typography variant="h6" color="textSecondary">
|
||||
Checking onboarding status...
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
@@ -82,156 +82,110 @@ const App: React.FC = () => {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minHeight="100vh"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
minHeight="100vh"
|
||||
gap={2}
|
||||
p={3}
|
||||
>
|
||||
<Typography variant="h6" color="error">
|
||||
<Typography variant="h5" color="error" gutterBottom>
|
||||
Error
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" textAlign="center">
|
||||
{error}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mt: 1 }}>
|
||||
Please refresh the page to try again.
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect based on onboarding status
|
||||
if (onboardingComplete) {
|
||||
return <Navigate to="/dashboard" replace />;
|
||||
} else {
|
||||
return <Navigate to="/onboarding" replace />;
|
||||
}
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
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();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
minHeight="100vh"
|
||||
gap={2}
|
||||
>
|
||||
<CircularProgress size={60} />
|
||||
<Typography variant="h6" color="textSecondary">
|
||||
Connecting to ALwrity...
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
minHeight="100vh"
|
||||
gap={2}
|
||||
p={3}
|
||||
>
|
||||
<Typography variant="h5" color="error" gutterBottom>
|
||||
Connection Error
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" textAlign="center">
|
||||
{error}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" textAlign="center">
|
||||
Please ensure the backend server is running and try refreshing the page.
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
{/* Dashboard Route */}
|
||||
<Route
|
||||
path="/dashboard"
|
||||
element={
|
||||
<DashboardWrapper />
|
||||
}
|
||||
/>
|
||||
|
||||
{/* SEO Dashboard Route */}
|
||||
<Route
|
||||
path="/seo-dashboard"
|
||||
element={
|
||||
<SEODashboard />
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Content Planning Dashboard Route */}
|
||||
<Route
|
||||
path="/content-planning"
|
||||
element={
|
||||
<ContentPlanningDashboard />
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Root Route - Show onboarding or redirect to dashboard */}
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
onboardingStatus?.onboarding_required ? (
|
||||
<Wizard onComplete={handleOnboardingComplete} />
|
||||
) : (
|
||||
<Navigate to="/dashboard" replace />
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Catch all other routes */}
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
<CopilotKit
|
||||
publicApiKey={process.env.REACT_APP_COPILOTKIT_API_KEY}
|
||||
showDevConsole={false}
|
||||
onError={(e) => console.error("CopilotKit Error:", e)}
|
||||
>
|
||||
<Router>
|
||||
<ConditionalCopilotKit>
|
||||
<Routes>
|
||||
<Route path="/" element={<InitialRouteHandler />} />
|
||||
<Route path="/onboarding" element={<Wizard />} />
|
||||
<Route path="/dashboard" element={<MainDashboard />} />
|
||||
<Route path="/seo" element={<SEODashboard />} />
|
||||
<Route path="/content-planning" element={<ContentPlanningDashboard />} />
|
||||
</Routes>
|
||||
</ConditionalCopilotKit>
|
||||
</Router>
|
||||
</CopilotKit>
|
||||
);
|
||||
};
|
||||
|
||||
// Separate component to handle dashboard logic
|
||||
const DashboardWrapper: React.FC = () => {
|
||||
const [dashboardLoading, setDashboardLoading] = useState(true);
|
||||
const [onboardingComplete, setOnboardingComplete] = useState(false);
|
||||
const [retryCount, setRetryCount] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const checkDashboardAccess = async () => {
|
||||
try {
|
||||
console.log('DashboardWrapper: Checking dashboard access...');
|
||||
// Check if onboarding is complete
|
||||
const response = await apiClient.get('/api/onboarding/status');
|
||||
const status = response.data;
|
||||
|
||||
console.log('DashboardWrapper: Backend status:', status);
|
||||
console.log('DashboardWrapper: is_completed:', status.is_completed);
|
||||
console.log('DashboardWrapper: current_step:', status.current_step);
|
||||
|
||||
if (status.is_completed) {
|
||||
console.log('DashboardWrapper: Onboarding is complete, showing dashboard');
|
||||
setOnboardingComplete(true);
|
||||
} else {
|
||||
console.log('DashboardWrapper: Onboarding not complete, retry count:', retryCount);
|
||||
|
||||
// If onboarding is not complete, try a few times with delay
|
||||
if (retryCount < 3) {
|
||||
console.log('DashboardWrapper: Retrying in 1 second...');
|
||||
setTimeout(() => {
|
||||
setRetryCount(prev => prev + 1);
|
||||
}, 1000);
|
||||
return;
|
||||
} else {
|
||||
console.log('DashboardWrapper: Max retries reached, redirecting to root');
|
||||
// If onboarding is not complete after retries, redirect to root
|
||||
window.location.href = '/';
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('DashboardWrapper: Error checking dashboard access:', error);
|
||||
|
||||
// If there's an error, try a few times before redirecting
|
||||
if (retryCount < 3) {
|
||||
console.log('DashboardWrapper: Error occurred, retrying in 1 second...');
|
||||
setTimeout(() => {
|
||||
setRetryCount(prev => prev + 1);
|
||||
}, 1000);
|
||||
return;
|
||||
} else {
|
||||
console.log('DashboardWrapper: Max retries reached after error, redirecting to root');
|
||||
// If there's an error after retries, redirect to root
|
||||
window.location.href = '/';
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
setDashboardLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
checkDashboardAccess();
|
||||
}, [retryCount]);
|
||||
|
||||
if (dashboardLoading) {
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minHeight="100vh"
|
||||
flexDirection="column"
|
||||
>
|
||||
<CircularProgress size={60} />
|
||||
<Typography variant="h6" sx={{ mt: 2 }}>
|
||||
Loading Dashboard...
|
||||
</Typography>
|
||||
{retryCount > 0 && (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
|
||||
Checking onboarding status... (Attempt {retryCount + 1}/3)
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (!onboardingComplete) {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return <MainDashboard />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user