feat: make e-mandate screen responsive.

This commit is contained in:
2025-09-25 15:27:16 +05:30
parent 6beed098c2
commit 649fc5acea
3 changed files with 320 additions and 52 deletions

View File

@@ -266,6 +266,8 @@ export default function QuickPay() {
setOtp(""); setOtp("");
setValidationStatus(null); setValidationStatus(null);
setBeneficiaryName(null); setBeneficiaryName(null);
setTimerActive(false);
setCountdown(180);
} else { } else {
notifications.show({ notifications.show({
title: "Error", title: "Error",
@@ -293,6 +295,8 @@ export default function QuickPay() {
setShowTxnPassword(false); setShowTxnPassword(false);
setShowOtpField(false); setShowOtpField(false);
setIsSubmitting(false); setIsSubmitting(false);
setTimerActive(false);
setCountdown(180);
} }
}; };

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useState, useEffect, memo, useRef } from "react"; 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 { notifications } from "@mantine/notifications";
import { Providers } from "@/app/providers"; import { Providers } from "@/app/providers";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@@ -112,7 +112,7 @@ export default function Login() {
} }
setIsLogging(true); setIsLogging(true);
if (response.ok) { if (response.ok) {
console.log(data); // console.log(data);
const token = data.token; const token = data.token;
localStorage.setItem("mandate_token", token); localStorage.setItem("mandate_token", token);
// localStorage.setItem("pswExpiryDate", data.loginPswExpiry); // localStorage.setItem("pswExpiryDate", data.loginPswExpiry);
@@ -160,50 +160,192 @@ export default function Login() {
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "auto", paddingTop: "5%" }}> <div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "auto", paddingTop: "5%" }}>
{/* Header */} {/* Header */}
<Box <Box
component="header"
style={{ style={{
position: 'fixed', width: '100%', height: '12%', top: 0, left: 0, zIndex: 100, position: "fixed",
top: 0,
left: 0,
width: "100%",
zIndex: 100,
display: "flex", display: "flex",
alignItems: "center",
justifyContent: "flex-start", justifyContent: "flex-start",
background: "linear-gradient(15deg,rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)", padding: "0.5rem 1rem",
}}> background:
"linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)",
}}
>
{/* Logo */}
<Image <Image
src={logo} src={logo}
component={NextImage} component={NextImage}
fit="contain" fit="contain"
alt="ebanking" alt="ebanking"
style={{ width: "100%", height: "auto", flexShrink: 0 }}
/>
<Title
order={2}
style={{ style={{
fontFamily: 'Roboto', width: "60px", // fixed height for logo
position: 'absolute', height: "auto",
top: '30%', marginRight: "0.75rem",
left: '7%', flexShrink: 0,
color: 'White', }}
transition: "opacity 0.5s ease-in-out", />
{/* Bank name + address stacked */}
<Box style={{ flex: 1, minWidth: 0 }}>
<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. THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title> </Title>
<Text size="sm" c="white" <Text
size="xs"
style={{ style={{
backgroundColor: '#1f1f14', // backgroundColor: "#1f1f14",
fontFamily: 'Roboto', fontFamily: "Roboto",
position: 'absolute', padding: "2px 6px",
top: '59%', borderRadius: "4px",
left: '72%', color: "white",
color: 'white', textShadow: "1px 1px 2px blue",
textShadow: '1px 1px 2px blue', fontSize: "clamp(10px, 1.5vw, 13px)", // responsive text
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}} }}
> >
Head Office : Dharmshala, District: Kangra(H.P), Pin: 176215 Head Office: Dharmshala, District Kangra (H.P), Pin: 176215
</Text> </Text>
</Box> </Box>
</Box>
<Box
component="header"
style={{
position: "fixed",
top: 0,
left: 0,
width: "100%",
zIndex: 100,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "0.5rem 1rem",
background:
"linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)",
}}
>
{/* Logo */}
<Image
src={logo}
component={NextImage}
fit="contain"
alt="ebanking"
style={{
width: "60px",
height: "auto",
flexShrink: 0,
}}
/>
{/* Desktop: Title + Address */}
<Box
className="desktop-header"
style={{
display: "flex",
flexDirection: "column",
marginLeft: "1rem",
flex: 1,
}}
>
<Title
order={4}
style={{
fontFamily: "Roboto",
color: "white",
fontSize: "clamp(14px, 2vw, 20px)",
}}
>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text
size="xs"
style={{
// backgroundColor: "#1f1f14",
fontFamily: "Roboto",
padding: "2px 6px",
borderRadius: "4px",
color: "white",
textShadow: "1px 1px 2px blue",
fontSize: "clamp(10px, 1.5vw, 13px)",
}}
>
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215
</Text>
</Box>
{/* Mobile: only Logo + Bank Name */}
<Box
className="mobile-header"
style={{
display: "none",
flexDirection: "column",
alignItems: "flex-start",
marginLeft: "0.75rem",
}}
>
<Title
order={5}
style={{
fontFamily: "Roboto",
color: "white",
fontSize: "16px",
}}
>
THE KANGRA CENTRAL
</Title>
<Title
order={5}
style={{
fontFamily: "Roboto",
color: "white",
fontSize: "16px",
}}
>
CO-OPERATIVE BANK LTD.
</Title>
</Box>
<style jsx>{`
@media (max-width: 768px) {
.desktop-header {
display: none;
}
.mobile-header {
display: flex;
}
}
@media (min-width: 769px) {
.desktop-header {
display: flex;
}
.mobile-header {
display: none;
}
}
`}
</style>
</Box>
<div style={{ marginTop: '10px' }}> <div style={{ marginTop: '10px' }}>
{/* Movable text */} {/* Movable text */}
<Box <Box
className="desktop-scroll-text"
style={{ style={{
width: "100%", width: "100%",
height: "0.5%", height: "0.5%",
@@ -236,13 +378,17 @@ export default function Login() {
0% { transform: translateX(0%); } 0% { transform: translateX(0%); }
100% { transform: translateX(-100%); } 100% { transform: translateX(-100%); }
} }
@media (max-width: 768px) {
.desktop-scroll-text { display: none; }
}
`} `}
</style> </style>
</Box> </Box>
{/* Main */} {/* Main */}
<div style={{ <Box visibleFrom="md"
style={{
display: "flex", height: "75vh", overflow: "hidden", position: "relative", display: "flex", height: "75vh", overflow: "hidden", position: "relative",
// background: 'linear-gradient(to right, #02081eff, #0a3d91)'
background: 'linear-gradient(179deg, #3faa56ff 49%, #3aa760ff 80%)' background: 'linear-gradient(179deg, #3faa56ff 49%, #3aa760ff 80%)'
}}> }}>
<div style={{ flex: 1, backgroundColor: "#c1e0f0", position: "relative" }}> <div style={{ flex: 1, backgroundColor: "#c1e0f0", position: "relative" }}>
@@ -302,7 +448,81 @@ export default function Login() {
</form> </form>
</Card> </Card>
</Box> </Box>
</div> </Box>
{/* Mobile layout */}
<Box
hiddenFrom="md"
style={{
marginTop: "25%",
padding: "1rem",
background: "linear-gradient(179deg, #3faa56ff 49%, #3aa760ff 80%)",
minHeight: "80vh",
display: "flex",
justifyContent: "center",
alignItems: "flex-start",
}}
>
<Card shadow="md" padding="lg" radius="md" style={{ width: "100%" }}>
<form onSubmit={handleMandateLogin}>
<Text ta="center" fw={700} style={{ fontSize: "16px" }}>
E-Mandate Login
</Text>
<TextInput
label="User ID"
placeholder="Enter your CIF No"
value={CIF}
onInput={(e) => {
const input = e.currentTarget.value.replace(/\D/g, "");
if (input.length <= 11) SetCIF(input);
}}
withAsterisk
mt="sm"
/>
<PasswordInput
label="Password"
placeholder="Enter your password"
value={psw}
onChange={(e) => SetPsw(e.currentTarget.value)}
withAsterisk
mt="sm"
/>
<Group mt="sm" align="center" grow>
<Box
style={{
backgroundColor: "#fff",
fontSize: "16px",
textDecoration: "line-through",
padding: "4px 8px",
fontFamily: "cursive",
}}
>
{captcha}
</Box>
<Button size="xs" variant="light" onClick={regenerateCaptcha}>
Refresh
</Button>
</Group>
<TextInput
label="Enter CAPTCHA"
placeholder="Enter above text"
value={inputCaptcha}
onChange={(e) => setInputCaptcha(e.currentTarget.value)}
withAsterisk
mt="sm"
/>
<Button type="submit" fullWidth mt="md" disabled={isLogging}>
{isLogging ? "Logging..." : "Login"}
</Button>
</form>
</Card>
</Box>
{/* Footer */} {/* Footer */}
<Box <Box
component="footer" component="footer"

View File

@@ -183,7 +183,7 @@ export default function MandatePage() {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
mobileNumber: '7890544527', mobileNumber: localStorage.getItem("userMobNo"),
type: 'EMandate' type: 'EMandate'
}), }),
}); });
@@ -213,20 +213,52 @@ export default function MandatePage() {
} }
}; };
// Resend OTP
const handleResendOtp = async () => {
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 // STEP 2: Verify OTP and complete action
const handleOtpSubmit = async () => { const handleOtpSubmit = async () => {
// if (!otp) { if (!otp) {
// notifications.show({ notifications.show({
// withBorder: true, withBorder: true,
// color: "red", color: "red",
// title: "Invalid Input", title: "Invalid Input",
// message: "Please enter OTP before proceed", message: "Please enter OTP before proceed",
// autoClose: 5000, autoClose: 5000,
// }); });
// } }
if (!pendingAction) return; if (!pendingAction) return;
try { try {
const response = await fetch(`/api/otp/verify?mobileNumber=${7890544527}`, { const response = await fetch(`/api/otp/verify?mobileNumber=${localStorage.getItem("userMobNo")}`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -400,8 +432,8 @@ export default function MandatePage() {
</Box> </Box>
{/* OTP MODAL */} {/* OTP MODAL */}
<Modal opened={otpModalOpen} onClose={() => setOtpModalOpen(false)} title="Enter OTP" centered> <Modal opened={otpModalOpen} onClose={() => setOtpModalOpen(false)} title="OTP Verification" centered>
<Text mb="sm">An OTP has been sent to your registered mobile number.</Text> <Text mb="sm" size="sm" ta="center" c="green">An OTP has been sent to your registered mobile number.</Text>
<TextInput <TextInput
label="OTP" label="OTP"
placeholder="Enter OTP" placeholder="Enter OTP"
@@ -413,7 +445,19 @@ export default function MandatePage() {
}} }}
/> />
<Text size="xs">if you did not received the OTP in SMS,you can click click here to resend the SMS</Text> <Text mt="md" size="xs" c="dimmed">
If you did not receive the OTP in SMS, you can{" "}
<Text
span
c="blue"
td="underline"
style={{ cursor: "pointer" }}
onClick={handleResendOtp}
>
click here to resend the SMS
</Text>
.
</Text>
<Group mt="md" grow> <Group mt="md" grow>
<Button color="gray" onClick={() => setOtpModalOpen(false)}> <Button color="gray" onClick={() => setOtpModalOpen(false)}>
Cancel Cancel