diff --git a/src/app/(main)/accounts/layout.tsx b/src/app/(main)/accounts/layout.tsx index fc6af88..813ab2c 100644 --- a/src/app/(main)/accounts/layout.tsx +++ b/src/app/(main)/accounts/layout.tsx @@ -37,8 +37,8 @@ export default function Layout({ children }: { children: React.ReactNode }) { }} > - - Accounts + + My Accounts diff --git a/src/app/(main)/funds_transfer/add_beneficiary/addBeneficiaryOthers.tsx b/src/app/(main)/funds_transfer/add_beneficiary/addBeneficiaryOthers.tsx index 5dd2c17..c349233 100644 --- a/src/app/(main)/funds_transfer/add_beneficiary/addBeneficiaryOthers.tsx +++ b/src/app/(main)/funds_transfer/add_beneficiary/addBeneficiaryOthers.tsx @@ -1,19 +1,9 @@ "use client"; import React, { useEffect, useState } from 'react'; -import { - TextInput, - Button, - Select, - Title, - Paper, - Grid, - Group, - Radio, - Text, - PasswordInput, -} from '@mantine/core'; +import { TextInput, Button, Grid, Text, PasswordInput } from '@mantine/core'; import { notifications } from '@mantine/notifications'; +import { useRouter } from "next/navigation"; const MockOthersAccountValidation = [ @@ -31,8 +21,9 @@ const MockOthersAccountValidation = }, ] - -const AddBeneficiaryOthers: React.FC = () => { +export default function AddBeneficiaryOthers() { + const router = useRouter(); + const [authorized, setAuthorized] = useState(null); const [bankName, setBankName] = useState(''); const [ifsccode, setIfsccode] = useState(''); const [branchName, setBranchName] = useState(''); @@ -40,24 +31,28 @@ const AddBeneficiaryOthers: React.FC = () => { const [confirmAccountNo, setConfirmAccountNo] = useState(''); const [nickName, setNickName] = useState(''); const [beneficiaryName, setBeneficiaryName] = useState(null); - const [otp, setOtp] = useState(''); const [generatedOtp, setGeneratedOtp] = useState(''); const [otpSent, setOtpSent] = useState(false); const [otpVerified, setOtpVerified] = useState(false); const [validationStatus, setValidationStatus] = useState<'success' | 'error' | null>(null); - - - const [isVisibilityLocked, setIsVisibilityLocked] = useState(false); const [showPayeeAcc, setShowPayeeAcc] = useState(true); const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); }; + useEffect(() => { + const token = localStorage.getItem("access_token"); + if (!token) { + setAuthorized(false); + router.push("/login"); + } else { + setAuthorized(true); + } + }, []); useEffect(() => { const ifscTrimmed = ifsccode.trim(); - if (ifscTrimmed.length === 11) { // Only call API when IFSC is valid length const fetchIfscDetails = async () => { try { @@ -115,15 +110,6 @@ const AddBeneficiaryOthers: React.FC = () => { return; } - // if ( !/^[A-Za-z ]{1,5}$/.test(bankName)) { - // notifications.show({ - // color: "red", - // title: "Invalid Bank Name", - // message: "Use only alphabets/spaces, max 100 characters.", - // }); - // return; - // } - const trimmedIfsc = ifsccode.trim().toUpperCase(); const isValidIfscCode = (code: string) => { return /^[A-Z]{4}0[0-9]{6}$/.test(code); @@ -161,12 +147,25 @@ const AddBeneficiaryOthers: React.FC = () => { } // Validation for now try { - const matched = MockOthersAccountValidation.find( - (entry) => entry.stBenAccountNo === accountNo - ); + // const matched = MockOthersAccountValidation.find( + // (entry) => entry.stBenAccountNo === accountNo + // ); - if (matched) { - setBeneficiaryName(matched.stBenName); + const token = localStorage.getItem("access_token"); + const response = await fetch( + `http://localhost:8080/api/beneficiary/validate/outside-bank?accountNo=${accountNo}&ifscCode=${ifsccode}&remitterName=""`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + const data = await response.json(); + + if (response.ok && data?.name) { + setBeneficiaryName(data.name); setValidationStatus("success"); setIsVisibilityLocked(true); setOtpSent(true); @@ -190,55 +189,6 @@ const AddBeneficiaryOthers: React.FC = () => { setBeneficiaryName("Something went wrong"); setValidationStatus("error"); } - - // Later need to add api - // try { - // // API will be changed - // const token = localStorage.getItem("access_token"); - // const response = await fetch(`/api/beneficiary/validate/within-bank?accountNumber=${accountNo}`, { - // method: "GET", - // headers: { - // "Content-Type": "application/json", - // Authorization: `Bearer ${token}`, - // }, - // }); - - // const data = await response.json(); - - // if (response.ok && data?.name) { - // setBeneficiaryName(data.name); - // setValidationStatus("success"); - // setIsVisibilityLocked(true); - // setOtpSent(true); - // await handleGenerateOtp(); - - // notifications.show({ - // withBorder: true, - // color: "green", - // title: "OTP Sent", - // message: "OTP has been sent to your registered mobile number.", - // autoClose: 5000, - // }); - // } else { - // setBeneficiaryName("Invalid beneficiary account number"); - // setValidationStatus("error"); - // setAccountNo(""); - // setConfirmAccountNo(""); - // } - // } - // catch (error) { - // setBeneficiaryName("Invalid beneficiary account number"); - // setValidationStatus("error"); - - - // notifications.show({ - // withBorder: true, - // color: "red", - // title: "Error", - // message: "Could not validate account number.", - // autoClose: 5000, - // }); - // } }; const verifyOtp = () => { @@ -271,6 +221,7 @@ const AddBeneficiaryOthers: React.FC = () => { }); }; + if (!authorized) return null; return ( @@ -278,9 +229,7 @@ const AddBeneficiaryOthers: React.FC = () => { setIfsccode(e.currentTarget.value)} onChange={(e) => { let val = e.currentTarget.value.toUpperCase(); val = val.replace(/[^A-Z0-9]/g, ""); // block special chars @@ -305,8 +254,6 @@ const AddBeneficiaryOthers: React.FC = () => { /> - - { ); }; -export default AddBeneficiaryOthers; + diff --git a/src/app/(main)/funds_transfer/add_beneficiary/page.tsx b/src/app/(main)/funds_transfer/add_beneficiary/page.tsx index ed9baa3..9c74e85 100644 --- a/src/app/(main)/funds_transfer/add_beneficiary/page.tsx +++ b/src/app/(main)/funds_transfer/add_beneficiary/page.tsx @@ -1,27 +1,20 @@ "use client"; -import React, { useState } from 'react'; -import { - TextInput, - Button, - Select, - Title, - Paper, - Grid, - Group, - Radio, - Text, - PasswordInput, -} from '@mantine/core'; +import React, { useEffect, useState } from 'react'; +import {TextInput,Button,Select,Title,Paper,Grid,Group,Radio,Text,PasswordInput} from '@mantine/core'; +import { useRouter } from "next/navigation"; import { notifications } from '@mantine/notifications'; import AddBeneficiaryOthers from './addBeneficiaryOthers'; +import { generateOTP } from '@/app/OTPGenerator'; const bankOptions = [ { value: 'KCCB', label: 'KCCB - The Kangra Central Co-Operative Bank' }, ]; const AddBeneficiary: React.FC = () => { + const router = useRouter(); const [bankName, setBankName] = useState(''); + const [authorized, setAuthorized] = useState(null); const [bankType, setBankType] = useState('own'); const [accountNo, setAccountNo] = useState(''); const [confirmAccountNo, setConfirmAccountNo] = useState(''); @@ -37,11 +30,20 @@ const AddBeneficiary: React.FC = () => { const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); }; const handleGenerateOtp = async () => { + // const value = await generateOTP(6); const value = "123456"; // Or generate a random OTP setGeneratedOtp(value); return value; }; - + useEffect(() => { + const token = localStorage.getItem("access_token"); + if (!token) { + setAuthorized(false); + router.push("/login"); + } else { + setAuthorized(true); + } + }, []); // const isValidBankName = (name: string) => { // const regex = /^[A-Za-z\s]{3,5}$/; // return regex.test(name.trim()); @@ -51,8 +53,6 @@ const AddBeneficiary: React.FC = () => { // return /^[A-Z]{4}0[0-9]{6}$/.test(code); // }; - - const validateAndSendOtp = async () => { if (!bankName || !accountNo || !confirmAccountNo) { notifications.show({ @@ -86,10 +86,6 @@ const AddBeneficiary: React.FC = () => { // return; // } - - - - if (accountNo.length < 10 || accountNo.length > 17) { notifications.show({ withBorder: true, @@ -101,9 +97,6 @@ const AddBeneficiary: React.FC = () => { return; } - - - if (accountNo !== confirmAccountNo) { notifications.show({ withBorder: true, @@ -193,6 +186,7 @@ const AddBeneficiary: React.FC = () => { }); }; + if (!authorized) return null; return ( @@ -254,9 +248,9 @@ const AddBeneficiary: React.FC = () => { // disabled={isVisibilityLocked} readOnly={isVisibilityLocked} required - onCopy={(e) => e.preventDefault()} - onPaste={(e) => e.preventDefault()} - onCut={(e) => e.preventDefault()} + onCopy={(e) => e.preventDefault()} + onPaste={(e) => e.preventDefault()} + onCut={(e) => e.preventDefault()} /> {validationStatus === "error" && ( diff --git a/src/app/(main)/funds_transfer/layout.tsx b/src/app/(main)/funds_transfer/layout.tsx index f798df6..28de9c4 100644 --- a/src/app/(main)/funds_transfer/layout.tsx +++ b/src/app/(main)/funds_transfer/layout.tsx @@ -38,7 +38,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { }} > - + Send Money diff --git a/src/app/(main)/funds_transfer/page.tsx b/src/app/(main)/funds_transfer/page.tsx index 155fe41..2244c02 100644 --- a/src/app/(main)/funds_transfer/page.tsx +++ b/src/app/(main)/funds_transfer/page.tsx @@ -6,6 +6,7 @@ import { notifications } from "@mantine/notifications"; import { useRouter } from "next/navigation"; import { generateOTP } from '@/app/OTPGenerator'; import OutsideQuickPay from "./outside_quick_pay"; +import { IconRefresh } from "@tabler/icons-react"; interface accountData { stAccountNo: string; @@ -34,6 +35,8 @@ export default function QuickPay() { const [validationStatus, setValidationStatus] = useState<"success" | "error" | null>(null); const [beneficiaryName, setBeneficiaryName] = useState(null); const [showOtpField, setShowOtpField] = useState(false); + const [countdown, setCountdown] = useState(60); + const [timerActive, setTimerActive] = useState(false); const [otp, setOtp] = useState(""); const [generateOtp, setGenerateOtp] = useState(""); @@ -41,6 +44,8 @@ export default function QuickPay() { // const value = await generateOTP(6); const value = "123456"; setGenerateOtp(value); + setCountdown(60); + setTimerActive(true); return value; } @@ -94,6 +99,22 @@ export default function QuickPay() { } }, [authorized]); + 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]); + async function handleValidate() { if (!selectedAccNo || !beneficiaryAcc || !confirmBeneficiaryAcc @@ -257,6 +278,7 @@ export default function QuickPay() { color: "red", }); } finally { + setValidationStatus(null); setSelectedAccNo(null); setBeneficiaryAcc(''); setBeneficiaryName(''); @@ -313,142 +335,161 @@ export default function QuickPay() { {/* main content */} - Quick Pay + Quick Pay - Own Bank -
- - - + { + const value = e.currentTarget.value; + if (/^\d*$/.test(value)) { + setBeneficiaryAcc(value); + setShowPayeeAcc(true); + } + }} + onBlur={() => setShowPayeeAcc(false)} + onFocus={() => setShowPayeeAcc(true)} + withAsterisk + readOnly={isVisibilityLocked} + /> - { - const value = e.currentTarget.value; - if (/^\d*$/.test(value)) { - setConfirmBeneficiaryAcc(value); - } - }} - onCopy={(e) => e.preventDefault()} - onPaste={(e) => e.preventDefault()} - onCut={(e) => e.preventDefault()} - withAsterisk - readOnly={isVisibilityLocked} - /> + { + const value = e.currentTarget.value; + if (/^\d*$/.test(value)) { + setConfirmBeneficiaryAcc(value); + } + }} + onCopy={(e) => e.preventDefault()} + onPaste={(e) => e.preventDefault()} + onCut={(e) => e.preventDefault()} + withAsterisk + readOnly={isVisibilityLocked} + /> + + + Available Balance : + {selectedAccount ? selectedAccount.stAvailableBalance : 0} + + + {validationStatus === "error" && {beneficiaryName}} - - Available Balance : - {selectedAccount ? selectedAccount.stAvailableBalance : 0} - - - {validationStatus === "error" && {beneficiaryName}} - - - - + + + - - setAmount(e.currentTarget.value)} - error={ - selectedAccount && Number(amount) > Number(selectedAccount.stAvailableBalance) ? - "Amount exceeds available balance" : false} - withAsterisk - readOnly={showOtpField} - /> + setAmount(e.currentTarget.value)} + error={ + selectedAccount && Number(amount) > Number(selectedAccount.stAvailableBalance) ? + "Amount exceeds available balance" : false} + withAsterisk + readOnly={showOtpField} + /> - setRemarks(e.currentTarget.value)} - // withAsterisk - readOnly={showOtpField} - withAsterisk - /> - - - {showOtpField && ( + setRemarks(e.currentTarget.value)} + // withAsterisk + readOnly={showOtpField} + withAsterisk + /> + + + {showOtpField && ( + <> setOtp(e.currentTarget.value)} withAsterisk disabled={showTxnPassword} /> - )} - {showTxnPassword && ( - setTxnPassword(e.currentTarget.value)} - withAsterisk - /> - )} - + {!showTxnPassword && ( + timerActive ? ( + + Resend OTP will be enabled in 00:{countdown < 10 ? `0${countdown}` : countdown} min + + ) : ( + + ) + )} + + )} + {showTxnPassword && ( + setTxnPassword(e.currentTarget.value)} + withAsterisk + /> + )} + - - - - - -
+ + + + + + {/* ) : (
diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index 4aa2a8d..bdd7bf2 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -143,7 +143,7 @@ export default function Home() { {/* Loan Account Card */} diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 6b149c9..bd1baa6 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -5,7 +5,7 @@ import { IconBook, IconCurrencyRupee, IconHome, IconLogout, IconPhoneFilled, Ico import Link from 'next/link'; import { useRouter, usePathname } from "next/navigation"; import { Providers } from '../providers'; -import logo from '@/app/image/logo.jpg'; +import logo from '@/app/image/logo1.jpg'; import NextImage from 'next/image'; import { notifications } from '@mantine/notifications'; @@ -25,7 +25,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) async function handleFetchUserName() { try { const token = localStorage.getItem("access_token"); - const response = await fetch('api/customer', { + const response = await fetch('/api/customer', { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -67,7 +67,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) async function handleFetchUserDetails(e: React.FormEvent) { e.preventDefault(); const token = localStorage.getItem("access_token"); - const response = await fetch('api/auth/user_details', { + const response = await fetch('/api/auth/user_details', { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -124,7 +124,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) width: '100%', display: "flex", justifyContent: "flex-start", - background: "linear-gradient(15deg,rgba(2, 163, 85, 1) 55%, rgba(101, 101, 184, 1) 100%)", + background: "linear-gradient(15deg,rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)", }} > ebanking + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. +
diff --git a/src/app/(main)/settings/change_login_password/page.tsx b/src/app/(main)/settings/change_login_password/page.tsx index 7ed1335..e86ff17 100644 --- a/src/app/(main)/settings/change_login_password/page.tsx +++ b/src/app/(main)/settings/change_login_password/page.tsx @@ -14,7 +14,7 @@ import { } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import { IconEye, IconEyeOff, IconLock } from "@tabler/icons-react"; -import CaptchaImage from "@/app/SetPassword/CaptchaImage"; +// import CaptchaImage from "@/app/SetPassword/CaptchaImage"; import { generateCaptcha } from "@/app/captcha"; const ChangePassword: React.FC = () => { @@ -193,7 +193,7 @@ const ChangePassword: React.FC = () => {
{/* CAPTCHA Image */}
- + {/* */}
{/* Refresh Button */} diff --git a/src/app/(main)/settings/layout.tsx b/src/app/(main)/settings/layout.tsx index 3ae6b1c..ba3d1f4 100644 --- a/src/app/(main)/settings/layout.tsx +++ b/src/app/(main)/settings/layout.tsx @@ -38,7 +38,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { }} > - + Settings diff --git a/src/app/SetPassword/CaptchaImage.tsx b/src/app/SetPassword/CaptchaImage.tsx deleted file mode 100644 index d63d34c..0000000 --- a/src/app/SetPassword/CaptchaImage.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { useEffect, useRef } from 'react'; - -const CaptchaImage = ({ text }: { text: string }) => { - const canvasRef = useRef(null); - - useEffect(() => { - const canvas = canvasRef.current; - const ctx = canvas?.getContext('2d'); - if (canvas && ctx) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = '#ffffff'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.font = '26px Arial'; - ctx.fillStyle = '#000'; - ctx.setTransform(1, 0.1, -0.1, 1, 0, 0); - ctx.fillText(text, 10, 30); - } - }, [text]); - - return ; -}; - -export default CaptchaImage; diff --git a/src/app/SetPassword/page.tsx b/src/app/SetPassword/page.tsx index 1aec987..c9a3f2c 100644 --- a/src/app/SetPassword/page.tsx +++ b/src/app/SetPassword/page.tsx @@ -1,14 +1,15 @@ "use client"; import React, { useState, useEffect } from "react"; -import { Text, Button, TextInput, PasswordInput, Title, Card, Box, Image } from "@mantine/core"; +import { Text, Button, TextInput, PasswordInput, Title, Card, Box, Image, Group } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import { Providers } from "@/app/providers"; import { useRouter } from "next/navigation"; import NextImage from "next/image"; -import logo from '@/app/image/logo.jpg'; -import changePwdImage from '@/app/image/changepw.png'; -import CaptchaImage from './CaptchaImage'; -import { IconEye, IconEyeOff, IconLock, IconLogout } from '@tabler/icons-react'; +import logo from '@/app/image/logo1.jpg'; +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"; export default function SetLoginPwd() { const router = useRouter(); @@ -16,33 +17,35 @@ export default function SetLoginPwd() { const [captcha, setCaptcha] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); - const [captchaInput, setCaptchaInput] = useState(''); - const [captchaError, setCaptchaError] = useState(''); - const [confirmVisible, setConfirmVisible] = useState(false); - const toggleConfirmVisibility = () => setConfirmVisible((v) => !v); + const [captchaInput, setCaptchaInput] = useState(""); + const [captchaValidate, setCaptchaValidate] = useState(false); + const [otp, setOtp] = useState(""); + const [countdown, setCountdown] = useState(60); + const [timerActive, setTimerActive] = useState(false); const icon = ; + const [generateOtp, setGenerateOtp] = useState(""); + async function handleGenerateOtp() { + // const value = await generateOTP(6); + const value = "123456"; + setGenerateOtp(value); + setCountdown(60); + setTimerActive(true); + // return value; + } async function handleLogout(e: React.FormEvent) { e.preventDefault(); localStorage.removeItem("access_token"); router.push("/login") } - - useEffect(() => { - generateCaptcha(); - }, []); - - const generateCaptcha = () => { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - let result = ''; - for (let i = 0; i < 6; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)); - } - setCaptcha(result); - setCaptchaInput(''); - setCaptchaError(''); + const regenerateCaptcha = () => { + const loadCaptcha = async () => { + const newCaptcha = await generateCaptcha(); + setCaptcha(newCaptcha); + }; + loadCaptcha(); + setCaptchaInput(""); }; - async function handleSetLoginPassword(e: React.FormEvent) { e.preventDefault(); const pwdRegex = /^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/; @@ -50,36 +53,72 @@ export default function SetLoginPwd() { notifications.show({ withBorder: true, color: "red", - title: "Both password fields are required.", - message: "Both password fields are required.", + title: "Field Required", + message: "Password and Confirm Password Both fields are required.", autoClose: 5000, }); return; - // alert("Both password fields are required."); - } else if (password !== confirmPassword) { - // alert("Passwords do not match."); + } + if (!captchaInput) { notifications.show({ withBorder: true, color: "red", - title: "Passwords do not match.", + title: "Field Required", + message: "Please Enter Captcha details", + autoClose: 5000, + }); + return; + } + if (password !== confirmPassword) { + notifications.show({ + withBorder: true, + color: "red", + title: "Password Mismatch", message: "Passwords do not match.", autoClose: 5000, }); return; } - else if (!pwdRegex.test(password)) { - // alert("Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long."); + if (!pwdRegex.test(password)) { notifications.show({ withBorder: true, color: "red", - title: "Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long.", - message: "Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long.", + title: "Invalid Password", + message: "Password must contain at least one capital letter, one number, one special character, and be 8-15 characters long.", autoClose: 5000, }); return; } - else if (captchaInput !== captcha) { - setCaptchaError("Incorrect CAPTCHA."); + if (captchaInput !== captcha) { + notifications.show({ + withBorder: true, + color: "red", + title: "Captcha Error", + message: "Please enter the correct captcha", + autoClose: 5000, + }); + regenerateCaptcha(); + return; + } + if (!captchaValidate) { + setCaptchaValidate(true); + handleGenerateOtp(); + return; + } + if (!otp) { + notifications.show({ + title: "Null Field", + message: "Please enter the OTP", + color: "red", + }); + return; + } + if (otp !== generateOtp) { + notifications.show({ + title: "Invalid OTP", + message: "The OTP entered does not match", + color: "red", + }); return; } const token = localStorage.getItem("access_token"); @@ -116,6 +155,29 @@ export default function SetLoginPwd() { router.push("/login"); } } + useEffect(() => { + const loadCaptcha = async () => { + const newCaptcha = await generateCaptcha(); + setCaptcha(newCaptcha); + }; + loadCaptcha(); + }, []); + + 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]); useEffect(() => { const token = localStorage.getItem("access_token"); @@ -136,16 +198,28 @@ export default function SetLoginPwd() { position: 'fixed', width: '100%', height: '12%', top: 0, left: 0, zIndex: 100, display: "flex", justifyContent: "flex-start", - background: "linear-gradient(15deg,rgba(2, 163, 85, 1) 55%, rgba(101, 101, 184, 1) 100%)" + background: "linear-gradient(15deg,rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)" }}> ebanking + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. +
- - Change Password Image + + Change Password Image - - + <Title order={4} // @ts-ignore align="center" mb="md">Set Login Password -
setPassword(e.currentTarget.value)} onCopy={(e) => e.preventDefault()} onPaste={(e) => e.preventDefault()} onCut={(e) => e.preventDefault()} + readOnly={captchaValidate} /> - setConfirmPassword(e.currentTarget.value)} - type={confirmVisible ? 'text' : 'password'} - onCopy={(e) => e.preventDefault()} onPaste={(e) => e.preventDefault()} onCut={(e) => e.preventDefault()} - + readOnly={captchaValidate} /> - - {/* CAPTCHA */} -
- -
- - -
- setCaptchaInput(e.currentTarget.value)} - required - /> - {captchaError &&

{captchaError}

} -
- + + {captcha} + + + setCaptchaInput(e.currentTarget.value)} + withAsterisk + readOnly={captchaValidate} + /> + + {captchaValidate && ( + + setOtp(e.currentTarget.value)} + withAsterisk + style={{ flex: 1 }} + /> + {timerActive ? ( + + Resend OTP will be enabled in 00:{countdown < 10 ? `0${countdown}` : countdown} min + + ) : ( + + )} + + )} + -

