nba-kyc-lib
Version:
A simple input form Web Component usable in React and Angular
1,349 lines (1,208 loc) • 72.1 kB
JavaScript
export async function renderSimpleForm(
container,
onSubmit,
config = {},
configURLS = {},
{ iconPath }
) {
// Load Yup using the loadYup function
let yup;
// Constants and configuration
const API_BASE_URL = configURLS.baseUrl;
const MAX_OTP_ATTEMPTS = 3;
const OTP_LENGTH = 4;
// State management
let currentStep = 1;
let otpAttempts = 0;
let businessDetails = null;
let addressDetails = null;
let bankList = [];
// Modify modal state management
let isModalOpen = config.openModal || false;
// Extract auth and custom parameters
const { auth = {}, customParams = {}, onSuccess = () => { } } = config;
const { token, customerId, companyId, ...otherAuthParams } = auth;
// Extract API endpoints and configurations
const { endpoints = {}, apiConfig = {} } = configURLS;
const {
sendOTP,
verifyOTP,
verifyIFSC,
updateBankDetails,
getBankDetails,
...otherEndpoints
} = endpoints;
// Utility functions
const showLoader = () => {
const loader = document.getElementById("loader");
if (loader) {
loader.style.display = "flex";
// Disable all interactive elements
const interactiveElements = document.querySelectorAll('button:not(#nextStep), input, select');
interactiveElements.forEach(element => {
element.disabled = true;
});
}
};
const hideLoader = () => {
const loader = document.getElementById("loader");
if (loader) {
loader.style.display = "none";
// Re-enable all interactive elements
const interactiveElements = document.querySelectorAll('button:not(#nextStep), input, select');
interactiveElements.forEach(element => {
element.disabled = false;
});
}
};
const showError = (message, fieldId = null) => {
const createErrorDiv = (message) => {
const errorDiv = document.createElement("div");
errorDiv.className = "field-error";
errorDiv.textContent = message;
errorDiv.style.color = "red";
errorDiv.style.fontSize = "12px";
errorDiv.style.marginTop = "4px";
return errorDiv;
};
if (fieldId) {
const field = document.getElementById(fieldId);
if (!field) return;
const existingError = field.parentElement.nextElementSibling;
if (existingError && existingError.classList.contains("field-error")) {
existingError.remove();
}
field.parentElement.parentElement.insertBefore(
createErrorDiv(message),
field.parentElement.nextSibling
);
} else {
let errorDiv = document.getElementById("error-message");
if (!errorDiv) {
errorDiv = createErrorDiv(message);
errorDiv.id = "error-message";
errorDiv.className = "error-message";
document.querySelector(".kyc-form").prepend(errorDiv);
}
errorDiv.textContent = message;
errorDiv.style.display = "block";
}
};
const clearErrors = () => {
document.querySelectorAll('.error-message').forEach(error => {
error.textContent = '';
error.style.display = 'none';
});
document.querySelectorAll('.field-error').forEach(error => {
error.textContent = '';
error.style.display = 'none';
});
};
const updateDetailsSection = (type) => {
const businessDetailsSection = document.querySelector(".business-details");
const addressDetailsSection = document.querySelector(".address-details");
if (type === "AADHAR") {
businessDetailsSection.style.display = "none";
addressDetailsSection.style.display = addressDetails ? "block" : "none";
} else {
businessDetailsSection.style.display = businessDetails ? "block" : "none";
addressDetailsSection.style.display = addressDetails ? "block" : "none";
}
};
const renderBusinessDetails = () => {
const businessDetailsSection = document.querySelector(".business-details");
if (!businessDetailsSection || !businessDetails) return;
const detailsHTML = `
<div class="form-row">
<div class="form-group">
<label>Type of Business</label>
<p class="detail-text">${businessDetails.businessType || "N/A"}</p>
</div>
<div class="form-group">
<label>Name of Business</label>
<p class="detail-text">${businessDetails.nameAsPerPAN || "N/A"}</p>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Trade Name</label>
<p class="detail-text">${businessDetails.tradeName || "N/A"}</p>
</div>
<div class="form-group">
<label>CIN</label>
<p class="detail-text">${businessDetails.cin || "N/A"}</p>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>PAN Number</label>
<p class="detail-text">${businessDetails.pan || "N/A"}</p>
</div>
<div class="form-group">
<label>TAN Number</label>
<p class="detail-text">${businessDetails.tan || "N/A"}</p>
</div>
</div>
`;
businessDetailsSection.innerHTML = `
<h3>
<span class="section-icon">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEwIDQuMzMzMDFINkM0LjExNDM4IDQuMzMzMDFDMy4xNzE1NyA0LjMzMzAxIDIuNTg1NzkgNC45MTg3OUMyIDUuNTA0NTggMiA2LjQ0NzM5IDIgOC4zMzMwMVY5Ljk5OTY3QzIgMTEuODg1MyAyIDEyLjgyODEgMi41ODU3OSAxMy40MTM5QzMuMTcxNTcgMTMuOTk5NyA0LjExNDM4IDEzLjk5OTcgNiAxMy45OTk3SDEwQzExLjg4NTYgMTMuOTk5NyAxMi44Mjg0IDEzLjk5OTcgMTMuNDE0MiAxMy40MTM5QzE0IDEyLjgyODEgMTQgMTEuODg1MyAxNCA5Ljk5OTY3VjguMzMzMDFDMTQgNi40NDczOSAxNCA1LjUwNDU4IDEzLjQxNDIgNC45MTg3OUMxMi44Mjg0IDQuMzMzMDEgMTEuODg1NiA0LjMzMzAxIDEwIDQuMzMzMDFaIiBzdHJva2U9IiMyMzhFRjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNNiA0LjMzMzMzVjRDNiAzLjA1NzE5IDYgMi41ODU3OSA2LjI5Mjg5IDIuMjkyODlDNi41ODU3OSAyIDcuMDU3MiAyIDggMkM4Ljk0MjggMiA5LjQxNDIgMiA5LjcwNzEzIDIuMjkyODlDMTAgMi41ODU3OSAxMCAzLjA1NzE5IDEwIDRWNC4zMzMzMyIgc3Ryb2tlPSIjMjM4RUY4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTQuNjY2NzUgNC4zMzMwMVYxMy45OTk3IiBzdHJva2U9IiMyMzhFRjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTEuMzMzMyA0LjMzMzAxVjEzLjk5OTciIHN0cm9rZT0iIzIzOEVGOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo="
alt="location icon"
class="kyc-image"
/>
</span>
Business Details
</h3>
${detailsHTML}
`;
};
const renderAddressDetails = () => {
const addressDetailsSection = document.querySelector(".address-details");
if (!addressDetailsSection || !addressDetails) return;
const kycType = document.getElementById("kycType")?.value || "GST";
const addressHTML = `
<div class="form-group">
<label>${kycType == "GST" ? "Business Address" : "Address"}</label>
<p class="detail-text address-text">
${addressDetails.nameAsPerAadhar || ""}
${addressDetails.addressLine1 || ""}
${addressDetails.addressLine2 || ""}
${addressDetails.city || ""} ${addressDetails.state || ""}
${addressDetails.pinCode || ""}
</p>
</div>
`;
addressDetailsSection.innerHTML = `
<h3>
<span class="section-icon" style="margin-bottom:-5px">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTkuMDc4NDcgMTQuMjQ0M0M4Ljc4OTQgMTQuNTE1IDguNDAyOTMgMTQuNjY2MyA4LjAwMDczIDE0LjY2NjNDNy41OTg1MyAxNC42NjYzIDcuMjEyMTMgMTQuNTE1IDYuOTIzIDE0LjI0NDNDNC4yNzUzNSAxMS43NTAzIDAuNzI3MTc0IDguOTY0MjcgMi40NTc1MSA0LjkxOTQ1QzMuMzkzMDkgMi43MzI0NSA1LjYzODg5IDEuMzMzMDEgOC4wMDA3MyAxLjMzMzAxQzEwLjM2MjYgMS4zMzMwMSAxMi42MDg0IDIuNzMyNDUgMTMuNTQ0IDQuOTE5NDVDMTUuMjcyMSA4Ljk1OTIxIDExLjczMjcgMTEuNzU4OSA5LjA3ODQ3IDE0LjI0NDNaIiBzdHJva2U9IiMyMzhFRjgiLz4KPHBhdGggZD0iTTEwLjMzMzQgNy4zMzMzM0MxMC4zMzM0IDguNjIyIDkuMjg4NzUgOS42NjY2NyA4LjAwMDA4IDkuNjY2NjdDNi43MTE0MSA5LjY2NjY3IDUuNjY2NzUgOC42MjIgNS42NjY3NSA3LjMzMzMzQzUuNjY2NzUgNi4wNDQ2NyA2LjcxMTQxIDUgOC4wMDAwOCA1QzkuMjg4NzUgNSAxMC4zMzM0IDYuMDQ0NjcgMTAuMzMzNCA3LjMzMzMzWiIgc3Ryb2tlPSIjMjM4RUY4Ii8+Cjwvc3ZnPgo="
alt="location icon"
class="kyc-image"
/>
</span>
Address Details
</h3>
${addressHTML}
`;
addressDetailsSection.style.display = "block";
};
const handleBankSubmit = async () => {
try {
const bankDetails = bankList.map((bank) => ({
accountHolderName: bank.accountHolderName,
accountNumber: bank.accountNumber,
bankName: bank.bankName,
ifscCode: bank.ifsc,
swiftCode: bank?.swift || "",
micrNumber: bank?.micr || "",
branchName: bank?.branch || "",
}));
const response = await apiCallBankSubmitToken(
endpoints?.updateBankDetails + companyId,
"PUT",
{ customer: { bankDetails } }
);
if (response?.data) {
bankList.length = 0;
renderBankList();
// Show the success modal (static values for now)
showKycSuccessModal("KYC28947", "July 21, 2023");
// currentStep = 1;
showStep(currentStep);
} else {
showError(response?.message || "Failed to submit bank details. Please try again.");
}
} catch (error) {
showError("Failed to submit bank details. Please try again.");
console.error("Bank submission error:", error);
}
};
const fetchBankDetails = async (
endpoint,
method = "GET",
body = null,
goNext = false
) => {
showLoader(); // Show loader when API call is initiated
try {
const options = {
method,
headers: { "Content-Type": "application/json" },
...(body && { body: JSON.stringify(body) }),
};
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
let temp = await response.json();
bankList = temp?.bankDetails;
renderBankList()
} catch (error) {
console.error("API Error:", error);
} finally {
hideLoader(); // Hide loader after API call is completed
}
};
const apiCall = async (
endpoint,
method = "GET",
body = null,
goNext = false
) => {
showLoader(); // Show loader when API call is initiated
try {
const options = {
method,
headers: { "Content-Type": "application/json" },
...(body && { body: JSON.stringify(body) }),
};
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
if (goNext) {
currentStep = 2;
showStep(currentStep);
}
return await response.json();
} catch (error) {
console.error("API Error:", error);
showError(error.message);
throw error;
} finally {
hideLoader(); // Hide loader after API call is completed
}
};
const apiCallBankSubmitToken = async (endpoint, method = "GET", body = null) => {
showLoader();
try {
const options = {
method,
headers: {
"Content-Type": "application/json",
Authorization: `${token}`,
// Add any custom headers from apiConfig
...(apiConfig.headers || {})
},
...(body && { body: JSON.stringify(body) }),
};
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
return await response.json();
} catch (error) {
showError(error.message);
throw error;
} finally {
hideLoader();
}
};
const apiCallToken = async (endpoint, method = "GET", body = null) => {
showLoader();
try {
const options = {
method,
headers: {
"Content-Type": "application/json",
Authorization: `${token}`,
// Add any custom headers from apiConfig
...(apiConfig.headers || {})
},
...(body && { body: JSON.stringify(body) }),
};
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
if (!response.ok) return response.ok;
return await response.json();
} catch (error) {
console.log(error, 'error')
console.error("API Error:", error);
showError(error.message);
throw error;
} finally {
hideLoader();
}
};
const initiateOtp = async () => {
try {
showLoader();
const kycType = document.getElementById("kycType")?.value || "GST";
const kycNumber = document.getElementById("kycNumber")?.value;
if (!kycNumber) throw new Error("Please enter a valid KYC number");
const result = await apiCall(
endpoints?.sendOTP + customerId,
"PUT",
{
type: kycType,
number: kycNumber,
}
);
if (result.kycStatus === "pending") {
document.getElementById("otp").disabled = false;
otpAttempts = 0;
// Start the resend timer
startResendTimer();
}
if (result.kycStatus === null) {
showError(result.message);
}
} catch (error) {
showError(error.message);
} finally {
hideLoader();
}
};
// Add timer function
const startResendTimer = () => {
const resendOtpElement = document.getElementById("resend-otp");
const initiateOtpButton = document.getElementById("initiateOtp");
let timeLeft = 90; // 2 minutes in seconds
// Disable the initiate OTP button
if (initiateOtpButton) {
initiateOtpButton.style.display = "none";
}
// Show the timer
if (resendOtpElement) {
resendOtpElement.style.display = "block";
}
const timer = setInterval(() => {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
if (resendOtpElement) {
resendOtpElement.textContent = `Resend OTP in ${minutes}:${seconds.toString().padStart(2, '0')}`;
}
if (timeLeft <= 0) {
clearInterval(timer);
if (resendOtpElement) {
// resendOtpElement.textContent = "Resend OTP";
resendOtpElement.style.display = "none";
// resendOtpElement.style.cursor = "pointer";
// resendOtpElement.onclick = () => {
// initiateOtp();
// };
}
if (initiateOtpButton) {
initiateOtpButton.style.display = "block";
}
}
timeLeft--;
}, 1000);
};
const verifyOtp = async () => {
try {
if (otpAttempts >= MAX_OTP_ATTEMPTS) {
showError(
"Maximum OTP attempts reached. Please try again later.",
"otp"
);
return;
}
const otp = document.getElementById("otp").value;
if (!otp) {
showError("Please enter a valid OTP", "otp");
return;
}
const result = await apiCall(
endpoints?.verifyOTP + customerId,
"PUT",
{
otp,
type: document.getElementById("kycType")?.value,
number: document.getElementById("kycNumber")?.value,
}
);
if (result?.kycStatus === "completed") {
const otpIcon = document.getElementById("otpIcon");
if (otpIcon) {
otpIcon.src = "data:image/svg+xml;base64,c3ZnIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMTEzOV8xMzI2KSI+CjxwYXRoIGQ9Ik0xNC42NjY2IDcuOTk5NjdDMTQuNjY2NiA0LjMxNzc3IDExLjY4MTggMS4zMzMwMSA3Ljk5OTkyIDEuMzMzMDFDNC4zMTgwMiAxLjMzMzAxIDEuMzMzMjUgNC4zMTc3NyAxLjMzMzI1IDcuOTk5NjdDMS4zMzMyNSAxMS42ODE1IDQuMzE4MDIgMTQuNjY2MyA3Ljk5OTkyIDE0LjY2NjNDMTEuNjgxOCAxNC42NjYzIDE0LjY2NjYgMTEuNjgxNSAxNC42NjY2IDcuOTk5NjdaIiBzdHJva2U9IiMwRUJDOTMiLz4KPHBhdGggZD0iTTUuMzMzMjUgOC4zMzMzM0w2Ljk5OTkyIDEwTDEwLjY2NjYgNiIgc3Ryb2tlPSIjMEVCQzkzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTM5XzEzMjYiPgo8cmVjdCB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+";
}
if (document.getElementById("kycType")?.value === "AADHAR") {
businessDetails = null;
addressDetails = {
...result?.kycData?.aadharResponse?.addressAsPerAadhar,
nameAsPerAadhar: result?.kycData?.aadharResponse?.nameAsPerAadhar,
};
renderAddressDetails();
updateDetailsSection("AADHAR");
onSuccess(result);
} else {
businessDetails = {
...result?.kycData?.businessPANResponse,
tradeName: result?.kycData?.nameasPerBusinessPan,
} || null;
addressDetails =
// data ||
result?.kycData?.businessPANResponse?.businessPanfromGST?.address ||
null;
renderBusinessDetails();
renderAddressDetails();
updateDetailsSection(document.getElementById("kycType")?.value);
}
// Check if we should skip bank details
if (!config.showBank) {
// Skip step 2 and show success modal directly
showKycSuccessModal("KYC28947", "July 21, 2023");
return;
}
// Enable the next step button only after successful OTP verification
const nextStepButton = document.getElementById("nextStep");
if (nextStepButton) {
nextStepButton.disabled = false;
}
} else {
otpAttempts++;
showError(result?.message || "Invalid OTP. Please try again.", "otp");
// Ensure next step button remains disabled on failed verification
const nextStepButton = document.getElementById("nextStep");
if (nextStepButton) {
nextStepButton.disabled = true;
}
}
} catch (error) {
showError(error.message, "otp");
// Ensure next step button remains disabled on error
const nextStepButton = document.getElementById("nextStep");
if (nextStepButton) {
nextStepButton.disabled = true;
}
}
};
let ifscDetails = null;
const updateProgress = (step) => {
document.querySelectorAll(".kyc-progress-bar .step").forEach((el, index) => {
el.classList.toggle("active", index + 1 === step);
});
};
const showStep = (step) => {
// If showBank is false and trying to show step 2, skip to success modal
if (!config.showBank && step === 2) {
showKycSuccessModal("KYC28947", "July 21, 2023");
return;
}
document.getElementById("step1-content").style.display =
step === 1 ? "block" : "none";
document.getElementById("step2-content").style.display =
step === 2 ? "block" : "none";
updateProgress(step);
if (step === 2) {
renderBankList();
}
};
const renderBankList = () => {
const bankListBody = document.getElementById("bankListBody");
if (!bankListBody) return;
if (bankList?.length === 0) {
bankListBody.innerHTML = `
<tr>
<td colspan="4" style="text-align: center; padding: 20px; color: #666;">
<div style="display: flex; flex-direction: column; align-items: center; gap: 8px;">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTI0IDQ0QzM1LjA0NiA0NCA0NCAzNS4wNDYgNDQgMjRDNDQgMTIuOTU0IDM1LjA0NiA0IDI0IDRDMTIuOTU0IDQgNCAxMi45NTQgNCAyNEM0IDM1LjA0NiAxMi45NTQgNDQgMjQgNDRaIiBzdHJva2U9IiNFNUU1RTUiIHN0cm9rZS13aWR0aD0iMiIvPgo8cGF0aCBkPSJNMjQgMTZWMjRNMjQgMzJIMjQuMDEiIHN0cm9rZT0iI0U1RTVFNSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+Cg=="
alt="no data"
style="width: 22px; height: 22px;"
/>
<span>No bank accounts added yet</span>
</div>
</td>
</tr>
`;
return;
}
bankListBody.innerHTML = bankList
.map(
(bank, index) => `
<tr style="color: #000;">
<td>${index + 1}</td>
<td>${bank.accountNumber}</td>
<td >${bank.bankName}<br><span style="font-size: 9px; color: #666;">${bank.branchName || "N/A"}</span> </td>
<td>${bank.accountHolderName}</td>
<td><button class="delete-btn" data-index="${index}" >
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTkuNzUgMi43NUw5LjQ0MDE1IDcuNzYyNTVDOS4zNjA5NSA5LjA0MzIgOS4zMjE0IDkuNjgzNTUgOS4wMDA0IDEwLjE0NEM4Ljg0MTY1IDEwLjM3MTYgOC42MzczNSAxMC41NjM3IDguNDAwMzUgMTAuNzA4QzcuOTIxMDUgMTEgNy4yNzk1IDExIDUuOTk2MzUgMTFDNC43MTE1NiAxMSA0LjA2OTE1IDExIDMuNTg5NTIgMTAuNzA3NEMzLjM1MjQgMTAuNTYyOCAzLjE0OCAxMC4zNzA0IDIuOTg5MzQgMTAuMTQyNEMyLjY2ODQ0IDkuNjgxMyAyLjYyOTcyIDkuMDQwMDUgMi41NTIzIDcuNzU3NkwyLjI1IDIuNzUiIHN0cm9rZT0iIzE4MUQyNyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0xLjUgMi43NUgxMC41TTguMDI3ODUgMi43NUw3LjY4NjU1IDIuMDQ1ODdDNy40NTk4IDEuNTc4MTMgNy4zNDY0IDEuMzQ0MjYgNy4xNTA4NSAxLjE5ODQxQzcuMTA3NSAxLjE2NjA1IDcuMDYxNTUgMS4xMzcyNyA3LjAxMzUgMS4xMTIzNUM2Ljc5Njk1IDEgNi41MzcwNSAxIDYuMDE3MjUgMUM1LjQ4NDQgMSA1LjIxOCAxIDQuOTk3ODQgMS4xMTcwNkM0Ljk0OTA1IDEuMTQzIDQuOTAyNDkgMS4xNzI5NSA0Ljg1ODY0IDEuMjA2NTlDNC42NjA4MiAxLjM1ODM1IDQuNTUwMzIgMS42MDA3NyA0LjMyOTMxIDIuMDg1NjNMNC4wMjY0NiAyLjc1IiBzdHJva2U9IiMxODFEMjciIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4KCg=="
alt="delete"
class="kyc-image"
/>
</button></td>
</tr>
`
)
.join("");
bankListBody.querySelectorAll(".delete-btn").forEach((btn) => {
btn.addEventListener("click", (e) => {
const index = parseInt(e.target.dataset.index);
bankList.splice(index, 1);
if (bankList.length === 0) {
const bankSubmitButton = document.getElementById("bank-submit");
if (bankSubmitButton) {
bankSubmitButton.disabled = true;
}
}
renderBankList();
});
});
};
const handleKycTypeChange = async (e) => {
clearErrors();
const kycType = e.target.value;
const kycInput = document.getElementById("kycNumber");
const otpInput = document.getElementById("otp");
const initiateOtpButton = document.getElementById("initiateOtp");
const verifyOtpButton = document.getElementById("verifyOtp");
const nextStepButton = document.getElementById("nextStep");
// Reset form fields
if (kycInput) {
kycInput.value = "";
kycInput.classList.remove("valid", "invalid");
}
if (otpInput) {
otpInput.value = "";
otpInput.disabled = true;
otpInput.classList.remove("valid", "invalid");
}
// Reset buttons
if (initiateOtpButton) {
initiateOtpButton.style.display = "none";
}
if (verifyOtpButton) {
verifyOtpButton.disabled = true;
}
if (nextStepButton) {
nextStepButton.disabled = true;
}
// Reset business and address details
businessDetails = null;
addressDetails = null;
updateDetailsSection(kycType);
// Update KYC input field properties
const validationRules = {
GST: {
placeholder: "Enter GST Number",
maxLength: 15,
pattern: "^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$",
},
AADHAR: {
placeholder: "Enter Aadhar Card Number",
maxLength: 12,
pattern: "^[0-9]{12}$",
},
};
const rules = validationRules[kycType] || {
placeholder: "Enter KYC Number",
maxLength: 15,
pattern: ".*",
};
if (kycInput) {
kycInput.placeholder = rules.placeholder;
kycInput.maxLength = rules.maxLength;
kycInput.pattern = rules.pattern;
}
// Reset OTP attempts
this.otpAttempts = 0;
};
const handleKycInput = async (e) => {
const kycType = document.getElementById("kycType").value;
const kycNumber = e.target.value;
const verifyButton = document.getElementById("initiateOtp");
clearErrors();
const error = validateKycNumber(kycNumber, kycType);
if (!error) {
e.target.classList.remove("invalid");
e.target.classList.add("valid");
verifyButton.style.display = "block";
verifyButton.disabled = false;
} else {
e.target.classList.remove("valid");
e.target.classList.add("invalid");
showFieldError(error, "kycNumber");
verifyButton.disabled = true;
}
};
const handleOTPInput = async (e) => {
const otp = e.target.value;
const verifyButton = document.getElementById("verifyOtp");
clearErrors();
addErrorRemovalListeners();
const error = validateOTP(otp);
if (!error) {
e.target.classList.remove("invalid");
e.target.classList.add("valid");
verifyButton.style.display = "block";
verifyButton.disabled = false;
} else {
e.target.classList.remove("valid");
e.target.classList.add("invalid");
showFieldError(error, "otp");
verifyButton.disabled = true;
}
};
const addErrorRemovalListeners = () => {
const inputFields = document.querySelectorAll("input, select");
inputFields.forEach((field) => {
field.addEventListener("input", (e) => {
const existingError = e.target.parentElement.nextElementSibling;
if (existingError && existingError.classList.contains("field-error")) {
existingError.remove();
}
});
});
};
// Helper to show error below a specific field
function showFieldError(message, fieldName) {
const errorElement = document.getElementById(`${fieldName}-error`);
if (errorElement) {
errorElement.textContent = message;
errorElement.style.display = 'block';
}
}
const handleAddBank = async () => {
const accountHolderName = document.querySelector('input[name="accountHolderName"]').value;
const accountNumber = document.querySelector('input[name="accountNumber"]').value;
const ifscCode = document.querySelector('input[name="ifscCode"]').value;
const addBankButton = document.querySelector("#addBank");
const errors = validateBankDetails({ accountHolderName, accountNumber, ifscCode });
if (!errors) {
bankList.push({
accountNumber,
accountHolderName,
bankName: ifscDetails?.bank || "Unknown Bank",
ifsc: ifscCode,
selected: false,
swiftCode: ifscDetails?.swift || "",
micrNumber: ifscDetails?.micr || "",
branchName: ifscDetails?.branch || "",
});
renderBankList();
// Reset form fields
document.querySelector('input[name="accountNumber"]').value = '';
document.querySelector('input[name="ifscCode"]').value = '';
document.querySelector('input[name="accountHolderName"]').value = '';
document.getElementById('bank-name').style.display = 'none';
// Reset validation state
document.querySelector('input[name="accountNumber"]').classList.remove('valid', 'invalid');
document.querySelector('input[name="ifscCode"]').classList.remove('valid', 'invalid');
document.querySelector('input[name="accountHolderName"]').classList.remove('valid', 'invalid');
// Clear error messages
document.getElementById('accountNumber-error').textContent = '';
document.getElementById('ifscCode-error').textContent = '';
document.getElementById('accountHolderName-error').textContent = '';
// Reset IFSC details
ifscDetails = null;
// Disable Add Bank button until new valid input
if (addBankButton) {
addBankButton.disabled = true;
}
// Enable bank submit button after adding a bank
const bankSubmitButton = document.getElementById("bank-submit");
if (bankSubmitButton) {
bankSubmitButton.disabled = false;
}
// Reset the form validation state
const form = document.getElementById('kycForm');
if (form) {
form.reset();
}
} else {
Object.entries(errors).forEach(([field, message]) => {
const errorElement = document.getElementById(`${field}-error`);
if (errorElement) {
errorElement.textContent = message;
errorElement.style.display = 'block';
}
const inputElement = document.querySelector(`input[name="${field}"]`);
if (inputElement) {
inputElement.classList.add('invalid');
inputElement.classList.remove('valid');
}
});
}
};
const handleFormSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const formValues = Object.fromEntries(formData.entries());
try {
if (currentStep === 1) {
const kycError = validateKycNumber(formValues.kycNumber, formValues.kycType);
const otpError = validateOTP(formValues.otp);
if (kycError) {
showFieldError(kycError, 'kycNumber');
return;
}
if (otpError) {
showFieldError(otpError, 'otp');
return;
}
showStep(2);
fetchBankDetails(endpoints.getBankDetails + companyId);
} else {
const errors = validateBankDetails(formValues);
if (errors) {
Object.entries(errors).forEach(([field, message]) => {
showFieldError(message, field);
});
return;
}
// Handle final form submission
if (onSubmit) {
onSubmit(formValues);
}
}
} catch (err) {
console.error('Form submission error:', err);
showError('An error occurred while submitting the form');
}
};
const toggleModal = (show) => {
const modalContainer = document.querySelector(".kyc-container");
if (modalContainer) {
modalContainer.style.display = show ? "flex" : "none";
isModalOpen = show; // Update internal state
}
};
// Improve handleCloseModal
const handleCloseModal = () => {
closeModal()
toggleModal(false);
isModalOpen = false; // Update internal state
if (config.onClose) {
config.onClose(); // Call the onClose callback
}
};
// Improve openModal
const openModal = () => {
if (!isModalOpen) {
// Only open if not already open
toggleModal(true);
}
};
// Improve closeModal function
const closeModal = () => {
console.log("111")
if (isModalOpen) {
toggleModal(false);
isModalOpen = false; // Update internal state
if (config.onClose) {
console.log("22222")
config.onClose(); // Call the onClose callback
}
}
};
// Modify the loadStyles function to include initial modal state
const loadStyles = () => {
const style = document.createElement("style");
style.textContent = `
.kyc-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.kyc-form-wrapper {
background: #f7f7f7;
border-radius: 24px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
overflow: hidden;
width: 100%;
max-width: 42%;
margin: 20px;
position: relative;
z-index: 1001;
}
/* Add loading overlay styles */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loader-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.loader {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #2f80ed;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Rest of your existing styles */
.form-section {
margin-bottom: 1.5rem;
background: #fff;
border-radius: 12px;
padding: 1.1rem;
border: 1px solid #f0f0f0;
max-height: 60vh;
overflow-y: auto;
scrollbar-width: thin;
}
/* For Webkit browsers (Chrome, Safari) */
.form-section::-webkit-scrollbar {
width: 6px;
}
.form-section::-webkit-scrollbar-track {
background: #f0f0f0;
border-radius: 3px;
}
.form-section::-webkit-scrollbar-thumb {
background: #2f80ed;
border-radius: 3px;
}
.kyc-header {
padding: 1.5rem 1.5rem 2.4rem 1.5rem;
display: flex;
align-items: center;
border-bottom: 1px solid #f0f0f0;
}
.header-icon {
font-size: 2rem;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.kyc-header h2 {
margin: 0;
font-size: 1.25rem;
color: #1a1a1a;
font-weight: 600;
}
.kyc-header p {
margin: 0.25rem 0 0;
color: #666;
font-size: 0.875rem;
}
.close-button {
margin-left: auto;
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 0.5rem;
}
.kyc-progress-bar {
display: flex;
padding: 0 1.5rem;
gap: 1rem;
background: #f8f9fa;
}
.kyc-progress-bar .step {
flex: 1;
height: 5px !important;
background: #e0e0e0 !important;
border-radius: 8px !important;
position: relative !important;
font-size: 0.875rem !important;
color: #666 !important;
padding-bottom: 2px !important;
}
.kyc-progress-bar .step.active {
background: #2f80ed !important;
color: #2f80ed !important;
}
.kyc-progress-bar span {
position: relative !important;
top: -20px !important;
}
.kyc-form {
padding: 1.5rem;
}
.form-section h3 {
display: flex;
align-items: center;
gap: 0.5rem;
color: #1a1a1a;
margin: 0 0 1rem 0;
font-size: 1.125rem;
font-weight: 600;
}
.section-icon {
font-size: 1.25rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
label {
display: block;
margin-bottom: 0.5rem;
color: #666;
font-size: 14px;
}
input,
select {
width: 100%;
padding: 0.75rem;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 12px;
color: #1a1a1a;
background: #fff;
}
input:focus,
select:focus {
outline: none;
border-color: #2f80ed;
}
.input-with-button {
position: relative;
display: flex;
align-items: center;
}
.input-with-button input {
padding-right: 3rem;
}
.verify-button {
background: none;
border: none;
color: white;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
transition: all 0.2s ease;
}
.verify-button:hover:not(:disabled) {
opacity: 0.8;
}
.verify-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.add-bank-btn {
background: #2f80ed;
border: none;
color: white;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
padding: 8px;
margin-bottom: 15px;
transition: all 0.2s ease;
}
.add-bank-btn:hover:not(:disabled) {
background: #1366d6;
}
.add-bank-btn:disabled {
background: #cccccc;
color: #666666;
cursor: not-allowed;
opacity: 0.7;
}
.detail-text {
margin: 0;
color: #1a1a1a;
font-size: 0.875rem;
font-weight: 600;
}
.address-text {
line-height: 1.6;
margin-top: 2px;
}
.secure-note {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
padding: 6px;
background: #fff5e9;
border-radius: 8px;
margin: 1rem 0;
color: #666;
font-size: 12px;
}
.secure-icon {
font-size: 1rem;
}
.submit-button {
width: 100%;
padding: 1rem;
background: #2f80ed;
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.submit-button:hover:not(:disabled) {
background: #1366d6;
}
.submit-button:disabled {
background: #cccccc;
color: #666666;
cursor: not-allowed;
opacity: 0.7;
}
input:disabled {
background: #f0f0f0;
color: #666;
cursor: not-allowed;
}
table {
width: 100%;
border-collapse: collapse;
border-radius: 8px;
overflow: hidden;
color: #666;
}
.bank-list table {
font-size: 11px;
}
.bank-list table th {
text-align: left;
}
.error-message {
color: #c62828;
font-size:11px;
margin-bottom: 10px;
display: none;
}
.field-error {
color: #dc3545;
font-size: 12px;
margin-top: 4px;
display: block;
}
.valid {
border-color: #28a745 !important;
}
.invalid {
border-color: #dc3545 !important;
}
.business-details label {
margin-bottom: 2px;
}
.delete-btn {
all: unset;
cursor: pointer;
}
`;
document.head.appendChild(style);
};
const createFormHTML = () => {
return `
<div id="app">
<div class="kyc-container">
<div class="kyc-form-wrapper">
<!-- Add loading overlay -->
<div id="loader" class="loading-overlay">
<div class="loader-container">
<div class="loader"></div>
<p style="color: #040404;">Loading...</p>
</div>
</div>
<div class="kyc-header">
<div class="header-icon">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiByeD0iMTYiIGZpbGw9IiNGRkVGREEiLz4KPG1hc2sgaWQ9Im1hc2swXzExMDJfODgwMCIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjgiIHk9IjgiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+CjxwYXRoIGQ9Ik04IDhIMjRWMjRIOFY4WiIgZmlsbD0id2hpdGUiLz4KPC9tYXNrPgo8ZyBtYXNrPSJ1cmwoI21hc2swXzExMDJfODgwMCkiPgo8bWFzayBpZD0ibWFzazFfMTEwMl84ODAwIiBzdHlsZT0ibWFzay10eXBlOmx1bWluYW5jZSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iOCIgeT0iOCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ij4KPHBhdGggZD0iTTggOEgyNFYyNEg4VjhaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazFfMTEwMl84ODAwKSI+CjxwYXRoIGQ9Ik0yMi4wMzM0IDIwLjQ3MTNMMTYuNzA3NSAxMC41ODA2QzE2LjMzIDkuODc5MzcgMTUuMzI0MyA5Ljg3OTM3IDE0Ljk0NjUgMTAuNTgwNkw5LjYyMDkxIDIwLjQ3MTNDOS40NDE3IDIwLjgwNDEgOS40NDg5MSAyMS4xMzI5IDkuNjQyNTIgMjEuNDU3NUM5LjgzNjE0IDIxLjc4MjIgMTAuMTIyIDIxLjk0NDcgMTAuNSAyMS45NDUzSDIxLjE1MjhDMjEuNTMxMSAyMS45NDU0IDIxLjgxNzQgMjEuNzgzIDIyLjAxMTQgMjEuNDU4MkMyMi4yMDU0IDIxLjEzMzQgMjIuMjEyOCAyMC44MDQ0IDIyLjAzMzQgMjAuNDcxM1pNMTUuODI3MSAyMC40MTQxQzE1LjY1NDYgMjAuNDE0MSAxNS41MDcyIDIwLjM1MyAxNS4zODUyIDIwLjIzMUMxNS4yNjMyIDIwLjEwOSAxNS4yMDIyIDE5Ljk2MTcgMTUuMjAyMiAxOS43ODkxQzE1LjIwMjIgMTkuNjE2NSAxNS4yNjMyIDE5LjQ2OTEgMTUuMzg1MiAxOS4zNDcxQzE1LjUwNzIgMTkuMjI1MSAxNS42NTQ2IDE5LjE2NDEgMTUuODI3MSAxOS4xNjQxQzE1Ljk5OTggMTkuMTY0MSAxNi4xNDcxIDE5LjIyNTEgMTYuMjY5MSAxOS4zNDcxQzE2LjM5MTEgMTkuNDY5MSAxNi40NTIyIDE5LjYxNjUgMTYuNDUyMiAxOS43ODkxQzE2LjQ1MjIgMTkuOTYxNyAxNi4zOTExIDIwLjEwOSAxNi4yNjkxIDIwLjIzMUMxNi4xNDcxIDIwLjM1MyAxNS45OTk4IDIwLjQxNDEgMTUuODI3MSAyMC40MTQxWk0xNi41MDU5IDE0LjEyODFMMTYuMzI2NiAxNy45NDA2QzE2LjMyNjYgMTguMDc4NyAxNi4yNzc3IDE4LjE5NjYgMTYuMTgwMSAxOC4yOTQyQzE2LjA4MjQgMTguMzkxOCAxNS45NjQ2IDE4LjQ0MDYgMTUuODI2NiAxOC40NDA2QzE1LjY4ODQgMTguNDQwNiAxNS41NzA2IDE4LjM5MTggMTUuNDczIDE4LjI5NDJDMTUuMzc1NCAxOC4xOTY2IDE1LjMyNjYgMTguMDc4NyAxNS4zMjY2IDE3Ljk0MDZMMTUuMTQ3MiAxNC4xMjk3QzE1LjEzODcgMTMuOTM3OCAxNS4yMDA1IDEzLjc3MiAxNS4zMzI2IDEzLjYzMjZDMTUuNDY0OCAxMy40OTMxIDE1LjYyNjkgMTMuNDIyNSAxNS44MTkgMTMuNDIwNkgxNS44MjU2QzE2LjAxOTEgMTMuNDIwNSAxNi4xODI4IDEzLjQ5MDQgMTYuMzE2NyAxMy42MzAyQzE2LjQ1MDYgMTMuNzcgMTYuNTEzMiAxMy45MzY2IDE2LjUwNDcgMTQuMTNMMTYuNTA1OSAxNC4xMjgxWiIgZmlsbD0iI0ZGQjgwMCIvPgo8L2c+CjwvZz4KPC9zdmc+Cgo="
alt="location icon"
class="kyc-image"
/>
</div>
<div>
<h2>KYC Verification</h2>
<p>Please complete your KYC verification</p>
</div>
<button class="close-button">×</button>
</div>
<div class="kyc-progress-bar">
<div class="step active" id="step1">
<span>Step 1/2 - KYC Verification</span>
</div>
<div class="step" id="step2">
<span>Step 2/2 - Bank Details</span>
</div>
</div>
<form id="kycForm" class="kyc-form">
<div class="form-section step-content" id="step1-content">
<div>
<h3>
<span class="section-icon">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzExMDJfOTEyMikiPgo8cGF0aCBkPSJNMTIuMDAwMSAxMi4zMzMzTDEyLjE4NTEgMTEuNDA5MkMxMi4yODI5IDEwLjkyMDQgMTIuNTQ0MiAxMC40Njk5IDEyLjYzNzQgOS45ODAyQzEyLjY1NjcgOS44Nzg2NyAxMi42NjY4IDkuNzczODcgMTIuNjY2OCA5LjY2NjY3QzEyLjY2NjggOC43NDYyIDExLjkyMDYgOCAxMS4wMDAxIDhDMTAuMDc5NyA4IDkuMzMzNDUgOC43NDYyIDkuMzMzNDUgOS42NjY2N0M5LjMzMzQ1IDkuNzczODcgOS4zNDM1OSA5Ljg3ODY3IDkuMzYyOTIgOS45ODAyQzkuNDU2MTIgMTAuNDY5OSA5LjcxNzQ1IDEwLjkyMDQgOS44MTUyNSAxMS40MDkyTDEwLjAwMDEgMTIuMzMzM00xMi4wMDAxIDEyLjMzMzNIMTAuMDAwMU0xMi4wMDAxIDEyLjMzMzNMMTMuNjY0MyAxMi43NzczQzE0LjI0OTkgMTIuOTA3NCAxNC42NjY2IDEzLjQyNjggMTQuNjY2NiAxNC4wMjY3QzE0LjY2NjYgMTQuMzgwMSAxNC4zODAxIDE0LjY2NjcgMTQuMDI2NyAxNC42NjY3SDEzLjY2NDNIOC4zMzMyNUg3Ljk3MzE5QzcuNjE5NzkgMTQuNjY2NyA3LjMzMzI1IDE0LjM4MDEgNy4zMzMyNSAxNC4wMjY3QzcuMzMzMjUgMTMuNDI2OCA3Ljc0OTkyIDEyLjkwNzQgOC4zMzU1MiAxMi43NzczTDEwLjAwMDEgMTIuMzMzMyIgc3Ryb2tlPSIjMjM4RUY4Ii8+CjxwYXRoIGQ9Ik0xMS4zMzMzIDUuOTk5NjdWNS4zMzMwMUMxMS4zMzMzIDMuNDQ3MzkgMTEuMzMzMyAyLjUwNDU4IDEwLjc0NzUgMS45MTg3OUMxMC4xNjE3IDEuMzMzMDEgOS4yMTg4NSAxLjMzMzAxIDcuMzMzMjUgMS4zMzMwMUg1LjMzMzI1QzMuNDQ3NjMgMS4zMzMwMSAyLjUwNDgzIDEuMzMzMDEgMS45MTkwNCAxLjkxODc5QzEuMzMzMjUgMi41MDQ1OCAxLjMzMzI1IDMuNDQ3MzkgMS4zMzMyNSA1LjMzMzAxVjEwLjY2NjNDMS4zMzMyNSAxMi41NTE5IDEuMzMzMjUgMTMuNDk0NyAxLjkxOTA0IDE0LjA4MDVDMi41MDQ4MyAxNC42NjYzIDMuNDQ3NjMgMTQuNjY2MyA1LjMzMzI1IDE0LjY2NjMiIHN0cm9rZT0iIzIzOEVGOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik00LjY2Njc1IDUuNzc3NzhDNC42NjY3NSA1Ljc3Nzc4IDUuMDgzNDEgNS43Nzc3OCA1LjUwMDA4IDYuNjY2NjdDNS41MDAwOCA2LjY2NjY3IDYuODIzNjEgNC40NDQ0NSA4LjAwMDA4IDQiIHN0cm9rZT0iIzIzOEVGOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik00IDkuMzMzMDFINi42NjY2NyIgc3Ryb2tlPSIjMjM4RUY4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTQgMTEuMzMzSDYuNjY2NjciIHN0cm9rZT0iIzIzOEVGOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMTEwMl85MTIyIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="
alt="location icon"
class="kyc-image"
/>
</span>
KYC Type
</h3>
<div class="form-group">
<label>Select KYC Type</label>
<select name="kycType" id="kycType" required>
<option value="">Choose verification type</option>
<option value="GST" selected>GST Registered</option>
<option value="AADHAR">Aadhar Card</option>
</select>
<div class="error-message" id="kycType-error"></div>
</div>
<div id="loader" style="display: none;text-align: center;">
<div class="loader-container">
<div class="loader"></div>
<p style="color: #040404;">Loading...</p>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>KYC Number</label>
<div class="input-with-button">
<input type="text" name="kycNumber" id="kycNumber" placeholder="Enter GST Number" maxlength="15" style="padding-right: 30px;" />
<button type="button" class="verify-button" id="initiateOtp" style="position: absolute; right: 5px; top: 50%; transform: translateY(-50%); display: none;">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIyMyIgaGVpZ2h0PSIyMyIgcng9IjMuNSIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIyMyIgaGVpZ2h0PSIyMyIgcng9IjMuNSIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIyMyIgaGVpZ2h0PSIyMyIgcng9IjMuNSIgc3Ryb2tlPSIjMjM4RUY4Ii8+CjxwYXRoIGQ9Ik0xNi42NjY4IDEySDcuMzMzNSIgc3Ryb2tlPSIjMjM4RUY4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEzLjc1MDEgMTQuOTE2M0MxMy43NTAxIDE0LjkxNjMgMTYuNjY2NyAxMi43NjgzIDE2LjY2NjcgMTEuOTk5N0MxNi42NjY3IDExLjIzMTEgMTMuNzUgOS4wODMwMSAxMy43NSA5LjA4MzAxIiBzdHJva2U9IiMyMzhFRjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K"
alt="location icon"
class="kyc-image"
/>
</button>
</div>
<div class="error-message" id="kycNumber-error"></div>
<div class="resend-otp" id="resend-otp"></div>
</div>
<div class="form-group">
<label>OTP</label>
<div class="input-with-button">
<input type="text" name="otp" value="" placeholder="Enter OTP" maxlength="6" id="otp" disabled />
<button type="button" class="verify-button" id="verifyOtp" style="position: absolute; right: 5px; top: 50%; transform: translateY(-50%); display: none;">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIyMyIgaGVpZ2h0PSIyMyIgcng9IjMuNSIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIyMyIgaGVpZ2h0PSIyMyIgcng9IjMuNSIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIyMyIgaGVpZ2h0PSIyMyIgcng9IjMuNSIgc3Ryb2tlPSIjMjM4RUY4Ii8+CjxwYXRoIGQ9Ik0xNi42NjY4IDEySDcuMzMzNSIgc3Ryb2tlPSIjMjM4RUY4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEzLjc1MDEgMTQuOTE2M0MxMy43NTAxIDE0LjkxNjMgMTYuNjY2NyAxMi43NjgzIDE2LjY2NjcgMTEuOTk5N0MxNi42NjY3IDExLjIzMTEgMTMuNzUgOS4wODMwMSAxMy43NSA5LjA4MzAxIiBzdHJva2U9IiMyMzhFRjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K"
alt="location icon"
class="kyc-image"
/>
</button>
</div>
<div class="error-message" id="otp-error"></div>
</div>
</div>
</div>
<div class="business-details" style="display: block;"></div>
<div class="address-details" style="display: block;"></div>
<div class="secure-note">
<span class="secure-icon">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNSAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuMzMzMjUgNi4xMjQ2N1Y1LjI0OTY3QzYuMzMzMjUgNC42MDUzNCA2Ljg1NTU3IDQuMDgzMDEgNy40OTk5MiA0LjA4MzAxQzguMTQ0MjcgNC4wODMwMSA4LjY2NjU5IDQuNjA1MzQgOC42NjY1OSA1LjI0OTY3VjYuMTI0NjdNNy4wNjI0MiA5LjMzMzAxSDcuOTM3NDJDOC42MjE1NSA5LjMzMzAxIDguOTYzNjIgOS4zMzMwMSA5LjE5OTIzIDkuMTUyMTdDOS4yNTk4OSA5LjEwNTYyIDkuMzE0MiA5LjA1MTMyIDkuMzYwNzUgOC45OTA2NUM5LjU0MTU5IDguNzU1MDQgOS41NDE1OSA4LjQxMjk3IDkuNTQxNTkgNy43Mjg4NEM5LjU0MTU5IDcuMDQ0NzEgOS41NDE1OSA2LjcwMjY0IDkuMzYwNzUgNi40NjcwM0M5LjMxNDIgNi40MDYzNyA5LjI1OTg5IDYuMzUyMDYgOS4xOTkyMyA2LjMwNTUxQzguOTYzNjIgNi4xMjQ2NyA4LjYyMTU1IDYuMTI0NjcgNy45Mzc0MiA2LjEyNDY3SDcuMDYyNDJDNi4zNzgyOSA2LjEyNDY3IDYuMDM2MjMgNi4xMjQ2NyA1LjgwMDU5IDYuMzA1NTFDNS43Mzk5MiA2LjM1MjA2IDUuNjg1NjIgNi40MDYzNyA1LjYzOTA3IDYuNDY3MDNDNS40NTgyNSA2LjcwMjY0IDUuNDU4MjUgNy4wNDQ3MSA1LjQ1ODI1IDcuNzI4ODRDNS40NTgyNSA4LjQxMjk3IDUuNDU4MjUgOC43NTUwNCA1LjYzOTA3IDguOTkwNjVDNS42ODU2MiA5LjA1MTMyIDUuNzM5OTIgOS4xMDU2MiA1LjgwMDU5IDkuMTUyMTdDNi4wMzYyMyA5LjMzMzAxIDYuMzc4MjkgOS4zMzMwMSA3LjA2MjQyIDkuMzMzMDFaIiBzdHJva2U9IiM1ODVCNUYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEyLjc1IDYuNTIzOTJWNC44MzA0OUMxMi43NSAzLjg3MzgzIDEyLjc1IDMuMzk1NDkgMTIuNTE0MyAzLjA4MzQxQzEyLjI3ODUgMi43NzEzMyAxMS43NDU2IDIuNjE5ODIgMTAuNjc5NiAyLjMxNjhDOS45NTEyOCAyLjEwOTc4IDkuMzA5MjcgMS44NjAzNiA4Ljc5NjM0IDEuNjMyNjZDOC4wOTY5OCAxLjMyMjIyIDcuNzQ3MzMgMS4xNjY5OSA3LjUgMS4xNjY5OUM3LjI1MjY3IDEuMTY2OTkgNi45MDMwMiAxLjM