Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"date-fns": "^2.30.0",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"lucide-react": "^0.542.0",
"monaco-editor": "^0.43.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
165 changes: 66 additions & 99 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,105 +16,70 @@ import SharedProjects from './pages/SharedProjects';
import Settings from './pages/Settings';
import NotFound from './pages/NotFound';
import ResetPassword from './pages/ResetPassword';
import GettingStarted from './pages/GettingStarted';

// Components
import ProtectedRoute from './components/Shared/ProtectedRoute';
import Layout from './components/Shared/Layout';
import ErrorBoundary from './components/Shared/ErrorBoundary';
import { LoadingSpinner } from './components/Shared/LoadingStates';

const App = () => {
const { loading, currentUser } = useContext(AuthContext);
const { isDarkMode } = useTheme();

if (loading) {
return (
<LoadingScreen $isDarkMode={isDarkMode} role="status" aria-live="polite" aria-label="Initializing CodeConclave">
<LoadingSpinner size="50px" label="Initializing CodeConclave" />
<LoadingText $isDarkMode={isDarkMode}>Initializing CodeConclave</LoadingText>
<LoadingSubtext $isDarkMode={isDarkMode}>Almost there...</LoadingSubtext>
<LoadingScreen $isDarkMode={isDarkMode}>
<Spinner />
<LoadingText>Initializing CodeConclave</LoadingText>
<LoadingSubtext>Almost there...</LoadingSubtext>
</LoadingScreen>
);
}

return (
<ErrorBoundary fallbackMessage="The application encountered an unexpected error. Please refresh the page to continue.">
<Routes>
{/* Landing Page - The initial animated page */}
<Route path="/" element={
currentUser ? <Navigate to="/dashboard" replace /> : <LandingPage />
} />

{/* Home Page - With Login/Register tabs */}
<Route path="/home" element={
currentUser ? <Navigate to="/dashboard" replace /> : (
<ErrorBoundary fallbackMessage="Error loading authentication page.">
<HomePage />
</ErrorBoundary>
)
} />

{/* Password Reset */}
<Route path="/reset-password" element={
currentUser ? <Navigate to="/dashboard" replace /> : (
<ErrorBoundary fallbackMessage="Error loading password reset page.">
<ResetPassword />
</ErrorBoundary>
)
} />

{/* Auth Routes (for direct access) */}
<Route path="/login" element={
currentUser ? <Navigate to="/dashboard" replace /> : (
<ErrorBoundary fallbackMessage="Error loading login page.">
<Login />
</ErrorBoundary>
)
} />
<Route path="/register" element={
currentUser ? <Navigate to="/dashboard" replace /> : (
<ErrorBoundary fallbackMessage="Error loading registration page.">
<Register />
</ErrorBoundary>
)
} />

{/* Protected Routes */}
<Route path="/" element={
<ProtectedRoute>
<ErrorBoundary fallbackMessage="Error loading main application layout.">
<Layout />
</ErrorBoundary>
</ProtectedRoute>
}>
<Route path="dashboard" element={
<ErrorBoundary fallbackMessage="Error loading dashboard. Please try refreshing the page.">
<Dashboard />
</ErrorBoundary>
} />
<Route path="projects/:projectId" element={
<ErrorBoundary fallbackMessage="Error loading project editor. Please try reopening the project.">
<ProjectEditor />
</ErrorBoundary>
} />
<Route path="shared" element={
<ErrorBoundary fallbackMessage="Error loading shared projects.">
<SharedProjects />
</ErrorBoundary>
} />
<Route path="settings" element={
<ErrorBoundary fallbackMessage="Error loading settings page.">
<Settings />
</ErrorBoundary>
} />
</Route>

<Routes>
{/* Landing Page */}
<Route
path="/"
element={currentUser ? <Navigate to="/getting-started" replace /> : <LandingPage />}
/>

{/* Home Page */}
<Route
path="/home"
element={currentUser ? <Navigate to="/getting-started" replace /> : <HomePage />}
/>

{/* Password Reset */}
<Route
path="/reset-password"
element={currentUser ? <Navigate to="/getting-started" replace /> : <ResetPassword />}
/>

{/* Auth Routes */}
<Route
path="/login"
element={currentUser ? <Navigate to="/getting-started" replace /> : <Login />}
/>
<Route
path="/register"
element={currentUser ? <Navigate to="/getting-started" replace /> : <Register />}
/>

{/* Protected Routes */}
<Route element={<ProtectedRoute><Layout /></ProtectedRoute>}>
<Route path="dashboard" element={<Dashboard />} />
<Route path="getting-started" element={<GettingStarted />} />
<Route path="projects/:projectId" element={<ProjectEditor />} />
<Route path="shared" element={<SharedProjects />} />
<Route path="settings" element={<Settings />} />
<Route path="*" element={<NotFound />} />
</Routes>
</ErrorBoundary>
</Route>
</Routes>
);
};

