feat : integrated forget password logic.

feat: make the screen responsive.
fix: change the frontend of login screen
This commit is contained in:
2025-12-30 15:59:51 +05:30
parent 9925905a23
commit 97272e2ded
11 changed files with 788 additions and 439 deletions

View File

@@ -1,91 +1,126 @@
"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, Container, Grid, Progress } 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 forget_psw from '@/app/image/forget_password.jpg';
import { generateCaptcha } from '@/app/captcha';
import { IconLock, IconRefresh, IconShieldCheck } from '@tabler/icons-react';
export default function ForgetLoginPwd() {
const router = useRouter();
const [authorized, SetAuthorized] = useState<boolean | null>(null);
const [authorized, setAuthorized] = useState<boolean | null>(null);
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 icon = <IconLock size={18} stroke={1.5} />;
async function handleLogout(e: React.FormEvent) {
e.preventDefault();
localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token")
localStorage.clear();
sessionStorage.clear();
router.push("/login")
}
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));
// Add this useEffect in your component
useEffect(() => {
// Check authorization on mount
const token = localStorage.getItem("reset_pwd_token");
if (!token) {
setAuthorized(false);
router.push("/login");
return;
}
setCaptcha(result);
setAuthorized(true);
// Handle back button navigation
const handlePopState = () => {
localStorage.removeItem("reset_pwd_token");
router.push("/login");
};
// Handle page refresh/reload
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
localStorage.removeItem("reset_pwd_token");
};
// Add event listeners
window.addEventListener('popstate', handlePopState);
window.addEventListener('beforeunload', handleBeforeUnload);
// Cleanup listeners on unmount
return () => {
window.removeEventListener('popstate', handlePopState);
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [router]);
useEffect(() => {
const token = localStorage.getItem("reset_pwd_token");
if (!token) {
setAuthorized(false);
router.push("/login");
} else {
setAuthorized(true);
}
}, [router]);
const handleRefreshCaptcha = async () => {
const newCaptcha = await generateCaptcha();
setCaptcha(newCaptcha);
setCaptchaInput('');
setCaptchaError('');
};
async function handleSetLoginPassword(e: React.FormEvent) {
const handleSetLoginPassword = async (e: React.FormEvent) => {
e.preventDefault();
const pwdRegex = /^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
if (captchaInput !== captcha) {
setCaptchaError("Incorrect CAPTCHA. Please try again.");
setCaptchaInput('');
return;
}
if (!password || !confirmPassword) {
notifications.show({
withBorder: true,
color: "red",
title: "Both password fields are required.",
title: "Required Fields",
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 (password !== confirmPassword) {
notifications.show({
withBorder: true,
color: "red",
title: "Passwords do not match.",
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.",
title: "Weak Password",
message: "Password must contain at least 1 capital letter, 1 number, 1 special character, and be at least 8 characters long.",
autoClose: 5000,
});
return;
}
else if (captchaInput !== captcha) {
setCaptchaError("Incorrect CAPTCHA.");
return;
}
const token = localStorage.getItem("access_token");
const token = localStorage.getItem("reset_pwd_token");
const response = await fetch('api/auth/login_password', {
method: 'POST',
headers: {
@@ -97,174 +132,305 @@ export default function ForgetLoginPwd() {
login_password: password,
}),
});
const data = await response.json();
if (response.ok) {
// console.log(data);
notifications.show({
withBorder: true,
color: "green",
title: "Login Password has been set",
message: "Login Password has been set",
title: "Success",
message: "Login Password has been set successfully",
autoClose: 5000,
});
router.push("/SetTxn");
}
else {
localStorage.removeItem("reset_pwd_token");
router.push("/login");
} else {
notifications.show({
withBorder: true,
color: "red",
title: "Please try again later ",
message: "Please try again later ",
title: "Error",
message: "Please try again later",
autoClose: 5000,
});
router.push("/login");
}
}
};
// useEffect(() => {
// const token = localStorage.getItem("access_token");
// if (!token) {
// SetAuthorized(false);
// router.push("/login");
// }
// else {
// SetAuthorized(true);
// }
// }, []);
// if (authorized) {
if (authorized) {
return (
<Providers>
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "auto", paddingTop: "5%" }}>
<Box style={{
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%)"
}}>
<Box style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
background: "linear-gradient(135deg, #f5f7fa 0%, #e8f5e9 100%)"
}}>
{/* Header */}
<Box
style={{
position: 'sticky',
top: 0,
zIndex: 100,
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
background: "linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)",
padding: "10px 15px",
minHeight: "80px",
}}>
<Image
// radius="md"
fit="cover"
src={logo}
component={NextImage}
fit="contain"
alt="ebanking"
style={{ width: "100%", height: "100%" }}
style={{
width: "60px",
height: "60px",
minWidth: "50px",
marginRight: "15px"
}}
/>
<Button style={{
position: 'absolute',
top: '50%',
left: '90%',
color: 'white',
textShadow: '1px 1px 2px black',
fontSize: "20px"
}}
leftSection={<IconLogout color='white' />} variant="subtle" onClick={handleLogout}>Logout</Button>
<Box style={{ flex: 1 }}>
<Title
order={3}
style={{
fontFamily: "Roboto",
color: "white",
marginBottom: 2,
fontSize: "clamp(0.9rem, 2.5vw, 1.25rem)",
lineHeight: 1.3
}}>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text
size="xs"
c="white"
style={{
opacity: 0.85,
fontSize: "clamp(0.65rem, 1.5vw, 0.75rem)"
}}>
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215
</Text>
</Box>
</Box>
<div>
<Box style={{ display: "flex", justifyContent: "center", alignItems: "center", columnGap: "5rem" }} bg="#c1e0f0">
<Image h="85vh" fit="contain" component={NextImage} src={changePwdImage} alt="Change Password Image" />
<Box h="100%" style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
<Card p="xl" w="40vw" h='82vh'>
<Title order={3}
// @ts-ignore
align="center" mb="md">Set Login Password</Title>
<form onSubmit={handleSetLoginPassword}>
<PasswordInput
label="Login Password"
placeholder="Enter your password"
required
id="loginPassword"
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
onCopy={(e) => e.preventDefault()}
onPaste={(e) => e.preventDefault()}
onCut={(e) => e.preventDefault()}
/>
<Box
style={{
flex: 1,
padding: "2rem 0",
display: "flex",
alignItems: "center"
}}
>
<Container size="xl" style={{ width: "100%" }}>
<Grid gutter={{ base: "md", md: "xl" }} align="center">
{/* Image Column - Now visible on all screens */}
<Grid.Col span={{ base: 12, md: 6, lg: 7 }}>
<Box
style={{
position: "relative",
height: "700px",
borderRadius: "24px",
overflow: "hidden",
boxShadow: "0 20px 60px rgba(0,0,0,0.15)",
background: `url(${forget_psw.src})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover",
}}
>
<Box style={{
position: "absolute",
bottom: 0,
left: 0,
right: 0,
padding: "2rem",
background: "linear-gradient(to top, rgba(0,0,0,0.7), transparent)",
}}>
<Title order={2} c="white" mb="sm">
Welcome to KCCB Internet Banking
</Title>
<Text c="white" size="sm" style={{ opacity: 0.9 }}>
Experience secure, fast, and convenient banking at your fingertips
</Text>
</Box>
</Box>
</Grid.Col>
<PasswordInput
label="Confirm Login Password"
placeholder="Enter your password"
required
rightSection={icon}
id="confirmPassword"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
type={confirmVisible ? 'text' : 'password'}
// rightSection={
// <button
// type="button"
// onClick={toggleConfirmVisibility}
// style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'grey' }}
// >
// {confirmVisible ? <IconEyeOff size={18} /> : <IconEye size={18} />}
// </button>
// }
onCopy={(e) => e.preventDefault()}
onPaste={(e) => e.preventDefault()}
onCut={(e) => e.preventDefault()}
{/* Form Column */}
<Grid.Col span={{ base: 12, md: 6, lg: 5 }}>
<Card
radius="xl"
shadow="xl"
p={{ base: "xl", sm: "2rem" }}
style={{
background: "rgba(255, 255, 255, 0.95)",
backdropFilter: "blur(10px)",
border: "1px solid rgba(255,255,255,0.5)",
maxWidth: "480px",
margin: "0 auto"
}}
>
<Box style={{ textAlign: "center", marginBottom: "2rem" }}>
/>
<Title order={3} style={{ color: "#35487eff", fontWeight: 700 }}>
Set Login Password
</Title>
<Text size="sm" c="dimmed">
Create a strong password to secure your account
</Text>
</Box>
{/* CAPTCHA */}
<div style={{ marginTop: 20 }}>
<label style={{ fontWeight: 600 }}>Enter CAPTCHA *</label>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 5 }}>
{/* <CaptchaImage text={captcha} /> */}
<Button size="xs" variant="outline" onClick={generateCaptcha}>Refresh</Button>
</div>
<TextInput
placeholder="Enter above text"
value={captchaInput}
onChange={(e) => setCaptchaInput(e.currentTarget.value)}
<Box mb="md">
<PasswordInput
label="Login Password"
placeholder="Enter your password"
required
size="md"
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
onCopy={(e) => e.preventDefault()}
onPaste={(e) => e.preventDefault()}
onCut={(e) => e.preventDefault()}
styles={{
input: {
borderRadius: "8px",
border: "2px solid #e9ecef"
}
}}
/>
{captchaError && <p style={{ color: 'red', fontSize: '12px' }}>{captchaError}</p>}
</div>
</Box>
<Box mb="md">
<PasswordInput
label="Confirm Login Password"
placeholder="Re-enter your password"
required
size="md"
rightSection={icon}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
onCopy={(e) => e.preventDefault()}
onPaste={(e) => e.preventDefault()}
onCut={(e) => e.preventDefault()}
styles={{
input: {
borderRadius: "8px",
border: "2px solid #e9ecef"
}
}}
/>
</Box>
{/* CAPTCHA Section */}
<Box mb="md">
<Text size="sm" fw={600} mb="xs">Verification Code</Text>
<Box style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
<Box
style={{
flex: 1,
padding: "5px",
background: "white",
borderRadius: "8px",
border: "2px dashed #dee6deff",
textAlign: "center",
fontWeight: 500,
fontSize: "22px",
letterSpacing: "6px",
color: "#0a7228",
fontFamily: "Verdana",
userSelect: "none",
textDecoration: "line-through"
// fontFamily: "monospace"
}}
>
{captcha}
</Box>
<Button
size="md"
variant="light"
color="green"
onClick={handleRefreshCaptcha}
leftSection={<IconRefresh size={16} />}
style={{ borderRadius: "8px" }}
>
Refresh
</Button>
</Box>
<TextInput
placeholder="Enter the text above"
value={captchaInput}
onChange={(e) => {
setCaptchaInput(e.currentTarget.value);
setCaptchaError('');
}}
required
size="md"
error={captchaError}
styles={{
input: {
borderRadius: "8px"
}
}}
/>
</Box>
<Button
type="submit"
fullWidth
mt="sm"
color="blue"
size="lg"
onClick={handleSetLoginPassword}
style={{
borderRadius: "10px",
fontWeight: 600,
marginBottom: "1.5rem"
}}
>
Set
Set Password
</Button>
</form>
<br></br>
<Box
style={{
flex: 1,
borderLeft: '1px solid #ccc',
paddingLeft: 16,
minHeight: 90,
}}
>
<Text size="sm">
<strong>Note:</strong> Password will contains minimum one alphabet, one digit, one special symbol and total 8 charecters.
</Text>
</Box>
</Card>
</Box>
</Box>
<Box
style={{
flexShrink: 0,
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f8f9fa",
marginTop: "0.5rem",
}}
>
<Text c="dimmed" size="xs">
© 2025 Kangra Central Co-Operative Bank
</Text>
</Box>
</div>
</div>
</Providers >
<Box
p="md"
style={{
background: "#cdffdfff",
borderRadius: "10px",
border: "1px solid #42c442ff",
borderLeft: "4px solid #42c442ff"
}}
>
<Text size="sm" fw={100} mb={4} c="#856404">
Password Requirements:
</Text>
<Text size="xs" c="#101010ff" style={{ lineHeight: 1.6 }}>
Minimum 8 characters<br />
At least 1 uppercase letter<br />
At least 1 number<br />
At least 1 special character (@$!%*#?&)
</Text>
</Box>
</Card>
</Grid.Col>
</Grid>
</Container>
</Box>
{/* Footer */}
<Box
style={{
flexShrink: 0,
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f8f9fa",
padding: "1rem",
}}
>
<Text c="dimmed" size="xs">
© 2025 Kangra Central Co-Operative Bank. All rights reserved.
</Text>
</Box>
</Box>
</Providers>
);
// }
}
}

View File

@@ -1,74 +0,0 @@
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.8rem 2rem;
background: linear-gradient(
15deg,
rgba(10, 114, 40, 1) 55%,
rgba(101, 101, 184, 1) 100%
);
flex-wrap: wrap;
}
.headerLogo {
width: 60px;
}
.headerText {
display: flex;
flex-direction: column;
flex: 1;
}
.desktopText {
color: white;
font-family: Roboto, sans-serif;
font-size: 1.5rem;
line-height: 1.2;
}
.desktopAddress {
font-family: Roboto, sans-serif;
color: white;
font-size: 0.9rem;
margin-top: 0.25rem;
}
.mobileText {
display: none;
color: white;
font-family: Roboto, sans-serif;
font-size: 0.9rem;
text-align: center;
}
@media screen and (max-width: 768px) {
.header {
justify-content: center;
padding: 0.5rem 0.75rem;
}
.headerLogo {
width: 50px;
margin-bottom: 0.5rem;
}
.headerText {
text-align: center;
}
.desktopText,
.desktopAddress {
display: none;
}
.mobileText {
display: block;
}
}

View File

@@ -1,13 +1,13 @@
"use client";
import React, { useEffect, useState, useRef } from "react";
import { Text, Button, TextInput, PasswordInput, Title, Card, Box, Image } from "@mantine/core";
import React, { useState } from "react";
import { Text, Button, TextInput, PasswordInput, Title, Card, Box, Image, Container, Grid, Stack, Divider, Badge } from "@mantine/core";
import { notifications } from "@mantine/notifications";
import { Providers } from "@/app/providers";
import NextImage from "next/image";
import logo from '@/app/image/logo1.jpg';
import changePwdImage from '@/app/image/changepw.png';
import ValidateUserId from '@/app/image/validate_page.jpg';
import { useRouter } from "next/navigation";
import { IconShieldCheck, IconPhone, IconKey } from '@tabler/icons-react';
export default function ValidateUser() {
const router = useRouter();
@@ -15,228 +15,456 @@ export default function ValidateUser() {
const [otp, setOTP] = useState("");
const [mobileNumber, setMobileNumber] = useState("");
const [isCifValidated, setIsCifValidated] = useState(false);
const [generateOtp, setGenerateOtp] = useState("");
const headerRef = useRef<HTMLHeadingElement>(null);
const [loading, setLoading] = useState(false);
const validUsers = [
{ cif: "11111111111", mobile: "7890544528" },
{ cif: "30022497139", mobile: "6230573848" },
{ cif: "11122233344", mobile: "9998887776" },
];
async function handleGenerateOtp() {
const value = "123456";
setGenerateOtp(value);
return value;
}
const handleValidateUser = async (e: React.FormEvent) => {
e.preventDefault();
if (!Cif) {
notifications.show({
title: "Invalid CIF",
message: "Please Enter your 11 digit CIF number.",
color: "red",
});
setIsCifValidated(false);
return;
}
const handleValidateUser = async () => {
if (!/^\d{11}$/.test(Cif)) {
notifications.show({
title: "Invalid CIF",
message: "CIF number must be exactly 11 digits.",
color: "red",
});
setIsCifValidated(false);
return;
}
const user = validUsers.find((u) => u.cif === Cif);
if (user) {
setMobileNumber(user.mobile);
setLoading(true);
try {
const res = await fetch(
`/api/otp/send/set-password?customerNo=${Cif}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Login-Type": "IB",
},
}
);
const data = await res.json();
if (!res.ok) {
throw new Error(data?.message || "OTP send failed");
}
setIsCifValidated(true);
const Otp = await handleGenerateOtp();
const masked = `xxxxxx${user.mobile.slice(-4)}`;
setMobileNumber(data?.mobileNo || "");
const masked = data?.mobileNo
// ? `xxxxxx${data.mobileNo.slice(-4)}`
? "registered number"
: "registered number";
notifications.show({
title: "OTP Sent",
message: `OTP sent to your registered mobile number ${masked}`,
message: `OTP sent to ${masked}`,
color: "green",
});
} else {
setIsCifValidated(false);
} catch (err) {
console.error("Validation error:", err);
notifications.show({
title: "User Not Found",
message: "No such user is present.",
title: "Validation Failed",
message: err instanceof Error ? err.message : "Unable to send OTP. Please try again.",
color: "red",
});
} finally {
setLoading(false);
}
};
const handleSubmitOtp = () => {
if (!otp) {
const handleSubmitOtp = async () => {
if (!otp || otp.length !== 6) {
notifications.show({
title: "Invalid OTP",
message: "Please Enter OTP Before You Submit.",
message: "Please enter 6 digit OTP.",
color: "red",
});
return;
}
if (otp === generateOtp) {
setLoading(true);
try {
const res = await fetch(
`/api/otp/verify/set-password?customerNo=${Cif}&otp=${otp}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Login-Type": "IB",
},
}
);
const data = await res.json();
// console.log(data);
if (!res.ok) {
throw new Error(data?.message || "OTP verification failed");
}
if (data?.token) {
localStorage.setItem("reset_pwd_token", data.token);
} else {
throw new Error("Token not received from server");
}
notifications.show({
title: "OTP Verified",
message: `OTP matched successfully`,
message: "OTP verified successfully.",
color: "green",
});
router.push("/ForgetPassword")
}
else {
setOTP("");
router.push("/ForgetPassword");
} catch (err) {
console.error("OTP verification error:", err);
notifications.show({
title: "Invalid OTP",
message: `The OTP you entered is incorrect`,
message: err instanceof Error ? err.message : "OTP verification failed.",
color: "red",
});
return;
setOTP("");
} finally {
setLoading(false);
}
};
}
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);
}, []);
const handleSubmit = () => {
if (isCifValidated) {
handleSubmitOtp();
} else {
handleValidateUser();
}
};
return (
<Providers>
<div style={{ backgroundColor: "#f8f9fa", width: "100%", paddingTop: "5%" }}>
<Box style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
background: "linear-gradient(135deg, #f5f7fa 0%, #e8f5e9 100%)"
}}>
{/* Header */}
<Box
style={{
position: 'fixed', width: '100%', height: '15%', top: 0, left: 0, zIndex: 100,
position: 'sticky',
top: 0,
zIndex: 100,
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
background: "linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)",
padding: "10px 15px",
minHeight: "80px",
}}>
<Image
src={logo}
component={NextImage}
fit="contain"
alt="ebanking"
style={{ width: "80px", height: "auto" ,padding:"10px"}}
/>
<Title
// ref={headerRef}
order={2}
style={{
fontFamily: 'Roboto',
position: 'absolute',
top: '30%',
left: '7%',
color: 'White',
transition: "opacity 0.5s ease-in-out",
width: "40px",
height: "60px",
minWidth: "50px",
marginRight: "15px"
}}
>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
</Box>
<Box style={{ display: "flex", justifyContent: "center", alignItems: "center", columnGap: "5rem" }} bg="#c1e0f0">
<Image h="85vh" fit="contain" component={NextImage} src={changePwdImage} alt="Change Password Image" />
<Box h="100%" style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
<Card p="xl" w="35vw" h="65vh" style={{ display: "flex", flexDirection: "column", justifyContent: "center" }}>
{/* @ts-ignore */}
<Title order={3} align="center" mb="md">Validate User</Title>
{isCifValidated && (
// @ts-ignore
<Text align="center"> Welcome {" "}{Cif}</Text>
)}
<form onSubmit={(e) => {
e.preventDefault();
isCifValidated ? handleSubmitOtp() : handleValidateUser(e);
/>
<Box style={{ flex: 1 }}>
<Title
order={3}
style={{
fontFamily: "Roboto",
color: "white",
marginBottom: 2,
fontSize: "clamp(0.9rem, 2.5vw, 1.25rem)",
lineHeight: 1.3
}}>
<TextInput
label="Enter Your CIF Number"
placeholder="Enter your CIF"
withAsterisk
value={Cif}
disabled={isCifValidated}
onChange={(e) => {
const input = e.currentTarget.value;
if (input.length <= 11) {
setCif(input);
}
}}
onKeyDown={(e) => {
const isNumberKey = /[0-9]/.test(e.key);
const isControlKey = ["Backspace", "Tab", "ArrowLeft", "ArrowRight"].includes(e.key);
if (!isNumberKey && !isControlKey) {
e.preventDefault();
}
}}
/>
<div style={{ marginTop: "1rem", minHeight: "80px" }}>
{!isCifValidated && (
<>
<br></br>
<Text fs="italic" c="dimmed">NOTE: Your CIF number travels in an encrypted and highly secured mode.</Text>
</>
)}
{isCifValidated && (
<>
<PasswordInput
label="Enter OTP"
placeholder="Enter your OTP"
withAsterisk
value={otp}
maxLength={6}
onKeyDown={(e) => {
const isNumberKey = /[0-9]/.test(e.key);
const isControlKey = ["Backspace", "Tab", "ArrowLeft", "ArrowRight"].includes(e.key);
if (!isNumberKey && !isControlKey) {
e.preventDefault();
}
}}
onChange={(e) => setOTP(e.currentTarget.value)}
/>
<Text size="xs" mt="xs" c='green'>
OTP sent to your registered mobile number{" "}
<b>{`xxxxxx${mobileNumber.slice(-4)}`}</b>
</Text>
</>
)}
</div>
<Button type="submit" mt="lg" color="blue">
{isCifValidated ? "Submit OTP" : "Validate"}
</Button>
</form>
</Card>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text
size="xs"
c="white"
style={{
opacity: 0.85,
fontSize: "clamp(0.65rem, 1.5vw, 0.75rem)"
}}>
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215
</Text>
</Box>
</Box>
{/* Main Content */}
<Box
style={{
flex: 1,
padding: "2rem 0",
display: "flex",
alignItems: "center"
}}
>
<Container size="xl" style={{ width: "100%" }}>
<Grid gutter={{ base: "md", md: "xl" }} align="center">
{/* Image Column - Hidden on mobile */}
<Grid.Col
span={{ base: 12, md: 6, lg: 7 }}
visibleFrom="md"
>
<Box
style={{
position: "relative",
height: "500px",
borderRadius: "24px",
overflow: "hidden",
boxShadow: "0 20px 60px rgba(0,0,0,0.15)",
background: `url(${ValidateUserId.src})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover",
}}
>
<Box style={{
position: "absolute",
bottom: 0,
left: 0,
right: 0,
padding: "2rem",
background: "linear-gradient(to top, rgba(0,0,0,0.7), transparent)",
}}>
<Title order={2} c="white" mb="sm">
Welcome to KCCB Internet Banking
</Title>
<Text c="white" size="sm" style={{ opacity: 0.9 }}>
Experience secure, fast, and convenient banking at your fingertips
</Text>
</Box>
</Box>
</Grid.Col>
{/* Form Column */}
<Grid.Col span={{ base: 12, md: 6, lg: 5 }}>
<Card
radius="xl"
shadow="xl"
p={{ base: "xl", sm: "2rem" }}
style={{
background: "rgba(255, 255, 255, 0.95)",
backdropFilter: "blur(10px)",
border: "1px solid rgba(255,255,255,0.5)",
maxWidth: "480px",
height:"100%",
margin: "0 auto"
}}
>
<Stack gap="lg">
{/* Header Section */}
<Box style={{ textAlign: "center" }}>
{/* <Box style={{
width: "70px",
height: "70px",
margin: "0 auto 1rem",
background: "linear-gradient(135deg, #3a3293ff 0%, #895fd1ff 100%)",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
boxShadow: "0 4px 20px rgba(10, 114, 40, 0.3)"
}}>
<IconShieldCheck size={36} color="white" />
</Box> */}
<Title order={3} mb="xs" style={{ color: "#35487eff", fontWeight: 700 }}>
Validate User
</Title>
<Text size="sm" c="dimmed" style={{ lineHeight: 1.6 }}>
Secure access to your KCCB Internet Banking
</Text>
</Box>
<Divider />
{/* Welcome Message */}
{isCifValidated && (
<Box style={{
background: "linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%)",
padding: "1rem",
borderRadius: "12px",
textAlign: "center",
border: "1px solid #a5d6a7"
}}>
<Text size="sm" c="dimmed">Welcome</Text>
<Text size="lg" fw={600} c="#0a7228">{Cif}</Text>
</Box>
)}
{/* Form Fields */}
<Stack gap="md">
<TextInput
label="CIF Number"
placeholder="Enter your 11-digit CIF"
size="md"
withAsterisk
value={Cif}
disabled={isCifValidated}
leftSection={<IconKey size={18} />}
styles={{
input: {
borderRadius: "12px",
border: "2px solid #e0e0e0",
fontSize: "1rem",
padding: "1.5rem 1rem 1.5rem 2.5rem",
transition: "all 0.3s ease",
'&:focus': {
borderColor: "#0a7228",
boxShadow: "0 0 0 3px rgba(10, 114, 40, 0.1)"
}
}
}}
onChange={(e) => {
const value = e.currentTarget.value;
if (value.length <= 11 && /^\d*$/.test(value)) {
setCif(value);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleSubmit();
}
}}
/>
{!isCifValidated && (
<Box style={{
background: "#f5f5f5",
padding: "0.75rem",
borderRadius: "8px",
borderLeft: "3px solid #4caf50"
}}>
<Text size="xs" c="dimmed" style={{ lineHeight: 1.5 }}>
🔒 Your CIF is transmitted securely using encrypted channels
</Text>
</Box>
)}
{isCifValidated && (
<>
<PasswordInput
label="One-Time Password (OTP)"
placeholder="Enter 6-digit OTP"
size="md"
withAsterisk
value={otp}
maxLength={6}
leftSection={<IconPhone size={18} />}
styles={{
input: {
borderRadius: "12px",
border: "2px solid #e0e0e0",
fontSize: "1rem",
padding: "1.5rem 1rem 1.5rem 2.5rem",
transition: "all 0.3s ease",
letterSpacing: "3px",
'&:focus': {
borderColor: "#0a7228",
boxShadow: "0 0 0 3px rgba(10, 114, 40, 0.1)"
}
}
}}
onChange={(e) => {
const value = e.currentTarget.value;
if (/^\d*$/.test(value)) {
setOTP(value);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleSubmit();
}
}}
/>
<Box style={{
background: "#e8f5e9",
padding: "0.75rem",
borderRadius: "8px",
borderLeft: "3px solid #4caf50"
}}>
<Text size="xs" c="#4caf50" style={{ lineHeight: 1.5 }}>
{/* 📱 OTP sent to <strong>{`xxxxxx${mobileNumber.slice(-4)}`}</strong> */}
📱 OTP sent to <strong>Registered Mobile Number</strong>
</Text>
</Box>
</>
)}
<Button
fullWidth
size="lg"
onClick={handleSubmit}
loading={loading}
style={{
marginTop: "1rem",
// background: "linear-gradient(135deg, #0a7228 0%, #1b5e20 100%)",
borderRadius: "12px",
height: "52px",
fontSize: "1rem",
fontWeight: 600,
letterSpacing: "0.5px",
boxShadow: "0 4px 15px #4caf50",
transition: "all 0.3s ease",
border: "none"
}}
styles={{
root: {
'&:hover': {
transform: 'translateY(-2px)',
boxShadow: "0 6px 20px rgba(10, 114, 40, 0.4)"
},
'&:active': {
transform: 'translateY(0)'
}
}
}}
>
{isCifValidated ? "Verify OTP" : "Send OTP"}
</Button>
</Stack>
{/* Security Note */}
<Box style={{
textAlign: "center",
padding: "0.75rem",
borderTop: "1px solid #e0e0e0"
}}>
<Text size="xs" c="dimmed" style={{ lineHeight: 1.6 }}>
Having trouble? Contact our support team
</Text>
</Box>
</Stack>
</Card>
</Grid.Col>
</Grid>
</Container>
</Box>
{/* Footer */}
<Box
style={{
flexShrink: 0,
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f8f9fa",
marginTop: "0.5rem",
padding: "15px 10px",
textAlign: "center",
}}
>
<Text c="dimmed" size="xs">
© 2025 Kangra Central Co-Operative Bank
</Text>
</Box>
</div>
</Box>
</Providers>
);
}

View File

@@ -30,7 +30,7 @@ export const generateActiveUsersPDF = (reportData: any, startDate: string, endDa
<div style="font-family:Arial, sans-serif; font-size:12px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
<div style="display:flex;align-items:center;gap:10px;">
<img src="/logo.jpg" alt="Bank Logo" style="height:50px;" />
<img src="/logo1.jpg" alt="Bank Logo" style="height:50px;" />
<h2 style="margin:0;">The Kangra Central Co Operative Bank</h2>
</div>
<div style="font-size:12px;color:#555;">

View File

@@ -38,7 +38,7 @@ export const generateInactiveUsersPDF = (
<div style="font-family:Arial, sans-serif; font-size:12px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
<div style="display:flex;align-items:center;gap:10px;">
<img src="/logo.jpg" alt="Bank Logo" style="height:50px;" />
<img src="/logo1.jpg" alt="Bank Logo" style="height:50px;" />
<h2 style="margin:0;">The Kangra Central Co Operative Bank</h2>
</div>
<div style="font-size:12px;color:#555;">

View File

@@ -77,7 +77,7 @@ export const generatePDF = (
padding-bottom:12px;
margin-bottom:15px;
">
<img src="/logo.jpg" style="height:55px;width:auto;" />
<img src="/logo1.jpg" style="height:55px;width:auto;" />
<div style="flex:1;">
<h2 style="margin:0 0 3px 0;font-size:18px;color:#1a5f3a;letter-spacing:0.3px;">
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -11,7 +11,7 @@ import logo from '@/app/image/logo1.jpg';
import frontPage from '@/app/image/ib_front_3.jpg';
import dynamic from 'next/dynamic';
import { generateCaptcha } from '@/app/captcha';
import { IconRefresh, IconShieldLockFilled } from "@tabler/icons-react";
import { IconAlertCircle, IconLock, IconPhone, IconRefresh, IconShieldLockFilled, IconUser } from "@tabler/icons-react";
import dayjs from "dayjs";
import { fetchAndStoreUserName } from "../_util/userdetails";
@@ -470,7 +470,7 @@ export default function Login() {
style={{ width: "60px", height: "auto" }}
/>
<div>
<Title order={3} ref={headerRef} style={{ fontFamily: "Roboto", color: "white", marginBottom: 2 }}>
<Title order={3} ref={headerRef} style={{ fontFamily: "Roboto", color: "white", marginBottom: 2 }}>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text size="xs" c="white" style={{ opacity: 0.85 }}>
@@ -479,17 +479,16 @@ export default function Login() {
</div>
</Group>
</Box>
<div style={{ marginTop: '10px' }}>
<div >
{/* Movable text */}
<Box
style={{
width: "100%",
height: "0.5%",
overflow: "hidden",
background: "linear-gradient(90deg, #cef7e3ff 0%, #82e8b8ff 100%)",
whiteSpace: "nowrap",
padding: "8px 0",
// borderBottom: "2px solid #3a95bcff"
}}
>
<Text
@@ -523,9 +522,9 @@ export default function Login() {
<div style={{
display: "flex", height: "75vh", overflow: "hidden", position: "relative",
// background: 'linear-gradient(to right, #02081eff, #0a3d91)'
background: 'linear-gradient(179deg,rgba(1, 13, 18, 1) 49%, rgba(77, 82, 79, 1) 87%)'
background: 'linear-gradient(179deg,rgba(92, 175, 210, 1) 49%, rgba(77, 82, 79, 1) 87%)'
}}>
<div style={{ flex: 1, backgroundColor: "#c1e0f0", position: "relative" }}>
<Box style={{ flex: 1, backgroundColor: "#c1e0f0", position: "relative" }}>
<Image
fit="cover"
src={frontPage}
@@ -533,7 +532,7 @@ export default function Login() {
alt="ebanking"
style={{ width: "100%", height: "100%" }}
/>
</div>
</Box>
<Box
w={{ base: "100%", md: "45%" }}
p="lg"
@@ -559,9 +558,13 @@ export default function Login() {
}}
>
<form onSubmit={handleLogin}>
{/* <Title order={3} style={{ color: "#0a7228", textAlign: "center" }}>
Internet Banking Login
</Title> */}
<TextInput
label="User ID / User Name"
placeholder="Enter your CIF No / User Name"
leftSection={<IconUser size={18} />}
value={CIF}
onInput={(e) => {
// const input = e.currentTarget.value.replace(/\D/g, "");
@@ -576,6 +579,7 @@ export default function Login() {
<PasswordInput
label="Password"
placeholder="Enter your password"
leftSection={<IconLock size={18} />}
value={psw}
onChange={(e) => SetPsw(e.currentTarget.value)}
withAsterisk
@@ -584,32 +588,49 @@ export default function Login() {
readOnly={isLogging}
// mt="sm"
/>
<Box style={{ textAlign: "right" }}>
{/* <Anchor
<Box style={{ textAlign: "right", marginTop: "0.5rem" }}>
<Anchor
style={{ fontSize: "14px", color: "#1c7ed6", cursor: "pointer" }}
onClick={() => router.push("/ValidateUser")}
>
Forgot Password?
</Anchor> */}
</Anchor>
</Box>
<Group align="center">
<Box style={{
backgroundColor: "#fff", fontSize: "18px", textDecoration: "line-through", padding: "4px 8px", fontFamily: "Verdana",
flex: 1,
padding: "5px",
background: "white",
borderRadius: "8px",
border: "2px dashed #dee6deff",
textAlign: "center",
fontWeight: 500,
fontSize: "22px",
letterSpacing: "6px",
color: "#0a7228",
fontFamily: "Verdana",
userSelect: "none",
pointerEvents: "none",
}}
textDecoration: "line-through"
}}
onCopy={(e) => e.preventDefault()}
onContextMenu={(e) => e.preventDefault()}
>
{captcha}
</Box>
<Button size="xs" variant="light" onClick={regenerateCaptcha} disabled={otpRequired}>Refresh</Button>
<Button
variant="light"
color="green"
onClick={regenerateCaptcha}
disabled={otpRequired}
leftSection={<IconRefresh size={16} />}
>
Refresh
</Button>
</Group>
<TextInput
label="Enter CAPTCHA"
placeholder="Enter above text"
placeholder="Enter the text above"
value={inputCaptcha}
onChange={(e) => setInputCaptcha(e.currentTarget.value)}
withAsterisk
@@ -621,10 +642,11 @@ export default function Login() {
{otpRequired && (
<Group align="end" gap="xs">
<PasswordInput
label="Enter OTP"
placeholder="Enter OTP"
label="One-Time Password"
placeholder="Enter 6-digit OTP"
value={otp}
maxLength={6}
leftSection={<IconPhone size={18} />}
onChange={(e) => setOtp(e.currentTarget.value)}
withAsterisk
style={{ flex: 1 }}
@@ -636,25 +658,33 @@ export default function Login() {
px={10}
disabled={isLogging || otpVerified}
onClick={() => handleSendOtp(mobile)}
leftSection={<IconRefresh size={20} />}
style={{ alignSelf: "flex-end", marginBottom: 4 }}
>
<IconRefresh size={20} /> Resend
Resend
</Button>
</Tooltip>
</Group>
)}
<Button type="submit" fullWidth mt="sm" loading={isLogging} disabled={isLogging}>
{isLogging ? "Processing..." : buttonLabel}
</Button>
<Box mt="xs">
<Text size="md">
<Text component="span" c="red" fw={600}>Note: </Text>
<Text component="span" c="black">
Existing users logging in to the new Internet Banking for the first time should use their CIF number to avoid login issues.
</Text>
</Text>
<Box mt="xs"
style={{
background: "#cdffdfff",
borderRadius: "8px",
border: "1px solid #42c442ff",
borderLeft: "4px solid #42c442ff"
}}>
<Group gap="xs" align="flex-start">
<IconAlertCircle size={20} color="#856404" style={{ marginTop: 1 }} />
<Box style={{ flex: 1 }}>
<Text size="sm" fw={600} c="#856404">Important Note</Text>
<Text size="sm" c="#151615ff" mt={4}>
Existing users logging in for the first time should use their CIF number to avoid login issues.
</Text>
</Box>
</Group>
</Box>
</form>
</Card>
@@ -677,7 +707,6 @@ export default function Login() {
>
https://kccbhp.bank.in/
</Anchor>
</Box>
</Flex>
{/* Footer */}
@@ -704,6 +733,6 @@ export default function Login() {
</Box>
</div>
</div>
</Providers>
</Providers >
);
}