feat : Home page password Expiry message

feat :  Statement Download
This commit is contained in:
2025-09-04 17:29:52 +05:30
parent dc1d7c3157
commit 686db990ac
16 changed files with 491 additions and 40 deletions

View File

@@ -1,18 +1,17 @@
"use client";
import { Paper, Select, Title, Button, Text, Grid, ScrollArea, Table, Divider, Center, Loader, Stack } from "@mantine/core";
import { Paper, Select, Title, Button, Text, Grid, ScrollArea, Table, Divider, Center, Loader, Stack, Group } from "@mantine/core";
import { DateInput } from '@mantine/dates';
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { useSearchParams } from "next/navigation";
import { notifications } from "@mantine/notifications";
import dayjs from 'dayjs';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import customParseFormat from 'dayjs/plugin/customParseFormat';
dayjs.extend(isSameOrAfter);
dayjs.extend(isSameOrBefore);
dayjs.extend(customParseFormat);
import { IconFileSpreadsheet, IconFileText, IconFileTypePdf } from "@tabler/icons-react";
import { generatePDF } from "@/app/_components/statement_download/PdfGenerator";
import { generateCSV } from "@/app/_components/statement_download/CsvGenerator";
export default function AccountStatementPage() {
const pdfRef = useRef<HTMLDivElement>(null);
const [accountOptions, setAccountOptions] = useState<{ value: string; label: string }[]>([]);
const [selectedAccNo, setSelectedAccNo] = useState<string | null>(null);
const [startDate, setStartDate] = useState<Date | null>(null);
@@ -21,6 +20,7 @@ export default function AccountStatementPage() {
const searchParams = useSearchParams();
const passedAccNo = searchParams.get("accNo");
const [loading, setLoading] = useState(false);
const [availableBalance, setAvailableBalance] = useState<string | null>(null);
useEffect(() => {
const saved = sessionStorage.getItem("accountData");
@@ -31,6 +31,8 @@ export default function AccountStatementPage() {
value: acc.stAccountNo,
}));
setAccountOptions(options);
if (passedAccNo) {
setSelectedAccNo(passedAccNo);
//Automatically fetch last 5 transactions if accNo is passed
@@ -122,6 +124,15 @@ export default function AccountStatementPage() {
const data = await response.json();
if (response.ok && Array.isArray(data)) {
setTransactions(data.reverse());
// SHowing Available balance
const saved = sessionStorage.getItem("accountData");
if (saved) {
const parsed = JSON.parse(saved);
const acc = parsed.find((a: any) => a.stAccountNo === selectedAccNo);
if (acc) {
setAvailableBalance(acc.stAvailableBalance);
}
}
}
} catch {
notifications.show({
@@ -177,10 +188,43 @@ export default function AccountStatementPage() {
<Grid.Col span={{ base: 12, md: 8 }}>
<Paper shadow="sm" radius="md" p="md" withBorder h={400} style={{ display: 'flex', flexDirection: 'column' }}>
<Title order={5} mb="xs">Account Transactions</Title>
<Text fw={500} fs="italic" >Account No : {selectedAccNo}</Text>
<Group justify="space-between" align="center" mt="sm">
<div>
<Text fw={500} ><strong>Account No :</strong> {selectedAccNo}</Text>
{availableBalance && (
<Text fw={500} >
<strong>Available Balance :</strong> {parseFloat(availableBalance).toLocaleString("en-IN", {
minimumFractionDigits: 2,
})}
</Text>
)}
</div>
<Group gap="xs">
<Text>Download :</Text>
{/* <IconFileTypePdf size={22} style={{ cursor: "pointer" }} onClick={() => console.log("Download PDF")} /> */}
<IconFileTypePdf
size={22}
style={{ cursor: "pointer" }}
onClick={() =>
generatePDF(selectedAccNo || "", availableBalance || "0", transactions)
}
/>
<IconFileSpreadsheet
size={22}
style={{ cursor: "pointer" }}
onClick={() =>
generateCSV(selectedAccNo || "NA", availableBalance || "0.00", transactions)
}
/>
</Group>
</Group>
<Divider size="xs" />
<ScrollArea style={{ flex: 1 }}>
{loading ? (
<Center h="100%">
<Stack align="center" gap="sm">
<Loader size="lg" color="cyan" type="bars" />

View File

@@ -6,6 +6,7 @@ import { IconBuildingBank, IconEye, IconLink } from '@tabler/icons-react';
import { useRouter } from "next/navigation";
import { Providers } from "../../providers";
import { notifications } from '@mantine/notifications';
import dayjs from 'dayjs';
interface accountData {
stAccountNo: string;
@@ -26,6 +27,7 @@ export default function Home() {
const [selectedLN, setSelectedLN] = useState(loanAccounts[0]?.stAccountNo || "");
const selectedLNData = loanAccounts.find(acc => acc.stAccountNo === selectedLN);
const [showBalance, setShowBalance] = useState(false);
const PassExpiryRemains =(dayjs(localStorage.getItem("pswExpiryDate"))).diff(dayjs(),"day")
// If back and forward button is clicked
useEffect(() => {
@@ -34,19 +36,25 @@ export default function Home() {
localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token");
localStorage.removeItem("remitter_name");
localStorage.removeItem("pswExpiryDate");
localStorage.clear();
sessionStorage.clear();
router.push("/login");
};
const handleBeforeUnload = () => {
// logout on tab close / refresh
localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token");
localStorage.removeItem("remitter_name");
};
// const handleBeforeUnload = () => {
// // logout on tab close / refresh
// localStorage.removeItem("access_token");
// sessionStorage.removeItem("access_token");
// localStorage.removeItem("remitter_name");
// localStorage.removeItem("pswExpiryDate");
// localStorage.clear();
// sessionStorage.clear();
// };
window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
// window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
window.removeEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
// window.addEventListener("beforeunload", handleBeforeUnload);
};
}, []);
@@ -229,7 +237,7 @@ export default function Home() {
<Stack>
<Text fw={700}> ** Book Balance includes uncleared effect.</Text>
<Text fw={700}> ** Click on the &quot;Show Balance&quot;to display balance for the Deposit and Loan account.</Text>
<Text fw={400} c="red"> ** Your Password will expire in 85 days.</Text>
<Text fw={400} c="red"> ** Your Password will expire in {PassExpiryRemains} days.</Text>
<Text></Text>
</Stack>
</div>

View File

@@ -20,6 +20,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token");
localStorage.removeItem("remitter_name");
localStorage.removeItem("pswExpiryDate");
localStorage.clear();
sessionStorage.clear();
router.push("/login");
}
@@ -79,15 +82,18 @@ export default function RootLayout({ children }: { children: React.ReactNode })
};
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
// logout on tab close / refresh
localStorage.removeItem("access_token");
sessionStorage.removeItem("access_token");
localStorage.removeItem("remitter_name");
// localStorage.removeItem("access_token");
// sessionStorage.removeItem("access_token");
// localStorage.removeItem("remitter_name");
// localStorage.removeItem("pswExpiryDate");
// localStorage.clear();
// sessionStorage.clear();
};
window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
// window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
window.removeEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
// window.addEventListener("beforeunload", handleBeforeUnload);
};
}, []);
@@ -120,6 +126,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })
else if (response.status === 401 || data.message === 'invalid or expired token') {
// console.log(data);
localStorage.removeItem("access_token");
localStorage.clear();
sessionStorage.clear();
router.push('/login');
}
else {

View File

@@ -133,6 +133,8 @@ export default function ChangePassword() {
if (response.status === 401) {
localStorage.removeItem("access_token");
localStorage.clear();
sessionStorage.clear();
router.push("/login");
return;
}
@@ -284,8 +286,9 @@ export default function ChangePassword() {
</Button>
</Group>
</div>
<Text size="sm" c="dimmed" style={{ marginTop: "40px" }}>
Note: Your new Login password must be 815 characters long and contain at least one number and one special character.
<Text size="sm" style={{ marginTop: "40px" }}>
<Text span c='red' fw={600}>Note : </Text>{""}
<Text span >Your new Login password must be 815 characters long and contain at least one number and one special character.</Text>
</Text>
</Paper>
);

View File

@@ -134,6 +134,8 @@ export default function ChangePassword() {
if (response.status === 401) {
localStorage.removeItem("access_token");
localStorage.clear();
sessionStorage.clear();
router.push("/login");
return;
}
@@ -285,8 +287,9 @@ export default function ChangePassword() {
</Group>
</div>
<Text size="sm" c="dimmed" style={{ marginTop: "40px" }}>
Note: Your new Transaction password must be 815 characters long and contain at least one number and one special character.
<Text size="sm" style={{ marginTop: "40px" }}>
<Text span c='red' fw={600}>Note : </Text>{""}
<Text span >Your new Login password must be 815 characters long and contain at least one number and one special character.</Text>
</Text>
</Paper>

View File

@@ -150,6 +150,8 @@ export default function ChangePassword() {
if (response.status === 401) {
localStorage.removeItem("access_token");
localStorage.clear();
sessionStorage.clear();
router.push("/login");
return;
}
@@ -296,8 +298,9 @@ export default function ChangePassword() {
</Button>
</Group>
<Text size="sm" c="dimmed" style={{ marginTop: "40px" }}>
Note: Your new Transaction password must be 815 characters long and contain at least one number and one special character.
<Text size="sm" style={{ marginTop: "40px" }}>
<Text span c='red' fw={600}>Note : </Text>{""}
<Text span >Your new Login password must be 815 characters long and contain at least one number and one special character.</Text>
</Text>
</Paper>
);