Files
IB/src/app/eMandate/otp_page/page.tsx
2025-12-15 13:46:45 +05:30

506 lines
20 KiB
TypeScript

"use client";
import React, { useEffect, useState, useRef } from "react";
import {
Text,
Title,
Box,
Image,
Button,
Group,
Container,
ActionIcon,
Divider,
PinInput,
Paper,
Stack,
Center,
Loader,
} 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, IconShieldCheck, IconRefresh } from "@tabler/icons-react";
import { useMediaQuery } from "@mantine/hooks";
import { sendOtp, verifyOtp } from "../otpUtils";
import { fetchUserDetails } from "../authUtils";
export default function VerifyOtpPage() {
const router = useRouter();
const [authorized, setAuthorized] = useState<boolean | null>(null);
const [custname, setCustname] = useState<string | null>(null);
const [otp, setOtp] = useState("");
const [isVerifying, setIsVerifying] = useState(false);
const [isResending, setIsResending] = useState(false);
const [timer, setTimer] = useState(60);
const [canResend, setCanResend] = useState(false);
const isMobile = useMediaQuery("(max-width: 768px)");
const timerRef = useRef<NodeJS.Timeout | null>(null);
const otpSentRef = useRef(false);
//On First Load: Check Token → Fetch User → Send OTP
useEffect(() => {
const token = localStorage.getItem("mandate_token");
if (!token) {
handleLogout();
setAuthorized(false);
return;
}
setAuthorized(true);
// Get User Name + Mobile
fetchUserDetails().then((res) => {
if (res) {
setCustname(res.name);
if (!otpSentRef.current) {
sendOtp(res.mobile);
// sendOtp("7890544527");
otpSentRef.current = true; // Prevent second OTP
}
}
});
// Prevent back button
const handlePopState = () => {
handleLogout();
};
window.addEventListener("popstate", handlePopState);
window.history.pushState(null, "", window.location.href);
return () => {
window.removeEventListener("popstate", handlePopState);
if (timerRef.current) clearInterval(timerRef.current);
};
}, []);
// Timer Countdown
useEffect(() => {
timerRef.current = setInterval(() => {
setTimer((prev) => {
if (prev === 1) {
setCanResend(true);
clearInterval(timerRef.current!);
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timerRef.current!);
}, []);
// Logout
const handleLogout = () => {
localStorage.clear();
router.push("/eMandate/logout");
};
//Resend OTP
const handleResendOtp = async () => {
setIsResending(true);
const mobile = localStorage.getItem("userMobNo");
// const mobile = "7890544527";
if (!mobile) return;
await sendOtp(mobile);
setTimer(60);
setCanResend(false);
setOtp("");
setIsResending(false);
};
// Verify OTP using verifyOtp() Utility
// const handleVerifyOtp = async () => {
// const mobile = localStorage.getItem("userMobNo");
// // const mobile = "7890544527";
// if (!mobile || otp.length !== 6) return;
// setIsVerifying(true);
// const success = await verifyOtp(otp, mobile);
// // if (success) {
// // // const encoded_data = localStorage.getItem("Validate_data");
// // // const res = await fetch(
// // // `https://apiemandate.kccb.in:9035/EMandate/auth-cbs-resp?data=${encoded_data}`,
// // // {
// // // method: "POST",
// // // headers: {
// // // "Content-Type": "application/json",
// // // },
// // // }
// // // );
// // const formData = new FormData();
// // const encoded_data = localStorage.getItem("Validate_data");
// // formData.append("data", encoded_data);
// // const res = await fetch(
// // "https://apiemandate.kccb.in:9035/EMandate/auth-cbs-resp",
// // {
// // method: "POST",
// // body: formData,
// // }
// // );
// // const result = await res.json();
// // console.log(result);
// // if (!res.ok) {
// // throw new Error("CBS response API failed");
// // }
// // if (res.ok) {
// // // navigate only after successful API hit
// // setTimeout(() => {
// // router.push("/eMandate/mandate_page");
// // }, 1500);
// // }
// // }
// if (success) {
// try {
// const encoded_data = localStorage.getItem("Validate_data");
// if (!encoded_data) {
// console.error("Validate_data not found in localStorage");
// return;
// }
// const formData = new FormData();
// formData.append("data", encoded_data);
// const res = await fetch(
// "https://apiemandate.kccb.in:9035/EMandate/auth-cbs-resp",
// {
// method: "POST",
// body: formData,
// }
// );
// if (!res.ok) {
// throw new Error(`CBS response API failed: ${res.status}`);
// }
// const contentType = res.headers.get("content-type");
// const result =
// contentType && contentType.includes("application/json")
// ? await res.json()
// : await res.text();
// console.log("CBS Response:", result);
// setTimeout(() => {
// router.push("/eMandate/mandate_page");
// }, 1000);
// } catch (error) {
// console.error("CBS API Error:", error);
// }
// }
// else {
// setOtp("");
// }
// setIsVerifying(false);
// };
const handleVerifyOtp = async () => {
const mobile = localStorage.getItem("userMobNo");
if (!mobile || otp.length !== 6) return;
setIsVerifying(true);
const success = await verifyOtp(otp, mobile);
if (success) {
const encoded_data = localStorage.getItem("Validate_data");
if (!encoded_data) {
console.error("Validate_data not found in localStorage");
setIsVerifying(false);
return;
}
const url = "https://apiemandate.kccb.in:9035/EMandate/auth-cbs-resp";
console.log("Redirecting (POST) to URL:", url);
console.log("POST Body Data:", { data: encoded_data });
// Create a form for POST redirect
const form = document.createElement("form");
form.method = "POST";
form.action = url;
// Add hidden field "data"
const input = document.createElement("input");
input.type = "hidden";
input.name = "data";
input.value = encoded_data;
form.appendChild(input);
document.body.appendChild(form);
// Submit → browser navigates to URL using POST
form.submit();
return;
}
else {
setOtp("");
}
setIsVerifying(false);
};
if (authorized === null) {
return (
<Center style={{ height: "100vh" }}>
<Loader size="lg" color="green" />
</Center>
);
}
return (
<Providers>
<Box
style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
// background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
background: 'linear-gradient(179deg, #3faa56ff 49%, #3aa760ff 80%)'
}}
>
{/* HEADER */}
<Paper
radius={0}
shadow="md"
style={{
position: "sticky",
top: 0,
zIndex: 100,
background: "linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)",
// padding: isMobile ? "0.75rem 1rem" : "1rem 1.5rem",
}}
>
<Container size="xl">
<Group justify="space-between">
<Group gap="md">
<Image
src={logo}
component={NextImage}
fit="contain"
alt="KCC Bank Logo"
style={{
width: isMobile ? "50px" : "70px",
height: isMobile ? "50px" : "70px",
}}
/>
{!isMobile && (
<Title
order={2}
c="white"
style={{
fontSize: "clamp(1rem, 2vw, 1.5rem)",
fontFamily: "Roboto",
}}
>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
)}
</Group>
{isMobile ? (
<ActionIcon
variant="subtle"
color="white"
size="lg"
onClick={handleLogout}
title="Logout"
>
<IconLogout size={24} />
</ActionIcon>
) : (
<Button
variant="white"
color="gray"
onClick={handleLogout}
leftSection={<IconLogout size={20} />}
>
Logout
</Button>
)}
</Group>
</Container>
</Paper>
{/* WELCOME BAR */}
<Paper
radius={0}
p={isMobile ? "sm" : "md"}
bg="white"
shadow="sm"
style={{ borderBottom: "3px solid #0a7228" }}
>
<Container size="xl">
<Stack gap="xs">
<Text
fw={600}
size={isMobile ? "lg" : "xl"}
style={{ fontFamily: "Inter" }}
>
Welcome, {custname ?? "User"}
</Text>
<Divider />
</Stack>
</Container>
</Paper>
{/* MAIN CONTENT */}
<Box style={{ flex: 1, padding: "2rem 1rem" }}>
<Container size="sm">
<Center>
<Paper
shadow="xl"
radius="xl"
p={isMobile ? "xl" : "2rem"}
style={{
maxWidth: "500px",
width: "100%",
background: "rgba(255, 255, 255, 0.95)",
backdropFilter: "blur(10px)",
}}
>
<Stack gap="xl" align="center">
{/* Icon */}
<Box
style={{
width: "80px",
height: "80px",
borderRadius: "50%",
background:
"linear-gradient(135deg, #0a7228 0%, #2563eb 100%)",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<IconShieldCheck size={48} color="white" />
</Box>
{/* Title */}
<Stack gap="xs" align="center">
<Title
order={2}
ta="center"
style={{
fontSize: isMobile ? "1.5rem" : "2rem",
}}
>
OTP Verification
</Title>
<Text size="sm" c="dimmed" ta="center">
Enter the 6-digit OTP sent to your registered mobile
number
</Text>
<Text size="sm" fw={600} c="blue">
{localStorage.getItem("userMobNo")?.replace(
/(\d{2})(\d{4})(\d{4})/,
"+91 $1****$3"
)}
</Text>
</Stack>
{/* OTP Input */}
<Stack gap="md" align="center" w="100%">
<PinInput
size={isMobile ? "lg" : "xl"}
length={6}
value={otp}
onChange={setOtp}
placeholder="○"
type="number"
oneTimeCode
styles={{
input: {
fontSize: isMobile ? "1.5rem" : "2rem",
fontWeight: 600,
borderColor: "#0a7228",
},
}}
/>
{/* Timer */}
<Group gap="xs">
<Text size="sm" c="dimmed">
{canResend ? (
"Didn't receive OTP?"
) : (
<>
Resend OTP in{" "}
<Text component="span" fw={600} c="blue">
{timer}s
</Text>
</>
)}
</Text>
</Group>
{/* Verify Button */}
<Button
fullWidth
size="lg"
onClick={handleVerifyOtp}
loading={isVerifying}
disabled={otp.length !== 6 || isVerifying}
gradient={{ from: "teal", to: "blue", deg: 60 }}
variant="gradient"
radius="md"
>
{isVerifying ? "Verifying..." : "Verify OTP"}
</Button>
{/* Resend Button */}
<Button
fullWidth
variant="light"
size="md"
onClick={handleResendOtp}
disabled={!canResend || isResending}
loading={isResending}
leftSection={<IconRefresh size={18} />}
>
{isResending ? "Sending..." : "Resend OTP"}
</Button>
</Stack>
{/* Info */}
<Paper p="md" radius="md" bg="blue.0" w="100%">
<Text size="xs" c="blue.9" ta="center">
<Text component="span" fw={600}>
Note:{" "}
</Text>
Please do not share your OTP with anyone. KCC Bank
will never ask for your OTP.
</Text>
</Paper>
</Stack>
</Paper>
</Center>
</Container>
</Box>
{/* FOOTER */}
<Paper
radius={0}
p="md"
bg="gray.9"
style={{
borderTop: "1px solid #ddd",
}}
>
<Container size="xl">
<Text size="xs" c="white" ta="center">
© 2025 The Kangra Central Co-Operative Bank Ltd. All rights
reserved.
</Text>
</Container>
</Paper>
</Box>
</Providers>
);
}