fix: Design : add recommended header.

This commit is contained in:
2025-11-20 13:45:55 +05:30
parent 6258080848
commit f4b1752fe2
6 changed files with 256 additions and 109 deletions

View File

@@ -33,31 +33,31 @@ export default function Home() {
const [loadingAccountNo, setLoadingAccountNo] = useState<string | null>(null); const [loadingAccountNo, setLoadingAccountNo] = useState<string | null>(null);
// If back and forward button is clicked // If back and forward button is clicked
useEffect(() => { // useEffect(() => {
window.history.pushState(null, "", window.location.href); // window.history.pushState(null, "", window.location.href);
const handlePopState = () => { // const handlePopState = () => {
localStorage.removeItem("access_token"); // localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token"); // sessionStorage.removeItem("access_token");
localStorage.removeItem("remitter_name"); // localStorage.removeItem("remitter_name");
localStorage.removeItem("pswExpiryDate"); // localStorage.removeItem("pswExpiryDate");
localStorage.clear(); // localStorage.clear();
sessionStorage.clear(); // sessionStorage.clear();
router.push("/login"); // router.push("/login");
}; // };
const handleBeforeUnload = () => { // const handleBeforeUnload = () => {
// logout on tab close / refresh // // logout on tab close / refresh
localStorage.removeItem("access_token"); // localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token"); // sessionStorage.removeItem("access_token");
localStorage.clear(); // localStorage.clear();
sessionStorage.clear(); // sessionStorage.clear();
}; // };
window.addEventListener("popstate", handlePopState); // window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload); // window.addEventListener("beforeunload", handleBeforeUnload);
return () => { // return () => {
window.removeEventListener("popstate", handlePopState); // window.removeEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload); // window.addEventListener("beforeunload", handleBeforeUnload);
}; // };
}, []); // }, []);
async function handleFetchUserDetails() { async function handleFetchUserDetails() {
try { try {

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Box, Button, Divider, Group, Image, Modal, Popover, Stack, Text, Title } from '@mantine/core'; import { Box, Button, Divider, Group, Image, Modal, Popover, Stack, Switch, Text, Title } from '@mantine/core';
import { IconBook, IconCurrencyRupee, IconHome, IconLogout, IconPhoneFilled, IconSettings } from '@tabler/icons-react'; import { IconBook, IconCurrencyRupee, IconHome, IconLogout, IconMoon, IconPhoneFilled, IconSettings, IconSun, IconUserCircle } from '@tabler/icons-react';
import Link from 'next/link'; import Link from 'next/link';
import { useRouter, usePathname } from "next/navigation"; import { useRouter, usePathname } from "next/navigation";
import { Providers } from '../providers'; import { Providers } from '../providers';
@@ -10,6 +10,7 @@ import NextImage from 'next/image';
import { notifications } from '@mantine/notifications'; import { notifications } from '@mantine/notifications';
import { useDisclosure, useMediaQuery } from '@mantine/hooks'; import { useDisclosure, useMediaQuery } from '@mantine/hooks';
import { fetchAndStoreUserName } from '../_util/userdetails'; import { fetchAndStoreUserName } from '../_util/userdetails';
import styles from './page.module.css';
export default function RootLayout({ children }: { children: React.ReactNode }) { export default function RootLayout({ children }: { children: React.ReactNode }) {
const router = useRouter(); const router = useRouter();
@@ -20,6 +21,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
const isMobile = useMediaQuery("(max-width: 768px)"); const isMobile = useMediaQuery("(max-width: 768px)");
const [sessionModal, setSessionModal] = useState(false); const [sessionModal, setSessionModal] = useState(false);
const [countdown, setCountdown] = useState(30); // 30 sec countdown before auto logout const [countdown, setCountdown] = useState(30); // 30 sec countdown before auto logout
const [darkMode, setDarkMode] = useState(false);
const firstName = custname ? custname.split(" ")[0] : "";
const [userOpened, setUserOpened] = useState(false); // Manage dropdown visibility
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
@@ -38,28 +42,32 @@ export default function RootLayout({ children }: { children: React.ReactNode })
doLogout() doLogout()
router.push("/login"); router.push("/login");
} }
// Toggle Dark/Light Mode
const toggleDarkMode = () => {
setDarkMode((prevMode) => !prevMode);
};
// When reload and click on back then logout // When reload and click on back then logout
useEffect(() => { // useEffect(() => {
// Push fake history state to trap navigation // // Push fake history state to trap navigation
window.history.pushState(null, "", window.location.href); // window.history.pushState(null, "", window.location.href);
const handlePopState = () => { // const handlePopState = () => {
doLogout(); // logout when back/forward pressed // doLogout(); // logout when back/forward pressed
}; // };
const handleBeforeUnload = (e: BeforeUnloadEvent) => { // const handleBeforeUnload = (e: BeforeUnloadEvent) => {
// logout on tab close / refresh // // logout on tab close / refresh
localStorage.removeItem("access_token"); // localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token"); // sessionStorage.removeItem("access_token");
localStorage.clear(); // localStorage.clear();
sessionStorage.clear(); // sessionStorage.clear();
}; // };
window.addEventListener("popstate", handlePopState); // window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload); // window.addEventListener("beforeunload", handleBeforeUnload);
return () => { // return () => {
window.removeEventListener("popstate", handlePopState); // window.removeEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload); // window.addEventListener("beforeunload", handleBeforeUnload);
}; // };
}, []); // }, []);
useEffect(() => { useEffect(() => {
const token = localStorage.getItem("access_token"); const token = localStorage.getItem("access_token");
@@ -187,33 +195,92 @@ export default function RootLayout({ children }: { children: React.ReactNode })
{/* HEADER */} {/* HEADER */}
<Box <Box
component="header"
className={styles.header}
style={{ style={{
width: "100%", width: "100%",
display: "flex", padding: "0.8rem 2rem",
height: "60px", background: darkMode
// flexDirection: "row", ? "linear-gradient(15deg, rgba(229, 101, 22, 1) 55%, rgba(28, 28, 30, 1) 100%)" // Dark Mode Gradient
: "linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)", // Light Mode Gradient
alignItems: "center", alignItems: "center",
justifyContent: "flex-start", justifyContent: "space-between",
// padding: isMobile ? "0.5rem" : "0.5rem 1rem", color: "white",
background: "linear-gradient(15deg,rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)", boxShadow: "0 2px 6px rgba(0,0,0,0.15)",
position: "relative", position: "sticky",
// position: "fixed", top: 0,
zIndex: 100,
}} }}
> >
{/* Logo */} <Group gap="md">
<Box style={{ width: isMobile ? "48px" : "65px", height: isMobile ? "50px" : "60px" }}> <Image
<Image src={logo} component={NextImage} alt="ebanking" style={{ width: "100%", height: "100%" }} /> src={logo}
</Box> component={NextImage}
fit="contain"
alt="ebanking"
style={{ width: "60px", height: "auto" }}
/>
<div>
<Title order={3} style={{ fontFamily: "Roboto", color: "white", marginBottom: 2 }}>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text size="xs" c="white" style={{ opacity: 0.85 }}>
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215
</Text>
</div>
</Group>
{/* Dark/Light Mode Toggle using Moon and Sun Icons */}
<Group>
{/* <Button
onClick={toggleDarkMode}
variant="subtle"
color={darkMode ? "yellow" : "blue"}
style={{ marginRight: 12 }}
>
{darkMode ? <IconSun size={20} /> : <IconMoon size={20} />}
</Button> */}
<Popover opened={userOpened} onChange={setUserOpened} position="bottom-end" withArrow shadow="md">
<Popover.Target>
<Button
leftSection={<IconUserCircle size={22} />}
variant="subtle"
onClick={() => setUserOpened((prev) => !prev)}
color='white'
style={{ fontWeight: 500 }}
>
Welcome, {firstName}
</Button>
</Popover.Target>
<Popover.Dropdown style={{ minWidth: 230, padding: 15 }}>
<Stack gap="xs">
<Box>
<Text size="sm" fw={700}>{custname}</Text>
<Text size="xs" c="dimmed">Full Name</Text>
</Box>
<Box>
<Text size="sm">
{userLastLoginDetails
? new Date(userLastLoginDetails).toLocaleString()
: "N/A"}
</Text>
<Text size="xs" c="dimmed">Last Login</Text>
</Box>
<Divider />
<Button
leftSection={<IconLogout size={18} />}
onClick={doLogout}
>
Logout
</Button>
</Stack>
</Popover.Dropdown>
</Popover>
</Group>
{/* Title & Phone */}
<Stack gap={isMobile ? 2 : 0} style={{ flex: 1, textAlign: isMobile ? "center" : "left", marginLeft: isMobile ? 0 : "1rem" }}>
<Title order={isMobile ? 5 : 2} style={{ color: "white", fontFamily: "Roboto", lineHeight: 1.2 }}>
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text style={{ color: "white", fontSize: isMobile ? "0.8rem" : "0.9rem", textShadow: "1px 1px 2px black" }}>
<IconPhoneFilled size={isMobile ? 16 : 20} /> Toll Free No : 1800-180-8008
</Text>
</Stack>
</Box> </Box>
{/* WELCOME + NAV */} {/* WELCOME + NAV */}
@@ -255,7 +322,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
); );
})} })}
<Popover opened={opened} onChange={close} position="bottom-end" withArrow shadow="md"> {/* <Popover opened={opened} onChange={close} position="bottom-end" withArrow shadow="md">
<Popover.Target> <Popover.Target>
<Button leftSection={<IconLogout size={isMobile ? 16 : 20} />} variant="subtle" size={isMobile ? "xs" : "sm"} onClick={open}> <Button leftSection={<IconLogout size={isMobile ? 16 : 20} />} variant="subtle" size={isMobile ? "xs" : "sm"} onClick={open}>
Logout Logout
@@ -272,7 +339,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<Button onClick={handleLogout}>Logout</Button> <Button onClick={handleLogout}>Logout</Button>
</Group> </Group>
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover> */}
</Group> </Group>
</Box> </Box>
@@ -331,7 +398,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
}} }}
> >
<Text c="dimmed" size={isMobile ? "xs" : "sm"}> <Text c="dimmed" size={isMobile ? "xs" : "sm"}>
© 2025 The Kangra Central Co-Operative Bank Ltd. © 2025 The Kangra Central Co-Operative Bank Ltd.
</Text> </Text>
</Box> </Box>
</Box> </Box>

