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
1,029 changes: 555 additions & 474 deletions package-lock.json

Large diffs are not rendered by default.

55 changes: 48 additions & 7 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,61 @@ import Register from "./pages/Register";
import CreateNote from "./pages/CreateNote";
import ViewNotes from "./pages/ViewNotes";
import Navbar from "./components/Navbar";
import ProtectedRoute from "./components/auth/ProtectedRoute";
import { useDispatch } from "react-redux";
import { useEffect } from "react";
import { checkAuthStatus} from "./store/slices/authSlice";

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 />} />

<Route path="/notes" element={<ViewNotes />} />
<Route path="/" element={<CreateNote />} />
</Routes>
<Routes>
{/* Public routes (only for guests) */}
<Route
path="/login"
element={
<ProtectedRoute requireAuth={false}>
<Login />
</ProtectedRoute>
}
/>
<Route
path="/register"
element={
<ProtectedRoute requireAuth={false}>
<Register />
</ProtectedRoute>
}
/>

{/* Protected routes (only for logged-in users) */}
<Route
path="/notes"
element={
<ProtectedRoute requireAuth={true}>
<ViewNotes />
</ProtectedRoute>
}
/>
<Route
path="/"
element={
<ProtectedRoute requireAuth={true}>
<CreateNote />
</ProtectedRoute>
}
/>
</Routes>
</main>
</div>
);
Expand Down
24 changes: 19 additions & 5 deletions src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const Navbar = () => {
<header className="bg-white shadow-sm">
<div className="container mx-auto px-4 py-4">
<nav className="flex items-center justify-between">
{/* Logo */}
<Link
to="/"
className="flex items-center gap-2 text-yellow-600 font-bold text-xl"
Expand All @@ -33,8 +34,13 @@ const Navbar = () => {
<span>Sticky Notes</span>
</Link>

<div className="flex gap-4">

{/* Links */}
<div className="flex items-center gap-6">
{isAuthenticated ? (
<>


{/* Authenticated links */}
<Link
to="/"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
Expand All @@ -58,6 +64,11 @@ const Navbar = () => {
<List size={18} />
<span>View All</span>
</Link>

{/* Welcome message */}
<span className="text-gray-700 font-medium">
{user?.name || user?.email || "User"}
</span>

<button
onClick={handleLogout}
Expand All @@ -66,8 +77,10 @@ const Navbar = () => {
<LogOut size={18} />
<span>Logout</span>
</button>


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

</>
)}
</div>
</nav>
</div>
Expand Down
9 changes: 8 additions & 1 deletion src/components/auth/ProtectedRoute.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ const ProtectedRoute = ({ children, requireAuth }) => {
<div className="flex items-center justify-center min-h-[60vh]">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-yellow-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading...</p>
<p className="mt-4 text-gray-600">Loading ...</p>
</div>
</div>
);
}

// 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
Expand Down
55 changes: 54 additions & 1 deletion src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,61 @@
import React from "react";
import {loginUser} from '../store/slices/authSlice'
import { useDispatch, useSelector } from "react-redux";
import {useForm} from "react-hook-form"
import {zodResolver} from '@hookform/resolvers/zod'
import {loginSchema} from '../schema/authSchema'
import { useNavigate } from "react-router-dom";

const Login = () => {

const dispatch = useDispatch();
const navigate = useNavigate();

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


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

const onSubmit = async(data) => {
try {
await dispatch(loginUser(data)).unwrap();
navigate('/notes');
} catch (error) {
console.error("Login failed:", error);
}
};

if(loading){
return (
<div className="flex items-center justify-center min-h-[60vh]">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-yellow-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading</p>
</div>
</div>
);
}




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">
{error && (
<p className="text-red-500 text-center mb-4">{error}</p>
)
}

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

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

<button
Expand Down
54 changes: 52 additions & 2 deletions src/pages/Register.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,56 @@
import React from "react";
import {registerUser} from '../store/slices/authSlice'
import { useDispatch } from "react-redux";
import {useForm} from "react-hook-form"
import {registerSchema} from '../schema/authSchema'
import {zodResolver} from '@hookform/resolvers/zod'
import { useNavigate } from "react-router-dom";


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


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

const onSubmit = async(data) => {
try {
await dispatch(registerUser(data)).unwrap();
navigate('/notes');
} catch (error) {
console.error("Login failed:", error);
}
};

if(loading){
return (
<div className="flex items-center justify-center min-h-[60vh]">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-yellow-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading</p>
</div>
</div>
);
}

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">
{error && (
<p className="text-red-500 text-center mb-4">{error}</p>
)
}

<form
onSubmit={handleSubmit(onSubmit)}
className="space-y-4">
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +61,11 @@ 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 py-3 rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500"
placeholder="Enter your email"
/>
{errors.email && <p className="text-red-500 text-sm mt-1">{errors.email.message}</p>}
</div>

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

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

<button
Expand Down
Loading