diff --git a/TODO.md b/TODO.md index 830d877..1051ef8 100644 --- a/TODO.md +++ b/TODO.md @@ -1,21 +1,22 @@ # Todo ### Security -- Back and front click on browser ( <- ->) logout. -- Cross from browser logout -- Taking Url and try to login -Logout +- >Back and front click on browser ( <- ->) logout. +- >Cross from browser logout +- >Taking Url and try to login -Logout ### Feature - Password Expiry Logic - login -> check password Expiry -> Change password -> login screen - Logout popup : - Are you sure want to logout? -- Home page password Expiry message +- >Home page password Expiry message - Set userId and login with userID - Limit of transaction daily -- Statement Download +- >Statement Download - In Every OTP page "Resend button" & 5 min timing of expiry. - OTP binding with actual mobile number. - IN settings page NOTE position Fixing. + diff --git a/package-lock.json b/package-lock.json index f5be102..92f79de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "date-fns": "^3.6.0", "dayjs": "^1.11.11", "embla-carousel-react": "^8.6.0", + "html2pdf.js": "^0.12.0", "ib": "file:", "IB": "file:", "iron-session": "^8.0.1", @@ -1349,6 +1350,12 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "license": "MIT" + }, "node_modules/@types/pg": { "version": "8.15.5", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", @@ -1368,6 +1375,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/react": { "version": "18.3.24", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", @@ -1395,6 +1409,13 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/validator": { "version": "13.15.2", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", @@ -2205,6 +2226,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2387,6 +2417,26 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/casbin": { "version": "5.38.0", "resolved": "https://registry.npmjs.org/casbin/-/casbin-5.38.0.tgz", @@ -2622,6 +2672,18 @@ "node": ">= 0.6" } }, + "node_modules/core-js": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", + "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2636,6 +2698,15 @@ "node": ">= 8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3055,6 +3126,16 @@ "domelementtype": "1" } }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", @@ -3973,6 +4054,17 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "license": "MIT", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -3989,6 +4081,12 @@ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "license": "MIT" }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4518,6 +4616,29 @@ "node": ">= 0.4" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html2pdf.js": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.12.0.tgz", + "integrity": "sha512-UiaAxJpkNiintpAKZ94V0GTmwDSootT78f5AHw5nUjDXo+RHsJJ0aVhoccrxdWiM7Lx2cJ929ca7mAnbSt13gw==", + "license": "MIT", + "dependencies": { + "html2canvas": "^1.0.0", + "jspdf": "^3.0.0" + } + }, "node_modules/htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -4645,6 +4766,12 @@ "node": ">= 0.10" } }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", + "license": "MIT" + }, "node_modules/iron-session": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/iron-session/-/iron-session-8.0.4.tgz", @@ -5215,6 +5342,23 @@ "json5": "lib/cli.js" } }, + "node_modules/jspdf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.2.tgz", + "integrity": "sha512-G0fQDJ5fAm6UW78HG6lNXyq09l0PrA1rpNY5i+ly17Zb1fMMFSmS+3lw4cnrAPGyouv2Y0ylujbY2Ieq3DSlKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.9", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "html2canvas": "^1.0.0-rc.5" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -6032,6 +6176,12 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6106,6 +6256,13 @@ "node": ">=8" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true + }, "node_modules/pg": { "version": "8.16.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", @@ -6495,6 +6652,16 @@ ], "license": "MIT" }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -6807,6 +6974,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -6888,6 +7062,16 @@ "node": ">=0.10.0" } }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -7380,6 +7564,16 @@ "node": "*" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -7717,6 +7911,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/sylvester": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.12.tgz", @@ -7746,6 +7950,15 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8355,6 +8568,15 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", diff --git a/package.json b/package.json index 783ea3a..8f40dfc 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "date-fns": "^3.6.0", "dayjs": "^1.11.11", "embla-carousel-react": "^8.6.0", + "html2pdf.js": "^0.12.0", "ib": "file:", "IB": "file:", "iron-session": "^8.0.1", diff --git a/public/logo.jpg b/public/logo.jpg new file mode 100644 index 0000000..9aa529c Binary files /dev/null and b/public/logo.jpg differ diff --git a/src/app/(main)/accounts/account_statement/accountStatement.tsx b/src/app/(main)/accounts/account_statement/accountStatement.tsx index 21b8ff5..3a9033b 100644 --- a/src/app/(main)/accounts/account_statement/accountStatement.tsx +++ b/src/app/(main)/accounts/account_statement/accountStatement.tsx @@ -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(null); const [accountOptions, setAccountOptions] = useState<{ value: string; label: string }[]>([]); const [selectedAccNo, setSelectedAccNo] = useState(null); const [startDate, setStartDate] = useState(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(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() { Account Transactions - Account No : {selectedAccNo} + +
+ Account No : {selectedAccNo} + {availableBalance && ( + + Available Balance : ₹ {parseFloat(availableBalance).toLocaleString("en-IN", { + minimumFractionDigits: 2, + })} + + )} +
+ + + Download : + {/* console.log("Download PDF")} /> */} + + generatePDF(selectedAccNo || "", availableBalance || "0", transactions) + } + /> + + generateCSV(selectedAccNo || "NA", availableBalance || "0.00", transactions) + } + /> + + +
+ {loading ? ( +
diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index 79a8161..986bb0d 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -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() { ** Book Balance includes uncleared effect. ** Click on the "Show Balance"to display balance for the Deposit and Loan account. - ** Your Password will expire in 85 days. + ** Your Password will expire in {PassExpiryRemains} days. diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 20cd861..925704d 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -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 { diff --git a/src/app/(main)/settings/change_login_password/page.tsx b/src/app/(main)/settings/change_login_password/page.tsx index 8e000a7..710724d 100644 --- a/src/app/(main)/settings/change_login_password/page.tsx +++ b/src/app/(main)/settings/change_login_password/page.tsx @@ -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() { - - Note: Your new Login password must be 8–15 characters long and contain at least one number and one special character. + + Note : {""} + Your new Login password must be 8–15 characters long and contain at least one number and one special character. ); diff --git a/src/app/(main)/settings/change_txn_password/page.tsx b/src/app/(main)/settings/change_txn_password/page.tsx index b59d7d5..9a68ba4 100644 --- a/src/app/(main)/settings/change_txn_password/page.tsx +++ b/src/app/(main)/settings/change_txn_password/page.tsx @@ -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() { - - Note: Your new Transaction password must be 8–15 characters long and contain at least one number and one special character. + + Note : {""} + Your new Login password must be 8–15 characters long and contain at least one number and one special character. diff --git a/src/app/(main)/settings/set_txn_password/page.tsx b/src/app/(main)/settings/set_txn_password/page.tsx index 82d87af..76c4ee8 100644 --- a/src/app/(main)/settings/set_txn_password/page.tsx +++ b/src/app/(main)/settings/set_txn_password/page.tsx @@ -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() { - - Note: Your new Transaction password must be 8–15 characters long and contain at least one number and one special character. + + Note : {""} + Your new Login password must be 8–15 characters long and contain at least one number and one special character. ); diff --git a/src/app/ForgetPassword/page.tsx b/src/app/ForgetPassword/page.tsx index 33e4188..21c0f99 100644 --- a/src/app/ForgetPassword/page.tsx +++ b/src/app/ForgetPassword/page.tsx @@ -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") } diff --git a/src/app/SetPassword/page.tsx b/src/app/SetPassword/page.tsx index aced174..a60cba8 100644 --- a/src/app/SetPassword/page.tsx +++ b/src/app/SetPassword/page.tsx @@ -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 = () => { diff --git a/src/app/SetTxn/page.tsx b/src/app/SetTxn/page.tsx index be4ecd5..6d71c89 100644 --- a/src/app/SetTxn/page.tsx +++ b/src/app/SetTxn/page.tsx @@ -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 = () => { diff --git a/src/app/_components/statement_download/CsvGenerator.tsx b/src/app/_components/statement_download/CsvGenerator.tsx new file mode 100644 index 0000000..6ad5686 --- /dev/null +++ b/src/app/_components/statement_download/CsvGenerator.tsx @@ -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 ( + + ); +} diff --git a/src/app/_components/statement_download/PdfGenerator.tsx b/src/app/_components/statement_download/PdfGenerator.tsx new file mode 100644 index 0000000..9665858 --- /dev/null +++ b/src/app/_components/statement_download/PdfGenerator.tsx @@ -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 = ` +
+
+ Bank Logo +

The Kangra Central Co Operative Bank

+
+
+ Report generated: ${dayjs().format("DD/MM/YYYY HH:mm")} +
+
+
+ `; + + // Table rows + const rows = txns.map( + (t: any) => ` + + ${t.name} + ${t.date} + + ${parseFloat(t.amount).toLocaleString("en-IN", { + minimumFractionDigits: 2, + })} ${t.type === "DR" ? "dr." : "cr."} + + + ` + ); + + const content = ` +
+ ${headerHTML} +

Account Statement

+

Account No: ${accountNo}

+

Available Balance: ₹ ${parseFloat( + balance + ).toLocaleString("en-IN", { minimumFractionDigits: 2 })}

+ + + + + + + + + + + ${rows.join("")} + +
NameDateAmount (₹)
+ +
+
+ `; + + // 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(); +}; diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index c1dce71..2ee2733 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -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") }