diff --git a/src/app/(main)/funds_transfer/page.tsx b/src/app/(main)/funds_transfer/page.tsx
index 27d035e..78b6087 100644
--- a/src/app/(main)/funds_transfer/page.tsx
+++ b/src/app/(main)/funds_transfer/page.tsx
@@ -9,6 +9,7 @@ import OutsideQuickPay from "./outside_quick_pay";
import { IconRefresh } from "@tabler/icons-react";
import Image from "next/image";
import img from '@/app/image/logo1.jpg'
+import { sendOtp, verifyOtp } from "@/app/_util/otp";
interface accountData {
stAccountNo: string;
@@ -42,15 +43,53 @@ export default function QuickPay() {
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 handleGenerateOtp() {
+ // // const value = await generateOTP(6);
+ // const value = "123456";
+ // setGenerateOtp(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: 'BENEFICIARY_DELETE' });
+ setShowOtpField(true);
+ 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;
+ }
+ }
+
+
+
+
+
const selectedAccount = accountData.find((acc) => acc.stAccountNo === selectedAccNo);
const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); };
@@ -335,7 +374,7 @@ export default function QuickPay() {
color="blue"
onClick={async () => {
setConfirmModel(false);
- const otp = await handleGenerateOtp();
+ const otp = await handleSendOtp();
setShowOtpField(true);
notifications.show({
title: "OTP Sent",
@@ -484,7 +523,7 @@ export default function QuickPay() {
)
)}
diff --git a/src/app/(main)/funds_transfer/view_beneficiary/page.tsx b/src/app/(main)/funds_transfer/view_beneficiary/page.tsx
index 3b4628e..02d1258 100644
--- a/src/app/(main)/funds_transfer/view_beneficiary/page.tsx
+++ b/src/app/(main)/funds_transfer/view_beneficiary/page.tsx
@@ -1,12 +1,14 @@
"use client";
import React, { useEffect, useState } from "react";
-import { Center, Group, Loader, Paper, ScrollArea, Table, Text, Title, TextInput, Button, } from "@mantine/core";
+import { Center, Group, Loader, Paper, ScrollArea, Table, Text, Title, TextInput, Button, Modal } from "@mantine/core";
import { notifications } from "@mantine/notifications";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { getBankLogo } from "@/app/_util/getBankLogo";
-import { IconTrash } from "@tabler/icons-react";
+import { IconRefresh, IconTrash } from "@tabler/icons-react";
+import { sendOtp, verifyOtp } from "@/app/_util/otp";
+
interface Beneficiary {
accountNo: string;
@@ -23,6 +25,118 @@ export default function ViewBeneficiary() {
const [beneficiaries, setBeneficiaries] = useState([]);
const [loading, setLoading] = useState(true);
+ const [opened, setOpened] = useState(false);
+ const [otpStep, setOtpStep] = useState(false);
+ const [otp, setOtp] = useState("");
+ const [selectedAccount, setSelectedAccount] = useState(null);
+ const [countdown, setCountdown] = useState(180);
+ const [timerActive, setTimerActive] = 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: 'IMPS' }); // type will be "BENEFICIARY_DELETE"
+ 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;
+ }
+ }
+
+ const openDeleteModal = (accountNo: string) => {
+ setSelectedAccount(accountNo);
+ setOpened(true);
+ setOtpStep(false); // Reset modal to confirmation step
+ setOtp("");
+ };
+
+
+ const handleModalConfirm = async () => {
+ if (!selectedAccount) return;
+
+ try {
+ await handleSendOtp(); // send OTP to user's mobile
+ setOtpStep(true); // move modal to OTP input step
+ } catch (err) {
+ notifications.show({
+ title: "Error",
+ message: "Failed to send OTP. Please try again.",
+ color: "red",
+ });
+ }
+ };
+
+ // Step 3: Handle OTP verification & delete
+ const handleVerifyOtpAndDelete = async () => {
+ if (!otp || !selectedAccount) return;
+
+ try {
+ const isOtpValid = await handleVerifyOtp(); // verify OTP
+ if (!isOtpValid) {
+ notifications.show({
+ title: "Invalid OTP",
+ message: "Please enter the correct OTP.",
+ color: "red",
+ });
+ return;
+ }
+
+ // OTP is valid → delete beneficiary
+ const token = localStorage.getItem("access_token");
+ const res = await fetch(`/api/beneficiary/${selectedAccount}`, {
+ method: "DELETE",
+ headers: { Authorization: `Bearer ${token}` },
+ });
+
+ if (res.ok) {
+ setBeneficiaries((prev) => prev.filter((b) => b.accountNo !== selectedAccount));
+ notifications.show({
+ title: "Deleted",
+ message: "Beneficiary deleted successfully.",
+ color: "green",
+ });
+ setOpened(false);
+ setOtpStep(false);
+ setOtp("");
+ } else {
+ notifications.show({
+ title: "Error",
+ message: "Failed to delete beneficiary.",
+ color: "red",
+ });
+ }
+ } catch (err) {
+ notifications.show({
+ title: "Error",
+ message: "Something went wrong.",
+ color: "red",
+ });
+ }
+ };
+
+
useEffect(() => {
const token = localStorage.getItem("access_token");
if (!token) {
@@ -59,6 +173,23 @@ export default function ViewBeneficiary() {
fetchBeneficiaries();
}, []);
+ //new use effect
+ useEffect(() => {
+ let interval: NodeJS.Timeout | null = null;
+ if (timerActive && countdown > 0) {
+ interval = setInterval(() => {
+ setCountdown((prev) => prev - 1);
+ }, 1000);
+ } else if (countdown === 0) {
+ setTimerActive(false);
+ }
+ return () => {
+ if (interval) clearInterval(interval);
+ };
+ }, [timerActive, countdown]);
+
+
+
if (loading) {
return (
@@ -69,60 +200,6 @@ export default function ViewBeneficiary() {
if (!authorized) return null;
- //add delete beneficiary
-
-
- async function handleDeleteBeneficiary(accountNo: string) {
- const isConfirmed = window.confirm("Are you sure you want to delete this beneficiary?");
- if (!isConfirmed) return;
-
- try {
- const token = localStorage.getItem("access_token");
-
- // Step 1: generate OTP
- await fetch("/api/otp/generate", {
- method: "POST",
- headers: { Authorization: `Bearer ${token}` },
- });
- const otp = prompt("An OTP has been sent to your registered number. Please enter it:");
-
- if (!otp) return; // cancelled
-
- // Step 2: verify OTP
- const verify = await fetch("/api/otp/verify", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${token}`,
- },
- body: JSON.stringify({ otp }),
- });
- if (!verify.ok) {
- alert("Invalid OTP!");
- return;
- }
-
- // Step 3: delete beneficiary
- const res = await fetch(`/api/beneficiary/${accountNo}`, {
- method: "DELETE",
- headers: { Authorization: `Bearer ${token}` },
- });
-
- if (res.ok) {
- setBeneficiaries((prev) => prev.filter((b) => b.accountNo !== accountNo));
- alert("Beneficiary deleted successfully.");
- } else {
- alert("Error deleting beneficiary.");
- }
- } catch (err) {
- alert("Something went wrong. Please try again.");
- }
- }
-
-
-
-
- //add delete beneficiary
return (
@@ -131,52 +208,115 @@ export default function ViewBeneficiary() {
{beneficiaries.length === 0 ? (
No beneficiaries found.
) : (
-
-
-
-
- Bank
- Account No
- Name
- Type
- IFSC
- Action Icon
-
-
-
- {beneficiaries.map((b, i) => (
-
-
-
-
- {b.bankName}
-
-
- {b.accountNo}
- {b.name}
- {b.accountType}
- {b.ifscCode}
- {/* */}
-
-
- handleDeleteBeneficiary(b.accountNo)}
- />
-
-
+ <>
+
+
+
+
+ Bank
+ Account No
+ Name
+ Type
+ IFSC
+ Action Icon
- ))}
-
-
-
+
+
+ {beneficiaries.map((b, i) => (
+
+
+
+
+ {b.bankName}
+
+
+ {b.accountNo}
+ {b.name}
+ {b.accountType}
+ {b.ifscCode}
+ {/* */}
+
+
+ openDeleteModal(b.accountNo)}
+ />
+
+
+
+ ))}
+
+
+
+
+ {
+ setOpened(false);
+ setOtpStep(false);
+ setOtp("");
+ }}
+ title="Delete Beneficiary"
+ centered
+ >
+ {!otpStep ? (
+ <>
+
+ Are you sure you want to delete this beneficiary?
+
+
+
+
+
+ >
+ ) : (
+ <>
+ Enter OTP sent to your registered number:
+ setOtp(e.currentTarget.value)}
+ placeholder="Enter OTP"
+ />
+
+ {/* Resend OTP Timer or Icon */}
+ {timerActive ? (
+
+ Resend OTP will be enabled in{" "}
+ {String(Math.floor(countdown / 60)).padStart(2, "0")}:
+ {String(countdown % 60).padStart(2, "0")}
+
+ ) : (
+
+ )}
+
+
+
+
+
+ >
+ )}
+
+ >
+
)}
);
diff --git a/src/app/(main)/settings/change_login_password/page.tsx b/src/app/(main)/settings/change_login_password/page.tsx
index a3c460d..8e4bc0d 100644
--- a/src/app/(main)/settings/change_login_password/page.tsx
+++ b/src/app/(main)/settings/change_login_password/page.tsx
@@ -6,6 +6,7 @@ import { notifications } from "@mantine/notifications";
import { IconLock, IconRefresh } from "@tabler/icons-react";
import { generateCaptcha } from "@/app/captcha";
import { useRouter } from "next/navigation";
+import { sendOtp, verifyOtp } from "@/app/_util/otp";
export default function ChangePassword() {
const router = useRouter();
@@ -22,14 +23,53 @@ export default function ChangePassword() {
const [step, setStep] = useState<"form" | "otp" | "final">("form");
const [passwordHistory] = useState(["Pass@1234", "OldPass@123", "MyPass#2023"]);
const icon = ;
+ 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() {
+ 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' });
+ setShowOtpField(true);
+ 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;
+ }
+ }
+
+
+
- const handleGenerateOtp = async () => {
- const value = "123456"; // Or generate a random OTP
- setGeneratedOtp(value);
- setCountdown(180);
- setTimerActive(true);
- return value;
- };
useEffect(() => {
regenerateCaptcha();
@@ -100,7 +140,7 @@ export default function ChangePassword() {
}
// Passed → move to OTP
- await handleGenerateOtp();
+ await handleSendOtp();
setStep("otp");
notifications.show({
title: "OTP Sent",
@@ -310,7 +350,7 @@ export default function ChangePassword() {
)
)}
diff --git a/src/app/(main)/settings/change_txn_password/page.tsx b/src/app/(main)/settings/change_txn_password/page.tsx
index aff3c0d..b616fb5 100644
--- a/src/app/(main)/settings/change_txn_password/page.tsx
+++ b/src/app/(main)/settings/change_txn_password/page.tsx
@@ -6,6 +6,7 @@ import { notifications } from "@mantine/notifications";
import { IconLock, IconRefresh } from "@tabler/icons-react";
import { generateCaptcha } from "@/app/captcha";
import { useRouter } from "next/navigation";
+import { sendOtp, verifyOtp } from "@/app/_util/otp";
export default function ChangePassword() {
const router = useRouter();
@@ -21,14 +22,52 @@ export default function ChangePassword() {
const [otpValidated, setOtpValidated] = useState(false);
const [step, setStep] = useState<"form" | "otp" | "final">("form");
const icon = ;
+ 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() {
+ 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_TPWORD' });
+ setShowOtpField(true);
+ 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;
+ }
+ }
- const handleGenerateOtp = async () => {
- const value = "123456"; // Or generate a random OTP
- setGeneratedOtp(value);
- setCountdown(180);
- setTimerActive(true);
- return value;
- };
useEffect(() => {
regenerateCaptcha();
@@ -100,7 +139,7 @@ export default function ChangePassword() {
}
// Passed → move to OTP
- await handleGenerateOtp();
+ await handleSendOtp();
setStep("otp");
notifications.show({
title: "OTP Sent",
@@ -309,7 +348,7 @@ export default function ChangePassword() {
)
)}
diff --git a/src/app/(main)/settings/set_txn_password/page.tsx b/src/app/(main)/settings/set_txn_password/page.tsx
index 951eb11..4d51b56 100644
--- a/src/app/(main)/settings/set_txn_password/page.tsx
+++ b/src/app/(main)/settings/set_txn_password/page.tsx
@@ -14,6 +14,7 @@ import { notifications } from "@mantine/notifications";
import { IconLock, IconRefresh } from "@tabler/icons-react";
import { generateCaptcha } from "@/app/captcha";
import { useRouter } from "next/navigation";
+import { sendOtp, verifyOtp } from "@/app/_util/otp";
export default function ChangePassword() {
const [newPassword, setNewPassword] = useState("");
@@ -29,14 +30,50 @@ export default function ChangePassword() {
const router = useRouter();
const icon = ;
+ const [showOtpField, setShowOtpField] = useState(false);
- const handleGenerateOtp = async () => {
- const value = "123456"; // Or generate a random OTP
- setGeneratedOtp(value);
- setCountdown(180);
- setTimerActive(true);
- return value;
- };
+ // const handleGenerateOtp = async () => {
+ // const value = "123456"; // Or generate a random OTP
+ // 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: 'SET_TPWORD' });
+ setShowOtpField(true);
+ 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;
+ }
+ }
const regenerateCaptcha = async () => {
const newCaptcha = await generateCaptcha();
@@ -111,7 +148,7 @@ export default function ChangePassword() {
return;
}
// Passed → move to OTP step
- await handleGenerateOtp();
+ await handleSendOtp();
setStep("otp");
notifications.show({
title: "OTP Sent",
@@ -310,7 +347,7 @@ export default function ChangePassword() {
)
)}
diff --git a/src/app/SetPassword/page.tsx b/src/app/SetPassword/page.tsx
index b004612..f842b19 100644
--- a/src/app/SetPassword/page.tsx
+++ b/src/app/SetPassword/page.tsx
@@ -10,6 +10,8 @@ import changePwdImage from '@/app/image/set_log_pass.jpg';
import { IconLock, IconLogout, IconRefresh } from '@tabler/icons-react';
import { generateCaptcha } from '@/app/captcha';
import { generateOTP } from "../OTPGenerator";
+import { sendOtp, verifyOtp } from "../_util/otp";
+//const [showOtpField, setShowOtpField] = useState(false);
export default function SetLoginPwd() {
const router = useRouter();
@@ -24,15 +26,57 @@ export default function SetLoginPwd() {
const [timerActive, setTimerActive] = useState(false);
const icon = ;
const [generateOtp, setGenerateOtp] = useState("");
+ const [showOtpField, setShowOtpField] = useState(false);
- async function handleGenerateOtp() {
- // const value = await generateOTP(6);
- const value = "123456";
- setGenerateOtp(value);
- setCountdown(60);
- setTimerActive(true);
- // return value;
+
+ // async function handleGenerateOtp() {
+ // // const value = await generateOTP(6);
+ // const value = "123456";
+ // setGenerateOtp(value);
+ // setCountdown(60);
+ // 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' });
+ setShowOtpField(true);
+ 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 handleLogout(e: React.FormEvent) {
e.preventDefault();
localStorage.removeItem("access_token");
@@ -104,7 +148,7 @@ export default function SetLoginPwd() {
}
if (!captchaValidate) {
setCaptchaValidate(true);
- handleGenerateOtp();
+ handleSendOtp();
return;
}
if (!otp) {
@@ -157,10 +201,10 @@ export default function SetLoginPwd() {
router.push("/login");
}
}
- useEffect(() => {
+ useEffect(() => {
regenerateCaptcha();
}, []);
-
+
useEffect(() => {
let interval: number | undefined;
if (timerActive && countdown > 0) {
@@ -296,7 +340,7 @@ export default function SetLoginPwd() {
}
>
Resend
diff --git a/src/app/_util/otp.ts b/src/app/_util/otp.ts
index 67bdb52..f5ecef3 100644
--- a/src/app/_util/otp.ts
+++ b/src/app/_util/otp.ts
@@ -15,9 +15,9 @@ interface SendOtpPayload {
username?:string
}
-function getStoredMobileNumber(): string | null {
+function getStoredMobileNumber(): string | null {
// const mobileNumber = localStorage.getItem('remitter_mobile_no');
- const mobileNumber= "7890544527";
+ const mobileNumber = "6297421727";
if (!mobileNumber) {
notifications.show({
title: 'Missing Mobile Number',
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index 0791c4a..41e9a13 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -13,6 +13,7 @@ import dynamic from 'next/dynamic';
import { generateCaptcha } from '@/app/captcha';
import { IconRefresh, IconShieldLockFilled } from "@tabler/icons-react";
import dayjs from "dayjs";
+import { IconTrash } from "@tabler/icons-react";
export default function Login() {