Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
sweepstake
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Arjun Jhukal
sweepstake
Commits
66776795
Commit
66776795
authored
Oct 10, 2025
by
Arjun Jhukal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
updated the minor changes
parent
80064300
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
320 additions
and
320 deletions
+320
-320
index.tsx
src/components/pages/auth/VerifyOtp/index.tsx
+195
-195
index.tsx
src/components/pages/auth/forgotPassword/index.tsx
+125
-125
No files found.
src/components/pages/auth/VerifyOtp/index.tsx
View file @
66776795
"use client"
;
import
React
,
{
useState
,
useEffect
,
useRef
}
from
"react"
;
import
{
Box
,
Button
,
Typography
,
CircularProgress
}
from
"@mui/material"
;
import
Image
from
"next/image"
;
import
{
useRouter
}
from
"next/navigation"
;
import
{
PATH
}
from
"@/routes/PATH"
;
import
{
useForgotPasswordMutation
,
useVerifyOTPMutation
}
from
"@/services/authApi"
;
import
{
useAppDispatch
}
from
"@/hooks/hook"
;
import
{
showToast
,
ToastVariant
}
from
"@/slice/toastSlice"
;
export
default
function
VerifyOTPPage
()
{
const
router
=
useRouter
();
const
dispatch
=
useAppDispatch
();
const
[
otp
,
setOtp
]
=
useState
<
string
[]
>
([
""
,
""
,
""
,
""
,
""
,
""
]);
const
inputRefs
=
useRef
<
(
HTMLInputElement
|
null
)[]
>
([]);
const
[
email
,
setEmail
]
=
useState
<
string
|
null
>
(
null
);
const
[
forgotPassword
,
{
isLoading
:
sending
}]
=
useForgotPasswordMutation
();
const
[
verifyOTP
,
{
isLoading
:
verifying
}]
=
useVerifyOTPMutation
();
// ✅ Get stored email
useEffect
(()
=>
{
const
storedData
=
localStorage
.
getItem
(
"passwordResetData"
);
if
(
storedData
)
{
const
parsed
=
JSON
.
parse
(
storedData
);
setEmail
(
parsed
.
email
||
parsed
.
emailAddress
||
null
);
}
},
[]);
// ✅ Back to login
const
handleBackToLogin
=
()
=>
{
localStorage
.
removeItem
(
"passwordResetData"
);
router
.
replace
(
PATH
.
AUTH
.
LOGIN
.
ROOT
);
};
// ✅ Handle OTP input change
const
handleChange
=
(
value
:
string
,
index
:
number
)
=>
{
if
(
!
/^
\d
*$/
.
test
(
value
))
return
;
const
newOtp
=
[...
otp
];
newOtp
[
index
]
=
value
;
setOtp
(
newOtp
);
if
(
value
&&
index
<
5
)
{
inputRefs
.
current
[
index
+
1
]?.
focus
();
}
};
const
handleKeyDown
=
(
e
:
React
.
KeyboardEvent
<
HTMLInputElement
>
,
index
:
number
)
=>
{
if
(
e
.
key
===
"Backspace"
&&
!
otp
[
index
]
&&
index
>
0
)
{
inputRefs
.
current
[
index
-
1
]?.
focus
();
}
};
// ✅ Verify OTP
const
handleSubmit
=
async
(
e
:
React
.
FormEvent
)
=>
{
e
.
preventDefault
();
const
enteredOTP
=
otp
.
join
(
""
);
if
(
enteredOTP
.
length
<
6
)
{
dispatch
(
showToast
({
message
:
"Please enter all 6 digits"
,
variant
:
ToastVariant
.
ERROR
}));
return
;
}
if
(
!
email
)
{
dispatch
(
showToast
({
message
:
"Email not available"
,
variant
:
ToastVariant
.
ERROR
}));
return
;
}
try
{
const
res
=
await
verifyOTP
({
email
,
otp
:
enteredOTP
}).
unwrap
();
dispatch
(
showToast
({
message
:
res
.
message
||
"OTP verified successfully!"
,
variant
:
ToastVariant
.
SUCCESS
,
})
);
router
.
push
(
PATH
.
AUTH
.
RESET_PASSWORD
.
ROOT
);
}
catch
(
err
:
any
)
{
dispatch
(
showToast
({
message
:
err
?.
data
?.
message
||
"Invalid OTP"
,
variant
:
ToastVariant
.
ERROR
,
})
);
}
};
// ✅ Resend OTP using stored email
const
handleResendOTP
=
async
()
=>
{
if
(
!
email
)
{
dispatch
(
showToast
({
message
:
"Email not found in session. Please restart the process."
,
variant
:
ToastVariant
.
ERROR
,
})
);
return
;
}
try
{
const
response
=
await
forgotPassword
({
email
}).
unwrap
();
dispatch
(
showToast
({
message
:
response
.
message
||
"OTP sent successfully!"
,
variant
:
ToastVariant
.
SUCCESS
,
})
);
}
catch
(
err
:
any
)
{
dispatch
(
showToast
({
message
:
err
?.
data
?.
message
||
"Failed to resend OTP"
,
variant
:
ToastVariant
.
ERROR
,
})
);
}
};
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=
"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
>
<
p
className=
"text-[14px] leading-[120%] font-normal lg:text-[16px] mb-6"
>
We’ve sent a 6-digit code to
{
" "
}
<
strong
className=
"underline text-secondary"
>
{
email
||
"your email"
}
</
strong
>
</
p
>
<
form
onSubmit=
{
handleSubmit
}
className=
"w-full flex flex-col items-center gap-6"
>
<
div
className=
"flex justify-center gap-3"
>
{
otp
.
map
((
digit
,
index
)
=>
(
<
input
key=
{
index
}
ref=
{
(
el
)
=>
(
inputRefs
.
current
[
index
]
=
el
)
}
// ✅ type safe ref
type=
"text"
inputMode=
"numeric"
maxLength=
{
1
}
value=
{
digit
}
onChange=
{
(
e
)
=>
handleChange
(
e
.
target
.
value
,
index
)
}
onKeyDown=
{
(
e
)
=>
handleKeyDown
(
e
,
index
)
}
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"
/>
))
}
</
div
>
<
Button
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
>
);
}
//
"use client";
//
import React, { useState, useEffect, useRef } from "react";
//
import { Box, Button, Typography, CircularProgress } from "@mui/material";
//
import Image from "next/image";
//
import { useRouter } from "next/navigation";
//
import { PATH } from "@/routes/PATH";
//
import { useForgotPasswordMutation, useVerifyOTPMutation } from "@/services/authApi";
//
import { useAppDispatch } from "@/hooks/hook";
//
import { showToast, ToastVariant } from "@/slice/toastSlice";
//
export default function VerifyOTPPage() {
//
const router = useRouter();
//
const dispatch = useAppDispatch();
//
const [otp, setOtp] = useState<string[]>(["", "", "", "", "", ""]);
//
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
//
const [email, setEmail] = useState<string | null>(null);
//
const [forgotPassword, { isLoading: sending }] = useForgotPasswordMutation();
//
const [verifyOTP, { isLoading: verifying }] = useVerifyOTPMutation();
//
// ✅ Get stored email
//
useEffect(() => {
//
const storedData = localStorage.getItem("passwordResetData");
//
if (storedData) {
//
const parsed = JSON.parse(storedData);
//
setEmail(parsed.email || parsed.emailAddress || null);
//
}
//
}, []);
//
// ✅ Back to login
//
const handleBackToLogin = () => {
//
localStorage.removeItem("passwordResetData");
//
router.replace(PATH.AUTH.LOGIN.ROOT);
//
};
//
// ✅ Handle OTP input change
//
const handleChange = (value: string, index: number) => {
//
if (!/^\d*$/.test(value)) return;
//
const newOtp = [...otp];
//
newOtp[index] = value;
//
setOtp(newOtp);
//
if (value && index < 5) {
//
inputRefs.current[index + 1]?.focus();
//
}
//
};
//
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
//
if (e.key === "Backspace" && !otp[index] && index > 0) {
//
inputRefs.current[index - 1]?.focus();
//
}
//
};
//
// ✅ Verify OTP
//
const handleSubmit = async (e: React.FormEvent) => {
//
e.preventDefault();
//
const enteredOTP = otp.join("");
//
if (enteredOTP.length < 6) {
//
dispatch(showToast({ message: "Please enter all 6 digits", variant: ToastVariant.ERROR }));
//
return;
//
}
//
if (!email) {
//
dispatch(showToast({ message: "Email not available", variant: ToastVariant.ERROR }));
//
return;
//
}
//
try {
//
const res = await verifyOTP({ email, otp: enteredOTP }).unwrap();
//
dispatch(
//
showToast({
//
message: res.message || "OTP verified successfully!",
//
variant: ToastVariant.SUCCESS,
//
})
//
);
//
router.push(PATH.AUTH.RESET_PASSWORD.ROOT);
//
} catch (err: any) {
//
dispatch(
//
showToast({
//
message: err?.data?.message || "Invalid OTP",
//
variant: ToastVariant.ERROR,
//
})
//
);
//
}
//
};
//
// ✅ Resend OTP using stored email
//
const handleResendOTP = async () => {
//
if (!email) {
//
dispatch(
//
showToast({
//
message: "Email not found in session. Please restart the process.",
//
variant: ToastVariant.ERROR,
//
})
//
);
//
return;
//
}
//
try {
//
const response = await forgotPassword({ email }).unwrap();
//
dispatch(
//
showToast({
//
message: response.message || "OTP sent successfully!",
//
variant: ToastVariant.SUCCESS,
//
})
//
);
//
} catch (err: any) {
//
dispatch(
//
showToast({
//
message: err?.data?.message || "Failed to resend OTP",
//
variant: ToastVariant.ERROR,
//
})
//
);
//
}
//
};
//
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="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>
//
<p className="text-[14px] leading-[120%] font-normal lg:text-[16px] mb-6">
//
We’ve sent a 6-digit code to{" "}
//
<strong className="underline text-secondary">
//
{email || "your email"}
//
</strong>
//
</p>
//
<form onSubmit={handleSubmit} className="w-full flex flex-col items-center gap-6">
//
<div className="flex justify-center gap-3">
//
{otp.map((digit, index) => (
//
<input
//
key={index}
//
ref={(el) => (inputRefs.current[index] = el)} // ✅ type safe ref
//
type="text"
//
inputMode="numeric"
//
maxLength={1}
//
value={digit}
//
onChange={(e) => handleChange(e.target.value, index)}
//
onKeyDown={(e) => handleKeyDown(e, index)}
//
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"
//
/>
//
))}
//
</div>
//
<Button
//
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>
//
);
//
}
src/components/pages/auth/forgotPassword/index.tsx
View file @
66776795
"use client"
;
//
"use client";
import
React
from
"react"
;
import
Link
from
"next/link"
;
import
{
PATH
}
from
"@/routes/PATH"
;
import
{
Box
,
InputLabel
,
OutlinedInput
,
}
from
"@mui/material"
;
import
AuthMessageBlock
from
"../authMessageBlock"
;
import
{
useFormik
}
from
"formik"
;
import
*
as
Yup
from
"yup"
;
import
{
useRouter
}
from
"next/navigation"
;
import
{
useAppDispatch
}
from
"@/hooks/hook"
;
import
{
useForgotPasswordMutation
}
from
"@/services/authApi"
;
import
{
showToast
,
ToastVariant
}
from
"@/slice/toastSlice"
;
//
import React from "react";
//
import Link from "next/link";
//
import { PATH } from "@/routes/PATH";
//
import {
//
Box,
//
InputLabel,
//
OutlinedInput,
//
} from "@mui/material";
//
import AuthMessageBlock from "../authMessageBlock";
//
import { useFormik } from "formik";
//
import * as Yup from "yup";
//
import { useRouter } from "next/navigation";
//
import { useAppDispatch } from "@/hooks/hook";
//
import { useForgotPasswordMutation } from "@/services/authApi";
//
import { showToast, ToastVariant } from "@/slice/toastSlice";
export
default
function
ForgotPasswordPage
()
{
const
router
=
useRouter
();
const
dispatch
=
useAppDispatch
();
//
export default function ForgotPasswordPage() {
//
const router = useRouter();
//
const dispatch = useAppDispatch();
const
[
forgotPassword
,
{
isLoading
}]
=
useForgotPasswordMutation
();
//
const [forgotPassword, { isLoading }] = useForgotPasswordMutation();
const
{
values
,
errors
,
touched
,
handleBlur
,
handleChange
,
handleSubmit
,
dirty
,
}
=
useFormik
({
initialValues
:
{
emailAddress
:
""
,
},
validationSchema
:
Yup
.
object
({
emailAddress
:
Yup
.
string
()
.
email
(
"Invalid email format"
)
.
required
(
"Email is required"
),
}),
onSubmit
:
async
(
values
)
=>
{
try
{
const
response
=
await
forgotPassword
({
email
:
values
.
emailAddress
,
}).
unwrap
();
//
const {
//
values,
//
errors,
//
touched,
//
handleBlur,
//
handleChange,
//
handleSubmit,
//
dirty,
//
} = useFormik({
//
initialValues: {
//
emailAddress: "",
//
},
//
validationSchema: Yup.object({
//
emailAddress: Yup.string()
//
.email("Invalid email format")
//
.required("Email is required"),
//
}),
//
onSubmit: async (values) => {
//
try {
//
const response = await forgotPassword({
//
email: values.emailAddress,
//
}).unwrap();
// ✅ Show success toast
dispatch
(
showToast
({
message
:
response
.
message
||
"OTP sent successfully!"
,
variant
:
ToastVariant
.
SUCCESS
,
})
);
//
// ✅ Show success toast
//
dispatch(
//
showToast({
//
message: response.message || "OTP sent successfully!",
//
variant: ToastVariant.SUCCESS,
//
})
//
);
// ✅ Store email in localStorage correctly
localStorage
.
setItem
(
"passwordResetData"
,
JSON
.
stringify
({
email
:
values
.
emailAddress
})
);
//
// ✅ Store email in localStorage correctly
//
localStorage.setItem(
//
"passwordResetData",
//
JSON.stringify({ email: values.emailAddress })
//
);
// ✅ Navigate to Verify OTP page
router
.
push
(
PATH
.
AUTH
.
VERIFY_OTP
.
ROOT
);
}
catch
(
error
:
any
)
{
console
.
error
(
"Error:"
,
error
);
//
// ✅ Navigate to Verify OTP page
//
router.push(PATH.AUTH.VERIFY_OTP.ROOT);
//
} catch (error: any) {
//
console.error("Error:", error);
dispatch
(
showToast
({
message
:
error
.
message
||
"Something went wrong"
,
variant
:
ToastVariant
.
ERROR
,
})
);
}
},
});
//
dispatch(
//
showToast({
//
message: error.message || "Something went wrong",
//
variant: ToastVariant.ERROR,
//
})
//
);
//
}
//
},
//
});
return
(
<>
<
AuthMessageBlock
title=
"Ready to get started and win BIG?"
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
>
//
return (
//
<>
//
<AuthMessageBlock
//
title="Ready to get started and win BIG?"
//
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
}
>
<
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
)
}
fullWidth
/>
{
touched
.
emailAddress
&&
errors
.
emailAddress
?
(
<
span
className=
"error"
>
{
errors
.
emailAddress
}
</
span
>
)
:
null
}
</
div
>
</
div
>
</
div
>
//
<form 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)}
//
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
>
//
<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
>
</>
);
}
//
<Link
//
href={PATH.AUTH.LOGIN.ROOT}
//
className="ss-btn bg-secondary-grad"
//
>
//
Back To Login
//
</Link>
//
</div>
//
</form>
//
</Box>
//
</>
//
);
//
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment