From e05b1a25871d71b26cfefda2a20e48fcdb9cf62b Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Wed, 28 Jan 2026 04:33:28 +0530 Subject: [PATCH] Enhance onboarding process with Redux state management - Integrated Redux state management into the Onboarding component by dispatching the setOnboarded action upon completion. - Updated authSlice to include isOnboarded state, allowing for tracking of onboarding status. - Modified the Redux store configuration to persist the onboarding status alongside the token, improving user experience and state consistency. These changes streamline the onboarding process and enhance state management for user authentication. --- src/components/ProtectedRoute.tsx | 36 +++++++++++++++++++++++++++++ src/components/PublicRoute.tsx | 32 +++++++++++++++++++++++++ src/pages/onboarding/Onboarding.tsx | 4 ++++ src/store/authSlice.ts | 8 ++++++- src/store/index.ts | 4 ++-- 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/components/ProtectedRoute.tsx create mode 100644 src/components/PublicRoute.tsx 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);