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
59 changes: 52 additions & 7 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,68 @@
import { Routes, Route } from "react-router-dom";
import { useEffect } from "react";
import ProtectedRoute from "./components/auth/ProtectedRoute";
import { useDispatch } from "react-redux";
import { checkAuthStatus } from "./store/slices/authSlice";
// pages
import Login from "./pages/Login";
import Register from "./pages/Register";
import CreateNote from "./pages/CreateNote";
import ViewNotes from "./pages/ViewNotes";
import Navbar from "./components/Navbar";

const App = () => {
const dispatch = useDispatch();

useEffect(() => {
dispatch(checkAuthStatus());
}, [dispatch]);

return (
<div className="min-h-screen bg-gray-50">
<Navbar />
<main className="container mx-auto px-4 py-8">

<Routes>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
{/* <Routes>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />

<Route path="/notes" element={<ViewNotes />} />
<Route path="/" element={<CreateNote />} />
</Routes> */}
<Routes>
<Route
path="/"
element={
<ProtectedRoute requireAuth={true}>
<CreateNote />
</ProtectedRoute>
}
/>
<Route
path="/notes"
element={
<ProtectedRoute requireAuth={true}>
<ViewNotes />
</ProtectedRoute>
}
/>

<Route path="/notes" element={<ViewNotes />} />
<Route path="/" element={<CreateNote />} />
</Routes>
<Route
path="/login"
element={
<ProtectedRoute requireAuth={false}>
<Login />
</ProtectedRoute>
}
/>
<Route
path="/register"
element={
<ProtectedRoute requireAuth={false}>
<Register />
</ProtectedRoute>
}
/>
</Routes>
</main>
</div>
);
Expand Down
11 changes: 7 additions & 4 deletions src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const Navbar = () => {
</Link>

<div className="flex gap-4">

{isAuthenticated ? (
<>
<Link
to="/"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
Expand Down Expand Up @@ -66,8 +67,9 @@ const Navbar = () => {
<LogOut size={18} />
<span>Logout</span>
</button>


</>
) : (
<>
<Link
to="/login"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
Expand All @@ -91,7 +93,8 @@ const Navbar = () => {
<UserPlus size={18} />
<span>Register</span>
</Link>

</>
)}
</div>
</nav>
</div>
Expand Down
11 changes: 9 additions & 2 deletions src/components/auth/ProtectedRoute.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Navigate, useLocation } from "react-router-dom";
import { useSelector } from "react-redux";

const ProtectedRoute = ({ children, requireAuth }) => {
const { isAuthenticated, loading } = useSelector((state) => state.auth);
const { isAuthenticated, status } = useSelector((state) => state.auth);
const loading = status === "loading";

// Show loading state while checking authentication
if (loading) {
Expand All @@ -17,10 +18,16 @@ const ProtectedRoute = ({ children, requireAuth }) => {
}

// TODO: If route requires authentication and user is not authenticated, redirect to login


if (requireAuth && !isAuthenticated) {
return <Navigate to="/login" replace />;
}

//TODO: If route requires unauthenticated user and user is authenticated, redirect to notes

if (!requireAuth && isAuthenticated) {
return <Navigate to="/notes" replace />;
}

// Otherwise, render the children
return children;
Expand Down
78 changes: 71 additions & 7 deletions src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { Link } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { login, clearError } from "../store/slices/authSlice";
import { loginSchema } from "../schema/authSchema";

const Login = () => {
const dispatch = useDispatch();
const navigate = useNavigate();

const { isAuthenticated, loading, error } = useSelector(
(state) => state.auth
);

const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(loginSchema),
defaultValues: {
email: "",
password: "",
},
});

const onSubmit = (data) => {
dispatch(login(data));
};

useEffect(() => {
if (isAuthenticated) {
navigate("/notes");
}
}, [isAuthenticated, navigate]);

useEffect(() => {
return () => {
dispatch(clearError());
};
}, [dispatch]);

return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md">
<h2 className="text-2xl font-bold text-center text-gray-800 mb-6">
Login to Your Account
</h2>

<form className="space-y-4">
<form className="space-y-4" onSubmit={handleSubmit(onSubmit)}>
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +61,17 @@ const Login = () => {
<input
type="email"
id="email"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500"
{...register("email")}
className={`mt-1 block w-full rounded-md border ${
errors.email ? "border-red-500" : "border-gray-300"
} shadow-sm focus:border-yellow-500 focus:ring-yellow-500`}
placeholder="Enter your email"
/>
{errors.email && (
<p className="text-red-600 text-sm mt-1">
{errors.email.message}
</p>
)}
</div>

<div>
Expand All @@ -32,24 +84,36 @@ const Login = () => {
<input
type="password"
id="password"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500"
{...register("password")}
className={`mt-1 block w-full rounded-md border ${
errors.password ? "border-red-500" : "border-gray-300"
} shadow-sm focus:border-yellow-500 focus:ring-yellow-500`}
placeholder="Enter your password"
/>
{errors.password && (
<p className="text-red-600 text-sm mt-1">
{errors.password.message}
</p>
)}
</div>

{error && <p className="text-red-600 text-sm">{error}</p>}
<button
type="submit"
className="w-full bg-yellow-500 text-white py-2 px-4 rounded-md hover:bg-yellow-600 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2"
disabled={loading}
>
Login
{loading ? "Logging in..." : "Login"}
</button>
</form>

<p className="mt-4 text-center text-sm text-gray-600">
Don't have an account?{" "}
<a href="/register" className="text-yellow-600 hover:text-yellow-500">
<Link
to="/register"
className="text-yellow-600 hover:text-yellow-500"
>
Register here
</a>
</Link>
</p>
</div>
</div>
Expand Down
83 changes: 75 additions & 8 deletions src/pages/Register.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,54 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate, Link } from "react-router-dom";
import { useEffect } from "react";
// import { Link } from "react-router-dom";
import { registerSchema } from "../schema/authSchema";
import {
register as registerThunk,
clearError,
} from "../store/slices/authSlice";

const Register = () => {
const dispatch = useDispatch();
const navigate = useNavigate();

const { isAuthenticated, loading, error } = useSelector(
(state) => state.auth
);

const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(registerSchema),
});

const onSubmit = async (data) => {
const resultAction = await dispatch(registerThunk(data));
if (registerThunk.fulfilled.match(resultAction)) {
reset();
navigate("/notes");
}
};

useEffect(() => {
return () => {
dispatch(clearError());
};
}, [dispatch]);

return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-md">
<h2 className="text-2xl font-bold text-center text-gray-800 mb-6">
Create an Account
</h2>

<form className="space-y-4">
<form className="space-y-4" onSubmit={handleSubmit(onSubmit)}>
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +59,17 @@ const Register = () => {
<input
type="email"
id="email"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500"
{...register("email")}
className={`mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 ${
errors.email ? "border-red-500" : ""
}`}
placeholder="Enter your email"
/>
{errors.email && (
<p className="text-sm text-red-600 mt-1">
{errors.email.message}
</p>
)}
</div>

<div>
Expand All @@ -32,9 +82,17 @@ const Register = () => {
<input
type="password"
id="password"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500"
{...register("password")}
className={`mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 ${
errors.password ? "border-red-500" : ""
}`}
placeholder="Enter your password"
/>
{errors.password && (
<p className="text-sm text-red-600 mt-1">
{errors.password.message}
</p>
)}
</div>

<div>
Expand All @@ -47,24 +105,33 @@ const Register = () => {
<input
type="password"
id="confirmPassword"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500"
{...register("confirmPassword")}
className={`mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 ${
errors.confirmPassword ? "border-red-500" : ""
}`}
placeholder="Confirm your password"
/>
{errors.confirmPassword && (
<p className="text-sm text-red-600 mt-1">
{errors.confirmPassword.message}
</p>
)}
</div>

{error && <p className="text-sm text-red-600 text-center">{error}</p>}
<button
type="submit"
disabled={loading}
className="w-full bg-yellow-500 text-white py-2 px-4 rounded-md hover:bg-yellow-600 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2"
>
Create Account
{loading ? "Creating Account..." : "Create Account"}
</button>
</form>

<p className="mt-4 text-center text-sm text-gray-600">
Already have an account?{" "}
<a href="/login" className="text-yellow-600 hover:text-yellow-500">
<Link to="/login" className="text-yellow-600 hover:text-yellow-500">
Login here
</a>
</Link>
</p>
</div>
</div>
Expand Down
Loading