395 lines
16 KiB
TypeScript
395 lines
16 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) {
|
|
setTimeout(() => router.push("/eMandate/mandate_page"), 1500);
|
|
} 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>
|
|
);
|
|
}
|