Files
IB/src/app/(main)/funds_transfer/add_beneficiary/page.tsx
tomosa.sarkar b2e84608c3 feat: Create login page for admin.
wip: Admin user Configuration
2025-08-08 14:57:33 +05:30

329 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState } from 'react';
import {
TextInput,
Button,
Select,
Title,
Paper,
Grid,
Group,
Radio,
Text,
PasswordInput,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import AddBeneficiaryOthers from './addBeneficiaryOthers';
const bankOptions = [
{ value: 'KCCB', label: 'KCCB - The Kangra Central Co-Operative Bank' },
];
const AddBeneficiary: React.FC = () => {
const [bankName, setBankName] = useState('');
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;
};
// 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 (1017 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",
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,
});
};
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>
);
};
export default AddBeneficiary;