Merge branch 'dev' of https://7o9o-lb-526275444.ap-south-1.elb.amazonaws.com/tomosa.sarkar/IB into settings
This commit is contained in:
@@ -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" />
|
||||
|
@@ -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 "Show Balance"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>
|
||||
|
@@ -27,6 +27,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");
|
||||
}
|
||||
|
||||
@@ -86,15 +89,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);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -127,6 +133,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 {
|
||||
|
@@ -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 8–15 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 8–15 characters long and contain at least one number and one special character.</Text>
|
||||
</Text>
|
||||
</Paper>
|
||||
);
|
||||
|
@@ -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 8–15 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 8–15 characters long and contain at least one number and one special character.</Text>
|
||||
</Text>
|
||||
|
||||
</Paper>
|
||||
|
@@ -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 8–15 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 8–15 characters long and contain at least one number and one special character.</Text>
|
||||
</Text>
|
||||
</Paper>
|
||||
);
|
||||
|
@@ -26,7 +26,8 @@ export default function ForgetLoginPwd() {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem("access_token");
|
||||
sessionStorage.removeItem("access_token")
|
||||
localStorage.removeItem("remitter_name");
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
router.push("/login")
|
||||
}
|
||||
|
||||
|
@@ -36,8 +36,8 @@ export default function SetLoginPwd() {
|
||||
async function handleLogout(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem("access_token");
|
||||
sessionStorage.removeItem("access_token")
|
||||
localStorage.removeItem("remitter_name");
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
router.push("/login")
|
||||
}
|
||||
const regenerateCaptcha = () => {
|
||||
|
@@ -35,8 +35,8 @@ export default function SetTransactionPwd() {
|
||||
async function handleLogout(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem("access_token");
|
||||
sessionStorage.removeItem("access_token")
|
||||
localStorage.removeItem("remitter_name");
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
router.push("/login")
|
||||
}
|
||||
const regenerateCaptcha = () => {
|
||||
|
60
src/app/_components/statement_download/CsvGenerator.tsx
Normal file
60
src/app/_components/statement_download/CsvGenerator.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
interface CsvGeneratorProps {
|
||||
accountNo: string;
|
||||
balance: string;
|
||||
txns: any[];
|
||||
}
|
||||
|
||||
export const generateCSV = (
|
||||
accountNo: string,
|
||||
balance: string,
|
||||
txns: any[]
|
||||
) => {
|
||||
// CSV Header
|
||||
let csv = `Bank Statement\n`;
|
||||
csv += `Account No:,${accountNo}\n`;
|
||||
csv += `Available Balance: ${balance}\n`;
|
||||
csv += `Generated:,${new Date().toLocaleString()}\n\n`;
|
||||
|
||||
// Column headers
|
||||
csv += "Date,Name,Type,Amount\n";
|
||||
|
||||
// Rows
|
||||
txns.forEach((txn) => {
|
||||
csv += `${txn.date},${txn.name},${txn.type.toLowerCase()},${txn.amount}\n`;
|
||||
});
|
||||
|
||||
// Trigger browser download
|
||||
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("href", url);
|
||||
link.setAttribute("download", `statement_${accountNo}.csv`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
export default function CsvGenerator({
|
||||
accountNo,
|
||||
balance,
|
||||
txns,
|
||||
}: CsvGeneratorProps) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => generateCSV(accountNo, balance, txns)}
|
||||
style={{
|
||||
padding: "6px 12px",
|
||||
borderRadius: "6px",
|
||||
background: "#02a355",
|
||||
color: "white",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Download CSV
|
||||
</button>
|
||||
);
|
||||
}
|
95
src/app/_components/statement_download/PdfGenerator.tsx
Normal file
95
src/app/_components/statement_download/PdfGenerator.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const generatePDF = (
|
||||
accountNo: string,
|
||||
balance: string,
|
||||
txns: any[],
|
||||
) => {
|
||||
const html2pdf = require("html2pdf.js");
|
||||
|
||||
// Header with logo + bank name + generated date
|
||||
const headerHTML = `
|
||||
<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;" />
|
||||
<h2 style="margin:0;">The Kangra Central Co Operative Bank</h2>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#555;">
|
||||
Report generated: ${dayjs().format("DD/MM/YYYY HH:mm")}
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
`;
|
||||
|
||||
// Table rows
|
||||
const rows = txns.map(
|
||||
(t: any) => `
|
||||
<tr style="page-break-inside: avoid;">
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:left;">${t.name}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:left;">${t.date}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:right;color:${t.type === "DR" ? "red" : "green"
|
||||
}">
|
||||
${parseFloat(t.amount).toLocaleString("en-IN", {
|
||||
minimumFractionDigits: 2,
|
||||
})} ${t.type === "DR" ? "dr." : "cr."}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
);
|
||||
|
||||
const content = `
|
||||
<div style="font-family:Arial, sans-serif;">
|
||||
${headerHTML}
|
||||
<h3>Account Statement</h3>
|
||||
<p><strong>Account No:</strong> ${accountNo}</p>
|
||||
<p><strong>Available Balance:</strong> ₹ ${parseFloat(
|
||||
balance
|
||||
).toLocaleString("en-IN", { minimumFractionDigits: 2 })}</p>
|
||||
|
||||
<table style="width:100%;border-collapse:collapse;font-size:12px;margin-top:10px;page-break-inside:auto;">
|
||||
<thead style="background:#f0f0f0;">
|
||||
<tr>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:left;">Name</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:left;">Date</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:right;">Amount (₹)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows.join("")}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="height:40px;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// PDF options
|
||||
const opt = {
|
||||
margin: [10, 10, 20, 10], // bottom margin for page count
|
||||
filename: `AccountStatement_${accountNo}.pdf`,
|
||||
image: { type: "jpeg", quality: 0.98 },
|
||||
html2canvas: { scale: 2 },
|
||||
jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
|
||||
pagebreak: { mode: ["avoid-all", "css", "legacy"] },
|
||||
};
|
||||
|
||||
html2pdf()
|
||||
.set(opt)
|
||||
.from(content)
|
||||
.toPdf()
|
||||
.get("pdf")
|
||||
.then((pdf: any) => {
|
||||
const totalPages = pdf.internal.getNumberOfPages();
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pdf.setPage(i);
|
||||
pdf.setFontSize(10);
|
||||
pdf.text(
|
||||
`Page ${i} of ${totalPages}`,
|
||||
pdf.internal.pageSize.getWidth() - 40,
|
||||
pdf.internal.pageSize.getHeight() - 10
|
||||
);
|
||||
}
|
||||
})
|
||||
.save();
|
||||
};
|
@@ -106,7 +106,8 @@ export default function Login() {
|
||||
autoClose: 5000,
|
||||
});
|
||||
localStorage.removeItem("access_token");
|
||||
localStorage.removeItem("remitter_name");
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
return;
|
||||
}
|
||||
setIsLogging(true);
|
||||
@@ -114,6 +115,7 @@ export default function Login() {
|
||||
console.log(data);
|
||||
const token = data.token;
|
||||
localStorage.setItem("access_token", token);
|
||||
localStorage.setItem("pswExpiryDate", data.loginPswExpiry);
|
||||
if (data.FirstTimeLogin === true) {
|
||||
router.push("/SetPassword")
|
||||
}
|
||||
|
Reference in New Issue
Block a user