Skip to content

Commit

Permalink
Consume reset password api
Browse files Browse the repository at this point in the history
- Integrated the reset password API into the form submission flow.
- Handled API request to send `old password`, `new password`, and `confirm password` data.
- Implemented error handling for invalid responses from the API.
- Displayed success and error messages based on API response.
- Updated form state to handle loading and submission status.
- Redirected to `sign-in` page if successful.
  • Loading branch information
Fingertips18 committed Sep 23, 2024
1 parent ab1024b commit 0e077b2
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 13 deletions.
4 changes: 4 additions & 0 deletions client/src/lib/DTO/reset-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ResetDTO = {
token: string;
password: string;
};
28 changes: 28 additions & 0 deletions client/src/lib/services/auth-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
});
Expand Down
2 changes: 1 addition & 1 deletion client/src/lib/stores/auth-store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import { create } from "zustand";

interface AuthStoreState {
email: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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),
});
Expand All @@ -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 ? (
<div className="p-4 lg:p-6 w-full md:w-fit rounded-md border border-primary/50 bg-primary/15 drop-shadow-2xl space-y-4 lg:space-y-6">
<div className="w-16 h-16 bg-accent rounded-full flex items-center justify-center mx-auto mb-4">
<Mail className="h-8 w-8 text-white" />
</div>
<p className="text-sm text-foreground/80 text-center lg:w-[386px] mx-auto">
If an account exists for{" "}
<span className="font-medium text-primary">{email}</span>, you will
receive a password reset link shortly.
</p>
</div>
) : (
<form
onSubmit={onSubmit}
className="p-4 lg:p-6 w-full md:w-fit rounded-md border border-primary/50 bg-primary/15 drop-shadow-2xl space-y-4 lg:space-y-6"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,46 @@
import { useNavigate, useParams } from "react-router-dom";
import { useMutation } from "@tanstack/react-query";
import { FormEvent, useState } from "react";
import { toast } from "sonner";

import { GenericResponse } from "@/lib/classes/generic-response-class";
import { ErrorResponse } from "@/lib/classes/error-response-class";
import { RESET_PASSWORD_INPUTS } from "@/constants/collections";
import { AuthService } from "@/lib/services/auth-service";
import { RESETPASSWORDKEY } from "@/constants/keys";
import { Button } from "@/components/text-button";
import { AppRoutes } from "@/constants/routes";
import { ResetDTO } from "@/lib/DTO/reset-dto";
import { Input } from "@/components/input";

const ResetPasswordForm = () => {
const navigate = useNavigate();
const { token } = useParams();
const [confirmPassword, setConfirmPassword] = useState("");
const { isPending } = useMutation({
const { mutate, isPending } = useMutation({
mutationKey: [RESETPASSWORDKEY],
mutationFn: AuthService.resetPassword,
onSuccess: (res: GenericResponse) => {
toast.success(res.message);
navigate(AppRoutes.SignIn);
},
onError: (error: ErrorResponse) => toast.error(error.message),
});

const onSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (!token) return;

const formData = new FormData(e.currentTarget);

const resetPasswordData = Object.fromEntries(
formData.entries()
) as ResetDTO;

resetPasswordData.token = token;

mutate(resetPasswordData);
};

return (
Expand Down
5 changes: 5 additions & 0 deletions client/src/pages/reset-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { SwitchAuth } from "@/components/switch-auth";
import { AppRoutes } from "@/constants/routes";

import { ResetPasswordForm } from "./_components/reset-password-form";

const ResetPasswordPage = () => {
Expand All @@ -11,6 +14,8 @@ const ResetPasswordPage = () => {
</h2>

<ResetPasswordForm />

<SwitchAuth href={AppRoutes.Root} tag="Back" />
</section>
);
};
Expand Down
6 changes: 3 additions & 3 deletions controllers/auth_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,14 @@ func ResetPassword(c fiber.Ctx) error {
return c.Status(fiber.StatusBadRequest).JSON(dto.ErrorDTO{Error: "Either password is invalid or empty"})
}

id := c.Params("token")
if id == "" {
token := c.Params("token")
if token == "" {
return c.Status(fiber.StatusBadRequest).JSON(dto.ErrorDTO{Error: "Missing token"})
}

var user models.User

res := database.Instance.Where("reset_password_token = ?", id).First(&user)
res := database.Instance.Where("reset_password_token = ?", token).First(&user)
if res.Error != nil {
if res.Error == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusNotFound).JSON(dto.ErrorDTO{Error: "User not found"})
Expand Down

0 comments on commit 0e077b2

Please sign in to comment.