Skip to content
Open

done #24

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
24 changes: 17 additions & 7 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@ 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 { checkAuthStatus } from "./store/slices/authSlice";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";

const App = () => {
const dispatch = useDispatch();
const { status } = useSelector((state) => state.auth);

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>
<Route path="/login" element={<ProtectedRoute requireAuth={false}><Login /></ProtectedRoute>} />
<Route path="/register" element={<ProtectedRoute requireAuth={false}><Register /></ProtectedRoute>} />
<Route path="/notes" element={<ProtectedRoute requireAuth={true}><ViewNotes /></ProtectedRoute>} />
<Route path="/" element={<ProtectedRoute requireAuth={true}><CreateNote /></ProtectedRoute>} />
</Routes>
</main>
</div>
);
Expand Down
14 changes: 9 additions & 5 deletions src/components/auth/ProtectedRoute.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Navigate, useLocation } from "react-router-dom";
import { Navigate } 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);

// Show loading state while checking authentication
if (loading) {
if (status === "loading") {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<div className="text-center">
Expand All @@ -17,10 +17,14 @@ 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
63 changes: 60 additions & 3 deletions src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,53 @@


import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { login } from "../store/slices/authSlice";

const loginSchema = z.object({
email: z.string().email("valid email is required"),
password: z.string().min(6, "Password must be at least 6 characters"),
});




const Login = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { status, error } = useSelector((state) => state.auth);
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(loginSchema),
});

const onSubmit = async (data) => {
try {
await dispatch(login(data)).unwrap();
navigate("/notes");
} catch (err) {
console.error("Failed to login:", err);
}
};

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 && (
<div className="mb-4 rounded-md bg-red-50 p-4 text-red-600">
{error}
</div>
)}
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +58,15 @@ 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="mt-1 text-sm text-red-600">
{errors.email.message}
</p>
)}
</div>

<div>
Expand All @@ -32,16 +79,26 @@ 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="mt-1 text-sm text-red-600">
{errors.password.message}
</p>
)}
</div>

<button
type="submit"
disabled={status === "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"
>
Login

{status === "loading" ? "Logging in..." : "Login"}

</button>
</form>

Expand Down
66 changes: 62 additions & 4 deletions src/pages/Register.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { register as registerPage } from "../store/slices/authSlice";
import { registerSchema } from "../schema/authSchema";




const Register = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { status, error } = useSelector((state) => state.auth);

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

const onSubmit = async (data) => {
try {
await dispatch(registerPage(data)).unwrap();

navigate("/login");
} catch (err) {
console.error("Failed to register:", err);
}
};
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
Create An Account
</h2>

<form className="space-y-4">
{error && (
<div className="mb-4 rounded-md bg-red-50 p-4 text-red-600">
{error}
</div>
)}
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +53,13 @@ const Register = () => {
<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="mt-1 text-sm text-red-600">{errors.email.message}</p>
)}
</div>

<div>
Expand All @@ -29,12 +69,19 @@ const Register = () => {
>
Password
</label>

<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="mt-1 text-sm text-red-600">
{errors.password.message}
</p>
)}
</div>

<div>
Expand All @@ -47,16 +94,24 @@ 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="mt-1 text-sm text-red-600">
{errors.confirmPassword.message}
</p>
)}
</div>

<button
type="submit"
disabled={status === "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
{status === "loading" ? "Registering..." : "Create Account "}
</button>
</form>

Expand All @@ -72,3 +127,6 @@ const Register = () => {
};

export default Register;



3 changes: 2 additions & 1 deletion src/pages/ViewNotes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const ViewNotes = () => {

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

}, []);

const handleDelete = async (id) => {
if (!window.confirm("Are you sure you want to delete this note?")) {
Expand Down
Loading