- Note: Password will contains minimum one alphabet, one digit, one special symbol and total 8 charecters. + Note: Password must contain at least one capital letter(A-Z), one digit(0-9), one special symbol(e.g.,@,#,$), and be 8-15 characters long.
@@ -247,7 +342,7 @@ export default function SetLoginPwd() { }} > - © 2025 Kangra Central Co-Operative Bank + © 2025 The Kangra Central Co-Operative Bank
diff --git a/src/app/SetTxn/CaptchaImage.tsx b/src/app/SetTxn/CaptchaImage.tsx deleted file mode 100644 index 007ae4d..0000000 --- a/src/app/SetTxn/CaptchaImage.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React, { useEffect, useRef } from 'react'; - - -const CaptchaImage = ({ text }: { text: string }) => { - const canvasRef = useRef(null); - - useEffect(() => { - const canvas = canvasRef.current; - const ctx = canvas?.getContext('2d'); - if (canvas && ctx) { - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = '#ffffff'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.font = '26px Arial'; - ctx.fillStyle = '#000'; - ctx.setTransform(1, 0.1, -0.1, 1, 0, 0); - ctx.fillText(text, 10, 30); - } - }, [text]); - - return ; -}; - -export default CaptchaImage; diff --git a/src/app/SetTxn/page.tsx b/src/app/SetTxn/page.tsx index 5a8be52..139fb54 100644 --- a/src/app/SetTxn/page.tsx +++ b/src/app/SetTxn/page.tsx @@ -1,14 +1,14 @@ "use client"; import React, { useState, useEffect } from "react"; -import { Text, Button, TextInput, PasswordInput, Title, Card, Box, Image } from "@mantine/core"; +import { Text, Button, TextInput, PasswordInput, Title, Card, Box, Image, Group } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import { Providers } from "@/app/providers"; import { useRouter } from "next/navigation"; import NextImage from "next/image"; -import logo from '@/app/image/logo.jpg'; -import changePwdImage from '@/app/image/changepw.png'; -import CaptchaImage from './CaptchaImage'; -import { IconEye, IconEyeOff, IconLock, IconLogout } from '@tabler/icons-react'; +import logo from '@/app/image/logo1.jpg'; +import changePwdImage from '@/app/image/set_tran_pass.jpg'; +import { generateCaptcha } from '@/app/captcha'; +import { IconLock, IconLogout, IconRefresh } from '@tabler/icons-react'; export default function SetTransactionPwd() { const router = useRouter(); @@ -16,32 +16,59 @@ export default function SetTransactionPwd() { const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [captcha, setCaptcha] = useState(""); - const [captchaInput, setCaptchaInput] = useState(''); - const [captchaError, setCaptchaError] = useState(''); - const [confirmVisible, setConfirmVisible] = useState(false); - const toggleConfirmVisibility = () => setConfirmVisible((v) => !v); + const [captchaInput, setCaptchaInput] = useState(""); + const [captchaValidate, setCaptchaValidate] = useState(false); + const [otp, setOtp] = useState(""); + const [countdown, setCountdown] = useState(60); + const [timerActive, setTimerActive] = useState(false); const icon = ; + const [generateOtp, setGenerateOtp] = useState(""); + + async function handleGenerateOtp() { + // const value = await generateOTP(6); + const value = "123456"; + setGenerateOtp(value); + setCountdown(60); + setTimerActive(true); + } async function handleLogout(e: React.FormEvent) { e.preventDefault(); localStorage.removeItem("access_token"); router.push("/login") } + const regenerateCaptcha = () => { + const loadCaptcha = async () => { + const newCaptcha = await generateCaptcha(); + setCaptcha(newCaptcha); + }; + loadCaptcha(); + setCaptchaInput(""); + }; useEffect(() => { - generateCaptcha(); + const loadCaptcha = async () => { + const newCaptcha = await generateCaptcha(); + setCaptcha(newCaptcha); + }; + loadCaptcha(); }, []); - const generateCaptcha = () => { - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - let result = ''; - for (let i = 0; i < 6; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)); + useEffect(() => { + let interval: number | undefined; + if (timerActive && countdown > 0) { + interval = window.setInterval(() => { + setCountdown((prev) => prev - 1); + }, 1000); } - setCaptcha(result); - setCaptchaInput(''); - setCaptchaError(''); - }; + if (countdown === 0) { + if (interval) clearInterval(interval); + setTimerActive(false); + } + return () => { + if (interval) clearInterval(interval); + }; + }, [timerActive, countdown]); async function handleSetTransactionPassword(e: React.FormEvent) { e.preventDefault(); @@ -50,36 +77,72 @@ export default function SetTransactionPwd() { notifications.show({ withBorder: true, color: "red", - title: "Both password fields are required.", + title: "Field Required", message: "Both password fields are required.", autoClose: 5000, }); return; - // alert("Both password fields are required."); - } else if (password !== confirmPassword) { - // alert("Passwords do not match."); + } + if (!captchaInput) { notifications.show({ withBorder: true, color: "red", - title: "Passwords do not match.", + title: "Field Required", + message: "Please Enter Captcha details", + autoClose: 5000, + }); + return; + } + if (password !== confirmPassword) { + notifications.show({ + withBorder: true, + color: "red", + title: "Password Mismatch", message: "Passwords do not match.", autoClose: 5000, }); return; } - else if (!pwdRegex.test(password)) { - // alert("Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long."); + if (!pwdRegex.test(password)) { notifications.show({ withBorder: true, color: "red", - title: "Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long.", - message: "Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long.", + title: "Invalid Password", + message: "Password must contain at least one capital letter, one number, one special character, and be 8-15 characters long.", autoClose: 5000, }); return; } - else if (captchaInput !== captcha) { - setCaptchaError("Incorrect CAPTCHA."); + if (captchaInput !== captcha) { + notifications.show({ + withBorder: true, + color: "red", + title: "Captcha Error", + message: "Please enter the correct captcha", + autoClose: 5000, + }); + regenerateCaptcha(); + return; + } + if (!captchaValidate) { + setCaptchaValidate(true); + handleGenerateOtp(); + return; + } + if (!otp) { + notifications.show({ + title: "Null Field", + message: "Please enter the OTP", + color: "red", + }); + return; + } + if (otp !== generateOtp) { + notifications.show({ + title: "Invalid OTP", + message: "The OTP entered does not match", + color: "red", + }); return; } const token = localStorage.getItem("access_token"); @@ -116,7 +179,6 @@ export default function SetTransactionPwd() { router.push("/login"); } } - useEffect(() => { const token = localStorage.getItem("access_token"); if (!token) { @@ -136,16 +198,28 @@ export default function SetTransactionPwd() { position: 'fixed', width: '100%', height: '12%', top: 0, left: 0, zIndex: 100, display: "flex", justifyContent: "flex-start", - background: "linear-gradient(15deg,rgba(2, 163, 85, 1) 55%, rgba(101, 101, 184, 1) 100%)" + background: "linear-gradient(15deg,rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)" }}> ebanking + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + - // } onChange={(e) => setConfirmPassword(e.currentTarget.value)} onCopy={(e) => e.preventDefault()} onPaste={(e) => e.preventDefault()} onCut={(e) => e.preventDefault()} + readOnly={captchaValidate} /> {/* CAPTCHA */} -
- -
- - -
- setCaptchaInput(e.currentTarget.value)} - required - /> - {captchaError &&

{captchaError}

} -
- + + {captcha} + + + setCaptchaInput(e.currentTarget.value)} + withAsterisk + mt="sm" + readOnly={captchaValidate} + /> + + {captchaValidate && ( + + setOtp(e.currentTarget.value)} + withAsterisk + style={{ flex: 1 }} + /> + {timerActive ? ( + + Resend OTP will be enabled in 00:{countdown < 10 ? `0${countdown}` : countdown} min + + ) : ( + + )} + + )} + - - -

