feat: Add captcha in login screen.

feat : add important link in home screen
This commit is contained in:
2025-06-29 17:35:53 +05:30
parent 054c4b8d0e
commit bc75470d33
4 changed files with 304 additions and 101 deletions

101
package-lock.json generated
View File

@@ -36,6 +36,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.51.3",
"react-simple-captcha": "^9.3.1",
"reflect-metadata": "^0.2.2",
"sharp": "^0.33.5",
"typeorm": "^0.3.20",
@@ -2548,6 +2549,62 @@
"csstype": "^3.0.2"
}
},
"node_modules/dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
"integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
"license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"entities": "^2.0.0"
}
},
"node_modules/dom-serializer/node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause"
},
"node_modules/dom-serializer/node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"license": "BSD-2-Clause",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"license": "BSD-2-Clause"
},
"node_modules/domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "1"
}
},
"node_modules/domutils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
"integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "0",
"domelementtype": "1"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@@ -2587,6 +2644,12 @@
"node": ">=10.13.0"
}
},
"node_modules/entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"license": "BSD-2-Clause"
},
"node_modules/es-abstract": {
"version": "1.23.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
@@ -3858,6 +3921,20 @@
"node": "*"
}
},
"node_modules/htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"license": "MIT",
"dependencies": {
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.1.1"
}
},
"node_modules/IB": {
"resolved": "",
"link": true
@@ -5815,6 +5892,30 @@
}
}
},
"node_modules/react-simple-captcha": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/react-simple-captcha/-/react-simple-captcha-9.3.1.tgz",
"integrity": "sha512-pQ/WKjXns9ukhjjbaSZhl2P0QygHs50N9hxMmPAiFMh+0xXt2AocvSsEe24LyW/eOzOso+2Ifiuym/TZMyYDsA==",
"license": "MIT",
"dependencies": {
"react-html-parser": "^2.0.2"
},
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-simple-captcha/node_modules/react-html-parser": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/react-html-parser/-/react-html-parser-2.0.2.tgz",
"integrity": "sha512-XeerLwCVjTs3njZcgCOeDUqLgNIt/t+6Jgi5/qPsO/krUWl76kWKXMeVs2LhY2gwM6X378DkhLjur0zUQdpz0g==",
"license": "MIT",
"dependencies": {
"htmlparser2": "^3.9.0"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16.0.0-0"
}
},
"node_modules/react-smooth": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz",

View File

@@ -21,12 +21,12 @@
"basic-ftp": "^5.0.5",
"casbin": "^5.29.0",
"casbin-basic-adapter": "^1.1.0",
"ib": "file:",
"IB": "file:",
"csv-parse": "^5.5.5",
"damerau-levenshtein": "^1.0.8",
"date-fns": "^3.6.0",
"dayjs": "^1.11.11",
"ib": "file:",
"IB": "file:",
"iron-session": "^8.0.1",
"luxon": "^3.4.4",
"natural": "^6.10.5",
@@ -37,6 +37,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.51.3",
"react-simple-captcha": "^9.3.1",
"reflect-metadata": "^0.2.2",
"sharp": "^0.33.5",
"typeorm": "^0.3.20",

View File

