diff --git a/client/src/App.tsx b/client/src/App.tsx
index 386f2b1..30811c4 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,6 +1,7 @@
import { Route, Routes } from "react-router-dom";
import { ForgotPasswordPage } from "@/pages/forgot-password/page";
+import { ResetPasswordPage } from "@/pages/reset-password/page";
import VerifyEmailPage from "@/pages/verify-email/page";
import PrivateGuard from "@/guards/private-guard";
import { AppRoutes } from "@/constants/routes";
@@ -25,6 +26,10 @@ function App() {
path={AppRoutes.ForgotPassword}
element={}
/>
+ }
+ />
);
diff --git a/client/src/constants/collections.ts b/client/src/constants/collections.ts
index 453f821..27a6ab2 100644
--- a/client/src/constants/collections.ts
+++ b/client/src/constants/collections.ts
@@ -7,7 +7,7 @@ import {
ValidateUsername,
} from "@/lib/utils/validations";
-export const SIGNUPINPUTS = [
+export const SIGNUP_INPUTS = [
{
name: "username",
label: "Username",
@@ -35,7 +35,7 @@ export const SIGNUPINPUTS = [
name: "password",
label: "Password",
tooltip:
- "Create a strong password with at least 8 characters. Include a mix of uppercase letters, lowercase letters, numbers, and special characters for better security.",
+ "Create a password with at least 8 characters, including uppercase, lowercase, numbers, and special characters for security.",
placeholder: "e.g. m#P52s@ap$V",
type: "password",
autoComplete: "new-password",
@@ -56,7 +56,7 @@ export const SIGNUPINPUTS = [
},
];
-export const SIGNININPUTS = [
+export const SIGNIN_INPUTS = [
{
name: "email",
label: "Email Address",
@@ -82,3 +82,40 @@ export const SIGNININPUTS = [
maxLength: 128,
},
];
+
+export const RESET_PASSWORD_INPUTS = [
+ {
+ name: "old-password",
+ label: "Old Password",
+ tooltip: "Enter your current password",
+ placeholder: "e.g. m#P52s@ap$V",
+ type: "password",
+ autoComplete: "new-password",
+ suffixIcon: Lock,
+ validation: ValidatePassword,
+ maxLength: 128,
+ },
+ {
+ name: "new-password",
+ label: "New Password",
+ tooltip:
+ "Create a password with at least 8 characters, including uppercase, lowercase, numbers, and special characters for security",
+ placeholder: "e.g. m#P52s@ap$V",
+ type: "password",
+ autoComplete: "new-password",
+ suffixIcon: Lock,
+ validation: ValidatePassword,
+ maxLength: 128,
+ },
+ {
+ label: "Confirm Password",
+ tooltip:
+ "Re-enter your password to confirm it matches the one you typed above. Ensure both passwords are identical.",
+ placeholder: "e.g. m#P52s@ap$V",
+ type: "password",
+ autoComplete: "new-password",
+ suffixIcon: Lock,
+ validation: ValidateConfirmPassword,
+ maxLength: 128,
+ },
+];
diff --git a/client/src/constants/keys.ts b/client/src/constants/keys.ts
index f22fa6f..a7c242f 100644
--- a/client/src/constants/keys.ts
+++ b/client/src/constants/keys.ts
@@ -5,3 +5,4 @@ export const VERIFYEMAILKEY = "verify-email";
export const RESENDVERIFYKEY = "resend-verify";
export const VERIFYTOKENKEY = "verify-token";
export const FORGOTPASSWORDKEY = "forgot-password";
+export const RESETPASSWORDKEY = "reset-password";
diff --git a/client/src/lib/DTO/reset-dto.ts b/client/src/lib/DTO/reset-dto.ts
new file mode 100644
index 0000000..9422bf1
--- /dev/null
+++ b/client/src/lib/DTO/reset-dto.ts
@@ -0,0 +1,4 @@
+export type ResetDTO = {
+ token: string;
+ password: string;
+};
diff --git a/client/src/lib/services/auth-service.ts b/client/src/lib/services/auth-service.ts
index 648bddb..a5a380c 100644
--- a/client/src/lib/services/auth-service.ts
+++ b/client/src/lib/services/auth-service.ts
@@ -4,6 +4,7 @@ import { UserResponse } from "@/lib/classes/user-response-class";
import { SignUpDTO } from "@/lib/DTO/sign-up-dto";
import { SignInDTO } from "@/lib/DTO/sign-in-dto";
import { AppRoutes } from "@/constants/routes";
+import { ResetDTO } from "../DTO/reset-dto";
const baseURL =
import.meta.env.VITE_ENV === "development"
@@ -158,6 +159,33 @@ export const AuthService = {
});
}
+ return new GenericResponse({
+ message: data.message,
+ });
+ },
+ resetPassword: async (reset: ResetDTO) => {
+ const res = await fetch(
+ `${baseURL}${AppRoutes.ResetPassword}/${reset.token}`,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ password: reset.password,
+ }),
+ }
+ );
+
+ const data = await res.json();
+
+ if (!res.ok) {
+ throw new ErrorResponse({
+ status: res.status,
+ message: data.error,
+ });
+ }
+
return new GenericResponse({
message: data.message,
});
diff --git a/client/src/lib/stores/auth-store.ts b/client/src/lib/stores/auth-store.ts
index 1f6860a..3581a56 100644
--- a/client/src/lib/stores/auth-store.ts
+++ b/client/src/lib/stores/auth-store.ts
@@ -1,5 +1,5 @@
-import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
+import { create } from "zustand";
interface AuthStoreState {
email: string;
diff --git a/client/src/pages/forgot-password/_components/forgot-password-form.tsx b/client/src/pages/forgot-password/_components/forgot-password-form.tsx
index 3db235e..a841d7e 100644
--- a/client/src/pages/forgot-password/_components/forgot-password-form.tsx
+++ b/client/src/pages/forgot-password/_components/forgot-password-form.tsx
@@ -1,7 +1,6 @@
import { useMutation } from "@tanstack/react-query";
-import { useNavigate } from "react-router-dom";
+import { FormEvent, useState } from "react";
import { Mail } from "lucide-react";
-import { FormEvent } from "react";
import { toast } from "sonner";
import { GenericResponse } from "@/lib/classes/generic-response-class";
@@ -10,18 +9,17 @@ import { AuthService } from "@/lib/services/auth-service";
import { ValidateEmail } from "@/lib/utils/validations";
import { FORGOTPASSWORDKEY } from "@/constants/keys";
import { Button } from "@/components/text-button";
-import { AppRoutes } from "@/constants/routes";
import { Input } from "@/components/input";
const ForgotPasswordForm = () => {
- const navigate = useNavigate();
-
+ const [submitted, setSubmitted] = useState(false);
+ const [email, setEmail] = useState("");
const { mutate, isPending } = useMutation({
mutationKey: [FORGOTPASSWORDKEY],
mutationFn: AuthService.forgotPassword,
onSuccess: (res: GenericResponse) => {
toast.success(res.message);
- navigate(AppRoutes.ResetPassword);
+ setSubmitted(true);
},
onError: (error: ErrorResponse) => toast.error(error.message),
});
@@ -33,10 +31,25 @@ const ForgotPasswordForm = () => {
const forgotPasswordData = Object.fromEntries(formData.entries());
- mutate(forgotPasswordData["email"] as string);
+ const emailData = forgotPasswordData["email"] as string;
+
+ setEmail(emailData);
+
+ mutate(emailData);
};
- return (
+ return submitted ? (
+
+
+
+
+
+ If an account exists for{" "}
+ {email}, you will
+ receive a password reset link shortly.
+
+
+ ) : (