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.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() {