feat : From home page user can get statement.
feat :Session timeout for 5 min feat : login time two step verification
This commit is contained in:
@@ -56,7 +56,6 @@ export default function SendToBeneficiaryOwn() {
|
|||||||
setCountdown(180);
|
setCountdown(180);
|
||||||
setTimerActive(true);
|
setTimerActive(true);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('Send OTP failed', err);
|
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
message: err.message || 'Send OTP failed.Please try again later.',
|
message: err.message || 'Send OTP failed.Please try again later.',
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export default function SendToBeneficiaryOthers() {
|
|||||||
setCountdown(180);
|
setCountdown(180);
|
||||||
setTimerActive(true);
|
setTimerActive(true);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('Send OTP failed', err);
|
|
||||||
notifications.show({
|
notifications.show({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
message: err.message || 'Send OTP failed.Please try again later.',
|
message: err.message || 'Send OTP failed.Please try again later.',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ interface SendOtpPayload {
|
|||||||
ref?: string;
|
ref?: string;
|
||||||
date?: string;
|
date?: string;
|
||||||
userOtp?: string;
|
userOtp?: string;
|
||||||
|
username?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStoredMobileNumber(): string | null {
|
function getStoredMobileNumber(): string | null {
|
||||||
@@ -43,7 +44,6 @@ export async function sendOtp(payload: SendOtpPayload) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function verifyOtp(otp: string) {
|
export async function verifyOtp(otp: string) {
|
||||||
try {
|
try {
|
||||||
const mobileNumber = getStoredMobileNumber();
|
const mobileNumber = getStoredMobileNumber();
|
||||||
@@ -58,3 +58,18 @@ export async function verifyOtp(otp: string) {
|
|||||||
throw error.response?.data || error;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,14 @@ import { Text, Button, TextInput, PasswordInput, Title, Card, Group, Flex, Box,
|
|||||||
import { notifications } from "@mantine/notifications";
|
import { notifications } from "@mantine/notifications";
|
||||||
import { Providers } from "@/app/providers";
|
import { Providers } from "@/app/providers";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { sendOtp, verifyLoginOtp } from '@/app/_util/otp';
|
||||||
import NextImage from "next/image";
|
import NextImage from "next/image";
|
||||||
import styles from './page.module.css';
|
import styles from './page.module.css';
|
||||||
import logo from '@/app/image/logo1.jpg';
|
import logo from '@/app/image/logo1.jpg';
|
||||||
import frontPage from '@/app/image/ib_front_1.jpg';
|
import frontPage from '@/app/image/ib_front_1.jpg';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { generateCaptcha } from '@/app/captcha';
|
import { generateCaptcha } from '@/app/captcha';
|
||||||
import { IconShieldLockFilled } from "@tabler/icons-react";
|
import { IconRefresh, IconShieldLockFilled } from "@tabler/icons-react";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
|
||||||
@@ -27,6 +28,46 @@ export default function Login() {
|
|||||||
const [otpStep, setOtpStep] = useState(false);
|
const [otpStep, setOtpStep] = useState(false);
|
||||||
const [otp, setOtp] = useState("");
|
const [otp, setOtp] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
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(() => {
|
useEffect(() => {
|
||||||
const loadCaptcha = async () => {
|
const loadCaptcha = async () => {
|
||||||
@@ -61,94 +102,329 @@ export default function Login() {
|
|||||||
setInputCaptcha("");
|
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) {
|
async function handleLogin(e: React.FormEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const onlyDigit = /^\d{11}$/;
|
setIsLogging(true); // show loading & disable inputs
|
||||||
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;
|
|
||||||
|
|
||||||
|
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) {
|
// --- LOGIN API FLOW ---
|
||||||
// console.log(data);
|
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;
|
const token = data.token;
|
||||||
localStorage.setItem("access_token", token);
|
localStorage.setItem("access_token", token);
|
||||||
localStorage.setItem("pswExpiryDate", data.loginPswExpiry);
|
localStorage.setItem("pswExpiryDate", data.loginPswExpiry);
|
||||||
// console.log("Expiry Date:",(dayjs(data.loginPswExpiry)).diff(dayjs(), "day"));
|
|
||||||
|
|
||||||
// Password Expiry Logic todo
|
if (
|
||||||
if (data.loginPswExpiry && (dayjs(data.loginPswExpiry)).diff(dayjs(), "day") < 0) {
|
data.loginPswExpiry &&
|
||||||
|
dayjs(data.loginPswExpiry).diff(dayjs(), "day") < 0
|
||||||
|
) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
withBorder: true,
|
withBorder: true,
|
||||||
color: "orange",
|
color: "orange",
|
||||||
@@ -161,39 +437,62 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.FirstTimeLogin === true) {
|
if (data.FirstTimeLogin === true) {
|
||||||
router.push("/SetPassword")
|
router.push("/SetPassword");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
router.push("/home");
|
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({
|
notifications.show({
|
||||||
withBorder: true,
|
color: "green",
|
||||||
color: "red",
|
title: "OTP Verified",
|
||||||
title: "Wrong User Id or Password",
|
message: "Please click Login to continue",
|
||||||
message: "Wrong User Id or Password",
|
|
||||||
autoClose: 5000,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setOtpVerified(true);
|
||||||
|
setButtonLabel("Login");
|
||||||
|
setIsLogging(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
} catch (error: any) {
|
||||||
catch (error: any) {
|
|
||||||
notifications.show({
|
notifications.show({
|
||||||
withBorder: true,
|
withBorder: true,
|
||||||
color: "red",
|
color: "red",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
message: "Internal Server Error,Please try again Later",
|
message: "Internal Server Error, Please try again later",
|
||||||
autoClose: 5000,
|
autoClose: 5000,
|
||||||
});
|
});
|
||||||
return;
|
} finally {
|
||||||
|
// Ensure we always stop loader if still active
|
||||||
|
setIsLogging(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For migration User
|
|
||||||
const sendOtp = async () => {
|
const sendMigrationOtp = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await fetch(`/api/otp/send/set-password?customerNo=${CIF}`, {
|
const res = await fetch(`/api/otp/send/set-password?customerNo=${CIF}`, {
|
||||||
@@ -220,7 +519,7 @@ export default function Login() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For migration User
|
// For migration User
|
||||||
const verifyOtp = async () => {
|
const verifyMigrationOtp = async () => {
|
||||||
if (!otp) {
|
if (!otp) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: "red",
|
color: "red",
|
||||||
@@ -294,7 +593,7 @@ export default function Login() {
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
{/* Call the API for send otp */}
|
{/* Call the API for send otp */}
|
||||||
<Button onClick={sendOtp} loading={loading}>
|
<Button onClick={sendMigrationOtp} loading={loading}>
|
||||||
OK
|
OK
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -321,7 +620,7 @@ export default function Login() {
|
|||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={verifyOtp} loading={loading}>
|
<Button onClick={verifyMigrationOtp} loading={loading}>
|
||||||
Verify OTP
|
Verify OTP
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -415,7 +714,7 @@ export default function Login() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Box w={{ base: "100%", md: "45%" }} p="lg">
|
<Box w={{ base: "100%", md: "45%" }} p="lg">
|
||||||
<Card shadow="md" padding="xl" radius="md" style={{ maxWidth: 550, margin: "0 auto", height: '68vh' }}>
|
<Card shadow="md" padding="xl" radius="md" style={{ maxWidth: 550, height: '70vh' }}>
|
||||||
<form onSubmit={handleLogin}>
|
<form onSubmit={handleLogin}>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="User ID"
|
label="User ID"
|
||||||
@@ -426,6 +725,9 @@ export default function Login() {
|
|||||||
if (input.length <= 11) SetCIF(input);
|
if (input.length <= 11) SetCIF(input);
|
||||||
}}
|
}}
|
||||||
withAsterisk
|
withAsterisk
|
||||||
|
// disabled={otpRequired}
|
||||||
|
disabled={isLogging || otpRequired}
|
||||||
|
readOnly={isLogging}
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label="Password"
|
label="Password"
|
||||||
@@ -433,7 +735,10 @@ export default function Login() {
|
|||||||
value={psw}
|
value={psw}
|
||||||
onChange={(e) => SetPsw(e.currentTarget.value)}
|
onChange={(e) => SetPsw(e.currentTarget.value)}
|
||||||
withAsterisk
|
withAsterisk
|
||||||
mt="sm"
|
// disabled={otpRequired}
|
||||||
|
disabled={isLogging || otpRequired}
|
||||||
|
readOnly={isLogging}
|
||||||
|
// mt="sm"
|
||||||
/>
|
/>
|
||||||
<Box style={{ textAlign: "right" }}>
|
<Box style={{ textAlign: "right" }}>
|
||||||
{/* <Anchor
|
{/* <Anchor
|
||||||
@@ -443,9 +748,9 @@ export default function Login() {
|
|||||||
Forgot Password?
|
Forgot Password?
|
||||||
</Anchor> */}
|
</Anchor> */}
|
||||||
</Box>
|
</Box>
|
||||||
<Group mt="sm" align="center">
|
<Group align="center">
|
||||||
<Box style={{ backgroundColor: "#fff", fontSize: "18px", textDecoration: "line-through", padding: "4px 8px", fontFamily: "cursive" }}>{captcha}</Box>
|
<Box style={{ backgroundColor: "#fff", fontSize: "18px", textDecoration: "line-through", padding: "4px 8px", fontFamily: "cursive" }}>{captcha}</Box>
|
||||||
<Button size="xs" variant="light" onClick={regenerateCaptcha}>Refresh</Button>
|
<Button size="xs" variant="light" onClick={regenerateCaptcha} disabled={otpRequired}>Refresh</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Enter CAPTCHA"
|
label="Enter CAPTCHA"
|
||||||
@@ -453,11 +758,41 @@ export default function Login() {
|
|||||||
value={inputCaptcha}
|
value={inputCaptcha}
|
||||||
onChange={(e) => setInputCaptcha(e.currentTarget.value)}
|
onChange={(e) => setInputCaptcha(e.currentTarget.value)}
|
||||||
withAsterisk
|
withAsterisk
|
||||||
mt="sm"
|
// disabled={otpRequired}
|
||||||
|
disabled={isLogging || otpRequired}
|
||||||
|
readOnly={isLogging}
|
||||||
|
// mt="sm"
|
||||||
/>
|
/>
|
||||||
<Button type="submit" fullWidth mt="md" disabled={isLogging}>
|
{otpRequired && (
|
||||||
{isLogging ? "Logging..." : "Login"}
|
<Group align="end" gap="xs">
|
||||||
|
<PasswordInput
|
||||||
|
label="Enter OTP"
|
||||||
|
placeholder="Enter OTP"
|
||||||
|
value={otp}
|
||||||
|
onChange={(e) => setOtp(e.currentTarget.value)}
|
||||||
|
withAsterisk
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
readOnly={otpVerified}
|
||||||
|
/>
|
||||||
|
<Tooltip label="Resend OTP">
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
px={10}
|
||||||
|
disabled={isLogging || otpVerified}
|
||||||
|
onClick={() => handleSendOtp(mobile)}
|
||||||
|
style={{ alignSelf: "flex-end", marginBottom: 4 }}
|
||||||
|
>
|
||||||
|
<IconRefresh size={20} /> Resend
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button type="submit" fullWidth mt="md" loading={isLogging} disabled={isLogging}>
|
||||||
|
{isLogging ? "Processing..." : buttonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user