View File

@@ -0,0 +1,74 @@
.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; /* Allow wrapping on mobile */
}
.header-text {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
text-align: left;
}
.desktop-text {
color: white;
font-family: Roboto, sans-serif;
font-size: 1.5rem;
line-height: 1.2;
}
.desktop-address {
font-family: Roboto, sans-serif;
color: white;
font-size: 0.9rem;
text-shadow: 1px 1px 2px blue;
margin-top: 0.25rem;
}
/* Mobile styles */
.mobile-text {
color: white;
font-family: Roboto, sans-serif;
font-size: 1rem;
line-height: 1.2;
display: none;
text-align: center;
}
/* Media query for mobile */
@media screen and (max-width: 768px) {
.header {
justify-content: center;
padding: 0.5rem 0.75rem;
}
.header-logo {
width: 50px;
margin-bottom: 0.5rem;
}
.header-text {
text-align: center;
flex-direction: column;
}
.desktop-text,
.desktop-address {
display: none;
}
.mobile-text {
display: block;
font-size: 0.9rem;
}
}

View File

@@ -14,8 +14,8 @@ interface SendOtpPayload {
} }
function getStoredMobileNumber(): string { function getStoredMobileNumber(): string {
const mobileNumber = localStorage.getItem('remitter_mobile_no'); // const mobileNumber = localStorage.getItem('remitter_mobile_no');
// const mobileNumber = "7890544527"; const mobileNumber = "7890544527";
if (!mobileNumber) throw new Error('Mobile number not found.'); if (!mobileNumber) throw new Error('Mobile number not found.');
return mobileNumber; return mobileNumber;
} }

