Skip to content

Commit

Permalink
Merge pull request #46 from Code-the-Dream-School/reset-password-page…
Browse files Browse the repository at this point in the history
…-feature

Reset password page feature
  • Loading branch information
Hermann27 authored Jan 21, 2025
2 parents fdde7e7 + a9c4142 commit 2a53f32
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import HomePage from "./views/HomePage";
import ShareAProject from "./views/ShareAProject";
import ExploreProjects from "./views/ExploreProjects";
import PrivateRoute from "./views/PrivateRoute";
import ResetPasswords from "./components/ResetPassword";
import AboutUs from "./views/AboutUsPage";
const URL = "http://localhost:8000/api/v1/";

Expand Down Expand Up @@ -35,6 +36,7 @@ function App() {
<Route path="/share-project" element={<ShareAProject />} />
</Route>
<Route path="/explore-project" element={<ExploreProjects />} />
<Route path="/reset-password" element={<ResetPasswords />} />
<Route path="/about-us" element={<AboutUs />} />
<Route path="/message" element={<h1>{message}</h1>} />
</Routes>
Expand Down
200 changes: 200 additions & 0 deletions src/components/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import React, { useState } from "react";
import { VStack, Box, Stack, Button, Text, Group } from "@chakra-ui/react";
import { PasswordInput } from "./ui/password-input";
import { Field } from "./ui/field";
import { Toaster, toaster } from "../components/ui/toaster";
import { Alert } from "../components/ui/alert";
import { useNavigate } from "react-router-dom";
import "../styles/ResetPassword.css";

const ResetPassword = () => {
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState("");
const navigate = useNavigate();
const token = new URLSearchParams(window.location.search).get("token");
const [errorMessage, setErrorMessage] = useState("");
const [userData, setUserData] = useState({
password_hash: "",
confirmPassword: "",
});
const [errors, setErrors] = useState({
password_hash: false,
confirmPassword: false,
});
const handleChange = (e) => {
const { name, value } = e.target;
setUserData((prevData) => ({
...prevData,
[name]: value,
}));

// Clear error message on input change
if (name === "password_hash" || name === "confirmPassword") {
setErrorMessage("");
}
};
const handleBlur = (e) => {
const { name, value } = e.target;
// Clear the error if the input is not empty
if (value.trim()) {
setErrors((prevErrors) => ({
...prevErrors,
[name]: false,
}));
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setMessage("");
// check if all fields are filled
const { password_hash, confirmPassword } = userData;
const newErrors = {
password_hash: !password_hash,
confirmPassword: !confirmPassword,
};
setErrors(newErrors);

// Check if passwords match
if (password_hash !== confirmPassword) {
setErrors((prevErrors) => ({
...prevErrors,
confirmPassword: true,
}));
setErrorMessage("Passwords do not match!");
setLoading(false);
return;
}
// If any field is missing, prevent user form submission
if (Object.values(newErrors).some((error) => error)) {
setLoading(false);
setMessage("all field is required");
return;
}
try {
console.log(token);
console.log(userData.password_hash);
const response = await fetch(
`http://localhost:8001/api/v1/reset-password/${token}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ password: userData.password_hash }),
}
);

if (response.ok) {
setMessage(
"Password reset successful, You can now login using the correct credentials!"
);
toaster.create({
title: "Password reset",
description: message,
type: "success",
duration: 4000,
action: {
label: "x",
},
});
navigate("/Home");
} else {
const errorData = await response.json();
setMessage(errorData.message || "Error resetting password.");
console.error("Error resetting password");
toaster.create({
title: "Error resetting password",
description: message,
type: "error",
action: {
label: "x",
},
});
}
} catch (error) {
setMessage("An unexpected error occurred.");
console.error("An unexpected error occurred.");
toaster.create({
title: "An unexpected error occurred.",
type: "error",
action: {
label: "x",
},
});
} finally {
setLoading(false);
}
};

return (
<>
<Group grow marginTop={"200px"}>
<VStack>
<Box>
<Text
fontWeight="800"
fontSize="30px"
letterSpacing="0.9px"
color="white"
marginTop={10}
>
Reset Password
</Text>
</Box>
<Box>
<Field w="308px" marginTop="10px" label="New Password" required>
<PasswordInput
placeholder="Password"
name="password_hash"
value={userData.password_hash}
onChange={handleChange}
onBlur={handleBlur}
className={errors.password_hash ? "error" : ""}
/>
</Field>
<Field w="308px" marginTop="10px" label="Confirm Password" required>
<PasswordInput
placeholder="Confirm Password"
name="confirmPassword"
value={userData.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
className={errors.confirmPassword ? "error" : ""}
/>
</Field>
<br />
{errorMessage && (
<Stack gap="2" width="308px">
<Alert status="error" title={errorMessage} />
</Stack>
)}
{/* Display error message */}
<br />
<Button
backgroundColor="#CF64EE"
width="308px"
h="50px"
borderRadius="100px"
marginBottom="25px"
onClick={handleSubmit}
>
<Text
fontWeight="800"
fontSize="16px"
letterSpacing="0.9px"
color="white"
>
{loading ? "Resetting..." : "Reset Password"}
</Text>
</Button>
</Box>
</VStack>
</Group>

<Toaster />
</>
);
};

export default ResetPassword;
44 changes: 44 additions & 0 deletions src/styles/ResetPassword.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.reset-password-container {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

.reset-password-container h2 {
margin-bottom: 20px;
font-size: 1.5rem;
}

.reset-password-container form {
display: flex;
flex-direction: column;
width: 100%;
}

.reset-password-container input {
margin-bottom: 15px;
padding: 10px;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}

.reset-password-container button {
padding: 10px;
font-size: 1rem;
color: white;
background-color: #007bff;
border: none;
border-radius: 4px;
cursor: pointer;
}

.reset-password-container button:disabled {
background-color: #ccc;
cursor: not-allowed;
}

.reset-password-container p {
margin-top: 10px;
color: green;
}
40 changes: 36 additions & 4 deletions src/views/HomePage.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState, useEffect } from "react";
import { Input } from "@chakra-ui/react";
import { Field } from "../components/ui/field";
import { Button } from "../components/ui/button";
Expand All @@ -7,6 +7,11 @@ import { useNavigate } from "react-router-dom";
import { Toaster, toaster } from "../components/ui/toaster";
const HomePage = () => {
const storedAuth = JSON.parse(localStorage.getItem("auth"));
const [databoard, setDataboard] = useState({
projects: 0,
users: 0,
comments: 0,
});
const navigate = useNavigate();

const handleAuthentificationCheck = () => {
Expand All @@ -26,6 +31,33 @@ const HomePage = () => {
}
};

useEffect(() => {
// Fetch data from JSON Server
const fetchDashboardData = async () => {
try {
//const response = await fetch("http://localhost:8001/api/v1/dashboard");
const url = "http://localhost:8001/api/v1/dashboard";
//const token = JSON.parse(localStorage.getItem("auth"));
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
//Authorization: `Bearer ${token}`, // Include the token in the Authorization header
},
});
if (response.ok) {
const dashboardData = await response.json();
setDataboard(dashboardData.data);
} else {
console.error("Failed to get dashboard data from the db");
}
} catch (error) {
console.error("Error get dashboard data:", error);
}
};

fetchDashboardData();
}, []); // Run only once when the component mounts
return (
<div className="sections-container">
<div className="section1">
Expand Down Expand Up @@ -69,9 +101,9 @@ const HomePage = () => {
next level.
</p>
<div className="home-data-rectangle">
<p>PROJECTS SHARED: 1,024+</p>
<p>REVIEWS GIVEN: 5,678+ </p>
<p>ACTIVE STUDENTS: 856+ </p>
<p>PROJECTS SHARED: {databoard.projects}</p>
<p>REVIEWS GIVEN: {databoard.comments} </p>
<p>ACTIVE STUDENTS: {databoard.users} </p>
</div>
<div className="section2">
<img className="section-image" src="./images/sectionimg-2.png"></img>
Expand Down

0 comments on commit 2a53f32

Please sign in to comment.