From 960624f934fed6c744662d0c501cea2f213d6cc2 Mon Sep 17 00:00:00 2001 From: "tomosa.sarkar" Date: Wed, 24 Sep 2025 13:23:07 +0530 Subject: [PATCH 1/4] wip: E-mandate home screen --- TODO.md | 2 +- instruction.md | 38 ++- .../account_statement/accountStatement.tsx | 8 +- src/app/eMandate/mandate_page/page.tsx | 255 ++++++++++++++++++ 4 files changed, 297 insertions(+), 6 deletions(-) create mode 100644 src/app/eMandate/mandate_page/page.tsx diff --git a/TODO.md b/TODO.md index e3f34cf..ba72956 100644 --- a/TODO.md +++ b/TODO.md @@ -22,6 +22,6 @@ - >view rights - Forget Password - >For Migration if user not have password - +- E-mandate diff --git a/instruction.md b/instruction.md index 4b6d9e4..fd03323 100644 --- a/instruction.md +++ b/instruction.md @@ -5,14 +5,46 @@ - Download **AWS Session Manager Plugin** - Generate **Key for KCCB** +____________________________________________________________ +### Production: (Run in systemctl) + - cd /etc/systemd/system + + - vi IB.service + ``` + [Unit] + Description= Internet Banking Frontened Application in Node + After=network.target + + [Service] + # Use absolute path for node or npm + User=ib + Group=ib + ExecStart=/usr/bin/npm start + WorkingDirectory=/home/ib/IB + Restart=on-failure + RestartSec=5 + Environment=NODE_ENV=production + Environment=PORT=3000 + SuccessExitStatus=143 + + [Install] + WantedBy=multi-user.target + + + ``` + - sudo systemctl status IB + - sudo systemctl start IB + - sudo systemctl stop IB + - sudo systemctl restart IB + --- ## Machine ```bash -UAT (IB- frontend) : i-0b55435e15425f1c3 -Linux : i-0c850dcf8b85b1447 -Prod : i-088e64c3435cb5078 +UAT (IB- frontend Test) : i-0b55435e15425f1c3 +Linux : i-0c850dcf8b85b1447 (Test) +Prod : i-088e64c3435cb5078 (For IB & MB) ``` ## 2. Port Forwarding diff --git a/src/app/(main)/accounts/account_statement/accountStatement.tsx b/src/app/(main)/accounts/account_statement/accountStatement.tsx index 054e235..23869cf 100644 --- a/src/app/(main)/accounts/account_statement/accountStatement.tsx +++ b/src/app/(main)/accounts/account_statement/accountStatement.tsx @@ -108,12 +108,12 @@ export default function AccountStatementPage() { }); return; } - if (end.diff(start, "day") > 90) { + if (end.diff(start, "day") > 30) { notifications.show({ withBorder: true, color: "red", title: "Invalid Date range", - message: "End date must be within 90 days from start date", + message: "End date must be within 30 days from start date", autoClose: 4000, }); return; @@ -179,12 +179,16 @@ export default function AccountStatementPage() { value={startDate} onChange={setStartDate} placeholder="Enter start date" + minDate={dayjs("1920-01-01").toDate()} + maxDate={dayjs().toDate()} /> + + + + ); +}; + +export default function MandatePage() { + const router = useRouter(); + const [mandates] = useState([ + { + id: "1", + category: "Insurance Premium", + amount: 11743, + maxAmount: 11743, + name: "LIFE INSURANCE CORPORATION", + frequency: "YEAR", + firstCollection: "2025-09-20", + finalCollection: "2038-03-28", + }, + { + id: "2", + category: "Loan EMI", + amount: 8500, + maxAmount: 8500, + name: "XYZ BANK", + frequency: "MONTH", + firstCollection: "2025-10-01", + finalCollection: "2030-10-01", + }, + + ]); + + const handleAccept = (id: string) => { + alert(`Accepted mandate ${id}`); + }; + + const handleReject = (id: string) => { + alert(`Rejected mandate ${id}`); + }; + const handleLogout = () => { + localStorage.removeItem("access_token"); + router.push("/eMandate/login"); // redirect to login page + }; + + return ( + +
+ {/* HEADER */} + + + ebanking + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + + + {/* Logout Icon */} + + + + + + {/* WELCOME BAR */} + + + Welcome, John Doe + + + Login Time: 9/24/2025, 12:52:05 PM + + + KCCB E-Mandate + + + + + {/* CONTENT */} + + + + {mandates.map((mandate) => ( + + + + ))} + + + + + {/* FOOTER */} + + + © 2025 The Kangra Central Co-Operative Bank + + +
+
+ ); +} From 6beed098c2035b9b8df7a607823cdec5222768ce Mon Sep 17 00:00:00 2001 From: "tomosa.sarkar" Date: Thu, 25 Sep 2025 11:36:58 +0530 Subject: [PATCH 2/4] wip: Add logic for e mandate --- src/app/eMandate/login/page.tsx | 12 +- src/app/eMandate/mandate_page/page.tsx | 298 ++++++++++++++++++++----- src/app/login/page.tsx | 4 +- 3 files changed, 246 insertions(+), 68 deletions(-) diff --git a/src/app/eMandate/login/page.tsx b/src/app/eMandate/login/page.tsx index f9e156d..a835ef6 100644 --- a/src/app/eMandate/login/page.tsx +++ b/src/app/eMandate/login/page.tsx @@ -88,6 +88,7 @@ export default function Login() { method: 'POST', headers: { 'Content-Type': 'application/json', + 'X-Login-Type': 'emandate', }, body: JSON.stringify({ customerNo: CIF, @@ -103,33 +104,34 @@ export default function Login() { message: data?.error || "Internal Server Error", autoClose: 5000, }); - localStorage.removeItem("access_token"); + regenerateCaptcha() + localStorage.removeItem("mandate_token"); localStorage.clear(); sessionStorage.clear(); return; - } setIsLogging(true); if (response.ok) { console.log(data); const token = data.token; - localStorage.setItem("emandate_token", token); + localStorage.setItem("mandate_token", token); // localStorage.setItem("pswExpiryDate", data.loginPswExpiry); if (data.FirstTimeLogin === true) { notifications.show({ withBorder: true, color: "red", title: "Error", - message: "Please set your credential into Internet Banking before login.", + message: "Please go to Internet Banking, set your credentials, and then try logging in here again.", autoClose: 5000, }); } else { - router.push("/mandate_page"); + router.push("/eMandate/mandate_page"); } } else { + regenerateCaptcha(); setIsLogging(false); notifications.show({ withBorder: true, diff --git a/src/app/eMandate/mandate_page/page.tsx b/src/app/eMandate/mandate_page/page.tsx index 4b60ee9..88a1450 100644 --- a/src/app/eMandate/mandate_page/page.tsx +++ b/src/app/eMandate/mandate_page/page.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { Text, Title, @@ -13,12 +13,16 @@ import { Container, ActionIcon, Divider, + Modal, + TextInput, } from "@mantine/core"; import { Providers } from "@/app/providers"; import { useRouter } from "next/navigation"; import NextImage from "next/image"; import logo from "@/app/image/logo1.jpg"; -import { IconLogout } from "@tabler/icons-react"; +import { IconLogout, IconX } from "@tabler/icons-react"; +import { notifications } from "@mantine/notifications"; +import { useMediaQuery } from "@mantine/hooks"; type Mandate = { id: string; @@ -33,15 +37,29 @@ type Mandate = { const MandateCard = ({ mandate, - onAccept, - onReject, + onAction, }: { mandate: Mandate; - onAccept: (id: string) => void; - onReject: (id: string) => void; + onAction: (id: string, action: "accept" | "reject") => void; }) => { const [agreed, setAgreed] = useState(false); + const handleClick = (action: "accept" | "reject") => { + if (!agreed) { + notifications.show({ + withBorder: true, + icon: , + color: "red", + title: "Error", + message: + "Please agree to the debit of mandate processing charges first.", + autoClose: 4000, + }); + return; + } + onAction(mandate.id, action); + }; + return ( {mandate.category} @@ -62,19 +80,10 @@ const MandateCard = ({ /> - - @@ -84,6 +93,64 @@ const MandateCard = ({ export default function MandatePage() { const router = useRouter(); + const [authorized, setAuthorized] = useState(null); + const [custname, setCustname] = useState(null); + const isMobile = useMediaQuery("(max-width: 768px)"); + + // OTP Modal states + const [otpModalOpen, setOtpModalOpen] = useState(false); + const [otp, setOtp] = useState(""); + const [pendingAction, setPendingAction] = useState<{ id: string; action: "accept" | "reject" } | null>(null); + + useEffect(() => { + const token = localStorage.getItem("mandate_token"); + if (!token) { + setAuthorized(false); + router.push("/eMandate/login"); + } else { + setAuthorized(true); + handleFetchUserName(); + } + }, []); + + const handleLogout = () => { + localStorage.removeItem("mandate_token"); + localStorage.removeItem("user_name"); + localStorage.removeItem("userMobNo"); + router.push("/eMandate/login"); + }; + + const handleFetchUserName = async () => { + try { + const token = localStorage.getItem("mandate_token"); + const response = await fetch("/api/customer", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + if (!response.ok) throw new Error(); + const data = await response.json(); + if (Array.isArray(data) && data.length > 0) { + const name = data[0].custname; + const mobileNumber = data[0].mobileno; + localStorage.setItem("user_name", name); + localStorage.setItem("userMobNo", mobileNumber); + setCustname(name); + } + } catch { + notifications.show({ + withBorder: true, + color: "red", + title: "Please try again later", + message: "Unable to Fetch, Please try again later", + autoClose: 5000, + }); + } + }; + + // Dummy mandates const [mandates] = useState([ { id: "1", @@ -105,21 +172,95 @@ export default function MandatePage() { firstCollection: "2025-10-01", finalCollection: "2030-10-01", }, - ]); - const handleAccept = (id: string) => { - alert(`Accepted mandate ${id}`); + // STEP 1: When Accept/Reject pressed → call send OTP API + const handleMandateAction = async (id: string, action: "accept" | "reject") => { + try { + const response = await fetch("/api/otp/send", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + mobileNumber: '7890544527', + type: 'EMandate' + }), + }); + const data = await response.json(); + console.log(data) + if (!response.ok) throw new Error("Failed to send OTP"); + + notifications.show({ + withBorder: true, + color: "green", + title: "OTP Sent", + message: "An OTP has been sent to your registered mobile number.", + autoClose: 4000, + }); + + setPendingAction({ id, action }); + setOtp(""); + setOtpModalOpen(true); + } catch (err) { + notifications.show({ + withBorder: true, + color: "red", + title: "Error", + message: "Failed to send OTP. Please try again.", + autoClose: 5000, + }); + } }; - const handleReject = (id: string) => { - alert(`Rejected mandate ${id}`); - }; - const handleLogout = () => { - localStorage.removeItem("access_token"); - router.push("/eMandate/login"); // redirect to login page + // STEP 2: Verify OTP and complete action + const handleOtpSubmit = async () => { + // if (!otp) { + // notifications.show({ + // withBorder: true, + // color: "red", + // title: "Invalid Input", + // message: "Please enter OTP before proceed", + // autoClose: 5000, + // }); + // } + if (!pendingAction) return; + try { + const response = await fetch(`/api/otp/verify?mobileNumber=${7890544527}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + otp: otp, + }), + }); + + if (!response.ok) throw new Error("Invalid OTP"); + + notifications.show({ + withBorder: true, + color: "green", + title: "Success", + message: `Mandate ${pendingAction.action}ed successfully!`, + autoClose: 4000, + }); + + setOtpModalOpen(false); + setPendingAction(null); + } catch { + notifications.show({ + withBorder: true, + color: "red", + title: "Error", + message: "Invalid OTP. Please try again.", + autoClose: 4000, + }); + } }; + if (!authorized) return null; + return (
- + ebanking - - THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. - + /> + {!isMobile && ( + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + )} - {/* Logout Icon */} - - - + {isMobile ? ( + + + + ) : ( + + + +
); diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 7eee408..a762ba1 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -129,6 +129,7 @@ export default function Login() { message: data?.error || "Internal Server Error", autoClose: 5000, }); + regenerateCaptcha(); localStorage.removeItem("access_token"); localStorage.clear(); sessionStorage.clear(); @@ -150,6 +151,7 @@ export default function Login() { } else { + regenerateCaptcha(); setIsLogging(false); notifications.show({ withBorder: true, @@ -218,7 +220,7 @@ export default function Login() { }); const data = await res.json(); - console.log(data) + // console.log(data) if (!res.ok) { notifications.show({ color: "red", From 649fc5aceaa4f0bbd8238cdff11e23f8c42822b3 Mon Sep 17 00:00:00 2001 From: "tomosa.sarkar" Date: Thu, 25 Sep 2025 15:27:16 +0530 Subject: [PATCH 3/4] feat: make e-mandate screen responsive. --- src/app/(main)/funds_transfer/page.tsx | 4 + src/app/eMandate/login/page.tsx | 296 +++++++++++++++++++++---- src/app/eMandate/mandate_page/page.tsx | 72 ++++-- 3 files changed, 320 insertions(+), 52 deletions(-) diff --git a/src/app/(main)/funds_transfer/page.tsx b/src/app/(main)/funds_transfer/page.tsx index 7dbb20d..62a2295 100644 --- a/src/app/(main)/funds_transfer/page.tsx +++ b/src/app/(main)/funds_transfer/page.tsx @@ -266,6 +266,8 @@ export default function QuickPay() { setOtp(""); setValidationStatus(null); setBeneficiaryName(null); + setTimerActive(false); + setCountdown(180); } else { notifications.show({ title: "Error", @@ -293,6 +295,8 @@ export default function QuickPay() { setShowTxnPassword(false); setShowOtpField(false); setIsSubmitting(false); + setTimerActive(false); + setCountdown(180); } }; diff --git a/src/app/eMandate/login/page.tsx b/src/app/eMandate/login/page.tsx index a835ef6..f7cf676 100644 --- a/src/app/eMandate/login/page.tsx +++ b/src/app/eMandate/login/page.tsx @@ -1,6 +1,6 @@ "use client"; import React, { useState, useEffect, memo, useRef } from "react"; -import { Text, Button, TextInput, PasswordInput, Title, Card, Group, Flex, Box, Image, Anchor, Tooltip, Modal } from "@mantine/core"; +import { Text, Button, TextInput, PasswordInput, Title, Card, Group, Flex, Box, Image, Anchor, Tooltip, Modal, Stack } from "@mantine/core"; import { notifications } from "@mantine/notifications"; import { Providers } from "@/app/providers"; import { useRouter } from "next/navigation"; @@ -88,7 +88,7 @@ export default function Login() { method: 'POST', headers: { 'Content-Type': 'application/json', - 'X-Login-Type': 'emandate', + 'X-Login-Type': 'emandate', }, body: JSON.stringify({ customerNo: CIF, @@ -112,7 +112,7 @@ export default function Login() { } setIsLogging(true); if (response.ok) { - console.log(data); + // console.log(data); const token = data.token; localStorage.setItem("mandate_token", token); // localStorage.setItem("pswExpiryDate", data.loginPswExpiry); @@ -160,50 +160,192 @@ export default function Login() {
{/* Header */} + padding: "0.5rem 1rem", + background: + "linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)", + }} + > + {/* Logo */} ebanking - + <Title + order={4} + style={{ + fontFamily: "Roboto", + color: "white", + fontSize: "clamp(14px, 2vw, 20px)", // auto resizes + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + }} + > + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + + Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 + + + + + + {/* Logo */} + ebanking + + {/* Desktop: Title + Address */} + - THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. - - + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + + Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 + + + + {/* Mobile: only Logo + Bank Name */} + - Head Office : Dharmshala, District: Kangra(H.P), Pin: 176215 - + + THE KANGRA CENTRAL + + + CO-OPERATIVE BANK LTD. + + + +
{/* Movable text */} + {/* Main */} -
+
-
+
+ + {/* Mobile layout */} + + +
+ + E-Mandate Login + + + { + const input = e.currentTarget.value.replace(/\D/g, ""); + if (input.length <= 11) SetCIF(input); + }} + withAsterisk + mt="sm" + /> + + SetPsw(e.currentTarget.value)} + withAsterisk + mt="sm" + /> + + + + {captcha} + + + + + setInputCaptcha(e.currentTarget.value)} + withAsterisk + mt="sm" + /> + + + +
+
+ {/* Footer */} { + try { + const response = await fetch("/api/otp/send", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mobileNumber: localStorage.getItem("userMobNo"), + type: "EMandate", + }), + }); + + if (!response.ok) throw new Error("Failed to resend OTP"); + + notifications.show({ + withBorder: true, + color: "blue", + title: "OTP Resent", + message: "A new OTP has been sent to your registered mobile number.", + autoClose: 4000, + }); + } catch { + notifications.show({ + withBorder: true, + color: "red", + title: "Error", + message: "Failed to resend OTP. Please try again.", + autoClose: 5000, + }); + } + }; + // STEP 2: Verify OTP and complete action const handleOtpSubmit = async () => { - // if (!otp) { - // notifications.show({ - // withBorder: true, - // color: "red", - // title: "Invalid Input", - // message: "Please enter OTP before proceed", - // autoClose: 5000, - // }); - // } + if (!otp) { + notifications.show({ + withBorder: true, + color: "red", + title: "Invalid Input", + message: "Please enter OTP before proceed", + autoClose: 5000, + }); + } if (!pendingAction) return; try { - const response = await fetch(`/api/otp/verify?mobileNumber=${7890544527}`, { + const response = await fetch(`/api/otp/verify?mobileNumber=${localStorage.getItem("userMobNo")}`, { method: "POST", headers: { "Content-Type": "application/json", @@ -400,8 +432,8 @@ export default function MandatePage() { {/* OTP MODAL */} - setOtpModalOpen(false)} title="Enter OTP" centered> - An OTP has been sent to your registered mobile number. + setOtpModalOpen(false)} title="OTP Verification" centered> + An OTP has been sent to your registered mobile number. - if you did not received the OTP in SMS,you can click click here to resend the SMS + + If you did not receive the OTP in SMS, you can{" "} + + click here to resend the SMS + + . + + ); + })} + + + + )} + + + {/* Content Area */} + {children} -
-
+ + ); } } diff --git a/src/app/(main)/accounts/page.tsx b/src/app/(main)/accounts/page.tsx index 9cc6bda..55ad973 100644 --- a/src/app/(main)/accounts/page.tsx +++ b/src/app/(main)/accounts/page.tsx @@ -1,6 +1,7 @@ "use client"; -import { Group, Paper, ScrollArea, Table, Text, Title } from "@mantine/core"; +import { Group, Paper, ScrollArea, Stack, Table, Text, Title } from "@mantine/core"; +import { useMediaQuery } from "@mantine/hooks"; import { notifications } from "@mantine/notifications"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; @@ -16,6 +17,7 @@ export default function AccountSummary() { const router = useRouter(); const [authorized, setAuthorized] = useState(null); const [accountData, setAccountData] = useState([]); + const isMobile = useMediaQuery("(max-width: 768px)"); async function FetchAccountDetails() { try { @@ -61,7 +63,8 @@ export default function AccountSummary() { const cellStyle = { border: "1px solid #ccc", - padding: "10px", + padding: isMobile ? "8px" : "10px", + fontSize: isMobile ? "13px" : "14px", }; // Filter accounts @@ -96,7 +99,7 @@ export default function AccountSummary() { - + <Title order={isMobile ? 5 : 4} mb="sm"> {title} @@ -117,25 +120,38 @@ export default function AccountSummary() { ); - if (authorized) { - return ( - + - <Title order={3} mb="md">Account Summary + Account Summary + + + {/* ✅ Responsive layout: Group for desktop, Stack for mobile */} + {isMobile ? ( + + {depositAccounts.length > 0 && + renderTable("Deposit Accounts (INR)", renderRows(depositAccounts))} + {loanAccounts.length > 0 && + renderTable("Loan Accounts (INR)", renderRows(loanAccounts))} + + ) : ( - {/* Left table for Deposit Accounts */} - {depositAccounts.length > 0 && renderTable("Deposit Accounts (INR)", renderRows(depositAccounts))} - - {/* Right table for Loan Accounts (only shown if available) */} - {loanAccounts.length > 0 && renderTable("Loan Accounts (INR)", renderRows(loanAccounts))} + {depositAccounts.length > 0 && + renderTable("Deposit Accounts (INR)", renderRows(depositAccounts))} + {loanAccounts.length > 0 && + renderTable("Loan Accounts (INR)", renderRows(loanAccounts))} - - * Book Balance includes uncleared effects. - - - ); - } + )} - return null; + + * Book Balance includes uncleared effects. + + + ); } diff --git a/src/app/(main)/funds_transfer/layout.tsx b/src/app/(main)/funds_transfer/layout.tsx index 28de9c4..115c329 100644 --- a/src/app/(main)/funds_transfer/layout.tsx +++ b/src/app/(main)/funds_transfer/layout.tsx @@ -1,14 +1,19 @@ "use client"; -import { Divider, Stack, Text } from '@mantine/core'; +import { Box, Burger, Button, Divider, Drawer, Stack, Text } from '@mantine/core'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; import React, { useEffect, useState } from 'react'; import { useRouter } from "next/navigation"; +import Image from "next/image"; +import logo from "@/app/image/logo1.jpg"; +import { useMediaQuery } from '@mantine/hooks'; export default function Layout({ children }: { children: React.ReactNode }) { const [authorized, SetAuthorized] = useState(null); const router = useRouter(); const pathname = usePathname(); + const isMobile = useMediaQuery("(max-width: 768px)"); + const [drawerOpened, setDrawerOpened] = useState(false); const links = [ { label: " Quick Pay", href: "/funds_transfer" }, @@ -29,45 +34,145 @@ export default function Layout({ children }: { children: React.ReactNode }) { if (authorized) { return ( -
-
- - - Send Money - - + + {/* Desktop Sidebar */} + {!isMobile && ( + + + + Send Money + + - - {links.map(link => { - const isActive = pathname === link.href; - return ( - - {link.label} - - ); - })} - -
+ + {links.map((link) => { + const isActive = pathname === link.href; + return ( + + {link.label} + + ); + })} + + + )} -
+ {/* Mobile: Burger & Drawer */} + {isMobile && ( + <> + {/* Top header with burger and title */} + + setDrawerOpened(!drawerOpened)} + size="sm" + color="white" + /> + + Send Money + + + + setDrawerOpened(false)} + padding="md" + size="75%" + overlayProps={{ color: "black", opacity: 0.55, blur: 3 }} + styles={{ + root: { + backgroundColor: "#e6f5ff", // soft background for drawer + // borderLeft: "4px solid #228be6", + // borderRadius: "8px", + }, + }} + > + {/* Logo and Drawer Header */} + + <> + KCCB Logo + + Send Money + + + + + {/* Menu Items */} + + {links.map((link) => { + const isActive = pathname === link.href; + + return ( + + ); + })} + + + + )} + + + {/* Content Area */} + {children} -
-
+ + ); } } diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index 48c9757..4f58504 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -7,6 +7,7 @@ import { useRouter } from "next/navigation"; import { Providers } from "../../providers"; import { notifications } from '@mantine/notifications'; import dayjs from 'dayjs'; +import { useMediaQuery } from '@mantine/hooks'; interface accountData { stAccountNo: string; @@ -19,6 +20,7 @@ interface accountData { export default function Home() { const [authorized, SetAuthorized] = useState(null); const router = useRouter(); + const isMobile = useMediaQuery("(max-width: 768px)"); const [accountData, SetAccountData] = useState([]); const depositAccounts = accountData.filter(acc => acc.stAccountType !== "LN"); const [selectedDA, setSelectedDA] = useState(depositAccounts[0]?.stAccountNo || ""); @@ -27,7 +29,7 @@ export default function Home() { const [selectedLN, setSelectedLN] = useState(loanAccounts[0]?.stAccountNo || ""); const selectedLNData = loanAccounts.find(acc => acc.stAccountNo === selectedLN); const [showBalance, setShowBalance] = useState(false); - const PassExpiryRemains =(dayjs(localStorage.getItem("pswExpiryDate"))).diff(dayjs(),"day") + const PassExpiryRemains = (dayjs(localStorage.getItem("pswExpiryDate"))).diff(dayjs(), "day") // If back and forward button is clicked useEffect(() => { @@ -42,11 +44,11 @@ export default function Home() { router.push("/login"); }; const handleBeforeUnload = () => { - // logout on tab close / refresh - localStorage.removeItem("access_token"); - sessionStorage.removeItem("access_token"); - localStorage.clear(); - sessionStorage.clear(); + // logout on tab close / refresh + localStorage.removeItem("access_token"); + sessionStorage.removeItem("access_token"); + localStorage.clear(); + sessionStorage.clear(); }; window.addEventListener("popstate", handlePopState); window.addEventListener("beforeunload", handleBeforeUnload); @@ -112,29 +114,62 @@ export default function Home() { if (authorized) { return ( -
- Accounts Overview - - - Show Balance - setShowBalance(event.currentTarget.checked)} /> + + + Accounts Overview + + + {/* Show Balance Switch */} + + + + Show Balance + + setShowBalance(event.currentTarget.checked)} + /> -
- + + {/* Cards Section */} + {isMobile ? ( + {/* Deposit Account Card */} - - - - Deposit Account + + + + + Deposit Account + {depositAccounts.length > 0 ? ( ({ + value: acc.stAccountNo, + label: `${acc.stAccountType}- ${acc.stAccountNo}`, + }))} + value={selectedLN} + // @ts-ignore + onChange={setSelectedLN} + size="xs" + styles={{ + input: { + backgroundColor: "white", + color: "black", + marginLeft: 5, + width: "120px", + }, + }} + /> + ) : ( + + No loan account available + + )} + + + {loanAccounts.length > 0 ? ( + <> + + {Number(selectedLNData?.stAccountNo || 0)} + + + {showBalance + ? `₹${Number(selectedLNData?.stAvailableBalance || 0).toLocaleString("en-IN")}` + : "****"} + + + + ) : ( + <> + + Apply for a loan account to get started + + + + )} + + + {/* Important Links Card */} + + + Quick Links + + + + + + + + + + ) : ( + + {/* Desktop Cards */} + {/* Deposit Account Card */} + + + + Deposit Account + {depositAccounts.length > 0 ? ( + ({ + data={loanAccounts.map((acc) => ({ value: acc.stAccountNo, - label: `${acc.stAccountType}- ${acc.stAccountNo}` + label: `${acc.stAccountType}- ${acc.stAccountNo}`, }))} value={selectedLN} // @ts-ignore @@ -194,12 +391,14 @@ export default function Home() { backgroundColor: "white", color: "black", marginLeft: 30, - width: 140 - } + width: 140, + }, }} /> ) : ( - No loan account available + + No loan account available + )} @@ -207,42 +406,60 @@ export default function Home() { <> {Number(selectedLNData?.stAccountNo || 0)} - {showBalance ? `₹${Number(selectedLNData?.stAvailableBalance || 0).toLocaleString('en-IN')}` : "****"} + {showBalance ? `₹${Number(selectedLNData?.stAvailableBalance || 0).toLocaleString("en-IN")}` : "****"} - + ) : ( <> - Apply for a loan account to get started - + + Apply for a loan account to get started + + )} {/* Important Links Card */} - - Quick Links + + + Quick Links + - - - - + + + + -
-
+ )} + + {/* Notes Section */} + ** Book Balance includes uncleared effect. - ** Click on the "Show Balance"to display balance for the Deposit and Loan account. - ** Your Password will expire in {PassExpiryRemains} days. - + ** Click on the "Show Balance" to display balance for the Deposit and Loan account. + + ** Your Password will expire in {PassExpiryRemains} days. + -
-
+ +
); } - return null; } \ No newline at end of file diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 1e05f12..83f3e14 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -8,7 +8,7 @@ import { Providers } from '../providers'; import logo from '@/app/image/logo1.jpg'; import NextImage from 'next/image'; import { notifications } from '@mantine/notifications'; -import { useDisclosure } from '@mantine/hooks'; +import { useDisclosure, useMediaQuery } from '@mantine/hooks'; export default function RootLayout({ children }: { children: React.ReactNode }) { const router = useRouter(); @@ -16,7 +16,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) const [authorized, SetAuthorized] = useState(null); const [userLastLoginDetails, setUserLastLoginDetails] = useState(null); const [custname, setCustname] = useState(null); - + const isMobile = useMediaQuery("(max-width: 768px)"); const [opened, { open, close }] = useDisclosure(false); @@ -167,104 +167,84 @@ export default function RootLayout({ children }: { children: React.ReactNode }) -
+ + + {/* HEADER */} - ebanking - - THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. - - - Toll Free No : 1800-180-8008 - + {/* Logo */} + + ebanking + + + {/* Title & Phone */} + + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + + Toll Free No : 1800-180-8008 + + -
- - + <Stack gap={isMobile ? 2 : 0} align={isMobile ? "flex-start" : "flex-start"}> + <Title order={isMobile ? 5 : 4} style={{ fontFamily: "inter", fontSize: isMobile ? "18px" : "22px" }}> Welcome, {custname ?? null} - + Last logged in at {userLastLoginDetails ? new Date(userLastLoginDetails).toLocaleString() : "N/A"} - + {navItems.map((item) => { const isActive = pathname.startsWith(item.href); const Icon = item.icon; return ( ); })} - {/* */} - + + - - Are you sure you want to logout? @@ -273,30 +253,31 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - + -
+
- + -
{children} -
+ - + + {/* FOOTER */} - + © 2025 The Kangra Central Co-Operative Bank -
+
diff --git a/src/app/(main)/settings/layout.tsx b/src/app/(main)/settings/layout.tsx index bc1b7bb..2b21fca 100644 --- a/src/app/(main)/settings/layout.tsx +++ b/src/app/(main)/settings/layout.tsx @@ -1,14 +1,19 @@ "use client"; -import { Divider, Stack, Text } from '@mantine/core'; +import { Box, Burger, Button, Divider, Drawer, Stack, Text } from '@mantine/core'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; import React, { useEffect, useState } from 'react'; import { useRouter } from "next/navigation"; +import { useMediaQuery } from '@mantine/hooks'; +import Image from "next/image"; +import logo from "@/app/image/logo1.jpg"; export default function Layout({ children }: { children: React.ReactNode }) { const [authorized, SetAuthorized] = useState(null); const router = useRouter(); const pathname = usePathname(); + const isMobile = useMediaQuery("(max-width: 768px)"); + const [drawerOpened, setDrawerOpened] = useState(false); const links = [ { label: "View Profile", href: "/settings" }, @@ -30,45 +35,143 @@ export default function Layout({ children }: { children: React.ReactNode }) { if (authorized) { return ( -
-
- - - Settings - - + + {/* Desktop Sidebar */} + {!isMobile && ( + + + + Settings + + - - {links.map(link => { - const isActive = pathname === link.href; - return ( - - {link.label} - - ); - })} - -
+ + {links.map((link) => { + const isActive = pathname === link.href; + return ( + + {link.label} + + ); + })} + + + )} -
+ {/* Mobile: Burger & Drawer */} + {isMobile && ( + <> + {/* Top header with burger and title */} + + setDrawerOpened(!drawerOpened)} + size="sm" + color="white" + /> + + Settings + + + + setDrawerOpened(false)} + padding="md" + size="75%" + overlayProps={{ color: "black", opacity: 0.55, blur: 3 }} + styles={{ + root: { + backgroundColor: "#e6f5ff", // soft background for drawer + }, + }} + > + {/* Logo and Drawer Header */} + + <> + KCCB Logo + + Settings + + + + + {/* Menu Items */} + + {links.map((link) => { + const isActive = pathname === link.href; + + return ( + + ); + })} + + + + )} + + + {/* Content Area */} + {children} -
-
+ + ); } } diff --git a/src/app/eMandate/login/Login.module.css b/src/app/eMandate/login/Login.module.css new file mode 100644 index 0000000..68faac2 --- /dev/null +++ b/src/app/eMandate/login/Login.module.css @@ -0,0 +1,89 @@ + +/* Header */ +.header { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 100; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem; + background: linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%); +} + +.header-text { + flex: 1; +} + +/* Desktop header text */ +.desktop-text { + color: white; +} + +.mobile-text { + color: white; + display: none; +} + +/* Movable scrolling text - desktop only */ +.desktop-scroll-text { + width: 100%; + overflow: hidden; + white-space: nowrap; + padding: 8px 0; +} + +.desktop-scroll-text span { + display: inline-block; + padding-left: 100%; + animation: scroll-left 60s linear infinite; + font-weight: bold; + color: #004d99; +} + +@keyframes scroll-left { + 0% { transform: translateX(0%); } + 100% { transform: translateX(-100%); } +} + +/* Main Login Section */ +.login-container { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + min-height: 75vh; + padding: 1rem; +} + +.login-image { + flex: 1 1 400px; + min-width: 300px; + height: 400px; + margin: 1rem; +} + +.login-card { + flex: 1 1 300px; + min-width: 280px; + margin: 1rem; +} + +/* Responsive - Mobile */ +@media (max-width: 768px) { + .desktop-text { display: none; } + .mobile-text { display: block; } + + .desktop-scroll-text { display: none; } + + .login-container { + flex-direction: column; + } + + .login-image { + height: 250px; + margin: 0.5rem 0; + } +} diff --git a/src/app/eMandate/login/page.tsx b/src/app/eMandate/login/page.tsx index f7cf676..ca3e101 100644 --- a/src/app/eMandate/login/page.tsx +++ b/src/app/eMandate/login/page.tsx @@ -8,6 +8,7 @@ import NextImage from "next/image"; import logo from '@/app/image/logo1.jpg'; import frontPage from '@/app/image/EMandate.jpg'; import { generateCaptcha } from '@/app/captcha'; +import styles from './Login.module.css'; export default function Login() { @@ -128,7 +129,6 @@ export default function Login() { else { router.push("/eMandate/mandate_page"); } - } else { regenerateCaptcha(); @@ -159,200 +159,21 @@ export default function Login() { {/* Main Screen */}
{/* Header */} - - {/* Logo */} - ebanking - {/* Bank name + address stacked */} - - - THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. - - - Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 - + + ebanking + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 + THE KANGRA CENTRAL + CO-OPERATIVE BANK LTD. - - {/* Logo */} - ebanking - - {/* Desktop: Title + Address */} - - - THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. - - - Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 - - - - {/* Mobile: only Logo + Bank Name */} - - - THE KANGRA CENTRAL - - - CO-OPERATIVE BANK LTD. - - - - - -
{/* Movable text */} {/* Header */} - + ebanking - - THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. - - - Head Office : Dharmshala, District: Kangra(H.P), Pin: 176215 - - {/* - - - - */} + + {/* Desktop */} + + THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. + + + Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 + + + {/* Mobile */} + + THE KANGRA CENTRAL + + + CO-OPERATIVE BANK LTD. + + + Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 + +