diff --git a/src/app/(main)/funds_transfer/send_beneficiary/page.tsx b/src/app/(main)/funds_transfer/send_beneficiary/page.tsx index 8b8135f..a7ac27b 100644 --- a/src/app/(main)/funds_transfer/send_beneficiary/page.tsx +++ b/src/app/(main)/funds_transfer/send_beneficiary/page.tsx @@ -56,7 +56,6 @@ export default function SendToBeneficiaryOwn() { setCountdown(180); setTimerActive(true); } catch (err: any) { - console.error('Send OTP failed', err); notifications.show({ title: 'Error', message: err.message || 'Send OTP failed.Please try again later.', diff --git a/src/app/(main)/funds_transfer/send_beneficiary/sendBeneficiaryOthers.tsx b/src/app/(main)/funds_transfer/send_beneficiary/sendBeneficiaryOthers.tsx index 0787f06..6ceb13c 100644 --- a/src/app/(main)/funds_transfer/send_beneficiary/sendBeneficiaryOthers.tsx +++ b/src/app/(main)/funds_transfer/send_beneficiary/sendBeneficiaryOthers.tsx @@ -71,7 +71,6 @@ export default function SendToBeneficiaryOthers() { setCountdown(180); setTimerActive(true); } catch (err: any) { - console.error('Send OTP failed', err); notifications.show({ title: 'Error', message: err.message || 'Send OTP failed.Please try again later.', diff --git a/src/app/_util/otp.ts b/src/app/_util/otp.ts index 414438b..67bdb52 100644 --- a/src/app/_util/otp.ts +++ b/src/app/_util/otp.ts @@ -12,6 +12,7 @@ interface SendOtpPayload { ref?: string; date?: string; userOtp?: string; + username?:string } function getStoredMobileNumber(): string | null { @@ -43,7 +44,6 @@ export async function sendOtp(payload: SendOtpPayload) { } } - export async function verifyOtp(otp: string) { try { const mobileNumber = getStoredMobileNumber(); @@ -58,3 +58,18 @@ export async function verifyOtp(otp: string) { throw error.response?.data || error; } } + +export async function verifyLoginOtp(otp: string,mobileNumber:string) { + try { + // const mobileNumber = getStoredMobileNumber(); + const response = await axios.post( + `http://localhost:8080/api/otp/verify?mobileNumber=${mobileNumber}`, + { otp }, + { headers: { 'Content-Type': 'application/json' } } + ); + return response.data; + } catch (error: any) { + console.error('Error verifying OTP:', error.response?.data || error.message); + throw error.response?.data || error; + } +} \ No newline at end of file diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index a14c64d..0791c4a 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -4,13 +4,14 @@ import { Text, Button, TextInput, PasswordInput, Title, Card, Group, Flex, Box, import { notifications } from "@mantine/notifications"; import { Providers } from "@/app/providers"; import { useRouter } from "next/navigation"; +import { sendOtp, verifyLoginOtp } from '@/app/_util/otp'; import NextImage from "next/image"; import styles from './page.module.css'; import logo from '@/app/image/logo1.jpg'; import frontPage from '@/app/image/ib_front_1.jpg'; import dynamic from 'next/dynamic'; import { generateCaptcha } from '@/app/captcha'; -import { IconShieldLockFilled } from "@tabler/icons-react"; +import { IconRefresh, IconShieldLockFilled } from "@tabler/icons-react"; import dayjs from "dayjs"; @@ -27,6 +28,46 @@ export default function Login() { const [otpStep, setOtpStep] = useState(false); const [otp, setOtp] = useState(""); const [loading, setLoading] = useState(false); + const [otpRequired, setOtpRequired] = useState(false); + const [otpVerified, setOtpVerified] = useState(false); + const [buttonLabel, setButtonLabel] = useState("Submit"); + const [mobile, setMobile] = useState(""); + + async function handleSendOtp(mobile?: string) { + console.log("hi mobile", mobile); + if (!mobile) { + notifications.show({ + title: 'Error', + message: 'Mobile number not found.Contact to administrator', + color: 'red', + }); + return false; + } + try { + console.log(CIF); + await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: mobile }); + return true; + } + catch (err: any) { + notifications.show({ + title: 'Error', + message: `${err.error}.SMS vendor facing some issue.Please try again later.` || 'Send OTP failed.Please try again later.', + color: 'red', + }); + return false; + } + } + + async function handleVerifyOtp(mobile?: string) { + try { + if (mobile) { + await verifyLoginOtp(otp, mobile); + return true; + } + } catch { + return false; + } + } useEffect(() => { const loadCaptcha = async () => { @@ -61,94 +102,329 @@ export default function Login() { setInputCaptcha(""); }; + // async function handleLogin(e: React.FormEvent) { + // e.preventDefault(); + + // if (!otpRequired && !otpVerified) { + // const onlyDigit = /^\d{11}$/; + // if (!onlyDigit.test(CIF)) { + // // setError('Input value must be 11 digit'); + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Invalid UserId", + // message: "UserID must be 11 digit", + // autoClose: 5000, + // }); + // return; + // } + // if (!inputCaptcha) { + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Invalid Captcha", + // message: "Please fill the Captcha filed", + // autoClose: 5000, + // }); + // return; + // } + // if (inputCaptcha !== captcha) { + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Captcha Error", + // message: "Please enter the correct captcha", + // autoClose: 5000, + // }); + // regenerateCaptcha(); + // return; + // } + // if (!CIF || !psw) { + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Invalid Input", + // message: "Please fill UserId and Password", + // autoClose: 5000, + // }); + // return; + // } + // } + // try { + // if (!otpRequired || otpVerified) { + // const response = await fetch('api/auth/login', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // 'X-Login-Type': 'IB', + // }, + // body: JSON.stringify({ + // customerNo: CIF, + // password: psw, + // otp: otp + // }), + // }); + // const data = await response.json(); + // console.log(data); + // setIsLogging(true); + + // if (data.status === "OTP_REQUIRED" && response.status === 202) { + // console.log(data.mobile); + // setMobile(data.mobile); + // setOtpRequired(true); + // setButtonLabel("Verify OTP"); + + // const otpSent = await handleSendOtp(data.mobile); // auto-send + // if (otpSent) { + // notifications.show({ + // color: "orange", + // title: "OTP Required", + // message: "OTP sent to your registered mobile number", + // }); + // } + // return; + // } + // if (data.error === "MIGRATED_USER_HAS_NO_PASSWORD") { + // //console.log("Migration issue detected → opening modal"); + // setOpened(true); + // return; + // } + // if (!response.ok) { + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Error", + // message: data?.error || "Internal Server Error", + // autoClose: 5000, + // }); + // regenerateCaptcha(); + // localStorage.removeItem("access_token"); + // localStorage.clear(); + // sessionStorage.clear(); + // return; + + // } + // // setIsLogging(true); + // if (response.ok) { + // // console.log(data); + // setOtp(""); + // const token = data.token; + // localStorage.setItem("access_token", token); + // localStorage.setItem("pswExpiryDate", data.loginPswExpiry); + // // console.log("Expiry Date:",(dayjs(data.loginPswExpiry)).diff(dayjs(), "day")); + + // // Password Expiry Logic + // if (data.loginPswExpiry && (dayjs(data.loginPswExpiry)).diff(dayjs(), "day") < 0) { + // notifications.show({ + // withBorder: true, + // color: "orange", + // title: "Password Expired", + // message: "Your password has expired, please set a new one.", + // autoClose: 4000, + // }); + // router.push("/ChangePassword"); + // return; + // } + + // if (data.FirstTimeLogin === true) { + // router.push("/SetPassword") + // } + // else { + // router.push("/home"); + // } + + // } + // else { + // regenerateCaptcha(); + // setIsLogging(false); + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Wrong User Id or Password", + // message: "Wrong User Id or Password", + // autoClose: 5000, + // }); + // } + // } + // if (otpRequired && !otpVerified) { + // if (!otp) { + // notifications.show({ + // color: "red", + // title: "Invalid OTP", + // message: "Please enter OTP before verifying", + // }); + // setIsLogging(false); + // return; + // } + // console.log("hi verify mob", mobile); + // const verified = await handleVerifyOtp(mobile); + // if (!verified) { + // notifications.show({ + // title: "Invalid OTP", + // message: "The OTP entered does not match", + // color: "red", + // }); + // return; + // } + + // notifications.show({ + // color: "green", + // title: "OTP Verified", + // message: "Please click Login to continue", + // }); + // setOtpVerified(true); + // setButtonLabel("Login"); + // setIsLogging(false); + // return; + // } + // } + // catch (error: any) { + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Error", + // message: "Internal Server Error,Please try again Later", + // autoClose: 5000, + // }); + // return; + // } + // finally { + // setIsLogging(false); + // } + // } + + + + // For migration User async function handleLogin(e: React.FormEvent) { e.preventDefault(); - const onlyDigit = /^\d{11}$/; - if (!onlyDigit.test(CIF)) { - // setError('Input value must be 11 digit'); - notifications.show({ - withBorder: true, - color: "red", - title: "Invalid UserId", - message: "UserID must be 11 digit", - autoClose: 5000, - }); - return; - } - if (!inputCaptcha) { - notifications.show({ - withBorder: true, - color: "red", - title: "Invalid Captcha", - message: "Please fill the Captcha filed", - autoClose: 5000, - }); - return; - } - if (inputCaptcha !== captcha) { - notifications.show({ - withBorder: true, - color: "red", - title: "Captcha Error", - message: "Please enter the correct captcha", - autoClose: 5000, - }); - regenerateCaptcha(); - return; - } - if (!CIF || !psw) { - notifications.show({ - withBorder: true, - color: "red", - title: "Invalid Input", - message: "Please fill UserId and Password", - autoClose: 5000, - }); - return; - } - try { - const response = await fetch('api/auth/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - customerNo: CIF, - password: psw, - }), - }); - const data = await response.json(); - // console.log(data); - if (data.error === "MIGRATED_USER_HAS_NO_PASSWORD") { - //console.log("Migration issue detected → opening modal"); - setOpened(true); - return; - } - if (!response.ok) { - notifications.show({ - withBorder: true, - color: "red", - title: "Error", - message: data?.error || "Internal Server Error", - autoClose: 5000, - }); - regenerateCaptcha(); - localStorage.removeItem("access_token"); - localStorage.clear(); - sessionStorage.clear(); - return; + setIsLogging(true); // show loading & disable inputs + try { + // --- Validation (before any API call) --- + if (!otpRequired && !otpVerified) { + const onlyDigit = /^\d{11}$/; + + if (!onlyDigit.test(CIF)) { + notifications.show({ + withBorder: true, + color: "red", + title: "Invalid UserId", + message: "UserID must be 11 digit", + autoClose: 5000, + }); + setIsLogging(false); + return; + } + + if (!inputCaptcha) { + notifications.show({ + withBorder: true, + color: "red", + title: "Invalid Captcha", + message: "Please fill the Captcha field", + autoClose: 5000, + }); + setIsLogging(false); + return; + } + + if (inputCaptcha !== captcha) { + notifications.show({ + withBorder: true, + color: "red", + title: "Captcha Error", + message: "Please enter the correct captcha", + autoClose: 5000, + }); + regenerateCaptcha(); + setIsLogging(false); + return; + } + + if (!CIF || !psw) { + notifications.show({ + withBorder: true, + color: "red", + title: "Invalid Input", + message: "Please fill UserId and Password", + autoClose: 5000, + }); + setIsLogging(false); + return; + } } - setIsLogging(true); - if (response.ok) { - // console.log(data); + + // --- LOGIN API FLOW --- + if (!otpRequired || otpVerified) { + const response = await fetch("api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Login-Type": "IB", + }, + body: JSON.stringify({ + customerNo: CIF, + password: psw, + otp: otp, + }), + }); + + const data = await response.json(); + console.log(data); + + // 1️⃣ OTP Required + if (data.status === "OTP_REQUIRED" && response.status === 202) { + setMobile(data.mobile); + setOtpRequired(true); + setButtonLabel("Verify OTP"); + + const otpSent = await handleSendOtp(data.mobile); + if (otpSent) { + notifications.show({ + color: "orange", + title: "OTP Required", + message: "OTP sent to your registered mobile number", + }); + } + setIsLogging(false); + return; + } + + // 2️⃣ Migrated user (no password) + if (data.error === "MIGRATED_USER_HAS_NO_PASSWORD") { + setOpened(true); + setIsLogging(false); + return; + } + + // 3️⃣ Invalid login + if (!response.ok) { + notifications.show({ + withBorder: true, + color: "red", + title: "Error", + message: data?.error || "Internal Server Error", + autoClose: 5000, + }); + regenerateCaptcha(); + localStorage.removeItem("access_token"); + localStorage.clear(); + sessionStorage.clear(); + setIsLogging(false); + return; + } + + // 4️⃣ Successful login + setOtp(""); const token = data.token; localStorage.setItem("access_token", token); localStorage.setItem("pswExpiryDate", data.loginPswExpiry); - // console.log("Expiry Date:",(dayjs(data.loginPswExpiry)).diff(dayjs(), "day")); - // Password Expiry Logic todo - if (data.loginPswExpiry && (dayjs(data.loginPswExpiry)).diff(dayjs(), "day") < 0) { + if ( + data.loginPswExpiry && + dayjs(data.loginPswExpiry).diff(dayjs(), "day") < 0 + ) { notifications.show({ withBorder: true, color: "orange", @@ -161,39 +437,62 @@ export default function Login() { } if (data.FirstTimeLogin === true) { - router.push("/SetPassword") - } - else { + router.push("/SetPassword"); + } else { router.push("/home"); } + } + + // --- OTP Verification Flow --- + if (otpRequired && !otpVerified) { + if (!otp) { + notifications.show({ + color: "red", + title: "Invalid OTP", + message: "Please enter OTP before verifying", + }); + setIsLogging(false); + return; + } + + const verified = await handleVerifyOtp(mobile); + if (!verified) { + notifications.show({ + title: "Invalid OTP", + message: "The OTP entered does not match", + color: "red", + }); + setIsLogging(false); + return; + } - } - else { - regenerateCaptcha(); - setIsLogging(false); notifications.show({ - withBorder: true, - color: "red", - title: "Wrong User Id or Password", - message: "Wrong User Id or Password", - autoClose: 5000, + color: "green", + title: "OTP Verified", + message: "Please click Login to continue", }); + + setOtpVerified(true); + setButtonLabel("Login"); + setIsLogging(false); + return; } - } - catch (error: any) { + } catch (error: any) { notifications.show({ withBorder: true, color: "red", title: "Error", - message: "Internal Server Error,Please try again Later", + message: "Internal Server Error, Please try again later", autoClose: 5000, }); - return; + } finally { + // Ensure we always stop loader if still active + setIsLogging(false); } } - // For migration User - const sendOtp = async () => { + + const sendMigrationOtp = async () => { try { setLoading(true); const res = await fetch(`/api/otp/send/set-password?customerNo=${CIF}`, { @@ -220,7 +519,7 @@ export default function Login() { }; // For migration User - const verifyOtp = async () => { + const verifyMigrationOtp = async () => { if (!otp) { notifications.show({ color: "red", @@ -294,7 +593,7 @@ export default function Login() { Cancel {/* Call the API for send otp */} - @@ -321,7 +620,7 @@ export default function Login() { > Back - @@ -415,7 +714,7 @@ export default function Login() { /> - +
SetPsw(e.currentTarget.value)} withAsterisk - mt="sm" + // disabled={otpRequired} + disabled={isLogging || otpRequired} + readOnly={isLogging} + // mt="sm" /> {/* */} - + {captcha} - + setInputCaptcha(e.currentTarget.value)} withAsterisk - mt="sm" + // disabled={otpRequired} + disabled={isLogging || otpRequired} + readOnly={isLogging} + // mt="sm" /> - + + + + )} + + +