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
12 changes: 1 addition & 11 deletions server/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,7 @@ router.post("/register", async (req, res) => {
const { email, password } = req.body;
const db = await getDbConnection();

// Validate input
if (!validateEmail(email)) {
return res.status(400).json({ message: "Invalid email format" });
}
if (!validatePassword(password)) {
return res.status(400).json({
message:
"Password must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one number, and one special character",
});
}


// Check if user already exists
const existingUser = await db.get("SELECT * FROM users WHERE email = ?", [
email,
Expand Down
36 changes: 32 additions & 4 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,47 @@ 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="/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="/notes" element={<ViewNotes />} />
<Route path="/" element={<CreateNote />} />
<Route path="/" element={
<ProtectedRoute requireAuth={true}>
<CreateNote />
</ProtectedRoute>
}/>
</Routes>
</main>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/CreateNoteForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const CreateNoteForm = () => {
});

const onSubmit = async (data) => {
const result = await dispatch(createNote(data));
const result = await dispatch(createNote(data)).unwrap();
if (!result.error) {
reset();
navigate("/notes");
Expand Down
60 changes: 30 additions & 30 deletions src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ const Navbar = () => {
const { isAuthenticated, user } = useSelector((state) => state.auth);

const handleLogout = () => {
dispatch(logout());
navigate("/login");
dispatch(logout()).unwrap(), navigate("/login");
};

return (
Expand All @@ -34,7 +33,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 All @@ -58,40 +58,40 @@ const Navbar = () => {
<List size={18} />
<span>View All</span>
</Link>

<button
onClick={handleLogout}
className="flex items-center gap-1 px-3 py-2 rounded-md transition-colors hover:bg-gray-100 text-gray-700"
>
<LogOut size={18} />
<span>Logout</span>
</button>


<Link
to="/login"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
pathname === "/login"
? "bg-yellow-100 text-yellow-700"
: "hover:bg-gray-100 text-gray-700"
}`}
>
<LogIn size={18} />
<span>Login</span>
</Link>

<Link
to="/register"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
pathname === "/register"
? "bg-yellow-100 text-yellow-700"
: "hover:bg-gray-100 text-gray-700"
}`}
>
<UserPlus size={18} />
<span>Register</span>
</Link>

</>
) : (
<>
<Link
to="/login"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
pathname === "/login"
? "bg-yellow-100 text-yellow-700"
: "hover:bg-gray-100 text-gray-700"
}`}
>
<LogIn size={18} />
<span>Login</span>
</Link>
<Link
to="/register"
className={`flex items-center gap-1 px-3 py-2 rounded-md transition-colors ${
pathname === "/register"
? "bg-yellow-100 text-yellow-700"
: "hover:bg-gray-100 text-gray-700"
}`}
>
<UserPlus size={18} />
<span>Register</span>
</Link>
</>
)}
</div>
</nav>
</div>
Expand Down
12 changes: 8 additions & 4 deletions src/components/auth/ProtectedRoute.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ 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, loading, 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
45 changes: 44 additions & 1 deletion src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from 'zod'
import { loginSchema } from "../schema/authSchema";
import { useSelector, useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { login as loginUser } from "../store/slices/authSlice";
import axios from "axios";

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

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

const onLogin = async (data) => {
try {
console.log(data)
const resolt = await dispatch(loginUser(data));
console.log(resolt)
// if(loginUser.fulfilled.match(resolt)) {
// navigate('/notes')
// } else {
// console.error('login Failed', resolt.payload)
// }
} catch(error) {
console.log(error)
}
};


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 onSubmit={handleSubmit(onLogin)} className="space-y-4">
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +57,11 @@ const Login = () => {
<input
type="email"
id="email"
{...registerFeild('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"
/>

</div>

<div>
Expand All @@ -32,6 +74,7 @@ const Login = () => {
<input
type="password"
id="password"
{...registerFeild('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"
/>
Expand Down
43 changes: 42 additions & 1 deletion src/pages/Register.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
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 { register as registerUser } from '../store/slices/authSlice';
import { registerSchema } from '../schema/authSchema';
import { useState } from 'react';

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

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

const onRegister = async (data) => {
try {
await dispatch(registerUser(data)).unwrap()
navigate('/login')
}catch(error) {
console.log('somthing went wrong', error)
}
};
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 onSubmit={handleSubmit(onRegister)} className="space-y-4">
<div>
<label
htmlFor="email"
Expand All @@ -17,9 +49,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 @@ -32,9 +68,13 @@ 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='mt-1 text-sm text-red-600'>{errors.password.message}</p>
)}
</div>

<div>
Expand All @@ -47,6 +87,7 @@ 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"
/>
Expand Down
1 change: 1 addition & 0 deletions src/pages/ViewNotes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const ViewNotes = () => {
const dispatch = useDispatch();
const { notes, loading, error } = useSelector((state) => state.notes);

console.log(notes)
useEffect(() => {
dispatch(fetchNotes());
}, [dispatch]);
Expand Down
10 changes: 5 additions & 5 deletions src/schema/authSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export const registerSchema = z
email: z.string().min(1, "Email is required").email("Invalid email format"),
password: z
.string()
.min(8, "Password must be at least 8 characters")
.regex(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
"Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
),
.min(8, "Password must be at least 8 characters"),
// .regex(
// /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
// "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
// ),
confirmPassword: z.string().min(1, "Please confirm your password"),
})
.refine((data) => data.password === data.confirmPassword, {
Expand Down
Loading