diff --git a/.env.sample b/.env.sample index 2d62b532..f46b3e57 100644 --- a/.env.sample +++ b/.env.sample @@ -2,6 +2,7 @@ MONGO_URL=mongodb://localhost:27017 PORT=8080 REFRESH_TOKEN_COOKIE_EXPIRE=30 REFRESH_TOKEN_SECRET=XYZ + GOOGLE_CLIENT_ID=your_client_id GOOGLE_CLIENT_SECRET=your_client_secret JWT_SECRET=yourSecretKey diff --git a/client/src/App.js b/client/src/App.js index f4c79b13..a12b8655 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -33,6 +33,7 @@ import CustomerProfile from './Pages/CustomerProfile.jsx'; import GiftCards from './Pages/GiftCards.jsx'; import Careers from './Pages/Careers.jsx'; import NotFound from './Pages/NotFound.jsx'; +import ResetPassword from "./Pages/ResetPassword.jsx"; function App() { const [darkMode, setDarkMode] = useState(false); @@ -79,6 +80,7 @@ function App() { } /> } /> } /> + } /> } /> {/* Fallback route */} diff --git a/client/src/Components/index.js b/client/src/Components/index.js deleted file mode 100644 index 2820e9f1..00000000 --- a/client/src/Components/index.js +++ /dev/null @@ -1,11 +0,0 @@ - -import Profile from "./CustomerProfile/Profile.jsx"; -import Product from "./Product.jsx" -import Footer from "./Footer/Footer.jsx" -import Navbar from "./NavBar/NavBar.jsx" -export { - Profile, - Product, - Footer, - Navbar -} \ No newline at end of file diff --git a/client/src/Pages/Home.jsx b/client/src/Pages/Home.jsx index 85af771f..f1aa4968 100644 --- a/client/src/Pages/Home.jsx +++ b/client/src/Pages/Home.jsx @@ -9,7 +9,7 @@ import Newarrivals from "./Newarrivals"; import Review from "./Review"; import Trending from "../Components/Trending"; import Book from "../Components/Card/Book"; -import Review from './Review'; +//import Review from './Review'; const Home = () => { const [isLoading, setIsLoading] = useState(true); diff --git a/client/src/Pages/LoginPage.jsx b/client/src/Pages/LoginPage.jsx index 454f3aae..a0880325 100644 --- a/client/src/Pages/LoginPage.jsx +++ b/client/src/Pages/LoginPage.jsx @@ -137,11 +137,21 @@ const LoginPage = () => { {/* Google Login Button */} -
-

- Don't have an account?{' '} Sign up -

-
+
+ {/* Sign up link */} +

+ Don't have an account?{' '} Sign up +

+ + {/* Add spacing between the two sections */} +
{/* This adds vertical spacing */} + + {/* Forgot password link */} +

+ Forgot Password?{' '} Reset here {/* Update link if necessary */} +