View File

@@ -7,9 +7,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0.5rem 1rem; padding: 0.8rem 2rem;
background: linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%); background: linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%);
flex-wrap: wrap; /* allow wrapping on mobile */ flex-wrap: wrap; /* Allow wrapping on mobile */
} }
.header-text { .header-text {
@@ -20,7 +20,6 @@
text-align: left; text-align: left;
} }
/* Desktop text */
.desktop-text { .desktop-text {
color: white; color: white;
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;

View File

@@ -45,8 +45,8 @@ export default function Login() {
} }
try { try {
await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: mobile }); // await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: mobile });
// await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: "7890544527" }); await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: "7890544527" });
notifications.show({ notifications.show({
color: 'orange', color: 'orange',
title: 'OTP Required', title: 'OTP Required',
@@ -67,8 +67,8 @@ export default function Login() {
async function handleVerifyOtp(mobile?: string) { async function handleVerifyOtp(mobile?: string) {
try { try {
if (mobile) { if (mobile) {
await verifyLoginOtp(otp, mobile); // await verifyLoginOtp(otp, mobile);
// await verifyLoginOtp(otp, '7890544527'); await verifyLoginOtp(otp, '7890544527');
return true; return true;
} }
} }
@@ -440,38 +440,45 @@ export default function Login() {
)} )}
</Modal> </Modal>
{/* Main Screen */} {/* Main Screen */}
<div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "auto", paddingTop: "5%" }}> <div style={{ backgroundColor: "#f8f9fa", width: "100%", height: "auto" }}>
{/* Header */} {/* Header */}
<Box className={styles.header}> <Box
<Image component="header"
src={logo} className={styles.header}
component={NextImage} style={{
fit="contain" width: "100%",
alt="ebanking" padding: "0.8rem 2rem",
style={{ width: "60px", height: "auto" }} background: "linear-gradient(15deg, rgba(10, 114, 40, 1) 55%, rgba(101, 101, 184, 1) 100%)",
/> display: "flex",
<Box className={styles['header-text']}> alignItems: "center",
{/* Desktop */} justifyContent: "space-between",
<Title className={styles['desktop-text']} ref={headerRef} order={2}> color: "white",
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD. boxShadow: "0 2px 6px rgba(0,0,0,0.15)",
</Title> position: "sticky",
<Text className={styles['desktop-address']} size="xs"> top: 0,
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 zIndex: 100,
</Text> }}
>
{/* Mobile */} <Group gap="md">
<Title className={styles['mobile-text']} order={5}> <Image
THE KANGRA CENTRAL src={logo}
</Title> component={NextImage}
<Title className={styles['mobile-text']} order={5}> fit="contain"
CO-OPERATIVE BANK LTD. alt="ebanking"
</Title> style={{ width: "60px", height: "auto" }}
<Text className={styles['mobile-text']} size="xs"> />
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215 <div>
</Text> <Title order={3} style={{ fontFamily: "Roboto", color: "white", marginBottom: 2 }}>
</Box> THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
</Title>
<Text size="xs" c="white" style={{ opacity: 0.85 }}>
Head Office: Dharmshala, District Kangra (H.P), Pin: 176215
</Text>
</div>
</Group>
</Box> </Box>
<div style={{ marginTop: '10px' }}> <div style={{ marginTop: '10px' }}>
{/* Movable text */} {/* Movable text */}
<Box <Box