Skip to content
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
8 changes: 8 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import UserList from "./pages/Users";
import { PostDetailsCard } from "./components/PostDetailsCard";
import ConfirmUnsubscribePage from './pages/UnsubscribePage';
import EmailVerificationPage from "./pages/EmailPage";
import ForgotPassword from "./pages/ForgotPassword"
import ResetPassword from "./pages/ResetPassword";
function App() {
return (
<div className="bg-cover bg-gray-200 h-screen w-screen overflow-y-scroll">
Expand All @@ -29,6 +31,12 @@ function App() {
<Route path="/subscription" exact element={<SubscriptionPage />} />
<Route path="/confirm-unsubscribe" element={<ConfirmUnsubscribePage />} />
<Route path="/email" element={<EmailVerificationPage />} />
<Route path="forgot-password" element={<ForgotPassword />} />
<Route
path="reset-password/:resetToken"
element={<ResetPassword />}
/>

{/* Private routes */}
<Route element={<RequiresAuth allowedRoles={["moderator"]} />}>
<Route path="/admin" exact element={<HomePage />} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Login/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const Signin = () => {
<label htmlFor="password" className="text-sm text-gray-600 dark:text-gray-200">
Password
</label>
<a href="*" className="text-sm text-gray-400 focus:text-blue-500 hover:text-blue-500 hover:underline">
<a href="forgot-password" className="text-sm text-gray-400 focus:text-blue-500 hover:text-blue-500 hover:underline">
Forgot password?
</a>
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Navbar/Location.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const useRouteVariables = () => {
isDetailPage: location.pathname.startsWith("/post/"),
hideHomeLink: location.pathname.startsWith("/posts/"),
isEmailPage: location.pathname==="/email",
isConfirmPage: location.pathname==="/confirm-unsubscribe"
isConfirmPage: location.pathname==="/confirm-unsubscribe",
isForgotPage: location.pathname ==="/forgot-password"
};
};
2 changes: 1 addition & 1 deletion src/components/Navbar/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const Navbar = () => {
const toggleMenu = () => {
setOpen(!open);
};
if (isLoginPage || isSignupPage) {
if (isLoginPage || isSignupPage ) {
return null;
}

Expand Down
3 changes: 2 additions & 1 deletion src/hooks/useAuthentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const useAuthentication = () => {
throw new Error("No roles or token received in response.");
}

Cookies.set("token", token, { secure: true, sameSite: "strict" });
// Actualizar el token almacenado localmente
Cookies.set("token", token, { secure: true, httpOnly: true, sameSite: "strict" });

if (roles.some((role) => role.name === "moderator")) {
navigate("/admin");
Expand Down
105 changes: 105 additions & 0 deletions src/pages/ForgotPassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import axios from "axios";
import toast from "react-hot-toast";

const ForgotPassword = () => {
const [email, setEmail] = useState("");
const navigate = useNavigate();

const handleForgotPassword = async () => {
try {
const response = await axios.post(
"http://localhost:4000/api/auth/forgot-password",
{
email,
}
);

if (response.status === 200) {
navigate("/email");
} else if (response.status === 204) {
toast.error("Tu correo electrónico no está registrado con nosotros");
} else {
toast.error(
response.data.message ||
"Error al enviar el correo de restablecimiento."
);
}
} catch (error) {
toast.error("Error al enviar el correo de restablecimiento.");
}
};

return (
<div className="container mx-auto">
<div className="flex justify-center px-6 my-12">
<div className="w-full xl:w-3/4 lg:w-11/12 flex">
<div
className="w-full h-auto bg-gray-400 hidden lg:block lg:w-1/2 bg-cover rounded-l-lg"
style={{
backgroundImage:
"url('https://images.unsplash.com/photo-1624969862644-791f3dc98927?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')",
}}
></div>

<div className="w-full lg:w-1/2 bg-white p-5 rounded-lg lg:rounded-l-none">
<div className="px-8 mb-4 text-center">
<h3 className="pt-4 mb-2 text-2xl">Forgot Your Password?</h3>
<p className="mb-4 text-sm text-gray-700">
We get it, stuff happens. Just enter your email address below
and we'll send you a link to reset your password!
</p>
</div>
<form className="px-8 pt-6 pb-8 mb-4 bg-white rounded">
<div className="mb-4">
<label
className="block mb-2 text-sm font-bold text-gray-700"
htmlFor="email"
>
Email
</label>
<input
className="w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline"
id="email"
type="email"
placeholder="Enter Email Address..."
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="mb-6 text-center">
<button
className="w-full px-4 py-2 font-bold text-white bg-blue-950 rounded-full hover:bg-blue-800 focus:outline-none focus:shadow-outline"
type="button"
onClick={handleForgotPassword}
>
Reset Password
</button>
</div>
<hr className="mb-6 border-t" />
<div className="text-center">
<Link
to="/signup"
className="inline-block text-sm text-blue-500 align-baseline hover:text-blue-800"
>
Create an Account!
</Link>
</div>
<div className="text-center">
<Link
to="/login"
className="inline-block text-sm text-blue-500 align-baseline hover:text-blue-800"
>
Already have an account? Login!
</Link>
</div>
</form>
</div>
</div>
</div>
</div>
);
};

export default ForgotPassword;
112 changes: 112 additions & 0 deletions src/pages/ResetPassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, { useState, useEffect } from "react";
import axios from "axios";
import toast from "react-hot-toast";
import { useNavigate, useLocation } from "react-router-dom";

const ResetPassword = () => {
const [token, setToken] = useState("");
const [newPassword, setNewPassword] = useState("");
const navigate = useNavigate();
const location = useLocation();

useEffect(() => {
const resetTokenFromUrl = location.pathname.split("/").pop();
setToken(resetTokenFromUrl);
}, [location.pathname]);

const handleResetPassword = async () => {
try {
const response = await axios.post(
"http://localhost:4000/api/auth/reset-password",
{
token,
newPassword,
}
);

if (response.status === 200) {
navigate("/login");
toast.success("Password Reset successful");
} else {
if (response.status === 204 && response.data.notFound) {
toast.error(
response.data.message || "Your email is not registered with us."
);
} else {
toast.error(response.data.message || "Error resetting the password.");
}
}
} catch (error) {
toast.error("Error resetting the password.");
}
};

return (
<div className="container mx-auto">
<div className="flex justify-center px-6 my-12">
<div className="w-full xl:w-3/4 lg:w-11/12 flex">
<div
className="w-full h-auto bg-gray-400 hidden lg:block lg:w-1/2 bg-cover rounded-l-lg"
style={{
backgroundImage: `url('https://images.unsplash.com/photo-1582139329536-e7284fece509?q=80&w=1480&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')`,
}}
></div>

<div className="w-full lg:w-1/2 bg-white p-5 rounded-lg lg:rounded-l-none">
<div className="px-8 mb-4 text-center">
<h3 className="pt-4 mb-2 text-2xl">Reset Your Password</h3>
<p className="mb-4 text-sm text-gray-700">
Enter your new password below to reset your password!
</p>
</div>
<form className="px-8 pt-6 pb-8 mb-4 bg-white rounded">
<div className="mb-4">
<label
className="block mb-2 text-sm font-bold text-gray-700"
htmlFor="token"
>
Token
</label>
<input
className=" cursor-not-allowed w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline bg-gray-100"
id="token"
type="text"
placeholder="Token"
value={token}
disabled
/>
</div>
<div className="mb-4">
<label
className="block mb-2 text-sm font-bold text-gray-700"
htmlFor="newPassword"
>
New Password
</label>
<input
className="w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline"
id="newPassword"
type="password"
placeholder="Enter New Password..."
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div className="mb-6 text-center">
<button
className="w-full px-4 py-2 font-bold text-white bg-blue-950 rounded-full hover:bg-blue-800 focus:outline-none focus:shadow-outline"
type="button"
onClick={handleResetPassword}
>
Reset Password
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};

export default ResetPassword;