Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Forgot Password #456 #464

Merged
merged 2 commits into from
Oct 16, 2024
Merged
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
10 changes: 8 additions & 2 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ 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 ChatBotEmbed from "./Components/ChatBotEmbed.jsx";

import ResetPassword from "./Pages/ResetPassword.jsx";
import ChatBotEmbed from './chatbot';


function App() {
const [darkMode, setDarkMode] = useState(false);
Expand Down Expand Up @@ -82,12 +85,15 @@ function App() {
<Route path="/careers" element={<Careers />} />
<Route path="/giftcards" element={<GiftCards />} />
<Route path="*" element={<NotFound />} /> {/* Fallback route */}
<Route path="/reset-password" element={<ResetPassword />} />
</Routes>
<Toast position="bottom-right" />
<Footer darkMode={darkMode} /> {/* Pass darkMode prop here */}
<Preloader /> {/* Ensure Preloader is correctly styled */}
<GoToTop /> {/* Added GoToTop component */}


<ChatBotEmbed />

</div>
</Router>
);
Expand Down
19 changes: 14 additions & 5 deletions client/src/Pages/LoginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,20 @@ const LoginPage = () => {
</button>
{/* Google Login Button */}
<GoogleLogin />
<div className='w-full flex items-center justify-center mt-3 mb-3'>
<p className='text-sm text-[#060606] dark:text-white'>
Don't have an account?{' '} <Link to="/signup">Sign up</Link>
</p>
</div>
<div className='w-full flex flex-col items-start justify-start mt-3 mb-3'>
{/* Sign up link */}
<p className='text-sm text-[#060606] dark:text-white'>
Don't have an account?{' '} <Link to="/signup">Sign up</Link>
</p>

{/* Add spacing between the two sections */}
<div className="my-2"></div> {/* This adds vertical spacing */}

{/* Forgot password link */}
<p className='text-sm text-[#060606] dark:text-white'>
Forgot Password?{' '} <Link to="/reset-password">Reset here</Link> {/* Update link if necessary */}
</p>
</div>
</form>
</Grid>
</Grid>
Expand Down
179 changes: 179 additions & 0 deletions client/src/Pages/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<Toaster />
<Container maxWidth="xl">
<div style={{ marginTop: "100px", marginBottom: "180px" }}>
<Grid container spacing={2} sx={{ justifyContent: "center" }}>
<Grid item xs={12} md={6}>
<Box sx={{ display: { xs: "none", md: "block" } }}>
<Lottie animationData={loginAnimation} style={{ height: "500px" }} />
</Box>
</Grid>
<Grid item xs={12} md={4} sx={{ maxWidth: "500px" }}>
{/* Add a Box with hover effect */}
<Box
sx={{
backgroundColor: "white",
p: 4,
borderRadius: "8px",
boxShadow: 3,
transition: "transform 0.3s",
"&:hover": {
transform: "scale(1.05)", // Zoom effect on hover
},
display: "flex",
flexDirection: "column",
}}
>
<form onSubmit={handleSubmit}>
<b>
<Typography variant="h5" align="center" gutterBottom className="dark:text-white">
Book4U
</Typography>
</b>
<Typography variant="h5" align="center" gutterBottom className="dark:text-white">
Reset Password
</Typography>
<TextField
variant="outlined"
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
margin="normal"
fullWidth // Use fullWidth for consistent sizing
required
/>
<Box sx={{ position: "relative", display: "flex", alignItems: "center", width: '100%' }}>
<TextField
variant="outlined"
label="New Password"
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
margin="normal"
fullWidth // Use fullWidth for consistent sizing
required
/>
<IconButton
onClick={togglePasswordVisibility}
sx={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
height: '100%', // Make button height equal to input height
width: '40px', // Adjust width as needed
padding: '0', // Remove default padding
borderRadius: '0', // Remove border radius
}}
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? <VisibilityIcon /> : <VisibilityOffIcon />}
</IconButton>
</Box>
<Box sx={{ position: "relative", display: "flex", alignItems: "center", width: '100%' }}>
<TextField
variant="outlined"
label="Re-enter New Password"
type={showPassword ? "text" : "password"}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
margin="normal"
fullWidth // Use fullWidth for consistent sizing
required
/>
<IconButton
onClick={togglePasswordVisibility}
sx={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
height: '100%', // Make button height equal to input height
width: '40px', // Adjust width as needed
padding: '0', // Remove default padding
borderRadius: '0', // Remove border radius
}}
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? <VisibilityIcon /> : <VisibilityOffIcon />}
</IconButton>
</Box>
{error && (
<Typography color="error" align="center">
{error}
</Typography>
)}
<Button variant="contained" type="submit" fullWidth sx={{ mt: 1 }}>
Reset Password
</Button>
<Typography align="left" sx={{ mt: 2, mr: 2 }}>
<u><Link to="/login">Go to Login</Link></u>
</Typography>
</form>
</Box>
</Grid>
</Grid>
</div>
</Container>
</>
);
};

export default ResetPassword;
33 changes: 33 additions & 0 deletions controllers/customerController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -270,6 +272,37 @@ exports.updateProfile = catchAsyncErrors(async (req, res, next) => {
});
});


// Move the resetPassword function outside and export it
exports.resetPassword = async (req, res) => {
const { email, newPassword } = req.body;

if (!newPassword) {
return res.status(400).json({ error: "New password is required." });
}

try {
const saltRounds = 10; // You might want to ensure this value is defined
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({
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ console.log(process.env.sample.MONGO_URL);
/* MONGODB CONNECTION START */
const MONGO_URL = process.env.sample.MONGO_URL;


// CORS
const cors = require("cors");
app.use(
Expand Down Expand Up @@ -181,4 +182,4 @@ function closeModal() {
function purchaseBook() {
alert('Book purchased!');
closeModal();
}
}
10 changes: 9 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"cors": "^2.8.5",
"disposable-email-domains": "^1.0.62",
"dotenv": "^16.4.5",
"env": "^0.0.2",
"express": "^4.21.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^7.6.11",
Expand Down
8 changes: 4 additions & 4 deletions routes/customerRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const {
updatePassword,
updateProfile,
logoutCustomer,
addFeedback
addFeedback,
resetPassword


} = require("../controllers/customerController.js");
Expand All @@ -32,8 +33,7 @@ router.route("/password/update").put(isAuthenticatedUser, updatePassword);

router.route("/me/update").put(isAuthenticatedUser, updateProfile);



router.route("/resetpassword").post(resetPassword);


//cart routes
Expand All @@ -56,4 +56,4 @@ router.route("/cart").get(isAuthenticatedUser,getCartItems);

//giving feedback
router.route("/add-feedback").post(isAuthenticatedUser,addFeedback);
module.exports = router;
module.exports = router;
Loading