UNPKG

nba-kyc-lib

Version:

A simple input form Web Component usable in React and Angular

1,349 lines (1,208 loc) 72.1 kB
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