feat : OTP verified all page

This commit is contained in:
2025-10-14 13:00:54 +05:30
12 changed files with 227 additions and 136 deletions

View File

@@ -4,6 +4,7 @@ import { TextInput, Button, Grid, Text, PasswordInput, Loader, Group, Select, St
import { notifications } from '@mantine/notifications'; import { notifications } from '@mantine/notifications';
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { IconRefresh } from '@tabler/icons-react'; import { IconRefresh } from '@tabler/icons-react';
import { sendOtp, verifyOtp } from '@/app/_util/otp';
export default function AddBeneficiaryOthers() { export default function AddBeneficiaryOthers() {
const router = useRouter(); const router = useRouter();
@@ -18,7 +19,6 @@ export default function AddBeneficiaryOthers() {
const [nickName, setNickName] = useState(''); const [nickName, setNickName] = useState('');
const [beneficiaryName, setBeneficiaryName] = useState<string | null>(null); const [beneficiaryName, setBeneficiaryName] = useState<string | null>(null);
const [otp, setOtp] = useState(''); const [otp, setOtp] = useState('');
const [generatedOtp, setGeneratedOtp] = useState('');
const [otpSent, setOtpSent] = useState(false); const [otpSent, setOtpSent] = useState(false);
const [otpVerified, setOtpVerified] = useState(false); const [otpVerified, setOtpVerified] = useState(false);
const [countdown, setCountdown] = useState(180); const [countdown, setCountdown] = useState(180);
@@ -27,19 +27,39 @@ export default function AddBeneficiaryOthers() {
const [isVisibilityLocked, setIsVisibilityLocked] = useState(false); const [isVisibilityLocked, setIsVisibilityLocked] = useState(false);
const [showPayeeAcc, setShowPayeeAcc] = useState(true); const [showPayeeAcc, setShowPayeeAcc] = useState(true);
const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); }; const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); };
const [generateOtp, setGenerateOtp] = useState("");
const handleGenerateOtp = async () => { async function handleSendOtp() {
const value = "123456"; // Or call your API to generate OTP const mobileNumber = localStorage.getItem('remitter_mobile_no');
setGeneratedOtp(value); if (!mobileNumber) {
notifications.show({
// start countdown at 180 seconds (3 minutes) title: 'Error',
message: 'Mobile number not found.Contact to administrator',
color: 'red',
});
return;
}
try {
await sendOtp({ type: 'BENEFICIARY_ADD', beneficiary: beneficiaryName || undefined, ifsc: ifsccode });
setCountdown(180); setCountdown(180);
setTimerActive(true); 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.',
color: 'red',
});
}
}
return value; async function handleVerifyOtp() {
}; try {
await verifyOtp(otp);
return true;
} catch {
return false;
}
}
// Countdown effect // Countdown effect
useEffect(() => { useEffect(() => {
@@ -113,29 +133,6 @@ export default function AddBeneficiaryOthers() {
} }
}, [ifsccode]); }, [ifsccode]);
// useEffect(() => {
// let interval: number | undefined;
// if (timerActive && countdown > 0) {
// interval = window.setInterval(() => {
// setCountdown((prev) => prev - 1);
// }, 1000);
// }
// if (countdown === 0) {
// if (interval) clearInterval(interval);
// setTimerActive(false);
// }
// return () => {
// if (interval) clearInterval(interval);
// };
// }, [timerActive, countdown]);
// const handleGenerateOtp = async () => {
// const value = "123456"; // Or generate a random OTP
// setGeneratedOtp(value);
// return value;
// };
const validateAndSendOtp = async () => { const validateAndSendOtp = async () => {
if (!bankName || !ifsccode || !branchName || !accountNo || !confirmAccountNo || !beneficiaryType) { if (!bankName || !ifsccode || !branchName || !accountNo || !confirmAccountNo || !beneficiaryType) {
notifications.show({ notifications.show({
@@ -202,8 +199,7 @@ export default function AddBeneficiaryOthers() {
setValidationStatus("success"); setValidationStatus("success");
setIsVisibilityLocked(true); setIsVisibilityLocked(true);
setOtpSent(true); setOtpSent(true);
await handleGenerateOtp(); const otp = await handleSendOtp();
notifications.show({ notifications.show({
withBorder: true, withBorder: true,
color: "green", color: "green",
@@ -227,7 +223,7 @@ export default function AddBeneficiaryOthers() {
} }
}; };
const verifyOtp = () => { const verify_otp = async () => {
if (!otp) { if (!otp) {
notifications.show({ notifications.show({
withBorder: true, withBorder: true,
@@ -239,24 +235,21 @@ export default function AddBeneficiaryOthers() {
return; return;
} }
if (otp === generatedOtp) { const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({
title: "Invalid OTP",
message: "The OTP entered does not match",
color: "red",
});
return;
}
setOtpVerified(true); setOtpVerified(true);
notifications.show({ notifications.show({
withBorder: true,
color: "green",
title: "OTP Verified", title: "OTP Verified",
message: "OTP validated successfully.", message: "OTP successfully verified.",
autoClose: 5000, color: "green",
}); });
} else {
notifications.show({
withBorder: true,
color: "red",
title: "OTP Error",
message: "OTP does not match.",
autoClose: 5000,
});
}
}; };
const AddBen = async () => { const AddBen = async () => {
try { try {
@@ -468,14 +461,14 @@ export default function AddBeneficiaryOthers() {
<IconRefresh <IconRefresh
size={22} size={22}
style={{ cursor: "pointer", color: "blue", marginBottom: "6px" }} style={{ cursor: "pointer", color: "blue", marginBottom: "6px" }}
onClick={handleGenerateOtp} onClick={handleSendOtp}
/> />
) )
)} )}
</Grid.Col > </Grid.Col >
<Grid.Col > <Grid.Col >
{!otpVerified ? ( {!otpVerified ? (
<Button onClick={verifyOtp}>Validate OTP</Button> <Button onClick={verify_otp}>Validate OTP</Button>
) : ( ) : (
<Button color="blue" size="sm" onClick={AddBen}> <Button color="blue" size="sm" onClick={AddBen}>
Add Add

View File

@@ -41,16 +41,6 @@ export default function QuickPay() {
const [countdown, setCountdown] = useState(180); const [countdown, setCountdown] = useState(180);
const [timerActive, setTimerActive] = useState(false); const [timerActive, setTimerActive] = useState(false);
const [otp, setOtp] = useState(""); const [otp, setOtp] = useState("");
const [generateOtp, setGenerateOtp] = useState("");
// async function handleGenerateOtp() {
// // const value = await generateOTP(6);
// const value = "123456";
// setGenerateOtp(value);
// setCountdown(180);
// setTimerActive(true);
// return value;
// }
async function handleSendOtp() { async function handleSendOtp() {
const mobileNumber = localStorage.getItem('remitter_mobile_no'); const mobileNumber = localStorage.getItem('remitter_mobile_no');
@@ -63,7 +53,7 @@ export default function QuickPay() {
return; return;
} }
try { try {
await sendOtp({ type: 'BENEFICIARY_DELETE' }); await sendOtp({ type: 'IMPS' });
setShowOtpField(true); setShowOtpField(true);
setCountdown(180); setCountdown(180);
setTimerActive(true); setTimerActive(true);
@@ -86,10 +76,6 @@ export default function QuickPay() {
} }
} }
const selectedAccount = accountData.find((acc) => acc.stAccountNo === selectedAccNo); const selectedAccount = accountData.find((acc) => acc.stAccountNo === selectedAccNo);
const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); }; const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); };
@@ -251,7 +237,16 @@ export default function QuickPay() {
}); });
return; return;
} }
if (otp !== generateOtp) { // if (otp !== generateOtp) {
// notifications.show({
// title: "Invalid OTP",
// message: "The OTP entered does not match",
// color: "red",
// });
// return;
// }
const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "The OTP entered does not match", message: "The OTP entered does not match",

View File

@@ -24,7 +24,6 @@ export default function ViewBeneficiary() {
const [authorized, setAuthorized] = useState<boolean | null>(null); const [authorized, setAuthorized] = useState<boolean | null>(null);
const [beneficiaries, setBeneficiaries] = useState<Beneficiary[]>([]); const [beneficiaries, setBeneficiaries] = useState<Beneficiary[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const [otpStep, setOtpStep] = useState(false); const [otpStep, setOtpStep] = useState(false);
const [otp, setOtp] = useState(""); const [otp, setOtp] = useState("");
@@ -32,6 +31,14 @@ export default function ViewBeneficiary() {
const [countdown, setCountdown] = useState(180); const [countdown, setCountdown] = useState(180);
const [timerActive, setTimerActive] = useState(false); const [timerActive, setTimerActive] = useState(false);
const maskAccount = (account: string) => {
if (!account) return undefined;
const length = account.length;
if (length <= 4) return account;
const masked = "X".repeat(length - 4) + account.slice(length - 4);
return masked;
};
async function handleSendOtp() { async function handleSendOtp() {
const mobileNumber = localStorage.getItem('remitter_mobile_no'); const mobileNumber = localStorage.getItem('remitter_mobile_no');
if (!mobileNumber) { if (!mobileNumber) {
@@ -43,7 +50,7 @@ export default function ViewBeneficiary() {
return; return;
} }
try { try {
await sendOtp({ type: 'IMPS' }); // type will be "BENEFICIARY_DELETE" await sendOtp({ type: 'BENEFICIARY_DELETE', beneficiary: selectedAccount ? maskAccount(selectedAccount) : undefined });
setCountdown(180); setCountdown(180);
setTimerActive(true); setTimerActive(true);
} catch (err: any) { } catch (err: any) {

View File

@@ -23,15 +23,7 @@ export default function ChangePassword() {
const [step, setStep] = useState<"form" | "otp" | "final">("form"); const [step, setStep] = useState<"form" | "otp" | "final">("form");
const [passwordHistory] = useState(["Pass@1234", "OldPass@123", "MyPass#2023"]); const [passwordHistory] = useState(["Pass@1234", "OldPass@123", "MyPass#2023"]);
const icon = <IconLock size={18} stroke={1.5} />; const icon = <IconLock size={18} stroke={1.5} />;
const [showOtpField, setShowOtpField] = useState(false);
// const handleGenerateOtp = async () => {
// const value = "123456"; // Or generate a random OTP
// setGeneratedOtp(value);
// setCountdown(180);
// setTimerActive(true);
// return value;
// };
async function handleSendOtp() { async function handleSendOtp() {
const mobileNumber = localStorage.getItem('remitter_mobile_no'); const mobileNumber = localStorage.getItem('remitter_mobile_no');
@@ -45,7 +37,6 @@ export default function ChangePassword() {
} }
try { try {
await sendOtp({ type: 'CHANGE_LPWORD' }); await sendOtp({ type: 'CHANGE_LPWORD' });
setShowOtpField(true);
setCountdown(180); setCountdown(180);
setTimerActive(true); setTimerActive(true);
} catch (err: any) { } catch (err: any) {
@@ -152,10 +143,11 @@ export default function ChangePassword() {
// Step 2 → validate OTP // Step 2 → validate OTP
if (step === "otp") { if (step === "otp") {
if (otp !== generatedOtp) { const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "Please enter the correct OTP.", message: "The OTP entered does not match",
color: "red", color: "red",
}); });
return; return;

View File

@@ -151,10 +151,11 @@ export default function ChangePassword() {
// Step 2 → validate OTP // Step 2 → validate OTP
if (step === "otp") { if (step === "otp") {
if (otp !== generatedOtp) { const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "Please enter the correct OTP.", message: "The OTP entered does not match",
color: "red", color: "red",
}); });
return; return;

View File

@@ -159,10 +159,11 @@ export default function ChangePassword() {
} }
// Step 2 → validate OTP // Step 2 → validate OTP
if (step === "otp") { if (step === "otp") {
if (otp !== generatedOtp) { const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "Please enter the correct OTP.", message: "The OTP entered does not match",
color: "red", color: "red",
}); });
return; return;

View File

@@ -19,6 +19,7 @@ import logo from "@/app/image/logo1.jpg";
import changePwdImage from "@/app/image/change_password.jpg"; // reuse background illustration import changePwdImage from "@/app/image/change_password.jpg"; // reuse background illustration
import { generateCaptcha } from "@/app/captcha"; import { generateCaptcha } from "@/app/captcha";
import { IconLock, IconRefresh } from "@tabler/icons-react"; import { IconLock, IconRefresh } from "@tabler/icons-react";
import { sendOtp, verifyOtp } from "../_util/otp";
export default function ChangePassword() { export default function ChangePassword() {
const router = useRouter(); const router = useRouter();
@@ -38,13 +39,49 @@ export default function ChangePassword() {
const icon = <IconLock size={18} stroke={1.5} />; const icon = <IconLock size={18} stroke={1.5} />;
// OTP generation // OTP generation
const handleGenerateOtp = async () => { // const handleGenerateOtp = async () => {
const value = "123456"; // replace with backend OTP if available // const value = "123456"; // replace with backend OTP if available
setGeneratedOtp(value); // setGeneratedOtp(value);
// setCountdown(180);
// setTimerActive(true);
// return value;
// };
async function handleSendOtp() {
const mobileNumber = localStorage.getItem('remitter_mobile_no');
if (!mobileNumber) {
notifications.show({
title: 'Error',
message: 'Mobile number not found.Contact to administrator',
color: 'red',
});
return;
}
try {
await sendOtp({ type: 'CHANGE_LPWORD' });
setCountdown(180); setCountdown(180);
setTimerActive(true); setTimerActive(true);
return value; } catch (err: any) {
}; console.error('Send OTP failed', err);
notifications.show({
title: 'Error',
message: err.message || 'Send OTP failed.Please try again later.',
color: 'red',
});
}
}
async function handleVerifyOtp() {
try {
await verifyOtp(otp);
return true;
} catch {
return false;
}
}
useEffect(() => { useEffect(() => {
regenerateCaptcha(); regenerateCaptcha();
@@ -120,7 +157,7 @@ export default function ChangePassword() {
return; return;
} }
await handleGenerateOtp(); await handleSendOtp();
setStep("otp"); setStep("otp");
notifications.show({ notifications.show({
title: "OTP Sent", title: "OTP Sent",
@@ -130,11 +167,13 @@ export default function ChangePassword() {
return; return;
} }
// Step 2 → validate OTP
if (step === "otp") { if (step === "otp") {
if (otp !== generatedOtp) { const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "Please enter the correct OTP.", message: "The OTP entered does not match",
color: "red", color: "red",
}); });
return; return;
@@ -363,7 +402,7 @@ export default function ChangePassword() {
<Button <Button
variant="subtle" variant="subtle"
px={8} px={8}
onClick={handleGenerateOtp} onClick={handleSendOtp}
leftSection={<IconRefresh size={16} />} leftSection={<IconRefresh size={16} />}
> >
Resend Resend

View File

@@ -13,6 +13,7 @@ import { generateOTP } from "../OTPGenerator";
import { sendOtp, verifyOtp } from "../_util/otp"; import { sendOtp, verifyOtp } from "../_util/otp";
//const [showOtpField, setShowOtpField] = useState(false); //const [showOtpField, setShowOtpField] = useState(false);
export default function SetLoginPwd() { export default function SetLoginPwd() {
const router = useRouter(); const router = useRouter();
const [authorized, SetAuthorized] = useState<boolean | null>(null); const [authorized, SetAuthorized] = useState<boolean | null>(null);
@@ -26,18 +27,8 @@ export default function SetLoginPwd() {
const [timerActive, setTimerActive] = useState(false); const [timerActive, setTimerActive] = useState(false);
const icon = <IconLock size={18} stroke={1.5} />; const icon = <IconLock size={18} stroke={1.5} />;
const [generateOtp, setGenerateOtp] = useState(""); const [generateOtp, setGenerateOtp] = useState("");
const [showOtpField, setShowOtpField] = useState(false); const [step, setStep] = useState<"form" | "otp" | "final">("form");
const [otpValidated, setOtpValidated] = useState(false);
// async function handleGenerateOtp() {
// // const value = await generateOTP(6);
// const value = "123456";
// setGenerateOtp(value);
// setCountdown(60);
// setTimerActive(true);
// // return value;
// }
async function handleSendOtp() { async function handleSendOtp() {
const mobileNumber = localStorage.getItem('remitter_mobile_no'); const mobileNumber = localStorage.getItem('remitter_mobile_no');
@@ -51,7 +42,6 @@ export default function SetLoginPwd() {
} }
try { try {
await sendOtp({ type: 'CHANGE_LPWORD' }); await sendOtp({ type: 'CHANGE_LPWORD' });
setShowOtpField(true);
setCountdown(180); setCountdown(180);
setTimerActive(true); setTimerActive(true);
} catch (err: any) { } catch (err: any) {
@@ -73,10 +63,6 @@ export default function SetLoginPwd() {
} }
} }
async function handleLogout(e: React.FormEvent) { async function handleLogout(e: React.FormEvent) {
e.preventDefault(); e.preventDefault();
localStorage.removeItem("access_token"); localStorage.removeItem("access_token");
@@ -159,7 +145,11 @@ export default function SetLoginPwd() {
}); });
return; return;
} }
if (otp !== generateOtp) {
// Step 2 → validate OTP
if (step === "otp") {
const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "The OTP entered does not match", message: "The OTP entered does not match",
@@ -167,6 +157,18 @@ export default function SetLoginPwd() {
}); });
return; return;
} }
setOtpValidated(true);
setStep("final");
notifications.show({
title: "OTP Verified",
message: "OTP has been successfully verified.",
color: "green",
});
return;
}
const token = localStorage.getItem("access_token"); const token = localStorage.getItem("access_token");
const response = await fetch('api/auth/login_password', { const response = await fetch('api/auth/login_password', {
method: 'POST', method: 'POST',

View File

@@ -9,6 +9,9 @@ import logo from '@/app/image/logo1.jpg';
import changePwdImage from '@/app/image/set_tran_pass.jpg'; import changePwdImage from '@/app/image/set_tran_pass.jpg';
import { generateCaptcha } from '@/app/captcha'; import { generateCaptcha } from '@/app/captcha';
import { IconLock, IconLogout, IconRefresh } from '@tabler/icons-react'; import { IconLock, IconLogout, IconRefresh } from '@tabler/icons-react';
import { sendOtp, verifyOtp } from "../_util/otp";
export default function SetTransactionPwd() { export default function SetTransactionPwd() {
const router = useRouter(); const router = useRouter();
@@ -23,6 +26,45 @@ export default function SetTransactionPwd() {
const [timerActive, setTimerActive] = useState(false); const [timerActive, setTimerActive] = useState(false);
const icon = <IconLock size={18} stroke={1.5} />; const icon = <IconLock size={18} stroke={1.5} />;
const [generateOtp, setGenerateOtp] = useState(""); const [generateOtp, setGenerateOtp] = useState("");
const [showOtpField, setShowOtpField] = useState(false);
const [step, setStep] = useState<"form" | "otp" | "final">("form");
const [otpValidated, setOtpValidated] = useState(false);
async function handleSendOtp() {
const mobileNumber = localStorage.getItem('remitter_mobile_no');
if (!mobileNumber) {
notifications.show({
title: 'Error',
message: 'Mobile number not found.Contact to administrator',
color: 'red',
});
return;
}
try {
await sendOtp({ type: 'CHANGE_LPWORD' });
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.',
color: 'red',
});
}
}
async function handleVerifyOtp() {
try {
await verifyOtp(otp);
return true;
} catch {
return false;
}
}
async function handleGenerateOtp() { async function handleGenerateOtp() {
// const value = await generateOTP(6); // const value = await generateOTP(6);
@@ -128,7 +170,7 @@ export default function SetTransactionPwd() {
} }
if (!captchaValidate) { if (!captchaValidate) {
setCaptchaValidate(true); setCaptchaValidate(true);
handleGenerateOtp(); handleSendOtp();
return; return;
} }
if (!otp) { if (!otp) {
@@ -139,7 +181,11 @@ export default function SetTransactionPwd() {
}); });
return; return;
} }
if (otp !== generateOtp) {
// Step 2 → validate OTP
if (step === "otp") {
const verified = await handleVerifyOtp();
if (!verified) {
notifications.show({ notifications.show({
title: "Invalid OTP", title: "Invalid OTP",
message: "The OTP entered does not match", message: "The OTP entered does not match",
@@ -147,6 +193,21 @@ export default function SetTransactionPwd() {
}); });
return; return;
} }
setOtpValidated(true);
setStep("final");
notifications.show({
title: "OTP Verified",
message: "OTP has been successfully verified.",
color: "green",
});
return;
}
const token = localStorage.getItem("access_token"); const token = localStorage.getItem("access_token");
const response = await fetch('api/auth/transaction_password', { const response = await fetch('api/auth/transaction_password', {
method: 'POST', method: 'POST',
@@ -306,7 +367,7 @@ export default function SetTransactionPwd() {
<Button <Button
variant="subtle" variant="subtle"
px={8} px={8}
onClick={handleGenerateOtp} onClick={handleSendOtp}
leftSection={<IconRefresh size={16} />} leftSection={<IconRefresh size={16} />}
> >
Resend Resend

View File

@@ -19,7 +19,7 @@ export default function ValidateUser() {
const headerRef = useRef<HTMLHeadingElement>(null); const headerRef = useRef<HTMLHeadingElement>(null);
const validUsers = [ const validUsers = [
{ cif: "11111111111", mobile: "7890544527" }, { cif: "11111111111", mobile: "6297421727" },
{ cif: "30022497139", mobile: "6230573848" }, { cif: "30022497139", mobile: "6230573848" },
{ cif: "11122233344", mobile: "9998887776" }, { cif: "11122233344", mobile: "9998887776" },
]; ];

View File

@@ -17,7 +17,7 @@ interface SendOtpPayload {
function getStoredMobileNumber(): string | null { function getStoredMobileNumber(): string | null {
// const mobileNumber = localStorage.getItem('remitter_mobile_no'); // const mobileNumber = localStorage.getItem('remitter_mobile_no');
const mobileNumber = "7890544527"; const mobileNumber = "6297421727";
if (!mobileNumber) { if (!mobileNumber) {
notifications.show({ notifications.show({
title: 'Missing Mobile Number', title: 'Missing Mobile Number',

View File

@@ -70,7 +70,7 @@ export default function Login() {
try { try {
if (mobile) { if (mobile) {
// await verifyLoginOtp(otp, mobile); // await verifyLoginOtp(otp, mobile);
await verifyLoginOtp(otp, '7890544527'); await verifyLoginOtp(otp, '6297421727');
return true; return true;
} }
} catch { } catch {