initial commit
This commit is contained in:
101
client/src/Pages/ForgetPw.jsx
Normal file
101
client/src/Pages/ForgetPw.jsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { useState } from "react";
|
||||
import { Container, Col, Row } from "react-bootstrap";
|
||||
import axios from "axios";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
|
||||
function ForgetPwPage() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const notifySuccess = (message) => {
|
||||
toast.success(message);
|
||||
};
|
||||
|
||||
const notifyError = (error) => {
|
||||
toast.error(error.message || "An error occurred");
|
||||
};
|
||||
|
||||
const notifyLoading = () => {
|
||||
toast.info("Sending verification link...");
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
notifyError("Please enter a valid email address");
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
notifyLoading();
|
||||
try {
|
||||
const response = await axios.post(
|
||||
"http://localhost:8080/password/forgot-password",
|
||||
{ email }
|
||||
);
|
||||
setMessage(response.data.message);
|
||||
notifySuccess(response.data.message);
|
||||
} catch (error) {
|
||||
console.error("Forgot password error:", error);
|
||||
notifyError(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenInbox = () => {
|
||||
const emailProviderUrl = "https://gmail.com/";
|
||||
window.open(emailProviderUrl, "_blank");
|
||||
};
|
||||
|
||||
const handleGoToLogin = () => {
|
||||
window.location.href = "/";
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<div className="LoginPage">
|
||||
<Container className="LoginPageContainer">
|
||||
<Row className="PwPageContainer">
|
||||
<Col md={12}>
|
||||
<div className="PwPage">
|
||||
<h1>Forgot Password</h1>
|
||||
<p>
|
||||
Enter your email address and we'll send you instructions on
|
||||
how to reset your password
|
||||
</p>
|
||||
<form className="form-pw" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Email"
|
||||
required
|
||||
/>
|
||||
<button type="submit" disabled={loading}>
|
||||
{loading ? "Sending..." : "Send Verification Link"}
|
||||
</button>
|
||||
</form>
|
||||
{message && (
|
||||
<div className="ResponseDiv">
|
||||
<p>{message}</p>
|
||||
<div className="ResponseDivButton">
|
||||
<button onClick={handleOpenInbox}>Open Gmail</button>
|
||||
<button onClick={handleGoToLogin}>Back to Login</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ForgetPwPage;
|
||||
107
client/src/Pages/HomePage.jsx
Normal file
107
client/src/Pages/HomePage.jsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Container, Col, Row, Button, Spinner } from "react-bootstrap";
|
||||
import axios from "axios";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
function HomePage(props) {
|
||||
|
||||
const notifyLoading = () => {
|
||||
toast.info("Logging Out Successfull..");
|
||||
};
|
||||
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
||||
const handleLogout = () => {
|
||||
axios
|
||||
.get("http://localhost:8080/auth/logout", {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then(() => {
|
||||
window.location.href = "/";
|
||||
setUser(null);
|
||||
localStorage.removeItem("user");
|
||||
notifyLoading();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error logging out:", error);
|
||||
});
|
||||
};
|
||||
|
||||
const fetchUser = async () => {
|
||||
const loggedInUser = localStorage.getItem("user");
|
||||
if (loggedInUser) {
|
||||
setUser(JSON.parse(loggedInUser));
|
||||
setLoading(false);
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://localhost:8080/api/user/profile/`,
|
||||
{
|
||||
withCredentials: true,
|
||||
}
|
||||
);
|
||||
setUser(response.data.user);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Error fetching user data:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUser();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<div className="LoginPage">
|
||||
<Container className="HomePageContainer ProfileContainer">
|
||||
{loading ? (
|
||||
<div className="loader">
|
||||
<Spinner animation="border" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</Spinner>
|
||||
</div>
|
||||
) : user ? (
|
||||
<>
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
<h1>Welcome to MERN Auth App</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
<h1>Profile</h1>
|
||||
<div className="profile-info">
|
||||
<div className="profile-image">
|
||||
<img src={user.profilePicture} alt="Profile" />
|
||||
</div>
|
||||
<div className="profile-details">
|
||||
<p>Username: {user.username}</p>
|
||||
<p>Email: {user.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button onClick={handleLogout}>Logout</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
) : (
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
<h1>Logging out...</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
|
||||
248
client/src/Pages/Login.jsx
Normal file
248
client/src/Pages/Login.jsx
Normal file
@@ -0,0 +1,248 @@
|
||||
import React, { useEffect , useState } from "react";
|
||||
import { Container, Col, Row } from "react-bootstrap";
|
||||
import { FcGoogle } from "react-icons/fc";
|
||||
import axios from "axios";
|
||||
import md5 from "md5";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
function AuthPage() {
|
||||
const [formData, setFormData] = useState({
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
const [signin, setSignin] = useState(false);
|
||||
|
||||
const notifyError = (message) => {
|
||||
toast.error(message);
|
||||
};
|
||||
|
||||
|
||||
function ToggleSign(event) {
|
||||
event.preventDefault();
|
||||
setSignin(!signin);
|
||||
setFormData({
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
}
|
||||
|
||||
function handleInputChange(event) {
|
||||
const { name, value } = event.target;
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
[name]: value,
|
||||
}));
|
||||
}
|
||||
|
||||
async function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
if (!formData.username.trim() && signin) {
|
||||
notifyError("Username cannot be empty");
|
||||
return;
|
||||
}
|
||||
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
if (!emailRegex.test(formData.email)) {
|
||||
notifyError("Enter a valid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check password length
|
||||
if (formData.password.length < 8) {
|
||||
notifyError("Password must be at least 8 characters long");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`http://localhost:8080/api/${
|
||||
!signin ? "login" : "register"
|
||||
}`,
|
||||
formData
|
||||
);
|
||||
const { user } = response.data;
|
||||
delete user.password;
|
||||
const gravatarUrl = `https://www.gravatar.com/avatar/${md5(
|
||||
user.email
|
||||
)}?d=identicon`;
|
||||
user.profilePicture = gravatarUrl;
|
||||
|
||||
localStorage.setItem("user", JSON.stringify(user));
|
||||
window.location.href = "/Home";
|
||||
} catch (error) {
|
||||
console.error("Authentication error:", error);
|
||||
if (
|
||||
error.response &&
|
||||
error.response.status === 400 &&
|
||||
error.response.data.message === "User already exists"
|
||||
) {
|
||||
notifyError("User already exists");
|
||||
} else {
|
||||
notifyError(error.response?.data.message || "An error occurred");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleGoogleLogin = (event) => {
|
||||
event.preventDefault();
|
||||
window.location.href =
|
||||
"http://localhost:8080/auth/google";
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<div className="LoginPage">
|
||||
<Container className={`LoginPageContainer ${signin ? "active" : ""}`}>
|
||||
<Row>
|
||||
<Col xs={12} md={6}>
|
||||
<div className="form-container sign-up">
|
||||
<SignUpForm
|
||||
formData={formData}
|
||||
handleInputChange={handleInputChange}
|
||||
handleGoogleLogin={handleGoogleLogin}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-container sign-in">
|
||||
<SignInForm
|
||||
formData={formData}
|
||||
handleInputChange={handleInputChange}
|
||||
handleGoogleLogin={handleGoogleLogin}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col md={6}>
|
||||
<TogglerContainer signin={signin} ToggleSign={ToggleSign} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<div className="OverlayAnimation">
|
||||
{signin ? (
|
||||
<div className="togglebtnlogin">
|
||||
<button className="hidden" onClick={ToggleSign}>
|
||||
Sign In
|
||||
</button>
|
||||
<span>Already Have an Account?</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="togglebtnlogin">
|
||||
<span>Don't have an account? Create one</span>
|
||||
<button className="hidden" onClick={ToggleSign}>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TogglerContainer(props) {
|
||||
return (
|
||||
<>
|
||||
<div className="toggle-container">
|
||||
<div className="toggle">
|
||||
<div className="toggle-panel toggle-left">
|
||||
<h1>Welcome to MERN Auth App</h1>
|
||||
<p>Already Have an Account?</p>
|
||||
<button className="hidden" onClick={props.ToggleSign}>
|
||||
Sign In
|
||||
</button>
|
||||
</div>
|
||||
<div className="toggle-panel toggle-right">
|
||||
<h1>Welcome to MERN Auth App</h1>
|
||||
<p>Don't have an account? Create one</p>
|
||||
<button className="hidden" onClick={props.ToggleSign}>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SignUpForm(props) {
|
||||
return (
|
||||
<>
|
||||
<form>
|
||||
<h1>Create Account</h1>
|
||||
<div className="Googlediv">
|
||||
<button className="GoogleBtn" onClick={props.handleGoogleLogin}>
|
||||
<FcGoogle className="icon" /> Sign up with Google
|
||||
</button>
|
||||
</div>
|
||||
<span>or use your email for registration</span>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
value={props.formData.username}
|
||||
onChange={props.handleInputChange}
|
||||
placeholder="Name"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={props.formData.email}
|
||||
onChange={props.handleInputChange}
|
||||
placeholder="Email"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
value={props.formData.password}
|
||||
onChange={props.handleInputChange}
|
||||
placeholder="Password"
|
||||
required
|
||||
/>
|
||||
<button onClick={props.handleSubmit}>Sign Up</button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SignInForm(props) {
|
||||
return (
|
||||
<>
|
||||
<form>
|
||||
<h1>Sign In</h1>
|
||||
<div>
|
||||
<button className="GoogleBtn" onClick={props.handleGoogleLogin}>
|
||||
<FcGoogle className="icon" /> Sign in with Google
|
||||
</button>
|
||||
</div>
|
||||
<span>or use your email password</span>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={props.formData.email}
|
||||
onChange={props.handleInputChange}
|
||||
placeholder="Email"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
value={props.formData.password}
|
||||
onChange={props.handleInputChange}
|
||||
placeholder="Password"
|
||||
required
|
||||
/>
|
||||
<a href="/ForgetPw">Forget Your Password?</a>
|
||||
<button onClick={props.handleSubmit}>Sign In</button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthPage;
|
||||
87
client/src/Pages/ResetPw.jsx
Normal file
87
client/src/Pages/ResetPw.jsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import React, { useState } from "react";
|
||||
import { Container, Col, Row } from "react-bootstrap";
|
||||
import { useParams } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
function ResetPwPage() {
|
||||
const { token } = useParams();
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
const notifySuccess = (message) => {
|
||||
toast.success(message);
|
||||
};
|
||||
|
||||
const notifyError = (error) => {
|
||||
toast.error(error.message || "An error occurred");
|
||||
};
|
||||
|
||||
const notifyLoading = () => {
|
||||
toast.info("Sending Reset Request...");
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (newPassword.length < 8) {
|
||||
toast.error("Password must be at least 8 characters long");
|
||||
return;
|
||||
}
|
||||
notifyLoading();
|
||||
try {
|
||||
const response = await axios.post(
|
||||
"http://:8080/password/reset-password",
|
||||
{ resetToken: token, newPassword }
|
||||
);
|
||||
setMessage(response.data.message);
|
||||
notifySuccess(response.data.message);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Reset password error:",
|
||||
error.response ? error.response.data : error
|
||||
);
|
||||
notifyError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoToLogin = () => {
|
||||
window.location.href = "/";
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<div className="LoginPage">
|
||||
<Container className="LoginPageContainer">
|
||||
<Row className="PwPageContainer">
|
||||
<Col md={12}>
|
||||
<div className="PwPage">
|
||||
<h1>Reset Password</h1>
|
||||
<p>Enter your new password below</p>
|
||||
<form className="form-pw" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="password"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
placeholder="New Password"
|
||||
required
|
||||
/>
|
||||
<button type="submit">Reset Password</button>
|
||||
</form>
|
||||
{message && (
|
||||
<div className="ResponseDiv">
|
||||
<p>{message}</p>
|
||||
<button onClick={handleGoToLogin}>Back to Login</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResetPwPage;
|
||||
28
client/src/Pages/Welcome.jsx
Normal file
28
client/src/Pages/Welcome.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const Welcome = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleRedirect = () => {
|
||||
navigate("/AuthpPage");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container text-center mt-5">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-md-6">
|
||||
<h1 className="mb-4">Welcome Page</h1>
|
||||
<button
|
||||
onClick={handleRedirect}
|
||||
className="btn btn-primary btn-lg"
|
||||
>
|
||||
SIGN IN / SIGN UP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Welcome;
|
||||
Reference in New Issue
Block a user