Skip to content
Merged
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
10 changes: 7 additions & 3 deletions src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useDispatch } from "react-redux";
import { Button } from "../components/common/button";
import Input from "../components/common/input";
Expand All @@ -11,6 +11,7 @@ import { loginUser } from "../features/auth/authThunks";
const Login = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [searchParams] = useSearchParams();

const [email, setEmail] = useState("");
const [password, setPassword] = useState("········");
Expand All @@ -30,7 +31,10 @@ const Login = () => {
const result = await dispatch(loginUser({ email, password }));

if (result.payload) {
navigate("/dashboard");
// Check for redirect query parameter
const redirectParam = searchParams.get('redirect');
const redirectPath = redirectParam ? decodeURIComponent(redirectParam) : '/dashboard';
navigate(redirectPath);
} else if (result.payload?.message) {
setError(result.payload.message);
} else {
Expand All @@ -41,7 +45,7 @@ const Login = () => {
} finally {
setLoading(false);
}
}, [email, password, dispatch, navigate]);
}, [email, password, dispatch, navigate, searchParams]);

return (
<div className="min-h-screen w-154 sm:w-auto bg-linear-to-br from-blue-100 via-sky-100 to-blue-50 flex items-center justify-center p-4 sm:p-6">
Expand Down
13 changes: 11 additions & 2 deletions src/routes/AppRouter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Spinner from '../components/common/Spinner';
import NotFound from '../pages/NotFound';
import ErrorTest from '../components/common/ErrorTest';
import AdminRoute from './AdminRoute';
import ProtectedRoute from './ProtectedRoute';

// Auth pages
import Login from '../pages/Login';
Expand Down Expand Up @@ -40,7 +41,11 @@ const AppRouter = () => {
<Route index element={<Home />} />
<Route path="explore" element={<Explore />} />
<Route path="campaign/:id" element={<CampaignDetails />} />
<Route path="create" element={<CreateCampaign />} />
<Route path="create" element={
<ProtectedRoute>
<CreateCampaign />
</ProtectedRoute>
} />
<Route path="test-error" element={<ErrorTest />} />
</Route>

Expand All @@ -51,7 +56,11 @@ const AppRouter = () => {
<Route path="verify-email/:token" element={<VerifyEmailPage />} />

{/* App pages */}
<Route path="dashboard" element={<Dashboard />} />
<Route path="dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="admin" element={
<AdminRoute>
<Admin />
Expand Down
27 changes: 27 additions & 0 deletions src/routes/ProtectedRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Navigate, useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { selectIsAuthenticated } from '../features/auth/authSelectors';

/**
* ProtectedRoute - A higher-order route component that restricts access to authenticated users
* Redirects unauthenticated users to /login?redirect=/original-path
*
* @param {Object} props - Component props
* @param {React.ReactNode} props.children - The component to render if user is authenticated
* @returns {React.ReactElement} Either the children or a redirect to login
*/
const ProtectedRoute = ({ children }) => {
const isAuthenticated = useSelector(selectIsAuthenticated);
const location = useLocation();

// If user is not authenticated, redirect to login with redirect query param
if (!isAuthenticated) {
const redirectPath = encodeURIComponent(location.pathname + location.search);
return <Navigate to={`/login?redirect=${redirectPath}`} replace />;
}

// User is authenticated, render the protected component
return children;
};

export default ProtectedRoute;