// Loading styles
const LoadingScreen = styled.div`
display: flex;
flex-direction: column;
Expand All @@ -126,34 +91,36 @@ const LoadingScreen = styled.div`
: 'linear-gradient(to bottom, #f8fafc, #e2e8f0)'
};
color: ${props => props.$isDarkMode ? 'white' : '#1a202c'};
transition: background 0.3s ease, color 0.3s ease;
`;

const LoadingText = styled.h2`
font-size: 1.5rem;
margin: 1rem 0 0.5rem 0;
font-weight: 600;
color: ${props => props.$isDarkMode ? 'white' : '#2d3748'};
transition: color 0.3s ease;
margin-top: 1rem;
margin-bottom: 0.5rem;
const Spinner = styled.div`
width: 50px;
height: 50px;
border: 5px solid rgba(0,0,0,0.1);
border-radius: 50%;
border-top-color: #3182ce;
animation: spin 1s ease-in-out infinite;
margin-bottom: 20px;

@keyframes spin {
to { transform: rotate(360deg); }
}
`;

const LoadingText = styled.p`
font-size: 1.2rem;
color: #4a5568;
`;

const LoadingSubtext = styled.p`
font-size: 1rem;
margin: 0;
color: ${props => props.$isDarkMode ? 'rgba(255, 255, 255, 0.8)' : '#718096'};
color: #718096;
animation: pulse 2s ease-in-out infinite;
transition: color 0.3s ease;


@keyframes pulse {
0%, 100% {
opacity: ${props => props.$isDarkMode ? '0.8' : '0.9'};
}
50% {
opacity: ${props => props.$isDarkMode ? '0.4' : '0.5'};
}
0%, 100% { opacity: 0.9; }
50% { opacity: 0.5; }
}
`;

export default App;
export default App;
2 changes: 1 addition & 1 deletion src/components/Auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const Login = props => {

try {
await login(formData);
navigate('/dashboard');
navigate('/getting-started');
} catch (err) {
console.error('Login error:', err);
} finally {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Auth/Register.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const Register = () => {
// Remove confirmPassword before sending to API
const { confirmPassword, ...userData } = formData;
await register(userData);
navigate('/dashboard');
navigate('/getting-started');
} catch (err) {
console.error('Registration error:', err);
// Error is handled by AuthContext
Expand Down
21 changes: 21 additions & 0 deletions src/components/Settings/GoogleDriveIntegration.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const GoogleDriveIntegration = ({ onConnected }) => {
return (
<div style={{ marginTop: "16px" }}>
<button
onClick={onConnected}
style={{
padding: "10px 16px",
backgroundColor: "#3182ce",
color: "white",
border: "none",
borderRadius: "6px",
cursor: "pointer",
}}
>
Connect to Google Drive
</button>
</div>
);
};

export default GoogleDriveIntegration;
18 changes: 18 additions & 0 deletions src/components/Settings/Settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
FaTimes,
} from 'react-icons/fa';
import { useLocation } from 'react-router-dom';
import GoogleDriveIntegration from "./GoogleDriveIntegration";


const Settings = () => {
const { currentUser } = useContext(AuthContext);
Expand Down Expand Up @@ -199,9 +201,11 @@ const Settings = () => {
};

const handleGoogleDriveCallback = async () => {
const handleGoogleDriveCallback = async () => {
const urlParams = new URLSearchParams(window.location.search);
const googleDriveStatusParam = urlParams.get('googleDrive');
const tokensParam = urlParams.get('tokens');
const authCode = urlParams.get('code');

if (googleDriveStatusParam === 'connected' && tokensParam) {
try {
Expand Down Expand Up @@ -236,6 +240,20 @@ const Settings = () => {
}
return false; // Not a callback
};
if (authCode) {
try {
const tokens = await googleDriveService.exchangeCodeForTokens(authCode);
setGoogleDriveSuccess('Google Drive connected successfully!');
setGoogleDriveStatus({ isConnected: true, tokenExpiry: tokens.expiry_date });
} catch (error) {
setGoogleDriveError('Failed to complete Google Drive connection');
} finally {
window.history.replaceState({}, document.title, window.location.pathname);
}
return true;
}
return false;
};

const handleInputChange = e => {
const { name, value, type, checked } = e.target;
Expand Down
Loading