fix: Create report for pdf and excel
feat: Change design the Application
This commit is contained in:
934
package-lock.json
generated
934
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@
|
||||
"date-fns": "^3.6.0",
|
||||
"dayjs": "^1.11.11",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"exceljs": "^4.4.0",
|
||||
"html2pdf.js": "^0.12.0",
|
||||
"ib": "file:",
|
||||
"IB": "file:",
|
||||
|
||||
BIN
public/kccb_watermark.png
Normal file
BIN
public/kccb_watermark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
@@ -7,7 +7,7 @@ import { notifications } from "@mantine/notifications";
|
||||
import dayjs from 'dayjs';
|
||||
import { IconFileSpreadsheet, IconFileText, IconFileTypePdf } from "@tabler/icons-react";
|
||||
import { generatePDF } from "@/app/_components/statement_download/PdfGenerator";
|
||||
import { generateCSV } from "@/app/_components/statement_download/CsvGenerator";
|
||||
import { generateExcel } from "@/app/_components/statement_download/CsvGenerator";
|
||||
import { useMediaQuery } from "@mantine/hooks";
|
||||
|
||||
export default function AccountStatementPage() {
|
||||
@@ -223,17 +223,34 @@ export default function AccountStatementPage() {
|
||||
size={22}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() =>
|
||||
generatePDF(selectedAccNo || "", availableBalance || "0", transactions,
|
||||
generatePDF(
|
||||
selectedAccNo || "",
|
||||
availableBalance || "0",
|
||||
transactions,
|
||||
localStorage.getItem("remitter_name") || "",
|
||||
startDate ? dayjs(startDate).format("DD/MM/YYYY") : "",
|
||||
endDate ? dayjs(endDate).format("DD/MM/YYYY") : "")
|
||||
endDate ? dayjs(endDate).format("DD/MM/YYYY") : "",
|
||||
localStorage.getItem("remitter_branch_no") || "",
|
||||
localStorage.getItem("remitter_cif_no") || "",
|
||||
localStorage.getItem("remitter_address") || ""
|
||||
)
|
||||
}
|
||||
/>
|
||||
<IconFileSpreadsheet
|
||||
size={22}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() =>
|
||||
generateCSV(selectedAccNo || "NA", availableBalance || "0.00", transactions)
|
||||
generateExcel(
|
||||
selectedAccNo || "",
|
||||
availableBalance || "0",
|
||||
transactions,
|
||||
localStorage.getItem("remitter_name") || "",
|
||||
startDate ? dayjs(startDate).format("DD/MM/YYYY") : "",
|
||||
endDate ? dayjs(endDate).format("DD/MM/YYYY") : "",
|
||||
localStorage.getItem("remitter_branch_no") || "",
|
||||
localStorage.getItem("remitter_cif_no") || "",
|
||||
localStorage.getItem("remitter_address") || ""
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ export default function AddBeneficiaryOthers() {
|
||||
}, [ifsccode]);
|
||||
|
||||
const validateAndSendOtp = async () => {
|
||||
if (!bankName || !ifsccode || !branchName || !accountNo || !confirmAccountNo || !beneficiaryType) {
|
||||
if (!bankName || !ifsccode || !branchName || !accountNo || !confirmAccountNo || !beneficiaryType || !nickName) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
@@ -430,6 +430,16 @@ export default function AddBeneficiaryOthers() {
|
||||
withAsterisk
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Nick Name (Optional)"
|
||||
placeholder="Enter nickname (optional)"
|
||||
value={nickName}
|
||||
required
|
||||
onChange={(e) => setNickName(e.currentTarget.value)}
|
||||
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Beneficiary Name"
|
||||
@@ -439,19 +449,10 @@ export default function AddBeneficiaryOthers() {
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Nick Name (Optional)"
|
||||
placeholder="Enter nickname (optional)"
|
||||
value={nickName}
|
||||
onChange={(e) => setNickName(e.currentTarget.value)}
|
||||
|
||||
/>
|
||||
</Grid.Col>
|
||||
{!otpSent && (
|
||||
<Grid.Col >
|
||||
<Group gap="sm">
|
||||
<Button onClick={validateAndSendOtp} disabled={loading}>Validate</Button>
|
||||
<Button onClick={validateAndSendOtp} disabled={loading}>Validate</Button>
|
||||
{loading && (
|
||||
<>
|
||||
<Loader size='sm' type="bars"></Loader>
|
||||
@@ -493,9 +494,9 @@ export default function AddBeneficiaryOthers() {
|
||||
</Grid.Col >
|
||||
<Grid.Col >
|
||||
{!otpVerified ? (
|
||||
<Button onClick={verify_otp}>Validate OTP</Button>
|
||||
<Button onClick={verify_otp}>Validate OTP</Button>
|
||||
) : (
|
||||
<Button size="sm" onClick={AddBen}>
|
||||
<Button size="sm" onClick={AddBen}>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
|
||||
322
src/app/(main)/beneficiary/add_beneficiary/page.tsx
Normal file
322
src/app/(main)/beneficiary/add_beneficiary/page.tsx
Normal file
@@ -0,0 +1,322 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {TextInput,Button,Select,Title,Paper,Grid,Group,Radio,Text,PasswordInput} from '@mantine/core';
|
||||
import { useRouter } from "next/navigation";
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import AddBeneficiaryOthers from '@/app/(main)/beneficiary/add_beneficiary/addBeneficiaryOthers';
|
||||
|
||||
|
||||
const bankOptions = [
|
||||
{ value: 'KCCB', label: 'KCCB - The Kangra Central Co-Operative Bank Ltd.' },
|
||||
];
|
||||
|
||||
const AddBeneficiary: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const [bankName, setBankName] = useState('');
|
||||
const [authorized, setAuthorized] = useState<boolean | null>(null);
|
||||
const [bankType, setBankType] = useState('own');
|
||||
const [accountNo, setAccountNo] = useState('');
|
||||
const [confirmAccountNo, setConfirmAccountNo] = useState('');
|
||||
const [nickName, setNickName] = useState('');
|
||||
const [otp, setOtp] = useState('');
|
||||
const [generatedOtp, setGeneratedOtp] = useState('');
|
||||
const [otpSent, setOtpSent] = useState(false);
|
||||
const [otpVerified, setOtpVerified] = useState(false);
|
||||
const [validationStatus, setValidationStatus] = useState<'success' | 'error' | null>(null);
|
||||
const [beneficiaryName, setBeneficiaryName] = useState<string | null>(null);
|
||||
const [isVisibilityLocked, setIsVisibilityLocked] = useState(false);
|
||||
const [showPayeeAcc, setShowPayeeAcc] = useState(true);
|
||||
const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); };
|
||||
|
||||
const handleGenerateOtp = async () => {
|
||||
const value = "123456"; // Or generate a random OTP
|
||||
setGeneratedOtp(value);
|
||||
return value;
|
||||
};
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem("access_token");
|
||||
if (!token) {
|
||||
setAuthorized(false);
|
||||
router.push("/login");
|
||||
} else {
|
||||
setAuthorized(true);
|
||||
}
|
||||
}, []);
|
||||
// const isValidBankName = (name: string) => {
|
||||
// const regex = /^[A-Za-z\s]{3,5}$/;
|
||||
// return regex.test(name.trim());
|
||||
// };
|
||||
|
||||
// const isValidIfscCode = (code: string) => {
|
||||
// return /^[A-Z]{4}0[0-9]{6}$/.test(code);
|
||||
// };
|
||||
|
||||
const validateAndSendOtp = async () => {
|
||||
if (!bankName || !accountNo || !confirmAccountNo) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Missing Field",
|
||||
message: "All fields must be completed.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if (bankType === "outside" && !/^[A-Za-z ]{1,5}$/.test(bankName)) {
|
||||
// notifications.show({
|
||||
// color: "red",
|
||||
// title: "Invalid Bank Name",
|
||||
// message: "Use only alphabets/spaces, max 100 characters.",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const trimmedIfsc = ifsccode.trim().toUpperCase();
|
||||
|
||||
// if (!isValidIfscCode(trimmedIfsc)) {
|
||||
// notifications.show({
|
||||
// title: "Invalid IFSC Code",
|
||||
// message: "Must be 11 characters: 4 uppercase letters, 0, then 6 digits (e.g., HDFC0123456)",
|
||||
// color: "red",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (accountNo.length < 10 || accountNo.length > 17) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Invalid Account Number",
|
||||
message: "Enter a valid account number (10–17 digits).",
|
||||
autoClose: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountNo !== confirmAccountNo) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Mismatch",
|
||||
message: "Account numbers do not match.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem("access_token");
|
||||
const response = await fetch(`/api/beneficiary/validate/within-bank?accountNumber=${accountNo}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Login-Type": "IB",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok && data?.name) {
|
||||
setBeneficiaryName(data.name);
|
||||
setValidationStatus("success");
|
||||
setIsVisibilityLocked(true);
|
||||
setOtpSent(true);
|
||||
await handleGenerateOtp();
|
||||
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "OTP Sent",
|
||||
message: "OTP has been sent to your registered mobile number.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
} else {
|
||||
setBeneficiaryName("Invalid beneficiary account number");
|
||||
setValidationStatus("error");
|
||||
setAccountNo("");
|
||||
setConfirmAccountNo("");
|
||||
}
|
||||
} catch (error) {
|
||||
setBeneficiaryName("Invalid beneficiary account number");
|
||||
setValidationStatus("error");
|
||||
|
||||
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Error",
|
||||
message: "Could not validate account number.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const verifyOtp = () => {
|
||||
if (otp === generatedOtp) {
|
||||
setOtpVerified(true);
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "OTP Verified",
|
||||
message: "OTP validated successfully.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "OTP Error",
|
||||
message: "OTP does not match.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const AddBen = () => {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "Beneficiary Added",
|
||||
message: "Beneficiary added successfully.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
};
|
||||
|
||||
if (!authorized) return null;
|
||||
|
||||
return (
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={500}>
|
||||
<Title order={3} mb="md">Add Beneficiary</Title>
|
||||
|
||||
{/* <Radio.Group value={bankType} onChange={setBankType} name="bankType" withAsterisk mb="md">
|
||||
<Group justify="center">
|
||||
<Radio value="own" label="Own Bank" />
|
||||
<Radio value="outside" label="Outside Bank" />
|
||||
</Group>
|
||||
</Radio.Group> */}
|
||||
|
||||
{/* {bankType === "own" ? (
|
||||
<Grid gutter="md">
|
||||
<Grid.Col span={4}>
|
||||
<Select
|
||||
label="Bank Name"
|
||||
placeholder="Select bank"
|
||||
data={bankOptions}
|
||||
value={bankName}
|
||||
onChange={(value) => setBankName(value || '')}
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Beneficiary Account Number"
|
||||
placeholder="Enter account number"
|
||||
value={showPayeeAcc ? accountNo : getFullMaskedAccount(accountNo)}
|
||||
onChange={(e) => {
|
||||
const value = e.currentTarget.value;
|
||||
if (/^\d*$/.test(value) && value.length <= 17) {
|
||||
setAccountNo(value);
|
||||
setShowPayeeAcc(true);
|
||||
}
|
||||
}}
|
||||
onBlur={() => setShowPayeeAcc(false)}
|
||||
onFocus={() => setShowPayeeAcc(true)}
|
||||
readOnly={isVisibilityLocked}
|
||||
maxLength={17}
|
||||
// disabled={isVisibilityLocked}
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Confirm Beneficiary Account Number"
|
||||
placeholder="Re-enter account number"
|
||||
value={confirmAccountNo}
|
||||
onChange={(e) => {
|
||||
const value = e.currentTarget.value;
|
||||
if (/^\d*$/.test(value) && value.length <= 17) {
|
||||
setConfirmAccountNo(value);
|
||||
}
|
||||
}}
|
||||
maxLength={17}
|
||||
// disabled={isVisibilityLocked}
|
||||
readOnly={isVisibilityLocked}
|
||||
required
|
||||
onCopy={(e) => e.preventDefault()}
|
||||
onPaste={(e) => e.preventDefault()}
|
||||
onCut={(e) => e.preventDefault()}
|
||||
/>
|
||||
|
||||
{validationStatus === "error" && (
|
||||
<Text c="red" size="sm" style={{ marginLeft: '1rem', whiteSpace: 'nowrap' }}>
|
||||
{beneficiaryName}
|
||||
</Text>
|
||||
)}
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label="Nick Name (Optional)"
|
||||
placeholder="Enter nickname (optional)"
|
||||
value={nickName}
|
||||
onChange={(e) => setNickName(e.currentTarget.value)}
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label="Beneficiary Name"
|
||||
value={validationStatus === "success" && beneficiaryName ? beneficiaryName : ""}
|
||||
disabled
|
||||
maxLength={100}
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
|
||||
{!otpSent && (
|
||||
<Grid.Col>
|
||||
<Button onClick={validateAndSendOtp}>Validate</Button>
|
||||
</Grid.Col>
|
||||
)}
|
||||
|
||||
{otpSent && (
|
||||
<>
|
||||
<Grid.Col span={3}>
|
||||
<PasswordInput
|
||||
label="Enter OTP"
|
||||
placeholder="Enter OTP"
|
||||
value={otp}
|
||||
onChange={(e) => setOtp(e.currentTarget.value)}
|
||||
maxLength={6}
|
||||
disabled={otpVerified} // ✅ Disable after verified
|
||||
/>
|
||||
</Grid.Col >
|
||||
<Grid.Col >
|
||||
{!otpVerified ? (
|
||||
<Button onClick={verifyOtp}>Validate OTP</Button>
|
||||
) : (
|
||||
<Button color="blue" size="sm" onClick={AddBen}>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
</Grid.Col>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
) : ( */}
|
||||
<Grid gutter="md">
|
||||
<AddBeneficiaryOthers />
|
||||
</Grid>
|
||||
{/* )} */}
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBeneficiary;
|
||||
@@ -27,8 +27,8 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
|
||||
/* Beneficiary Options */
|
||||
const links = [
|
||||
{ label: "Add Beneficiary", href: "/beneficiary" },
|
||||
{ label: "View Beneficiary", href: "/beneficiary/view_beneficiary" },
|
||||
{ label: "View Beneficiary", href: "/beneficiary" },
|
||||
{ label: "Add Beneficiary", href: "/beneficiary/add_beneficiary" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,322 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {TextInput,Button,Select,Title,Paper,Grid,Group,Radio,Text,PasswordInput} from '@mantine/core';
|
||||
import { useRouter } from "next/navigation";
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import AddBeneficiaryOthers from '@/app/(main)/beneficiary/add_beneficiary/addBeneficiaryOthers';
|
||||
import React from "react";
|
||||
import ViewBeneficiary from "./view_beneficiary/page";
|
||||
|
||||
|
||||
const bankOptions = [
|
||||
{ value: 'KCCB', label: 'KCCB - The Kangra Central Co-Operative Bank Ltd.' },
|
||||
];
|
||||
|
||||
const AddBeneficiary: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const [bankName, setBankName] = useState('');
|
||||
const [authorized, setAuthorized] = useState<boolean | null>(null);
|
||||
const [bankType, setBankType] = useState('own');
|
||||
const [accountNo, setAccountNo] = useState('');
|
||||
const [confirmAccountNo, setConfirmAccountNo] = useState('');
|
||||
const [nickName, setNickName] = useState('');
|
||||
const [otp, setOtp] = useState('');
|
||||
const [generatedOtp, setGeneratedOtp] = useState('');
|
||||
const [otpSent, setOtpSent] = useState(false);
|
||||
const [otpVerified, setOtpVerified] = useState(false);
|
||||
const [validationStatus, setValidationStatus] = useState<'success' | 'error' | null>(null);
|
||||
const [beneficiaryName, setBeneficiaryName] = useState<string | null>(null);
|
||||
const [isVisibilityLocked, setIsVisibilityLocked] = useState(false);
|
||||
const [showPayeeAcc, setShowPayeeAcc] = useState(true);
|
||||
const getFullMaskedAccount = (acc: string) => { return "X".repeat(acc.length); };
|
||||
|
||||
const handleGenerateOtp = async () => {
|
||||
const value = "123456"; // Or generate a random OTP
|
||||
setGeneratedOtp(value);
|
||||
return value;
|
||||
};
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem("access_token");
|
||||
if (!token) {
|
||||
setAuthorized(false);
|
||||
router.push("/login");
|
||||
} else {
|
||||
setAuthorized(true);
|
||||
}
|
||||
}, []);
|
||||
// const isValidBankName = (name: string) => {
|
||||
// const regex = /^[A-Za-z\s]{3,5}$/;
|
||||
// return regex.test(name.trim());
|
||||
// };
|
||||
|
||||
// const isValidIfscCode = (code: string) => {
|
||||
// return /^[A-Z]{4}0[0-9]{6}$/.test(code);
|
||||
// };
|
||||
|
||||
const validateAndSendOtp = async () => {
|
||||
if (!bankName || !accountNo || !confirmAccountNo) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Missing Field",
|
||||
message: "All fields must be completed.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if (bankType === "outside" && !/^[A-Za-z ]{1,5}$/.test(bankName)) {
|
||||
// notifications.show({
|
||||
// color: "red",
|
||||
// title: "Invalid Bank Name",
|
||||
// message: "Use only alphabets/spaces, max 100 characters.",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const trimmedIfsc = ifsccode.trim().toUpperCase();
|
||||
|
||||
// if (!isValidIfscCode(trimmedIfsc)) {
|
||||
// notifications.show({
|
||||
// title: "Invalid IFSC Code",
|
||||
// message: "Must be 11 characters: 4 uppercase letters, 0, then 6 digits (e.g., HDFC0123456)",
|
||||
// color: "red",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (accountNo.length < 10 || accountNo.length > 17) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Invalid Account Number",
|
||||
message: "Enter a valid account number (10–17 digits).",
|
||||
autoClose: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountNo !== confirmAccountNo) {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Mismatch",
|
||||
message: "Account numbers do not match.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem("access_token");
|
||||
const response = await fetch(`/api/beneficiary/validate/within-bank?accountNumber=${accountNo}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Login-Type": "IB",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok && data?.name) {
|
||||
setBeneficiaryName(data.name);
|
||||
setValidationStatus("success");
|
||||
setIsVisibilityLocked(true);
|
||||
setOtpSent(true);
|
||||
await handleGenerateOtp();
|
||||
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "OTP Sent",
|
||||
message: "OTP has been sent to your registered mobile number.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
} else {
|
||||
setBeneficiaryName("Invalid beneficiary account number");
|
||||
setValidationStatus("error");
|
||||
setAccountNo("");
|
||||
setConfirmAccountNo("");
|
||||
}
|
||||
} catch (error) {
|
||||
setBeneficiaryName("Invalid beneficiary account number");
|
||||
setValidationStatus("error");
|
||||
|
||||
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Error",
|
||||
message: "Could not validate account number.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const verifyOtp = () => {
|
||||
if (otp === generatedOtp) {
|
||||
setOtpVerified(true);
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "OTP Verified",
|
||||
message: "OTP validated successfully.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "OTP Error",
|
||||
message: "OTP does not match.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const AddBen = () => {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "Beneficiary Added",
|
||||
message: "Beneficiary added successfully.",
|
||||
autoClose: 5000,
|
||||
});
|
||||
};
|
||||
|
||||
if (!authorized) return null;
|
||||
export default function Beneficiary() {
|
||||
|
||||
return (
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={400}>
|
||||
<Title order={3} mb="md">Add Beneficiary</Title>
|
||||
|
||||
{/* <Radio.Group value={bankType} onChange={setBankType} name="bankType" withAsterisk mb="md">
|
||||
<Group justify="center">
|
||||
<Radio value="own" label="Own Bank" />
|
||||
<Radio value="outside" label="Outside Bank" />
|
||||
</Group>
|
||||
</Radio.Group> */}
|
||||
|
||||
{/* {bankType === "own" ? (
|
||||
<Grid gutter="md">
|
||||
<Grid.Col span={4}>
|
||||
<Select
|
||||
label="Bank Name"
|
||||
placeholder="Select bank"
|
||||
data={bankOptions}
|
||||
value={bankName}
|
||||
onChange={(value) => setBankName(value || '')}
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Beneficiary Account Number"
|
||||
placeholder="Enter account number"
|
||||
value={showPayeeAcc ? accountNo : getFullMaskedAccount(accountNo)}
|
||||
onChange={(e) => {
|
||||
const value = e.currentTarget.value;
|
||||
if (/^\d*$/.test(value) && value.length <= 17) {
|
||||
setAccountNo(value);
|
||||
setShowPayeeAcc(true);
|
||||
}
|
||||
}}
|
||||
onBlur={() => setShowPayeeAcc(false)}
|
||||
onFocus={() => setShowPayeeAcc(true)}
|
||||
readOnly={isVisibilityLocked}
|
||||
maxLength={17}
|
||||
// disabled={isVisibilityLocked}
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<TextInput
|
||||
label="Confirm Beneficiary Account Number"
|
||||
placeholder="Re-enter account number"
|
||||
value={confirmAccountNo}
|
||||
onChange={(e) => {
|
||||
const value = e.currentTarget.value;
|
||||
if (/^\d*$/.test(value) && value.length <= 17) {
|
||||
setConfirmAccountNo(value);
|
||||
}
|
||||
}}
|
||||
maxLength={17}
|
||||
// disabled={isVisibilityLocked}
|
||||
readOnly={isVisibilityLocked}
|
||||
required
|
||||
onCopy={(e) => e.preventDefault()}
|
||||
onPaste={(e) => e.preventDefault()}
|
||||
onCut={(e) => e.preventDefault()}
|
||||
/>
|
||||
|
||||
{validationStatus === "error" && (
|
||||
<Text c="red" size="sm" style={{ marginLeft: '1rem', whiteSpace: 'nowrap' }}>
|
||||
{beneficiaryName}
|
||||
</Text>
|
||||
)}
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label="Nick Name (Optional)"
|
||||
placeholder="Enter nickname (optional)"
|
||||
value={nickName}
|
||||
onChange={(e) => setNickName(e.currentTarget.value)}
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6}>
|
||||
<TextInput
|
||||
label="Beneficiary Name"
|
||||
value={validationStatus === "success" && beneficiaryName ? beneficiaryName : ""}
|
||||
disabled
|
||||
maxLength={100}
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
|
||||
|
||||
{!otpSent && (
|
||||
<Grid.Col>
|
||||
<Button onClick={validateAndSendOtp}>Validate</Button>
|
||||
</Grid.Col>
|
||||
)}
|
||||
|
||||
{otpSent && (
|
||||
<>
|
||||
<Grid.Col span={3}>
|
||||
<PasswordInput
|
||||
label="Enter OTP"
|
||||
placeholder="Enter OTP"
|
||||
value={otp}
|
||||
onChange={(e) => setOtp(e.currentTarget.value)}
|
||||
maxLength={6}
|
||||
disabled={otpVerified} // ✅ Disable after verified
|
||||
/>
|
||||
</Grid.Col >
|
||||
<Grid.Col >
|
||||
{!otpVerified ? (
|
||||
<Button onClick={verifyOtp}>Validate OTP</Button>
|
||||
) : (
|
||||
<Button color="blue" size="sm" onClick={AddBen}>
|
||||
Add
|
||||
</Button>
|
||||
)}
|
||||
</Grid.Col>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
) : ( */}
|
||||
<Grid gutter="md">
|
||||
<AddBeneficiaryOthers />
|
||||
</Grid>
|
||||
{/* )} */}
|
||||
</Paper>
|
||||
<ViewBeneficiary />
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBeneficiary;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Center, Group, Loader, Paper, ScrollArea, Table, Text, Title, TextInput, Button, Modal } from "@mantine/core";
|
||||
import { Center, Group, Loader, Paper, ScrollArea, Table, Text, Title, TextInput, Button, Modal, PasswordInput } from "@mantine/core";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
@@ -215,7 +215,7 @@ export default function ViewBeneficiary() {
|
||||
|
||||
|
||||
return (
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={400}>
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={500}>
|
||||
<Group justify="space-between" align="center" mb="md" wrap="wrap">
|
||||
<Title order={3}>My Beneficiaries</Title>
|
||||
|
||||
@@ -223,8 +223,7 @@ export default function ViewBeneficiary() {
|
||||
color="green"
|
||||
variant="filled"
|
||||
size="sm"
|
||||
component="a"
|
||||
href="/beneficiary"
|
||||
onClick={() => router.push("/beneficiary/add_beneficiary")}
|
||||
style={{ minWidth: "150px" }}
|
||||
>
|
||||
+ Add Beneficiary
|
||||
@@ -235,7 +234,7 @@ export default function ViewBeneficiary() {
|
||||
<Text>No beneficiaries found.</Text>
|
||||
) : (
|
||||
<>
|
||||
<ScrollArea h={300} type="always">
|
||||
<ScrollArea h={400} type="always">
|
||||
<Table highlightOnHover withTableBorder stickyHeader style={{ borderCollapse: "collapse", width: "100%" }}>
|
||||
<thead style={{
|
||||
background: "linear-gradient(56deg, rgba(24,140,186,1) 0%, rgba(62,230,132,1) 86%)",
|
||||
@@ -314,7 +313,7 @@ export default function ViewBeneficiary() {
|
||||
) : (
|
||||
<>
|
||||
<Text mb="sm">Enter OTP sent to your registered number:</Text>
|
||||
<TextInput
|
||||
<PasswordInput
|
||||
value={otp}
|
||||
onChange={(e) => setOtp(e.currentTarget.value)}
|
||||
placeholder="Enter OTP"
|
||||
|
||||
@@ -395,10 +395,8 @@ export default function SendToBeneficiaryOwn() {
|
||||
</Group>
|
||||
</Modal>
|
||||
|
||||
|
||||
|
||||
{/* main content */}
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={400}>
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={500}>
|
||||
<Title order={3} mb="md">
|
||||
Send To Beneficiary
|
||||
</Title>
|
||||
|
||||
@@ -546,7 +546,7 @@ export default function SendToBeneficiaryOthers() {
|
||||
|
||||
{/* main content */}
|
||||
{!showIntroModal && (
|
||||
<div style={{ maxHeight: "290px", overflowY: "auto" }}>
|
||||
<div style={{ maxHeight: "350px", overflowY: "auto" }}>
|
||||
<Stack gap={5} justify="flex-start">
|
||||
<Group grow gap='xs' >
|
||||
<Select
|
||||
|
||||
@@ -493,7 +493,7 @@ export default function Home() {
|
||||
<Box mt="md">
|
||||
<Stack>
|
||||
<Text fw={700}> ** Book Balance includes uncleared effect.</Text>
|
||||
<Text fw={700}> ** Click "Show Balance" to display account balances.</Text>
|
||||
<Text fw={700}> ** Click "Show Balance" to display account balances.</Text>
|
||||
<Text fw={400} c="red">
|
||||
** Your Password will expire in {PassExpiryRemains} days.
|
||||
</Text>
|
||||
|
||||
@@ -1,60 +1,619 @@
|
||||
"use client";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
interface CsvGeneratorProps {
|
||||
accountNo: string;
|
||||
balance: string;
|
||||
txns: any[];
|
||||
}
|
||||
|
||||
export const generateCSV = (
|
||||
export const generateExcel = (
|
||||
accountNo: string,
|
||||
balance: string,
|
||||
txns: any[]
|
||||
txns: any[],
|
||||
customerName: string,
|
||||
periodFrom: string,
|
||||
periodTo: string,
|
||||
branchCode: string,
|
||||
cifNumber: string,
|
||||
address: string
|
||||
) => {
|
||||
// CSV Header
|
||||
let csv = `Bank Statement\n`;
|
||||
csv += `Account No:,${accountNo}\n`;
|
||||
csv += `Available Balance:,${balance}\n`;
|
||||
csv += `Generated:,${new Date().toLocaleString()}\n\n`;
|
||||
// Import ExcelJS dynamically
|
||||
const ExcelJS = require("exceljs");
|
||||
|
||||
// Column headers
|
||||
csv += "Date,Name,Type,Amount\n";
|
||||
|
||||
// Rows
|
||||
txns.forEach((txn) => {
|
||||
csv += `${txn.date},${txn.name},${txn.type.toLowerCase()},${txn.amount}\n`;
|
||||
// Create a new workbook and worksheet
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Account Statement", {
|
||||
pageSetup: {
|
||||
paperSize: 9, // A4
|
||||
orientation: "portrait",
|
||||
fitToPage: true,
|
||||
fitToWidth: 1,
|
||||
margins: {
|
||||
left: 0.5,
|
||||
right: 0.5,
|
||||
top: 0.75,
|
||||
bottom: 0.75,
|
||||
header: 0.3,
|
||||
footer: 0.3,
|
||||
},
|
||||
},
|
||||
views: [{ showGridLines: false }],
|
||||
});
|
||||
|
||||
// Trigger browser download
|
||||
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
let currentRow = 1;
|
||||
|
||||
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);
|
||||
/* ---------------------------------------------------------
|
||||
* 1) HEADER SECTION
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
// Bank Name
|
||||
const headerRow = worksheet.getRow(currentRow);
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const headerCell = worksheet.getCell(`A${currentRow}`);
|
||||
headerCell.value = "THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.";
|
||||
headerCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 16,
|
||||
bold: true,
|
||||
color: { argb: "FF1a5f3a" },
|
||||
};
|
||||
headerCell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
headerRow.height = 25;
|
||||
currentRow++;
|
||||
|
||||
// Bank Address
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const addressCell = worksheet.getCell(`A${currentRow}`);
|
||||
addressCell.value = "Head Office: Dharmsala, District Kangra (H.P.), Pin. 176215";
|
||||
addressCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
addressCell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
currentRow++;
|
||||
|
||||
// e-Statement Service & Date
|
||||
worksheet.mergeCells(`A${currentRow}:B${currentRow}`);
|
||||
const eStatementCell = worksheet.getCell(`A${currentRow}`);
|
||||
eStatementCell.value = "e-Statement Service";
|
||||
eStatementCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
color: { argb: "FF1a5f3a" },
|
||||
};
|
||||
eStatementCell.alignment = { vertical: "middle", horizontal: "left" };
|
||||
|
||||
worksheet.mergeCells(`C${currentRow}:D${currentRow}`);
|
||||
const dateCell = worksheet.getCell(`C${currentRow}`);
|
||||
dateCell.value = `Generated: ${dayjs().format("DD/MM/YYYY HH:mm")}`;
|
||||
dateCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
dateCell.alignment = { vertical: "middle", horizontal: "right" };
|
||||
currentRow++;
|
||||
|
||||
// Border line
|
||||
currentRow++;
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 2) ACCOUNT DETAILS SECTION
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
// Account Details Header
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const detailsHeaderCell = worksheet.getCell(`A${currentRow}`);
|
||||
detailsHeaderCell.value = "ACCOUNT DETAILS";
|
||||
detailsHeaderCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 11,
|
||||
bold: true,
|
||||
color: { argb: "FF1a5f3a" },
|
||||
};
|
||||
detailsHeaderCell.fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
detailsHeaderCell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
detailsHeaderCell.border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
currentRow++;
|
||||
|
||||
// Account Name
|
||||
worksheet.getCell(`A${currentRow}`).value = "Account Name";
|
||||
worksheet.getCell(`A${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`A${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).value = customerName;
|
||||
worksheet.getCell(`B${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
worksheet.getCell(`C${currentRow}`).value = "Branch Name";
|
||||
worksheet.getCell(`C${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`C${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).value = branchCode;
|
||||
worksheet.getCell(`D${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
// Apply borders
|
||||
["A", "B", "C", "D"].forEach((col) => {
|
||||
worksheet.getCell(`${col}${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
});
|
||||
currentRow++;
|
||||
|
||||
// Account Number
|
||||
worksheet.getCell(`A${currentRow}`).value = "Account Number";
|
||||
worksheet.getCell(`A${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`A${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).value = accountNo;
|
||||
worksheet.getCell(`B${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
worksheet.getCell(`C${currentRow}`).value = "Branch Code";
|
||||
worksheet.getCell(`C${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`C${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).value = branchCode;
|
||||
worksheet.getCell(`D${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
["A", "B", "C", "D"].forEach((col) => {
|
||||
worksheet.getCell(`${col}${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
});
|
||||
currentRow++;
|
||||
|
||||
// CIF Number
|
||||
worksheet.getCell(`A${currentRow}`).value = "CIF Number";
|
||||
worksheet.getCell(`A${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`A${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).value = cifNumber;
|
||||
worksheet.getCell(`B${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
worksheet.getCell(`C${currentRow}`).value = "Branch Address";
|
||||
worksheet.getCell(`C${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`C${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).value = "DHARAMSHALA, KANGRA";
|
||||
worksheet.getCell(`D${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
["A", "B", "C", "D"].forEach((col) => {
|
||||
worksheet.getCell(`${col}${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
});
|
||||
currentRow++;
|
||||
|
||||
// Address
|
||||
worksheet.getCell(`A${currentRow}`).value = "Address";
|
||||
worksheet.getCell(`A${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
worksheet.getCell(`A${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
worksheet.mergeCells(`B${currentRow}:D${currentRow}`);
|
||||
worksheet.getCell(`B${currentRow}`).value = address;
|
||||
worksheet.getCell(`B${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF9F9F9" },
|
||||
};
|
||||
|
||||
["A", "B", "C", "D"].forEach((col) => {
|
||||
worksheet.getCell(`${col}${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
});
|
||||
currentRow++;
|
||||
|
||||
currentRow++; // Empty row
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 3) WARNING SECTION
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const warningCell = worksheet.getCell(`A${currentRow}`);
|
||||
warningCell.value =
|
||||
"⚠ NEVER SHARE your Card number, CVV, PIN, OTP, Internet Banking User ID, Password or URB with anyone even if the caller claims to be a bank employee. Sharing these details can lead to unauthorized access to your account.";
|
||||
warningCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF721c24" },
|
||||
};
|
||||
warningCell.fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF8D7DA" },
|
||||
};
|
||||
warningCell.border = {
|
||||
top: { style: "thin", color: { argb: "FFF5C6CB" } },
|
||||
bottom: { style: "thin", color: { argb: "FFF5C6CB" } },
|
||||
left: { style: "medium", color: { argb: "FFD32F2F" } },
|
||||
right: { style: "thin", color: { argb: "FFF5C6CB" } },
|
||||
};
|
||||
warningCell.alignment = { vertical: "middle", horizontal: "left", wrapText: true };
|
||||
worksheet.getRow(currentRow).height = 40;
|
||||
currentRow++;
|
||||
|
||||
currentRow++; // Empty row
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 4) STATEMENT PERIOD
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const periodCell = worksheet.getCell(`A${currentRow}`);
|
||||
periodCell.value = `Account statement from ${periodFrom} to ${periodTo}`;
|
||||
periodCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 11,
|
||||
bold: true,
|
||||
color: { argb: "FF1a5f3a" },
|
||||
};
|
||||
periodCell.fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FFF5F5F5" },
|
||||
};
|
||||
periodCell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
periodCell.border = {
|
||||
top: { style: "medium", color: { argb: "FF1a5f3a" } },
|
||||
bottom: { style: "medium", color: { argb: "FF1a5f3a" } },
|
||||
};
|
||||
worksheet.getRow(currentRow).height = 22;
|
||||
currentRow++;
|
||||
|
||||
currentRow++; // Empty row
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 5) TRANSACTION TABLE
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
// Table Headers
|
||||
const headerRowNum = currentRow;
|
||||
worksheet.getCell(`A${currentRow}`).value = "Date";
|
||||
worksheet.getCell(`B${currentRow}`).value = "Mode / Particulars";
|
||||
worksheet.getCell(`C${currentRow}`).value = "Withdrawals / Deposits";
|
||||
worksheet.getCell(`D${currentRow}`).value = "Balance";
|
||||
|
||||
["A", "B", "C", "D"].forEach((col) => {
|
||||
const cell = worksheet.getCell(`${col}${currentRow}`);
|
||||
cell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 10,
|
||||
bold: true,
|
||||
color: { argb: "FFFFFFFF" },
|
||||
};
|
||||
cell.fill = {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FF2e7d32" },
|
||||
};
|
||||
cell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
cell.border = {
|
||||
top: { style: "thin", color: { argb: "FF1a5f3a" } },
|
||||
bottom: { style: "thin", color: { argb: "FF1a5f3a" } },
|
||||
left: { style: "thin", color: { argb: "FF1a5f3a" } },
|
||||
right: { style: "thin", color: { argb: "FF1a5f3a" } },
|
||||
};
|
||||
});
|
||||
worksheet.getRow(currentRow).height = 20;
|
||||
currentRow++;
|
||||
|
||||
// Transaction Rows
|
||||
txns.forEach((t: any) => {
|
||||
worksheet.getCell(`A${currentRow}`).value = t.date;
|
||||
worksheet.getCell(`A${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
};
|
||||
worksheet.getCell(`A${currentRow}`).alignment = {
|
||||
vertical: "middle",
|
||||
horizontal: "center",
|
||||
};
|
||||
worksheet.getCell(`A${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
|
||||
worksheet.getCell(`B${currentRow}`).value = t.name;
|
||||
worksheet.getCell(`B${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).alignment = {
|
||||
vertical: "middle",
|
||||
horizontal: "left",
|
||||
};
|
||||
worksheet.getCell(`B${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
|
||||
const amount = parseFloat(t.amount).toLocaleString("en-IN", {
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
worksheet.getCell(`C${currentRow}`).value = `${amount} ${t.type}`;
|
||||
worksheet.getCell(`C${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
bold: true,
|
||||
color: { argb: t.type === "DR" ? "FFD32F2F" : "FF2E7D32" },
|
||||
};
|
||||
worksheet.getCell(`C${currentRow}`).alignment = {
|
||||
vertical: "middle",
|
||||
horizontal: "right",
|
||||
};
|
||||
worksheet.getCell(`C${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
|
||||
worksheet.getCell(`D${currentRow}`).value = t.balance;
|
||||
worksheet.getCell(`D${currentRow}`).font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
bold: true,
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).alignment = {
|
||||
vertical: "middle",
|
||||
horizontal: "right",
|
||||
};
|
||||
worksheet.getCell(`D${currentRow}`).border = {
|
||||
top: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
left: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
right: { style: "thin", color: { argb: "FFD0D0D0" } },
|
||||
};
|
||||
|
||||
currentRow++;
|
||||
});
|
||||
|
||||
currentRow++; // Empty row
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 6) END OF STATEMENT
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const endCell = worksheet.getCell(`A${currentRow}`);
|
||||
endCell.value = "*** END OF STATEMENT ***";
|
||||
endCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 11,
|
||||
bold: true,
|
||||
color: { argb: "FF1a5f3a" },
|
||||
};
|
||||
endCell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
endCell.border = {
|
||||
top: { style: "medium", color: { argb: "FF1a5f3a" } },
|
||||
};
|
||||
currentRow++;
|
||||
|
||||
currentRow += 2; // Empty rows
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 7) IMPORTANT INFORMATION
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const infoHeaderCell = worksheet.getCell(`A${currentRow}`);
|
||||
infoHeaderCell.value = "IMPORTANT INFORMATION:";
|
||||
infoHeaderCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 11,
|
||||
bold: true,
|
||||
color: { argb: "FF1a5f3a" },
|
||||
};
|
||||
infoHeaderCell.alignment = { vertical: "middle", horizontal: "left" };
|
||||
infoHeaderCell.border = {
|
||||
bottom: { style: "medium", color: { argb: "FF1a5f3a" } },
|
||||
};
|
||||
currentRow++;
|
||||
|
||||
const importantPoints = [
|
||||
"The Kangra Central Cooperative Bank Officials or representatives will NEVER ask you for your personal information i.e. your card details, passwords, PIN, CVV, OTP etc. Do not share such details with anyone over phone, SMS or email.",
|
||||
"Always stay vigilant to suspicious emails. Do not open any suspicious emails.",
|
||||
"Always stay vigilant when giving out sensitive personal or account information.",
|
||||
"Beware of messages that instill a sense of urgency (e.g., account will expire unless you \"verify\" your information). Contact the Bank directly if unsure.",
|
||||
"Always log out of secondary devices and reset your passwords frequently.",
|
||||
"Use strong passwords: Create strong passwords that are difficult for hackers to guess.",
|
||||
"Use public Wi-Fi with caution: Be careful when using public Wi-Fi networks.",
|
||||
"Back up your data regularly to a secure, encrypted, off-site location.",
|
||||
"Follow corporate security policies: Adhere to your company's security guidelines.",
|
||||
"Assess third-party app permissions carefully before granting access.",
|
||||
"Lock your computer and mobile phone when not in use.",
|
||||
"Don't leave devices unattended. Keep all mobile devices, such as laptops and cell phones, physically secured.",
|
||||
"Don't leave Bluetooth / Wireless turned on when not in use. Enable them only when needed and in a safe environment.",
|
||||
];
|
||||
|
||||
importantPoints.forEach((point) => {
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const pointCell = worksheet.getCell(`A${currentRow}`);
|
||||
pointCell.value = `• ${point}`;
|
||||
pointCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
color: { argb: "FF333333" },
|
||||
};
|
||||
pointCell.alignment = {
|
||||
vertical: "top",
|
||||
horizontal: "left",
|
||||
wrapText: true,
|
||||
};
|
||||
worksheet.getRow(currentRow).height = 25;
|
||||
currentRow++;
|
||||
});
|
||||
|
||||
currentRow += 2; // Empty rows
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 8) FOOTER NOTE
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
worksheet.mergeCells(`A${currentRow}:D${currentRow}`);
|
||||
const footerCell = worksheet.getCell(`A${currentRow}`);
|
||||
footerCell.value = "** This is only for information purpose and not for legal use **";
|
||||
footerCell.font = {
|
||||
name: "Times New Roman",
|
||||
size: 9,
|
||||
italic: true,
|
||||
color: { argb: "FF666666" },
|
||||
};
|
||||
footerCell.alignment = { vertical: "middle", horizontal: "center" };
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 9) COLUMN WIDTHS
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
worksheet.getColumn(1).width = 15; // Date
|
||||
worksheet.getColumn(2).width = 45; // Particulars
|
||||
worksheet.getColumn(3).width = 25; // Withdrawals/Deposits
|
||||
worksheet.getColumn(4).width = 20; // Balance
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 10) SAVE FILE
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
workbook.xlsx.writeBuffer().then((buffer: any) => {
|
||||
const blob = new Blob([buffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `Statement_${accountNo}_${dayjs().format("DDMMYYYY_HHmm")}.xlsx`;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
};
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,107 +7,369 @@ export const generatePDF = (
|
||||
txns: any[],
|
||||
customerName: string,
|
||||
periodFrom: string,
|
||||
periodTo: string
|
||||
periodTo: string,
|
||||
// branchName: string,
|
||||
branchCode: string,
|
||||
cifNumber: string,
|
||||
address: string
|
||||
) => {
|
||||
const html2pdf = require("html2pdf.js");
|
||||
|
||||
// Build rows
|
||||
const rows = txns.map(
|
||||
(t: any) => `
|
||||
<tr style="page-break-inside: avoid;">
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">${t.date}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:left;">${t.name}</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>
|
||||
<td style="border:1px solid #ccc; padding:6px; text-align:right; color: blue;">${t.balance}</td>
|
||||
</tr>
|
||||
`
|
||||
);
|
||||
/* ---------------------------------------------------------
|
||||
* 1) BUILD TRANSACTION ROWS
|
||||
* --------------------------------------------------------- */
|
||||
|
||||
// Content for first page
|
||||
const content = `
|
||||
<div style="font-family:Arial, sans-serif;">
|
||||
<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 Ltd.</h2>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#555;">
|
||||
Report generated: ${dayjs().format("DD/MM/YYYY HH:mm")}
|
||||
const rows = txns
|
||||
.map((t: any) => {
|
||||
return `
|
||||
<tr class="txn-row">
|
||||
<td style="border:1px solid #d0d0d0;padding:8px 10px;text-align:center;font-size:11px;">${t.date}</td>
|
||||
<td style="border:1px solid #d0d0d0;padding:8px 10px;text-align:left;font-size:11px;">${t.name}</td>
|
||||
<td style="border:1px solid #d0d0d0;padding:8px 10px;text-align:right;font-size:11px;color:${t.type === "DR" ? "#d32f2f" : "#2e7d32"};font-weight:500;">
|
||||
${parseFloat(t.amount).toLocaleString("en-IN", {
|
||||
minimumFractionDigits: 2,
|
||||
})} ${t.type}
|
||||
</td>
|
||||
<td style="border:1px solid #d0d0d0;padding:8px 10px;text-align:right;font-size:11px;font-weight:500;">${t.balance}</td>
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 2) MAIN PAGE CONTENT
|
||||
* --------------------------------------------------------- */
|
||||
const firstPage = `
|
||||
<div style="font-family:'Times New Roman', serif; font-size:12px;line-height:1.5;">
|
||||
|
||||
<style>
|
||||
@page {
|
||||
margin: 15mm 10mm 20mm 10mm;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.txn-row {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
table {
|
||||
page-break-inside: auto;
|
||||
}
|
||||
tr {
|
||||
page-break-inside: avoid;
|
||||
page-break-after: auto;
|
||||
}
|
||||
thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
tfoot {
|
||||
display: table-footer-group;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- HEADER -->
|
||||
<div style="
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:12px;
|
||||
border-bottom:2px solid #1a5f3a;
|
||||
padding-bottom:12px;
|
||||
margin-bottom:15px;
|
||||
">
|
||||
<img src="/logo.jpg" style="height:55px;width:auto;" />
|
||||
<div style="flex:1;">
|
||||
<h2 style="margin:0 0 3px 0;font-size:18px;color:#1a5f3a;letter-spacing:0.3px;">
|
||||
THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.
|
||||
</h2>
|
||||
<p style="margin:0;font-size:10px;color:#666;">Head Office: Dharmsala, District Kangra (H.P.), Pin. 176215</p>
|
||||
</div>
|
||||
<div style="text-align:right;">
|
||||
<div style="font-size:11px;font-weight:bold;color:#1a5f3a;margin-bottom:2px;">e-Statement Service</div>
|
||||
<div style="font-size:9px;color:#666;">
|
||||
Generated: ${dayjs().format("DD/MM/YYYY HH:mm")}
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<h3 style="text-align:center;">Account Statement</h3>
|
||||
<p><strong>Account No:</strong> ${accountNo}</p>
|
||||
<p><strong>Account Holder:</strong> ${customerName}</p>
|
||||
<p><strong>Statement Period:</strong> ${periodFrom} to ${periodTo}</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:center;">Date</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:left;">Description</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:right;">Amount (₹)</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:right;">Available Amount (₹)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows.join("")}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- ACCOUNT DETAILS -->
|
||||
<table style="
|
||||
width:100%;
|
||||
margin-bottom:12px;
|
||||
border:1px solid #d0d0d0;
|
||||
background:#f9f9f9;
|
||||
border-collapse:collapse;
|
||||
">
|
||||
<tr>
|
||||
<td style="padding:8px 12px;border-bottom:1px solid #e0e0e0;width:50%;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">Account Name</span>
|
||||
<strong style="font-size:11px;color:#000;">${customerName}</strong>
|
||||
</td>
|
||||
<td style="padding:8px 12px;border-bottom:1px solid #e0e0e0;border-left:1px solid #e0e0e0;width:50%;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">Branch Name</span>
|
||||
<strong style="font-size:11px;color:#000;">${branchCode}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px 12px;border-bottom:1px solid #e0e0e0;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">Account Number</span>
|
||||
<strong style="font-size:11px;color:#000;">${accountNo}</strong>
|
||||
</td>
|
||||
<td style="padding:8px 12px;border-bottom:1px solid #e0e0e0;border-left:1px solid #e0e0e0;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">Branch Code</span>
|
||||
<strong style="font-size:11px;color:#000;">${branchCode}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px 12px;border-bottom:1px solid #e0e0e0;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">CIF Number</span>
|
||||
<strong style="font-size:11px;color:#000;">${cifNumber}</strong>
|
||||
</td>
|
||||
<td style="padding:8px 12px;border-bottom:1px solid #e0e0e0;border-left:1px solid #e0e0e0;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">Branch Address</span>
|
||||
<strong style="font-size:11px;color:#000;">DHARAMSHALA, KANGRA</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="padding:8px 12px;">
|
||||
<span style="font-size:10px;color:#666;display:block;margin-bottom:2px;">Address</span>
|
||||
<strong style="font-size:11px;color:#000;">${address}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- RED WARNING -->
|
||||
<div style="
|
||||
margin-bottom:15px;
|
||||
border:1px solid #f5c6cb;
|
||||
background:#f8d7da;
|
||||
padding:10px 12px;
|
||||
border-radius:4px;
|
||||
border-left:4px solid #d32f2f;
|
||||
">
|
||||
<div style="display:flex;gap:10px;align-items:flex-start;">
|
||||
<div style="font-size:16px;color:#d32f2f;line-height:1;margin-top:1px;">⚠</div>
|
||||
<div style="font-size:10px;line-height:1.5;color:#721c24;">
|
||||
<strong>NEVER SHARE</strong> your Card number, CVV, PIN, OTP, Internet Banking User ID, Password or URB with
|
||||
anyone even if the caller claims to be a bank employee. Sharing these details can lead to unauthorized
|
||||
access to your account.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PERIOD -->
|
||||
<div style="
|
||||
text-align:center;
|
||||
margin:15px 0 12px 0;
|
||||
padding:10px;
|
||||
background:#f5f5f5;
|
||||
border-top:2px solid #1a5f3a;
|
||||
border-bottom:2px solid #1a5f3a;
|
||||
">
|
||||
<h3 style="margin:0;font-size:13px;color:#1a5f3a;font-weight:600;">
|
||||
Account statement from <strong>${periodFrom}</strong> to <strong>${periodTo}</strong>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<!-- TABLE -->
|
||||
<table style="
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
font-size:11px;
|
||||
margin-bottom:15px;
|
||||
border:1px solid #d0d0d0;
|
||||
">
|
||||
|
||||
<thead style="display:table-header-group;">
|
||||
<tr style="background:#2e7d32;color:white;">
|
||||
<th style="border:1px solid #1a5f3a;padding:10px 8px;text-align:center;font-weight:600;font-size:11px;width:15%;">Date</th>
|
||||
<th style="border:1px solid #1a5f3a;padding:10px 8px;text-align:left;font-weight:600;font-size:11px;width:40%;">Mode / Particulars</th>
|
||||
<th style="border:1px solid #1a5f3a;padding:10px 8px;text-align:center;font-weight:600;font-size:11px;width:22.5%;">Withdrawals / Deposits</th>
|
||||
<th style="border:1px solid #1a5f3a;padding:10px 8px;text-align:center;font-weight:600;font-size:11px;width:22.5%;">Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="background:white;">${rows}</tbody>
|
||||
</table>
|
||||
|
||||
<!-- END OF STATEMENT -->
|
||||
<div style="
|
||||
text-align:center;
|
||||
margin-top:20px;
|
||||
padding:15px;
|
||||
border-top:2px solid #1a5f3a;
|
||||
page-break-before:auto;
|
||||
page-break-after:always;
|
||||
">
|
||||
<p style="font-weight:bold;font-size:12px;color:#1a5f3a;margin:0;letter-spacing:1px;">
|
||||
*** END OF STATEMENT ***
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 3) LAST PAGE
|
||||
* --------------------------------------------------------- */
|
||||
const lastPage = `
|
||||
<div style="font-family:'Times New Roman', serif; font-size:11px;line-height:1.6;margin-top:0;padding-top:20px;">
|
||||
<div style="
|
||||
border:2px solid #1a5f3a;
|
||||
padding:20px 25px;
|
||||
margin:0 20px;
|
||||
background:#fafafa;
|
||||
border-radius:4px;
|
||||
">
|
||||
<h3 style="
|
||||
font-weight:bold;
|
||||
margin:0 0 15px 0;
|
||||
font-size:14px;
|
||||
color:#1a5f3a;
|
||||
border-bottom:2px solid #1a5f3a;
|
||||
padding-bottom:8px;
|
||||
">IMPORTANT INFORMATION:</h3>
|
||||
|
||||
<div style="color:#333;">
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
The Kangra Central Cooperative Bank Officials or representatives will NEVER ask you for your personal information i.e. your card details, passwords, PIN, CVV, OTP etc. Do not share such details with anyone over phone, SMS or email.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Always stay vigilant to suspicious emails. Do not open any suspicious emails.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Always stay vigilant when giving out sensitive personal or account information.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Beware of messages that instill a sense of urgency (e.g., account will expire unless you "verify" your information). Contact the Bank directly if unsure.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Always log out of secondary devices and reset your passwords frequently.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Use strong passwords: Create strong passwords that are difficult for hackers to guess.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Use public Wi-Fi with caution: Be careful when using public Wi-Fi networks.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Back up your data regularly to a secure, encrypted, off-site location.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Follow corporate security policies: Adhere to your company's security guidelines.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Assess third-party app permissions carefully before granting access.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Lock your computer and mobile phone when not in use.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 10px 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Don't leave devices unattended. Keep all mobile devices, such as laptops and cell phones, physically secured.
|
||||
</p>
|
||||
|
||||
<p style="margin:0 0 0 0;padding-left:15px;position:relative;">
|
||||
<span style="position:absolute;left:0;color:#1a5f3a;">•</span>
|
||||
Don't leave Bluetooth / Wireless turned on when not in use. Enable them only when needed and in a safe environment.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 4) PDF GENERATION
|
||||
* --------------------------------------------------------- */
|
||||
const opt = {
|
||||
margin: [20, 10, 20, 10],
|
||||
filename: `AccountStatement_${accountNo}_${dayjs().format("DD/MM/YYYY HH:mm")}.pdf`,
|
||||
margin: [15, 10, 20, 10],
|
||||
filename: `Statement_${accountNo}_${dayjs().format("DDMMYYYY_HHmm")}.pdf`,
|
||||
image: { type: "jpeg", quality: 0.98 },
|
||||
html2canvas: { scale: 2 },
|
||||
jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
|
||||
pagebreak: { mode: ["avoid-all", "css", "legacy"] },
|
||||
html2canvas: {
|
||||
scale: 2,
|
||||
useCORS: true,
|
||||
letterRendering: true,
|
||||
},
|
||||
jsPDF: {
|
||||
unit: "mm",
|
||||
format: "a4",
|
||||
orientation: "portrait",
|
||||
compress: true,
|
||||
},
|
||||
pagebreak: {
|
||||
mode: ["css", "legacy"],
|
||||
before: '.page-break-before',
|
||||
after: '.page-break-after',
|
||||
avoid: ['tr', '.txn-row']
|
||||
},
|
||||
};
|
||||
|
||||
html2pdf()
|
||||
.set(opt)
|
||||
.from(content)
|
||||
.from(firstPage + lastPage)
|
||||
.toPdf()
|
||||
.get("pdf")
|
||||
.then((pdf: any) => {
|
||||
const totalPages = pdf.internal.getNumberOfPages();
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
const total = pdf.internal.getNumberOfPages();
|
||||
const w = pdf.internal.pageSize.getWidth();
|
||||
const h = pdf.internal.pageSize.getHeight();
|
||||
|
||||
for (let i = 2; i <= totalPages; i++) {
|
||||
const hasGState =
|
||||
typeof pdf.GState === "function" && typeof pdf.setGState === "function";
|
||||
|
||||
for (let i = 1; i <= total; i++) {
|
||||
pdf.setPage(i);
|
||||
pdf.setFontSize(10);
|
||||
|
||||
// ✅ Left side Account No
|
||||
pdf.setFont("helvetica", "bold");
|
||||
// pdf.text(`Account No: ${accountNo}`, 15, 18);
|
||||
/* LIGHT WATERMARK */
|
||||
try {
|
||||
if (hasGState) {
|
||||
const light = pdf.GState({ opacity: 0.03 });
|
||||
pdf.setGState(light);
|
||||
pdf.addImage("/kccb_watermark.png", "PNG", 35, 70, 140, 140);
|
||||
pdf.setGState(pdf.GState({ opacity: 1 }));
|
||||
} else {
|
||||
pdf.addImage("/kccb_watermark.png", "PNG", 35, 70, 140, 140);
|
||||
}
|
||||
} catch { }
|
||||
|
||||
// ✅ Centered Statement Period
|
||||
/* FOOTER */
|
||||
pdf.setFont("times", "italic");
|
||||
pdf.setFontSize(9);
|
||||
pdf.setTextColor(100, 100, 100);
|
||||
pdf.text(
|
||||
`Statement Period: ${periodFrom} to ${periodTo}`,
|
||||
pageWidth / 2,
|
||||
18,
|
||||
"** This is only for information purpose and not for legal use **",
|
||||
w / 2,
|
||||
h - 12,
|
||||
{ align: "center" }
|
||||
);
|
||||
|
||||
// Footer page numbers
|
||||
/* PAGE NUMBER */
|
||||
pdf.setFont("times", "normal");
|
||||
pdf.setFontSize(9);
|
||||
pdf.text(
|
||||
`Page ${i} of ${totalPages}`,
|
||||
pageWidth - 40,
|
||||
pdf.internal.pageSize.getHeight() - 10
|
||||
);
|
||||
pdf.setTextColor(0, 0, 0);
|
||||
pdf.text(`Page ${i} of ${total}`, w - 15, h - 12, {
|
||||
align: "right",
|
||||
});
|
||||
}
|
||||
})
|
||||
.save();
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function fetchAndStoreUserName(token: string) {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Login-Type": "IB",
|
||||
"X-Login-Type": "IB",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
@@ -28,14 +28,20 @@ export async function fetchAndStoreUserName(token: string) {
|
||||
|
||||
const data = await response.json();
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
const { custname, mobileno } = data[0];
|
||||
const { custname, mobileno, cifNumber, stBranchNo,custaddress } = data[0];
|
||||
localStorage.setItem("remitter_name", custname);
|
||||
localStorage.setItem("remitter_mobile_no", mobileno);
|
||||
localStorage.setItem("remitter_cif_no", cifNumber);
|
||||
localStorage.setItem("remitter_branch_no", stBranchNo);
|
||||
localStorage.setItem("remitter_address", custaddress);
|
||||
}
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
localStorage.removeItem("remitter_name");
|
||||
localStorage.removeItem("remitter_mobile_no");
|
||||
localStorage.removeItem("remitter_cif_no");
|
||||
localStorage.removeItem("remitter_branch_no");
|
||||
localStorage.removeItem("remitter_address");
|
||||
console.error('Error sending OTP:', error.response?.data || error.message);
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
|
||||
BIN
src/app/image/ib_front_2.jpg
Normal file
BIN
src/app/image/ib_front_2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
@@ -8,7 +8,7 @@ import { sendOtp, verifyLoginOtp } from '@/app/_util/otp';
|
||||
import NextImage from "next/image";
|
||||
import styles from './page.module.css';
|
||||
import logo from '@/app/image/logo1.jpg';
|
||||
import frontPage from '@/app/image/ib_front_1.jpg';
|
||||
import frontPage from '@/app/image/ib_front_2.jpg';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { generateCaptcha } from '@/app/captcha';
|
||||
import { IconRefresh, IconShieldLockFilled } from "@tabler/icons-react";
|
||||
@@ -78,6 +78,7 @@ export default function Login() {
|
||||
title: `${err.message}`,
|
||||
message: 'OTP verification failed. Please try again later.',
|
||||
color: 'red',
|
||||
autoClose: 700,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user