Commit 25d1b083 by Arjun Jhukal

updated the new functionality for the the verify email and completed the flow of reset password

parent c16f74ef
import ForgotPasswordPage from '@/components/pages/auth/forgotPassword'
import React from 'react' import React from 'react'
export default function ForgotPassword() { export default function ForgotPassword() {
return ( return (
<h1>Forgot Passwoord</h1> <ForgotPasswordPage />
) )
} }
import ResetPasswordPage from '@/components/pages/auth/resetPassword'
import React from 'react'
export default function ResetPassword() {
return (
<ResetPasswordPage />
)
}
// "use client";
// import { useAppDispatch } from '@/hooks/hook';
// import { PATH } from '@/routes/PATH';
// import { useSendVerificationLinkAgainMutation } from '@/services/authApi';
// import { showToast, ToastVariant } from '@/slice/toastSlice';
// import { Box, Button, Typography } from '@mui/material'
// import Image from 'next/image'
// import Link from 'next/link'
// import { useRouter, useSearchParams } from 'next/navigation';
// import React from 'react'
// export default function VerifyEmail() {
// const router = useRouter();
// const searchParams = useSearchParams();
// const email = searchParams.get("email");
// const [sendEmailVerificationAgain, { isLoading: sendingLink }] = useSendVerificationLinkAgainMutation();
// const dispatch = useAppDispatch();
// const handleLinkResend = async () => {
// try {
// const response = await sendEmailVerificationAgain({ email: email || "" }).unwrap();
// dispatch(
// showToast({
// message: response?.message || "Link sent successfully",
// variant: ToastVariant.SUCCESS,
// autoTime: true,
// }),
// );
// }
// catch (e: any) {
// dispatch(
// showToast({
// message: e?.data?.message || "Unable to send link. Try again later",
// variant: ToastVariant.ERROR,
// autoTime: true,
// }),
// );
// }
// }
// return (
// <Box className="max-w-[520px] mx-auto flex flex-col gap-3 items-center text-center">
// <Image src={"/assets/images/verify-email.png"} alt='' width={180} height={140} />
// <h1 className='text-[24px] lg:text-[32px] leading-[120%] font-bold mb-4'>Verify your email to
// get the fun started</h1>
// {/* <Typography variant="h1" className='font-[700]'></Typography> */}
// <p className='text-[14px] leading-[120%] font-normal lg:text-[16px] mb-4'>Check the link sent to <strong className='underline text-secondary'>{email}</strong> to activate your account.</p>
// {/* <p className='text-[14px] leading-[120%] font-normal lg:text-[16px] mb-4'>Check your email for verification link.</p> */}
// <Button fullWidth size="large" type="submit" variant="contained" color="primary" className='!mb-6' onClick={() => {
// router.replace(PATH.DASHBOARD.ROOT)
// }}>
// Verify now
// </Button>
// <h2 className='text-[20px] lg:text-[28px] leading-[120%] font-bold'>Don’t see the email?</h2>
// <p className='text-[11px] lg:text-[14px] leading-[150%] font-normal'>Please check <strong>your spam or junk folder,</strong> as the email may have been filtered there. Also, ensure that the email address you entered is correct. Still not found?</p>
// <Button fullWidth variant="contained" color="secondary" onClick={handleLinkResend}>
// {sendingLink ? "Sending Again" : "Send Again"}
// </Button>
// </Box>
// )
// }
"use client"; "use client";
import { useAppDispatch } from '@/hooks/hook'; import { useAppDispatch } from '@/hooks/hook';
......
// import VerifyOTPPage from '@/components/pages/auth/VerifyOtp' // import VerifyOTPPage from '@/components/pages/auth/VerifyOtp'
import VerifyOTPPage from '@/components/pages/auth/VerifyOtp'
import React from 'react' import React from 'react'
export default function VerifyOTP() { export default function VerifyOTP() {
return ( return (
<h1>Verify OTP</h1> <VerifyOTPPage />
) )
} }
// "use client"; "use client";
// import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
// import { Box, Button, Typography, CircularProgress } from "@mui/material"; import { Box, Button, Typography, CircularProgress } from "@mui/material";
// import Image from "next/image"; import Image from "next/image";
// import { useRouter } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
// import { PATH } from "@/routes/PATH"; import { PATH } from "@/routes/PATH";
// import { useForgotPasswordMutation, useVerifyOTPMutation } from "@/services/authApi"; import { useForgotPasswordMutation, useVerifyOTPMutation } from "@/services/authApi";
// import { useAppDispatch } from "@/hooks/hook"; import { useAppDispatch } from "@/hooks/hook";
// import { showToast, ToastVariant } from "@/slice/toastSlice"; import { showToast, ToastVariant } from "@/slice/toastSlice";
// export default function VerifyOTPPage() { export default function VerifyOTPPage() {
// const router = useRouter(); const router = useRouter();
// const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const searchParams = useSearchParams();
// const [otp, setOtp] = useState<string[]>(["", "", "", "", "", ""]); const [email, setEmail] = useState<string | null>(null);
// const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
// const [email, setEmail] = useState<string | null>(null); const [otp, setOtp] = useState<string[]>(["", "", "", "", "", ""]);
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
// const [forgotPassword, { isLoading: sending }] = useForgotPasswordMutation();
// const [verifyOTP, { isLoading: verifying }] = useVerifyOTPMutation(); const [forgotPassword, { isLoading: sending }] = useForgotPasswordMutation();
const [verifyOTP, { isLoading: verifying }] = useVerifyOTPMutation();
// // ✅ Get stored email
// useEffect(() => { // ✅ Get email from URL parameters
// const storedData = localStorage.getItem("passwordResetData"); useEffect(() => {
// if (storedData) { const emailFromUrl = searchParams.get("email");
// const parsed = JSON.parse(storedData);
// setEmail(parsed.email || parsed.emailAddress || null); if (!emailFromUrl) {
// } dispatch(showToast({
// }, []); message: "Email not found. Please start the password reset process again.",
variant: ToastVariant.ERROR
// // ✅ Back to login }));
// const handleBackToLogin = () => { router.replace(PATH.AUTH.LOGIN.ROOT);
// localStorage.removeItem("passwordResetData"); return;
// router.replace(PATH.AUTH.LOGIN.ROOT); }
// };
setEmail(emailFromUrl);
// // ✅ Handle OTP input change }, [searchParams, router, dispatch]);
// const handleChange = (value: string, index: number) => {
// if (!/^\d*$/.test(value)) return; // ✅ Back to login
// const newOtp = [...otp]; const handleBackToLogin = () => {
// newOtp[index] = value; router.replace(PATH.AUTH.LOGIN.ROOT);
// setOtp(newOtp); };
// if (value && index < 5) { // ✅ Handle OTP input change
// inputRefs.current[index + 1]?.focus(); const handleChange = (value: string, index: number) => {
// } if (!/^\d*$/.test(value)) return;
// }; const newOtp = [...otp];
newOtp[index] = value;
// const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => { setOtp(newOtp);
// if (e.key === "Backspace" && !otp[index] && index > 0) {
// inputRefs.current[index - 1]?.focus(); if (value && index < 5) {
// } inputRefs.current[index + 1]?.focus();
// }; }
};
// // ✅ Verify OTP
// const handleSubmit = async (e: React.FormEvent) => { const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
// e.preventDefault(); if (e.key === "Backspace" && !otp[index] && index > 0) {
// const enteredOTP = otp.join(""); inputRefs.current[index - 1]?.focus();
}
// if (enteredOTP.length < 6) { };
// dispatch(showToast({ message: "Please enter all 6 digits", variant: ToastVariant.ERROR }));
// return; // ✅ Verify OTP
// } const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// if (!email) { const enteredOTP = otp.join("");
// dispatch(showToast({ message: "Email not available", variant: ToastVariant.ERROR }));
// return; if (enteredOTP.length < 6) {
// } dispatch(showToast({ message: "Please enter all 6 digits", variant: ToastVariant.ERROR }));
return;
// try { }
// const res = await verifyOTP({ email, otp: enteredOTP }).unwrap();
// dispatch( if (!email) {
// showToast({ dispatch(showToast({ message: "Email not available", variant: ToastVariant.ERROR }));
// message: res.message || "OTP verified successfully!", return;
// variant: ToastVariant.SUCCESS, }
// })
// ); try {
// router.push(PATH.AUTH.RESET_PASSWORD.ROOT); const res = await verifyOTP({ email, otp: enteredOTP }).unwrap();
// } catch (err: any) { dispatch(
// dispatch( showToast({
// showToast({ message: res.message || "OTP verified successfully!",
// message: err?.data?.message || "Invalid OTP", variant: ToastVariant.SUCCESS,
// variant: ToastVariant.ERROR, })
// }) );
// ); router.push(`${PATH.AUTH.RESET_PASSWORD.ROOT}?email=${email}`);
// } } catch (err: any) {
// }; dispatch(
showToast({
// // ✅ Resend OTP using stored email message: err?.data?.message || "Invalid OTP",
// const handleResendOTP = async () => { variant: ToastVariant.ERROR,
// if (!email) { })
// dispatch( );
// showToast({ }
// message: "Email not found in session. Please restart the process.", };
// variant: ToastVariant.ERROR,
// }) const handleResendOTP = async () => {
// ); if (!email) {
// return; dispatch(
// } showToast({
message: "Email not found. Please restart the process.",
// try { variant: ToastVariant.ERROR,
// const response = await forgotPassword({ email }).unwrap(); })
// dispatch( );
// showToast({ return;
// message: response.message || "OTP sent successfully!", }
// variant: ToastVariant.SUCCESS,
// }) try {
// ); const response = await forgotPassword({ email }).unwrap();
// } catch (err: any) { dispatch(
// dispatch( showToast({
// showToast({ message: response.message || "OTP sent successfully!",
// message: err?.data?.message || "Failed to resend OTP", variant: ToastVariant.SUCCESS,
// variant: ToastVariant.ERROR, })
// }) );
// ); } catch (err: any) {
// } dispatch(
// }; showToast({
message: err?.data?.message || "Failed to resend OTP",
// return ( variant: ToastVariant.ERROR,
// <Box className="max-w-[520px] mx-auto flex flex-col gap-3 items-center text-center"> })
// <Image src="/assets/images/verify-email.png" alt="Verify OTP" width={180} height={140} /> );
}
// <h1 className="text-[24px] lg:text-[32px] leading-[120%] font-bold mb-4"> };
// Enter the OTP to verify your account
// </h1> const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.preventDefault();
// <p className="text-[14px] leading-[120%] font-normal lg:text-[16px] mb-6"> const pastedData = e.clipboardData.getData("text").trim();
// We’ve sent a 6-digit code to{" "}
// <strong className="underline text-secondary"> // Check if pasted data contains only digits
// {email || "your email"} if (!/^\d+$/.test(pastedData)) return;
// </strong>
// </p> const digits = pastedData.slice(0, 6).split("");
const newOtp = [...otp];
// <form onSubmit={handleSubmit} className="w-full flex flex-col items-center gap-6">
// <div className="flex justify-center gap-3"> digits.forEach((digit, idx) => {
// {otp.map((digit, index) => ( if (idx < 6) {
// <input newOtp[idx] = digit;
// key={index} }
// ref={(el) => (inputRefs.current[index] = el)} // ✅ type safe ref });
// type="text"
// inputMode="numeric" setOtp(newOtp);
// maxLength={1}
// value={digit} // Focus on the next empty field or the last field
// onChange={(e) => handleChange(e.target.value, index)} const nextIndex = Math.min(digits.length, 5);
// onKeyDown={(e) => handleKeyDown(e, index)} inputRefs.current[nextIndex]?.focus();
// className="w-12 h-14 text-center text-[24px] font-semibold border border-gray-300 rounded-md focus:outline-none focus:border-primary transition-all" };
// />
// ))} if (!email) {
// </div> return (
<Box className="max-w-[520px] mx-auto flex justify-center items-center min-h-[400px]">
// <Button <CircularProgress />
// fullWidth </Box>
// size="large" );
// type="submit" }
// variant="contained"
// color="primary" return (
// disabled={verifying} <Box className="max-w-[520px] mx-auto flex flex-col gap-3 items-center text-center">
// > <Image src="/assets/images/verify-email.png" alt="Verify OTP" width={180} height={140} />
// {verifying ? "Verifying..." : "Verify OTP"}
// </Button> <h1 className="text-[24px] lg:text-[32px] leading-[120%] font-bold mb-4">
// </form> Enter the OTP to verify your account
</h1>
// <Typography variant="h6" className="text-[20px] lg:text-[28px] leading-[120%] font-bold">
// Didn’t get the code? <p className="text-[14px] leading-[120%] font-normal lg:text-[16px] mb-6">
// </Typography> We've sent a 6-digit code to{" "}
<strong className="underline text-secondary">
// <p className="text-[11px] lg:text-[14px] leading-[150%] font-normal"> {email}
// Please check your <strong>spam or junk folder</strong>. Still not found?{" "} </strong>
// <strong>Resend OTP below</strong>. </p>
// </p>
<form onSubmit={handleSubmit} className="w-full flex flex-col items-center gap-6">
// <Button <div className="flex justify-center gap-3">
// fullWidth {otp.map((digit, index) => (
// variant="contained" <input
// color="secondary" key={index}
// disabled={sending} ref={(el) => {
// onClick={handleResendOTP} inputRefs.current[index] = el;
// startIcon={sending && <CircularProgress size={18} color="inherit" />} }}
// > type="text"
// {sending ? "Sending..." : "Send Again"} inputMode="numeric"
// </Button> maxLength={1}
value={digit}
// <Button onChange={(e) => handleChange(e.target.value, index)}
// fullWidth onKeyDown={(e) => handleKeyDown(e, index)}
// variant="outlined" onPaste={handlePaste}
// color="primary" className="w-12 h-14 text-center text-[24px] font-semibold border border-gray-300 rounded-md focus:outline-none focus:border-primary transition-all"
// onClick={handleBackToLogin} />
// className="!mt-4" ))}
// > </div>
// Back to Login
// </Button> <Button
// </Box> fullWidth
// ); size="large"
// } type="submit"
variant="contained"
color="primary"
// disabled={verifying}
>
{verifying ? "Verifying..." : "Verify OTP"}
</Button>
</form>
<Typography variant="h6" className="text-[20px] lg:text-[28px] leading-[120%] font-bold">
Didn't get the code?
</Typography>
<p className="text-[11px] lg:text-[14px] leading-[150%] font-normal">
Please check your <strong>spam or junk folder</strong>. Still not found?{" "}
<strong>Resend OTP below</strong>.
</p>
<Button
fullWidth
variant="contained"
color="secondary"
disabled={sending}
onClick={handleResendOTP}
startIcon={sending && <CircularProgress size={18} color="inherit" />}
>
{sending ? "Sending..." : "Send Again"}
</Button>
<Button
fullWidth
variant="outlined"
color="primary"
onClick={handleBackToLogin}
className="!mt-4"
>
Back to Login
</Button>
</Box>
);
}
\ No newline at end of file
// "use client"; "use client";
// import React from "react"; import React from "react";
// import Link from "next/link"; import Link from "next/link";
// import { PATH } from "@/routes/PATH"; import { PATH } from "@/routes/PATH";
// import { import {
// Box, Box,
// InputLabel, InputLabel,
// OutlinedInput, OutlinedInput,
// } from "@mui/material"; } from "@mui/material";
// import AuthMessageBlock from "../authMessageBlock"; import AuthMessageBlock from "../authMessageBlock";
// import { useFormik } from "formik"; import { useFormik } from "formik";
// import * as Yup from "yup"; import * as Yup from "yup";
// import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
// import { useAppDispatch } from "@/hooks/hook"; import { useAppDispatch } from "@/hooks/hook";
// import { useForgotPasswordMutation } from "@/services/authApi"; import { useForgotPasswordMutation } from "@/services/authApi";
// import { showToast, ToastVariant } from "@/slice/toastSlice"; import { showToast, ToastVariant } from "@/slice/toastSlice";
// export default function ForgotPasswordPage() { export default function ForgotPasswordPage() {
// const router = useRouter(); const router = useRouter();
// const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
// const [forgotPassword, { isLoading }] = useForgotPasswordMutation(); const [forgotPassword, { isLoading }] = useForgotPasswordMutation();
// const { const {
// values, values,
// errors, errors,
// touched, touched,
// handleBlur, handleBlur,
// handleChange, handleChange,
// handleSubmit, handleSubmit,
// dirty, dirty,
// } = useFormik({ } = useFormik({
// initialValues: { initialValues: {
// emailAddress: "", emailAddress: "",
// }, },
// validationSchema: Yup.object({ validationSchema: Yup.object({
// emailAddress: Yup.string() emailAddress: Yup.string()
// .email("Invalid email format") .email("Invalid email format")
// .required("Email is required"), .required("Email is required"),
// }), }),
// onSubmit: async (values) => { onSubmit: async (values) => {
// try { try {
// const response = await forgotPassword({ const response = await forgotPassword({
// email: values.emailAddress, email: values.emailAddress,
// }).unwrap(); }).unwrap();
// // ✅ Show success toast
// dispatch(
// showToast({
// message: response.message || "OTP sent successfully!",
// variant: ToastVariant.SUCCESS,
// })
// );
// // ✅ Store email in localStorage correctly dispatch(
// localStorage.setItem( showToast({
// "passwordResetData", message: response.message || "OTP sent successfully!",
// JSON.stringify({ email: values.emailAddress }) variant: ToastVariant.SUCCESS,
// ); })
);
// // ✅ Navigate to Verify OTP page
// router.push(PATH.AUTH.VERIFY_OTP.ROOT);
// } catch (error: any) {
// console.error("Error:", error);
// dispatch( // localStorage.setItem(
// showToast({ // "passwordResetData",
// message: error.message || "Something went wrong", // JSON.stringify({ email: values.emailAddress })
// variant: ToastVariant.ERROR, // );
// })
// );
// }
// },
// });
// return ( // ✅ Navigate to Verify OTP page
// <> router.push(`${PATH.AUTH.VERIFY_OTP.ROOT}?email=${values.emailAddress}`);
// <AuthMessageBlock } catch (error: any) {
// title="Ready to get started and win BIG?" console.error("Error:", error);
// features={[
// "Fun & Fair Gameplay",
// "Exciting Prizes",
// "Accessible Anytime, Anywhere",
// "Trusted & Secure",
// ]}
// />
// <Box className="auth__form__wrapper lg:w-[50%] p-8">
// <div className="section__title mb-4 lg:mb-6">
// <h1 className="text-[24px] leading-[120%] font-bold lg:text-[48px]">
// Setup an account
// </h1>
// </div>
// <form onSubmit={handleSubmit}> dispatch(
// <div className="grid md:grid-cols-2 gap-x-3 gap-y-5"> showToast({
// <div className="col-span-2"> message: error.message || "Something went wrong",
// <div className="input_field"> variant: ToastVariant.ERROR,
// <InputLabel htmlFor="emailAddress">Email Address*</InputLabel> })
// <OutlinedInput );
// name="emailAddress" }
// id="emailAddress" },
// placeholder="example@example.com" });
// value={values.emailAddress}
// onChange={handleChange}
// onBlur={handleBlur}
// error={Boolean(touched.emailAddress && errors.emailAddress)}
// fullWidth
// />
// {touched.emailAddress && errors.emailAddress ? (
// <span className="error">{errors.emailAddress}</span>
// ) : null}
// </div>
// </div>
// </div>
// <div className="action__group text-center flex flex-col gap-4 mt-8"> return (
// <button <>
// type="submit" <AuthMessageBlock
// className="ss-btn bg-primary-grad flex items-center justify-center" title="Ready to get started and win BIG?"
// disabled={!dirty || isLoading} features={[
// > "Fun & Fair Gameplay",
// {isLoading ? "Sending OTP..." : "Send OTP"} "Exciting Prizes",
// </button> "Accessible Anytime, Anywhere",
"Trusted & Secure",
]}
/>
<Box className="auth__form__wrapper lg:w-[50%] p-8">
<div className="section__title mb-4 lg:mb-6">
<h1 className="text-[24px] leading-[120%] font-bold lg:text-[48px]">
Setup an account
</h1>
</div>
// <Link <form onSubmit={handleSubmit}>
// href={PATH.AUTH.LOGIN.ROOT} <div className="grid md:grid-cols-2 gap-x-3 gap-y-5">
// className="ss-btn bg-secondary-grad" <div className="col-span-2">
// > <div className="input_field">
// Back To Login <InputLabel htmlFor="emailAddress">Email Address*</InputLabel>
// </Link> <OutlinedInput
// </div> name="emailAddress"
// </form> id="emailAddress"
// </Box> placeholder="example@example.com"
// </> value={values.emailAddress}
// ); onChange={handleChange}
// } onBlur={handleBlur}
error={Boolean(touched.emailAddress && errors.emailAddress)}
fullWidth
/>
{touched.emailAddress && errors.emailAddress ? (
<span className="error">{errors.emailAddress}</span>
) : null}
</div>
</div>
</div>
<div className="action__group text-center flex flex-col gap-4 mt-8">
<button
type="submit"
className="ss-btn bg-primary-grad flex items-center justify-center"
disabled={!dirty || isLoading}
>
{isLoading ? "Sending OTP..." : "Send OTP"}
</button>
<Link
href={PATH.AUTH.LOGIN.ROOT}
className="ss-btn bg-secondary-grad"
>
Back To Login
</Link>
</div>
</form>
</Box>
</>
);
}
...@@ -136,7 +136,7 @@ export default function LoginPage() { ...@@ -136,7 +136,7 @@ export default function LoginPage() {
</div> </div>
<div className="action__group text-center flex flex-col gap-4 mt-8"> <div className="action__group text-center flex flex-col gap-4 mt-8">
<button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Logging In" : "Login"}</button> <button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Logging In" : "Login"}</button>
<p className='text-[12px] leading-[120%] font-bold lg:text-[16px] text-secondary'>Forgot password? <Link href={PATH.AUTH.RESET_PASSWORD.ROOT}>Reset Here</Link></p> <p className='text-[12px] leading-[120%] font-bold lg:text-[16px] '>Forgot password? <Link href={PATH.AUTH.FORGOT_PASSWORD.ROOT} className="text-secondary">Reset Here</Link></p>
<p className='text-[12px] leading-[120%] font-bold lg:text-[16px]'>Don’t have an account yet?</p> <p className='text-[12px] leading-[120%] font-bold lg:text-[16px]'>Don’t have an account yet?</p>
<Link href={PATH.AUTH.REGISTER.ROOT} className='ss-btn bg-secondary-grad'>Setup an account</Link> <Link href={PATH.AUTH.REGISTER.ROOT} className='ss-btn bg-secondary-grad'>Setup an account</Link>
</div> </div>
......
...@@ -37,8 +37,8 @@ const validationSchema = Yup.object().shape({ ...@@ -37,8 +37,8 @@ const validationSchema = Yup.object().shape({
}) })
export default function RegisterPage() { export default function RegisterPage() {
const router = useRouter();
const [registerUser, { isLoading }] = useRegisterUserMutation(); const [registerUser, { isLoading }] = useRegisterUserMutation();
const router = useRouter();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const initialValues = { const initialValues = {
emailAddress: "", emailAddress: "",
...@@ -66,7 +66,7 @@ export default function RegisterPage() { ...@@ -66,7 +66,7 @@ export default function RegisterPage() {
autoTime: true, autoTime: true,
}), }),
); );
router.replace(`${PATH.AUTH.VERIFY_EMAIL.ROOT}?email=${response?.data?.data?.user?.email}`); router.replace(`${PATH.AUTH.VERIFY_EMAIL.ROOT}?email=${values.emailAddress}`);
} }
catch (e: any) { catch (e: any) {
console.log("Error", e); console.log("Error", e);
......
"use client";
import React, { useEffect, useState } from 'react'
import AuthMessageBlock from '../authMessageBlock'
import { Box, InputLabel, OutlinedInput } from '@mui/material'
import PasswordField from '@/components/molecules/PasswordField'
import { ArrowLeft } from '@wandersonalwes/iconsax-react'
import Link from 'next/link'
import { PATH } from '@/routes/PATH'
import * as Yup from 'yup';
import { useFormik } from 'formik';
import { useAppDispatch } from '@/hooks/hook'
import { showToast, ToastVariant } from '@/slice/toastSlice';
import { useResetPasswordMutation } from '@/services/authApi';
import { useRouter, useSearchParams } from 'next/navigation';
const validationSchema = Yup.object().shape({
emailAddress: Yup.string()
.email('Must be a valid email')
.max(255)
.required('Email is required'),
password: Yup.string()
.required('Password is required')
.test(
'no-leading-trailing-whitespace',
'Password cannot start or end with spaces',
(value) => value === value?.trim()
)
.max(16, 'Password must be less than 10 characters'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Passwords must match')
.required('Confirm Password is required'),
})
export default function ResetPasswordPage() {
const router = useRouter();
const dispatch = useAppDispatch();
const initialValues = {
emailAddress: "",
password: "",
confirmPassword: "",
}
const [resetPassword, { isLoading }] = useResetPasswordMutation();
const { handleSubmit, handleBlur, handleChange, errors, dirty, values, touched } = useFormik(
{
initialValues,
validationSchema,
onSubmit: async (values) => {
try {
const response = await resetPassword({
email: values.emailAddress,
password: values.password,
password_confirmation: values.confirmPassword,
}).unwrap();
dispatch(
showToast({
message: response?.message || "New password is updated",
variant: ToastVariant.SUCCESS,
autoTime: true,
}),
);
router.replace(PATH.AUTH.LOGIN.ROOT);
}
catch (e: any) {
console.log("Error", e);
dispatch(
showToast({
message: e?.data?.message || "Unable to reset password. Try again later",
variant: ToastVariant.ERROR,
autoTime: true,
}),
);
}
}
}
)
return (
<>
<AuthMessageBlock
title="Forgot your password? Let’s get you back in!"
features={[
"Secure Account Recovery",
"Quick & Easy Process",
"No Worries, We've Got You Covered",
"24/7 Support Availability"
]}
/>
<Box className="auth__form__wrapper lg:w-[50%] p-8">
<div className="section__title mb-4 lg:mb-6">
<Link href={PATH.DASHBOARD.ROOT} className='text-[12px] leading-[120%] font-bold lg:text-[16px] hover:text-primary flex gap-2 items-center'><ArrowLeft />Back to Dashboard</Link>
<h1 className="text-[24px] leading-[120%] font-bold lg:text-[48px]">Forgot Password</h1>
</div>
<form action="" onSubmit={handleSubmit}>
<div className="grid md:grid-cols-2 gap-x-3 gap-y-5">
<div className="col-span-2">
<div className="input_field">
<InputLabel htmlFor="emailAddress">Email Address*</InputLabel>
<OutlinedInput
name='emailAddress'
id='emailAddress'
placeholder='example@example.com'
value={values.emailAddress}
onChange={handleChange}
onBlur={handleBlur}
error={Boolean(touched.emailAddress && errors.emailAddress)}
/>
{
touched.emailAddress && errors.emailAddress ?
<span className="error ">{errors.emailAddress}</span> : null
}
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="password"
label="Password*"
placeholder="XXXXXXX"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
error={touched.password ? errors.password : undefined}
/>
</div>
</div>
<div className="col-span-2">
<div className="input_field">
<PasswordField
name="confirmPassword"
label="Confirm Password*"
placeholder="XXXXXXX"
value={values.confirmPassword}
onChange={handleChange}
onBlur={handleBlur}
error={touched.confirmPassword ? errors.confirmPassword : undefined}
/>
</div>
</div>
</div>
<div className="action__group text-center flex flex-col gap-4 mt-8">
<button className='ss-btn bg-primary-grad' disabled={!dirty}>{isLoading ? "Changing Password" : "Reset Password"}</button>
<Link href={PATH.DASHBOARD.ROOT} className='ss-btn bg-secondary-grad'>Login</Link>
</div>
</form>
</Box>
</>
)
}
...@@ -12,6 +12,12 @@ export const PATH = { ...@@ -12,6 +12,12 @@ export const PATH = {
RESET_PASSWORD: { RESET_PASSWORD: {
ROOT: "/reset-password" ROOT: "/reset-password"
}, },
FORGOT_PASSWORD: {
ROOT: "/forgot-password"
},
VERIFY_OTP: {
ROOT: "/verify-otp"
},
VERIFY_EMAIL: { VERIFY_EMAIL: {
ROOT: "/verify-email" ROOT: "/verify-email"
} }
......
import { createApi } from "@reduxjs/toolkit/query/react"; import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery"; import { baseQuery } from "./baseQuery";
import { LoginProps, LoginResponse, RegisterProps } from "@/types/auth"; import { LoginProps, LoginResponse, RegisterProps } from "@/types/auth";
import { GlobalResponse } from "@/types/config";
export const authApi = createApi({ export const authApi = createApi({
reducerPath: "authApi", reducerPath: "authApi",
...@@ -34,8 +35,35 @@ export const authApi = createApi({ ...@@ -34,8 +35,35 @@ export const authApi = createApi({
method: "POST", method: "POST",
body: { email }, body: { email },
}) })
}),
forgotPassword: builder.mutation<GlobalResponse, { email: string }>({
query: ({ email }) => ({
url: "/api/forgot-password/send",
method: "POST",
body: { email },
})
}),
verifyOTP: builder.mutation<GlobalResponse, { email: string; otp: string }>({
query: ({ email, otp }) => ({
url: "/api/forgot-password/send",
method: "POST",
body: { email, otp },
}) })
}),
resetPassword: builder.mutation<GlobalResponse, { email: string, password: string, password_confirmation: string }>({
query: ({ email,
password,
password_confirmation }) => ({
url: `/api/forgot-password/reset`,
method: "POST",
body: {
email,
password,
password_confirmation
},
})
}),
}) })
}) })
export const { useLoginMutation, useRegisterUserMutation, useSendVerificationLinkAgainMutation } = authApi; export const { useLoginMutation, useRegisterUserMutation, useSendVerificationLinkAgainMutation, useForgotPasswordMutation, useVerifyOTPMutation, useResetPasswordMutation } = authApi;
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment