Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add Mismatch Error for New Password and Confirm New Password Fields on Password Settings page #1317

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
Original file line number Diff line number Diff line change
@@ -1,64 +1,52 @@
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import axios from "axios";
import { useSession } from "next-auth/react";
import { ChangeEvent, useState } from "react";
import { useState } from "react";
import { useForm } from "react-hook-form";

import { getApiUrl } from "~/actions/getApiUrl";
import CustomButton from "~/components/common/common-button/common-button";
import CustomInput from "~/components/common/input/input";
import PasswordSuccessfulModal from "~/components/common/modals/password-successful";
import { toast } from "~/components/ui/use-toast";
import { cn } from "~/lib/utils";
import { passwordSchema, type PasswordFormData } from "./schema";

const Password = () => {
const { data } = useSession();

const [open, setOpen] = useState<boolean>(false);

const [isPending, setIsPending] = useState(false);

const [formData, setFormData] = useState({
oldPassword: "",
password: "",
confirmPassword: "",
const {
register,
handleSubmit,
reset,
formState: { errors, isValid },
} = useForm<PasswordFormData>({
resolver: zodResolver(passwordSchema),
mode: "all",
});
const formDataHandler = (
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
setFormData((previous) => ({
...previous,
[event.target.name]: event.target.value,
}));
};

const submit = async () => {
if (formData.password !== formData.confirmPassword) {
return toast({
title: "Warning!",
description: "Password does not match",
});
}
const submitHandler = async (values: PasswordFormData) => {
try {
setIsPending(true);
const baseUrl = await getApiUrl();
const API_URL = `${baseUrl}/api/v1/auth/change-password`;

const payload = {
oldPassword: formData.oldPassword,
newPassword: formData.password,
old_password: values.currentPassword,
new_password: values.newPassword,
confirm_new_password: values.confirmPassword,
};
const baseUrl = await getApiUrl();
const API_URL = `${baseUrl}/api/v1/auth/password`;

await axios.post(API_URL, payload, {
await axios.put(API_URL, payload, {
headers: {
Authorization: `Bearer ${data?.access_token}`,
},
});
setOpen(true);
setFormData({
oldPassword: "",
password: "",
confirmPassword: "",
});
reset({ currentPassword: "", newPassword: "", confirmPassword: "" });
} catch (error) {
const errorMessage = (error as HttpError)?.response?.data?.message;
toast({
Expand All @@ -71,9 +59,6 @@ const Password = () => {
}
};

const disabled =
!formData.confirmPassword || !formData.oldPassword || !formData.password;

return (
<div className="w-full max-w-[674px] px-8 pt-[50px]">
<div className="mb-8">
Expand All @@ -84,50 +69,96 @@ const Password = () => {
Update password for enhanced account security
</p>
</div>
<div>
<form onSubmit={handleSubmit(submitHandler)}>
<div className="mb-6 grid gap-4">
<CustomInput
placeholder="Enter current password"
label="Current Password"
className="border-border"
type="password"
name="oldPassword"
value={formData.oldPassword}
onChange={formDataHandler}
/>
<CustomInput
placeholder="Enter new password"
label="New Password"
className="border-border"
type="password"
name="password"
value={formData.password}
onChange={formDataHandler}
/>
<CustomInput
placeholder="Confirm new password"
label="Confrim new Password"
className="border-border"
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={formDataHandler}
/>
<div className="">
<div className="flex w-full flex-col gap-2 rounded-md border-border text-sm text-primary transition-colors focus:border-primary focus:outline-none">
<label className="flex border-0 text-sm font-medium text-foreground">
Current Password
</label>
<div className="flex w-full items-center">
<input
className={cn(
"flex h-10 w-full flex-col gap-2 rounded-md border border-border bg-background px-4 py-2 text-sm text-foreground ring-offset-background transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary focus-visible:border-primary focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
errors.currentPassword && "border-red-500",
)}
placeholder="Enter current password"
type="password"
{...register("currentPassword")}
/>
</div>
</div>
{errors.currentPassword && (
<p className="mt-0.5 text-xs text-red-500">
{errors.currentPassword?.message}
</p>
)}
</div>
<div className="">
<div className="flex w-full flex-col gap-2 rounded-md border-border text-sm text-primary transition-colors focus:border-primary focus:outline-none">
<label className="flex border-0 text-sm font-medium text-foreground">
New Password
</label>
<div className="flex w-full items-center">
<input
className={cn(
"flex h-10 w-full flex-col gap-2 rounded-md border border-border bg-background px-4 py-2 text-sm text-foreground ring-offset-background transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary focus-visible:border-primary focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
errors.newPassword && "border-red-500",
)}
placeholder="Enter new password"
type="password"
{...register("newPassword")}
/>
</div>
</div>
{errors.newPassword && (
<p className="mt-0.5 text-xs text-red-500">
{errors.newPassword?.message}
</p>
)}
</div>
<div className="">
<div className="flex w-full flex-col gap-2 rounded-md border-border text-sm text-primary transition-colors focus:border-primary focus:outline-none">
<label className="flex border-0 text-sm font-medium text-foreground">
Confirm new password
</label>
<div className="flex w-full items-center">
<input
className={cn(
"flex h-10 w-full flex-col gap-2 rounded-md border border-border bg-background px-4 py-2 text-sm text-foreground ring-offset-background transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary focus-visible:border-primary focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
errors.confirmPassword && "border-red-500",
)}
placeholder="Confrim new Password"
type="password"
{...register("confirmPassword")}
/>
</div>
</div>
{errors.confirmPassword && (
<p className="mt-0.5 text-xs text-red-500">
{errors.confirmPassword?.message}
</p>
)}
</div>
</div>
<div className="flex items-center justify-start gap-6">
<CustomButton variant="outline" onClick={() => setOpen(false)}>
<CustomButton
type="button"
variant="outline"
onClick={() => setOpen(false)}
>
Cancel
</CustomButton>
<CustomButton
isDisabled={disabled}
onClick={submit}
className="bg-primary"
isDisabled={!isValid}
type="submit"
className={`bg-primary ${isPending && "opacity-50"}`}
isLoading={isPending}
>
Update Password
</CustomButton>
</div>
</div>
</form>
<PasswordSuccessfulModal onClose={() => setOpen(!open)} show={open} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as z from "zod";

export const passwordSchema = z
.object({
currentPassword: z
.string()
.min(8, "Current password must be at least 8 characters long"),
newPassword: z
.string()
.min(8, "New password must be at least 8 characters long"),
confirmPassword: z
.string()
.min(8, "Confirm password must be at least 8 characters long"),
})
.refine((data) => data.newPassword === data.confirmPassword, {
message: "Passwords do not match",
path: ["confirmPassword"],
});
export type PasswordFormData = z.infer<typeof passwordSchema>;
Loading