Commit 80064300 by Arjun Jhukal

updated the minor changes

parent cc7ef5d3
......@@ -23,6 +23,7 @@
"js-cookie": "^3.0.5",
"lightgallery": "^2.9.0-beta.1",
"next": "15.5.3",
"nprogress": "^0.2.0",
"react": "^19.1.0",
"react-apexcharts": "^1.7.0",
"react-dom": "19.1.0",
......@@ -36,6 +37,7 @@
"@tailwindcss/postcss": "^4",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/nprogress": "^0.2.3",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
......@@ -15402,6 +15404,13 @@
"undici-types": "~6.21.0"
}
},
"node_modules/@types/nprogress": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@types/nprogress/-/nprogress-0.2.3.tgz",
"integrity": "sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
......@@ -21128,6 +21137,12 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/nprogress": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==",
"license": "MIT"
},
"node_modules/nwsapi": {
"version": "2.2.22",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz",
......
......@@ -24,6 +24,7 @@
"js-cookie": "^3.0.5",
"lightgallery": "^2.9.0-beta.1",
"next": "15.5.3",
"nprogress": "^0.2.0",
"react": "^19.1.0",
"react-apexcharts": "^1.7.0",
"react-dom": "19.1.0",
......@@ -37,6 +38,7 @@
"@tailwindcss/postcss": "^4",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/nprogress": "^0.2.3",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
......
// import ForgotPasswordPage from '@/components/pages/auth/forgotPassword'
// import React from 'react'
// export default function ForgotPassword() {
// return (
// <ForgotPasswordPage />
// )
// }
// import VerifyOTPPage from '@/components/pages/auth/VerifyOtp'
// import React from 'react'
// export default function VerifyOTP() {
// return (
// <VerifyOTPPage />
// )
// }
"use client";
import { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
export default function TopLoader() {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
NProgress.configure({
showSpinner: false,
trickleSpeed: 100,
easing: "ease",
speed: 400,
});
NProgress.start();
const timer = setTimeout(() => {
NProgress.done();
}, 300); // short delay to simulate loading
return () => {
clearTimeout(timer);
NProgress.done();
};
}, [pathname, searchParams]); // runs on route change
return null;
}
......@@ -161,7 +161,7 @@
font-size: 14px;
font-weight: 400;
line-height: 120%;
color: var(--para-light);
color: var(--white);
}
.tab__link {
......@@ -258,3 +258,9 @@
.analytics__wrapper>div:nth-child(4n+4) .analytics__card {
background: rgba(192, 74, 225, 0.10);
}
/* NProgress custom color */
#nprogress .bar {
background: var(--primary) !important;
height: 4px !important;
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
import "./globals.css";
import ProviderWrapper from "./ProviderWrapper";
import { AgeChecker } from "./AgeChecker";
import TopLoader from "./TopLoader";
export const metadata: Metadata = {
title: "Sweepstake",
......@@ -29,7 +30,7 @@ export default function RootLayout({
{/* className="dark" */}
<body className={`${inter.className} scroll-smooth dark`} cz-shortcut-listen="true">
<ProviderWrapper>
<TopLoader />
{children}
</ProviderWrapper>
</body>
......
"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 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();
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();
// ✅ 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 })
);
// ✅ 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,
})
);
}
},
});
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>
<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>
</>
);
}
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