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);
|
||||
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.',
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
</Button>
|
||||
{/* Call the API for send otp */}
|
||||
<Button onClick={sendOtp} loading={loading}>
|
||||
<Button onClick={sendMigrationOtp} loading={loading}>
|
||||
OK
|
||||
</Button>
|
||||
</Group>
|
||||
@@ -321,7 +620,7 @@ export default function Login() {
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button onClick={verifyOtp} loading={loading}>
|
||||
<Button onClick={verifyMigrationOtp} loading={loading}>
|
||||
Verify OTP
|
||||
</Button>
|
||||
</Group>
|
||||
@@ -415,7 +714,7 @@ export default function Login() {
|
||||
/>
|
||||
</div>
|
||||
<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}>
|
||||
<TextInput
|
||||
label="User ID"
|
||||
@@ -426,6 +725,9 @@ export default function Login() {
|
||||
if (input.length <= 11) SetCIF(input);
|
||||
}}
|
||||
withAsterisk
|
||||
// disabled={otpRequired}
|
||||
disabled={isLogging || otpRequired}
|
||||
readOnly={isLogging}
|
||||
/>
|
||||
<PasswordInput
|
||||
label="Password"
|
||||
@@ -433,7 +735,10 @@ export default function Login() {
|
||||
value={psw}
|
||||
onChange={(e) => SetPsw(e.currentTarget.value)}
|
||||
withAsterisk
|
||||
mt="sm"
|
||||
// disabled={otpRequired}
|
||||
disabled={isLogging || otpRequired}
|
||||
readOnly={isLogging}
|
||||
// mt="sm"
|
||||
/>
|
||||
<Box style={{ textAlign: "right" }}>
|
||||
{/* <Anchor
|
||||
@@ -443,9 +748,9 @@ export default function Login() {
|
||||
Forgot Password?
|
||||
</Anchor> */}
|
||||
</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>
|
||||
<Button size="xs" variant="light" onClick={regenerateCaptcha}>Refresh</Button>
|
||||
<Button size="xs" variant="light" onClick={regenerateCaptcha} disabled={otpRequired}>Refresh</Button>
|
||||
</Group>
|
||||
<TextInput
|
||||
label="Enter CAPTCHA"
|
||||
@@ -453,11 +758,41 @@ export default function Login() {
|
||||
value={inputCaptcha}
|
||||
onChange={(e) => setInputCaptcha(e.currentTarget.value)}
|
||||
withAsterisk
|
||||
mt="sm"
|
||||
// disabled={otpRequired}
|
||||
disabled={isLogging || otpRequired}
|
||||
readOnly={isLogging}
|
||||
// mt="sm"
|
||||
/>
|
||||
<Button type="submit" fullWidth mt="md" disabled={isLogging}>
|
||||
{isLogging ? "Logging..." : "Login"}
|
||||
{otpRequired && (
|
||||
<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>
|
||||
|
||||
</form>
|
||||
</Card>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user