diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx
new file mode 100644
index 000000000..e65fb5735
--- /dev/null
+++ b/src/components/ProtectedRoute.tsx
@@ -0,0 +1,36 @@
+import { Navigate } from 'react-router-dom';
+import { useAppSelector } from '../store/hooks';
+
+interface ProtectedRouteProps {
+ children: React.ReactNode;
+ requireAuth?: boolean;
+ requireOnboarded?: boolean;
+ redirectTo?: string;
+}
+
+/**
+ * Protected route component that handles authentication and onboarding checks
+ */
+const ProtectedRoute = ({
+ children,
+ requireAuth = true,
+ requireOnboarded = false,
+ redirectTo,
+}: ProtectedRouteProps) => {
+ const token = useAppSelector((state) => state.auth.token);
+ const isOnboarded = useAppSelector((state) => state.auth.isOnboarded);
+
+ // If auth is required but user is not logged in
+ if (requireAuth && !token) {
+ return ;
+ }
+
+ // If onboarding is required but user is not onboarded
+ if (requireOnboarded && !isOnboarded) {
+ return ;
+ }
+
+ return <>{children}>;
+};
+
+export default ProtectedRoute;
diff --git a/src/components/PublicRoute.tsx b/src/components/PublicRoute.tsx
new file mode 100644
index 000000000..48071e81c
--- /dev/null
+++ b/src/components/PublicRoute.tsx
@@ -0,0 +1,32 @@
+import { Navigate } from 'react-router-dom';
+import { useAppSelector } from '../store/hooks';
+
+interface PublicRouteProps {
+ children: React.ReactNode;
+ redirectTo?: string;
+}
+
+/**
+ * Public route component that redirects authenticated users
+ * If logged in and onboarded -> redirect to /home
+ * If logged in but not onboarded -> redirect to /onboarding
+ */
+const PublicRoute = ({ children, redirectTo }: PublicRouteProps) => {
+ const token = useAppSelector((state) => state.auth.token);
+ const isOnboarded = useAppSelector((state) => state.auth.isOnboarded);
+
+ // If user is logged in and onboarded, redirect to home
+ if (token && isOnboarded) {
+ return ;
+ }
+
+ // If user is logged in but not onboarded, redirect to onboarding
+ if (token && !isOnboarded) {
+ return ;
+ }
+
+ // User is not logged in, show public route
+ return <>{children}>;
+};
+
+export default PublicRoute;
diff --git a/src/pages/onboarding/Onboarding.tsx b/src/pages/onboarding/Onboarding.tsx
index 5223883bd..91594be4a 100644
--- a/src/pages/onboarding/Onboarding.tsx
+++ b/src/pages/onboarding/Onboarding.tsx
@@ -1,5 +1,7 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
+import { useAppDispatch } from '../../store/hooks';
+import { setOnboarded } from '../../store/authSlice';
import ProgressIndicator from '../../components/ProgressIndicator';
import LottieAnimation from '../../components/LottieAnimation';
import FeaturesStep from './steps/FeaturesStep';
@@ -10,6 +12,7 @@ import GetStartedStep from './steps/GetStartedStep';
const Onboarding = () => {
const navigate = useNavigate();
+ const dispatch = useAppDispatch();
const [currentStep, setCurrentStep] = useState(1);
const totalSteps = 5;
@@ -29,6 +32,7 @@ const Onboarding = () => {
};
const handleComplete = () => {
+ dispatch(setOnboarded(true));
navigate('/home');
};
diff --git a/src/store/authSlice.ts b/src/store/authSlice.ts
index 17e9e6c5f..2ebbb5497 100644
--- a/src/store/authSlice.ts
+++ b/src/store/authSlice.ts
@@ -2,10 +2,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface AuthState {
token: string | null;
+ isOnboarded: boolean;
}
const initialState: AuthState = {
token: null,
+ isOnboarded: false,
};
const authSlice = createSlice({
@@ -17,9 +19,13 @@ const authSlice = createSlice({
},
clearToken: (state) => {
state.token = null;
+ state.isOnboarded = false;
+ },
+ setOnboarded: (state, action: PayloadAction) => {
+ state.isOnboarded = action.payload;
},
},
});
-export const { setToken, clearToken } = authSlice.actions;
+export const { setToken, clearToken, setOnboarded } = authSlice.actions;
export default authSlice.reducer;
diff --git a/src/store/index.ts b/src/store/index.ts
index aa726403b..c695e14bf 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -8,8 +8,8 @@ import socketReducer from './socketSlice';
const authPersistConfig = {
key: 'auth',
storage,
- // Only persist the token
- whitelist: ['token'],
+ // Persist token and onboarding status
+ whitelist: ['token', 'isOnboarded'],
};
const persistedAuthReducer = persistReducer(authPersistConfig, authReducer);