@@ -46,7 +46,7 @@ export default function Home() {
if (authorized) {
return (
<Providers>
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: '100vh'}}>
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: '100vh' }}>
<Image
// radius="md"
fit="cover"
@@ -75,7 +75,7 @@ export default function Home() {
<div
style={{
flex: 1,
padding: "20px",
marginLeft:'20px',
display: "flex",
flexDirection: "column",
justifyContent: "left",
@@ -85,12 +85,12 @@ export default function Home() {
<Text size="xs" c="gray">Last logged in at 29/06/25, 05:35 PM</Text>
</div>
<Group
style={{ flex: 1, padding: "10px 10px 4px 10px", display: "flex", alignItems: "center", justifyContent: "left", height: "5vh" }}>
style={{ flex: 1, padding: "10px 10px 4px 10px", marginLeft:'20px',display: "flex", alignItems: "center", justifyContent: "left", height: "5vh" }}>
<Text fw={700}>Show Balance </Text>
<Switch size="md" onLabel="ON" offLabel="OFF" checked={showBalance}
onChange={(event) => setShowBalance(event.currentTarget.checked)} />
</Group>
<div style={{ flex: 1, padding: "10px", display: "flex", alignItems: "center", justifyContent: "left" }}>
<div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "flex-start" ,marginLeft:'20px'}}>
<Group grow gap="lg">
<Paper p="md" radius="md" style={{ backgroundColor: '#c1e0f0', width: 300 }}>
{/* <IconBuildingBank /> */}
@@ -150,6 +150,15 @@ export default function Home() {
</Title>
<Button fullWidth mt="xs">Get Statement</Button>
</Paper>
<Paper p="md" radius="md" style={{ width: 250, backgroundColor: '#f5f5f5',marginLeft:'170px'}}>
<Title order={5} mb="sm">Important Links</Title>
<Stack gap="xs">
<Button variant="light" color="blue" fullWidth>Loan EMI Calculator</Button>
<Button variant="light" color="blue" fullWidth>Branch Locator</Button>
<Button variant="light" color="blue" fullWidth>Customer Care</Button>
<Button variant="light" color="blue" fullWidth>FAQs</Button>
</Stack>
</Paper>
</Group>
</div>

View File

@@ -1,51 +1,88 @@
"use client"
import React, { useState } from "react";
import { Text, Button, Paper, TextInput, MantineProvider, Image, Box, Anchor, PasswordInput, Title, Checkbox, Card, Group, Flex, Stack } from "@mantine/core";
"use client";
import React, { useState, useEffect } from "react";
import {
Text,
Button,
Paper,
TextInput,
PasswordInput,
Title,
Card,
Group,
Flex,
Box,
Image,
Anchor,
Stack
} from "@mantine/core";
import { notifications } from "@mantine/notifications";
import myImage from '@/app/image/ebanking.jpg';
import bgImage from '@/app/image/media.jpg';
import frontPage from '@/app/image/ib_front_page.jpg'
import NextImage from 'next/image';
import frontPage from '@/app/image/ib_front_page.jpg';
import NextImage from "next/image";
import { Providers } from "@/app/providers";
import { useRouter } from "next/navigation";
import { notifications } from "@mantine/notifications";
function generateCaptcha(length = 6) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join("");
}
export default function Login() {
const [CIF, SetCIF] = useState('');
const [psw, SetPsw] = useState('');
const router = useRouter();
const [error, setError] = useState<string | null>(null);
const [CIF, SetCIF] = useState("");
const [psw, SetPsw] = useState("");
const [captcha, setCaptcha] = useState("");
const [inputCaptcha, setInputCaptcha] = useState("");
useEffect(() => {
setCaptcha(generateCaptcha());
}, []);
const regenerateCaptcha = () => {
setCaptcha(generateCaptcha());
setInputCaptcha("");
};
async function handleLogin(e: React.FormEvent) {
e.preventDefault();
// const login_password =localStorage.setItem("password",psw);
if (inputCaptcha !== captcha) {
notifications.show({
withBorder: true,
color: "red",
title: "Captcha Error",
message: "Please enter the correct captcha",
autoClose: 5000,
});
return;
}
if (CIF === "30022497139" && psw === "SecurePass123!") {
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";
const token ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";
localStorage.setItem("customerNumber", CIF);
localStorage.setItem("password", psw);
localStorage.setItem("access_token", token);
router.push("/home");
}
else {
} else {
// SetCIF('');
// SetPsw('');
// setCaptcha('');
notifications.show({
withBorder: true,
color: 'red',
color: "red",
title: "Wrong User Id or Password",
message: "Wrong User Id or Password",
autoClose: 5000,
})
});
}
}
return (
<Providers>
<div
style={{
backgroundColor: "#f8f9fa",
width:"100%",
height: '100vh',
}}
>
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "100vh" }}>
<Image
fit="cover"
src={myImage}
@@ -54,33 +91,36 @@ export default function Login() {
style={{ width: "100%", height: "12%" }}
/>
<Box style={{ width: "100%", overflow: "hidden", whiteSpace: "nowrap", padding: "8px 0" }}>
<Box
style={{
width: "100%",
overflow: "hidden",
whiteSpace: "nowrap",
padding: "8px 0",
}}
>
<Text
component="span"
style={{
display: "inline-block", paddingLeft: "100%", animation: "scroll-left 60s linear infinite", fontWeight: "bold", color: "#004d99"
}}>
Always login to our Net Banking site directly or through Banks website.
Do not disclose your User Id and Password to any third party and keep Your UserId and Password strictly confidential.
KCC Bank never asks for UserId,Passwords and Pins through email or phone.
Be ware of Phishing mails with links to fake bank's websites asking for personal information are in circulation.
⚠️ Please DO NOT Click on the links given in the emails asking for personal details like bank account number, userID and password.
⚠️ If you had shared your User Id and Password through such mails or links, please change your Password immediately.
⚠️ Inform the Bank/branch in which your account is maintained for resetting your password.
display: "inline-block",
paddingLeft: "100%",
animation: "scroll-left 60s linear infinite",
fontWeight: "bold",
color: "#004d99",
}}
>
Always login to our Net Banking site directly or through Banks website...
</Text>
<style>
{`
@keyframes scroll-left {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-100%);
}
}
`}
@keyframes scroll-left {
0% { transform: translateX(0%); }
100% { transform: translateX(-100%); }
}
`}
</style>
</Box>
<div style={{ display: "flex", height: "70vh" }}>
<div style={{ flex: 1, backgroundColor: "#c1e0f0", display: "flex" }}>
<Image
@@ -91,73 +131,125 @@ export default function Login() {
style={{ width: "100%", height: "100%" }}
/>
</div>
<div style={{ flex: 1, backgroundColor: "#c1e0f0", display: "flex", justifyContent: 'center', alignItems: 'center' }}>
<Card shadow="lg" padding="xl" radius="md" style={{
width: 480, height: 400, backgroundColor: '#f0f0f0',
borderRadius: "12px", boxShadow: "0 2px 10px rgba(0,0,0,0.1)",
}}>
<Title order={5} style={{ marginLeft: 10 }}>Welcome To KCC Bank</Title>
<Title order={1} style={{ marginLeft: 10, color: "#000066" }}>Internet Banking</Title>
<br></br>
<Box style={{ width: "370px", padding: "20px", border: "1px solid #e0e0d1", marginLeft: 30 }}>
<Flex justify="center" gap="md" >
<form onSubmit={handleLogin}>
<TextInput
label="User ID"
placeholder="Enter your CIF no"
type="numeric"
maxLength={11}
inputMode="numeric"
pattern="\d*"
value={CIF}
onInput={(e) => {
const input = e.currentTarget.value.replace(/\D/g, '');
if (input.length <= 11) {
SetCIF(input);
}
}}
style={{ flex: 1 }}
required
/>
<br></br>
<PasswordInput
label="Password"
placeholder="Enter your password"
value={psw}
onChange={(e) => SetPsw(e.currentTarget.value)}
style={{ flex: 1 }}
required
/>
{/* <br></br> */}
<Button type="submit" fullWidth variant="filled" mt="md" style={{ width: 100, align: "center", marginLeft: 40 }}>Login</Button>
</form>
</Flex>
</Box>
<div
style={{
flex: 1,
backgroundColor: "#c1e0f0",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<Card
shadow="lg"
padding="xl"
radius="md"
style={{
maxWidth: 480,
height: 380,
marginTop: 0,
paddingTop: 20,
backgroundColor: "#f0f0f0",
borderRadius: "12px",
boxShadow: "0 2px 10px rgba(0,0,0,0.1)",
// border:"1px solid black"
}}
>
<Stack gap="xs">
<Title order={6} style={{ marginLeft: 10 }}>
<Text fs="italic">Welcome To KCC Bank</Text>
<Text fw={700} style={{ fontSize: "18px", color: "#000066" }}>Internet Banking</Text>
</Title>
<Box
style={{
width: 370,
height: 4500,
padding: "20px",
border: "1px solid #e0e0d1",
}}
>
<Flex justify="center" gap="md">
<form onSubmit={handleLogin}>
<TextInput
size="xs"
label="User ID"
placeholder="Enter your CIF no"
type="text"
inputMode="numeric"
value={CIF}
onInput={(e) => {
const input = e.currentTarget.value.replace(/\D/g, "");
if (input.length <= 11) {
SetCIF(input);
}
}}
required
/>
<PasswordInput
label="Password"
size="xs"
placeholder="Enter your password"
value={psw}
onChange={(e) => SetPsw(e.currentTarget.value)}
required
/>
<Group >
<Box
style={{
padding: "10px 5px",
backgroundColor: "#ffffff",
fontFamily: "'UnifrakturCook',cursive",
textDecoration:"line-through",
userSelect: "none",
}}
>
{captcha}
</Box>
<Button variant="outline" size="xs" onClick={regenerateCaptcha}>
Refresh
</Button>
</Group>
<TextInput
label="Enter CAPTCHA"
size="xs"
placeholder="Enter above text"
value={inputCaptcha}
onChange={(e) => setInputCaptcha(e.currentTarget.value)}
required
/>
<Button type="submit" fullWidth variant="filled" mt="md">
Login
</Button>
</form>
</Flex>
</Box>
</Stack>
</Card>
</div>
</div>
<Box
component="footer"
style={{
width: '100%',
// backgroundColor: '#004d99',
// color: 'white',
textAlign: 'center',
padding: '10px 0',
position: 'fixed',
width: "100%",
textAlign: "center",
padding: "10px 0",
position: "fixed",
bottom: 0,
left: 0,
zIndex: 1000,
fontSize: '14px',
fontSize: "14px",
}}
>
<Text>
© 2025 KCC Bank. All rights reserved. | <Anchor href="/disclaimer" >Disclaimer</Anchor> | <Anchor href="/privacy" >Privacy Policy</Anchor>
© 2025 KCC Bank. All rights reserved. |{" "}
<Anchor href="/disclaimer">Disclaimer</Anchor> |{" "}
<Anchor href="/privacy">Privacy Policy</Anchor>
</Text>
</Box>
</div>
</Providers>
);
}
}