- Note: Password will contains minimum one alphabet, one digit, one special symbol and total 8 charecters. + Note: Password must contain at least one capital letter(A-Z), one digit(0-9), one special symbol(e.g.,@,#,$), and be 8-15 characters long. diff --git a/src/app/_themes/KccbTheme.ts b/src/app/_themes/KccbTheme.ts index 3167518..cbf4c29 100644 --- a/src/app/_themes/KccbTheme.ts +++ b/src/app/_themes/KccbTheme.ts @@ -12,6 +12,23 @@ export const KccbTheme = createTheme({ primaryColor: 'kccb-colors', colors: { 'kccb-colors': KccbColors - } - // primaryColor: 'kccb-colors' + }, + //Typography settings + fontFamily: "Inter, Roboto, sans-serif", + headings: { + fontFamily: "Inter, Roboto, sans-serif", + fontWeight: '700', + sizes: { + h1: { fontSize: "2.2rem", lineHeight: '1.2' }, + h2: { fontSize: "1.8rem", lineHeight: '1.3' }, + h3: { fontSize: "1.5rem", lineHeight: '1.35' }, + }, + }, + fontSizes: { + xs: "0.75rem", + sm: "0.875rem", + md: "1rem", + lg: "1.125rem", + xl: "1.25rem", + }, }); \ No newline at end of file diff --git a/src/app/image/changepw_b.jpg b/src/app/image/changepw_b.jpg deleted file mode 100644 index 2e7cebb..0000000 Binary files a/src/app/image/changepw_b.jpg and /dev/null differ diff --git a/src/app/image/logo1.jpg b/src/app/image/logo1.jpg new file mode 100644 index 0000000..9aa529c Binary files /dev/null and b/src/app/image/logo1.jpg differ diff --git a/src/app/image/set_log_pass.jpg b/src/app/image/set_log_pass.jpg new file mode 100644 index 0000000..bc91e52 Binary files /dev/null and b/src/app/image/set_log_pass.jpg differ diff --git a/src/app/image/set_tran_pass.jpg b/src/app/image/set_tran_pass.jpg new file mode 100644 index 0000000..0c86226 Binary files /dev/null and b/src/app/image/set_tran_pass.jpg differ diff --git a/src/app/login/clientCarousel.tsx b/src/app/login/clientCarousel.tsx index 9f901d1..84537a4 100644 --- a/src/app/login/clientCarousel.tsx +++ b/src/app/login/clientCarousel.tsx @@ -40,7 +40,7 @@ export default function CustomCarousel() { }, [currentIndex]); return ( - + {/* Scrollable container */} (null); const [CIF, SetCIF] = useState(""); const [psw, SetPsw] = useState(""); const [captcha, setCaptcha] = useState(""); const [inputCaptcha, setInputCaptcha] = useState(""); const [isLogging, setIsLogging] = useState(false); const ClientCarousel = dynamic(() => import('./clientCarousel'), { ssr: false }); + const headerRef = useRef(null); useEffect(() => { const loadCaptcha = async () => { @@ -42,7 +43,25 @@ export default function Login() { e.preventDefault(); const onlyDigit = /^\d{11}$/; if (!onlyDigit.test(CIF)) { - setError('Input value must be 11 digit'); + // 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({ @@ -55,6 +74,16 @@ export default function Login() { regenerateCaptcha(); return; } + if (!CIF || !psw) { + notifications.show({ + withBorder: true, + color: "red", + title: "Invalid Input", + message: "Please fill UserId and Password", + autoClose: 5000, + }); + return; + } const response = await fetch('api/auth/login', { method: 'POST', headers: { @@ -90,38 +119,75 @@ export default function Login() { }); } } + useEffect(() => { + const headerData = [ + "THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.", + "कांगड़ा केन्द्रीय सहकारी बैंक सीमित", + ]; + let index = 0; + const interval = setInterval(() => { + index = (index + 1) % headerData.length; + if (headerRef.current) { + headerRef.current.textContent = headerData[index]; + } + }, 2000); + return () => clearInterval(interval); + }, []); return (
{/* Header */} - + ebanking - + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + - {/* */} - Head Office : Dharmshala, District: Kangra(H.P), Pincode: 176215 + Head Office : Dharmshala, District: Kangra(H.P), Pin: 176215 + {/* + + + + */} +
{/* Movable text */}