+
+ diff --git a/client/src/Pages/ResetPassword.jsx b/client/src/Pages/ResetPassword.jsx new file mode 100644 index 00000000..432855b6 --- /dev/null +++ b/client/src/Pages/ResetPassword.jsx @@ -0,0 +1,179 @@ +import React, { useState } from "react"; +import TextField from "@mui/material/TextField"; +import Button from "@mui/material/Button"; +import IconButton from "@mui/material/IconButton"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; +import { Box, Container, Grid, Typography } from "@mui/material"; +import Lottie from "lottie-react"; +import loginAnimation from "../Lottie-animation/loginAnimation.json"; // Ensure the animation is correct for this page +import axios from "axios"; +import toast, { Toaster } from "react-hot-toast"; +import { Link, useNavigate } from "react-router-dom"; + +const ResetPassword = () => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); // Added state for confirm password + const [showPassword, setShowPassword] = useState(false); // State for toggling password visibility + const [error, setError] = useState(""); + let navigate = useNavigate(); + + const handleSubmit = async (e) => { + e.preventDefault(); + + // Reset error state + setError(""); + + // Validate that password and confirmPassword match + if (password !== confirmPassword) { + setError("Re-enter password does not match with new password!"); + return; + } + + try { + const response = await axios.post("http://localhost:8080/customer/resetpassword", { + email, + newPassword: password, // Send new password + }); + toast.success("Password reset successfully!"); + navigate("/login", { replace: true }); + } catch (err) { + // Handle different error responses + if (err.response && err.response.data && err.response.data.error) { + setError(err.response.data.error); + } else { + setError("An unexpected error occurred."); + } + } + }; + + // Function to toggle password visibility + const togglePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + return ( + <> + + +
+ + + + + + + + {/* Add a Box with hover effect */} + +
+ + + Book4U + + + + Reset Password + + setEmail(e.target.value)} + margin="normal" + fullWidth // Use fullWidth for consistent sizing + required + /> + + setPassword(e.target.value)} + margin="normal" + fullWidth // Use fullWidth for consistent sizing + required + /> + + {showPassword ? : } + + + + setConfirmPassword(e.target.value)} + margin="normal" + fullWidth // Use fullWidth for consistent sizing + required + /> + + {showPassword ? : } + + + {error && ( + + {error} + + )} + + + Go to Login + + +
+
+
+
+
+ + ); +}; + +export default ResetPassword; diff --git a/controllers/customerController.js b/controllers/customerController.js index cde40bdc..6563432d 100644 --- a/controllers/customerController.js +++ b/controllers/customerController.js @@ -6,6 +6,8 @@ const errorHandler = require("../utils/errorHandler"); const responseHandler = require("../utils/responseHandler"); const jwt = require("jsonwebtoken"); require("dotenv").config(); +const bcrypt = require("bcryptjs"); +const saltRounds = 10; const validator = require("validator"); const disposableEmailDomains = require("disposable-email-domains"); @@ -204,6 +206,8 @@ exports.getCustomerDetails = catchAsyncErrors(async (req, res, next) => { }); }); + + // UPDATE CUSTOMER PASSWORD exports.updatePassword = catchAsyncErrors(async (req, res, next) => { const customer = await Customer.findById(req.user.id).select("+password"); @@ -213,17 +217,11 @@ exports.updatePassword = catchAsyncErrors(async (req, res, next) => { ); if (!isPasswordMatched) { - return res - .status(404) - .send( - errorHandler(404, "Bad Request", "Please enter the correct password") - ); + return next(new ErrorHandler("Old password is incorrect", 400)); } if (req.body.newPassword !== req.body.confirmPassword) { - return res - .status(404) - .send(errorHandler(404, "Bad Request", "Password do not match")); + return next(new ErrorHandler("Password does not match", 400)); } customer.password = req.body.newPassword; @@ -233,6 +231,8 @@ exports.updatePassword = catchAsyncErrors(async (req, res, next) => { sendToken(customer, 200, res); }); + + // UPDATE CUSTOMER PROFILE exports.updateProfile = catchAsyncErrors(async (req, res, next) => { const newCustomerData = { @@ -261,6 +261,35 @@ exports.updateProfile = catchAsyncErrors(async (req, res, next) => { }); }); +//RESET Password +exports.resetPassword = async (req, res) => { + const { email, newPassword } = req.body; // expecting newPassword + + if (!newPassword) { + return res.status(400).json({ error: 'New password is required.' }); + } + + try { + const hashedPassword = await bcrypt.hash(newPassword, saltRounds); // Hash new password + + const updatedUser = await Customer.findOneAndUpdate( + { email }, // Find user by email + { $set: { password: hashedPassword } }, // Set new hashed password + { new: true } + ); + + if (updatedUser) { + return res.json({ message: 'Password updated successfully.' }); + } else { + return res.status(404).json({ error: 'User not found.' }); + } + } catch (error) { + console.error('Error updating password:', error.message); + res.status(500).json({ error: 'Internal Server Error' }); + } +}; + + exports.addFeedback = catchAsyncErrors(async (req, res, next) => { const { feedback, topic } = req.body; const newFeedback = await Feedback.create({ diff --git a/index.js b/index.js index 9355b814..9d52231c 100644 --- a/index.js +++ b/index.js @@ -181,4 +181,4 @@ function closeModal() { function purchaseBook() { alert('Book purchased!'); closeModal(); -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 70beb7af..b5771433 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2330,6 +2330,31 @@ "node": ">= 0.8" } }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -2444,6 +2469,15 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", diff --git a/routes/customerRoutes.js b/routes/customerRoutes.js index 4bd14b4f..2ee19fc1 100644 --- a/routes/customerRoutes.js +++ b/routes/customerRoutes.js @@ -6,9 +6,8 @@ const { updatePassword, updateProfile, logoutCustomer, - addFeedback - - + addFeedback, + resetPassword } = require("../controllers/customerController.js"); const { addTocart, @@ -32,8 +31,7 @@ router.route("/password/update").put(isAuthenticatedUser, updatePassword); router.route("/me/update").put(isAuthenticatedUser, updateProfile); - - +router.route("/resetpassword").post(resetPassword); //cart routes @@ -56,4 +54,4 @@ router.route("/cart").get(isAuthenticatedUser,getCartItems); //giving feedback router.route("/add-feedback").post(isAuthenticatedUser,addFeedback); -module.exports = router; \ No newline at end of file +module.exports = router;