1import { decorateIcons } from '../../scripts/aem.js'; 2import { 3 getMaxLengthByCountryCode, 4 getCountryIso, 5 getConfigValue, 6 CACHE_KEYS, 7} from '../../scripts/configs.js'; 8import { openModal, closeModal, createModalFromContent } from '../../scripts/utils.js'; 9import { 10 loadFragment, fetchPlaceholdersForLocale, isLoggedInUser, 11} from '../../scripts/scripts.js'; 12import { 13 PHONE_PATTERN, 14 FULLNAME_PATTERN, 15 EMAIL_PATTERN, 16 showErrorMessage, 17 validateForm, 18} from '../../scripts/forms.js'; 19 20import { 21 loginMobile, checkUserExists, userRegister, verifyOtpApiCall, getAuraCustomerData, 22} from '../../scripts/aura/api.js'; 23import { 24 nextStage, 25 prevStage, 26 resetOtp, 27 phonenumberValidation, 28 submitOnEnter, 29 auraModalRedirection, 30 debouncedFormValidation, 31 addFormValidation, 32 phonenumberBlurValidation, 33} from '../../scripts/aura-utils/aura-utils.js'; 34 35import { dataLayerAuraBlock } from '../../scripts/analytics/google-data-layer.js'; 36 37const placeholders = await fetchPlaceholdersForLocale(); 38let auraProfileExistsByPhone = false; 39let auraProfileExistsByEmail = false; 40let isSubmitting = false; 41// const requiredFullData = (await getConfigValue('required-full-customerdata')) === 'true'; 42 43export default async function decorate(block) { 44 const containerClasses = ['aura-title', 'aura-mobile-number', 'aura-otp', 'aura-user-details']; 45 46 Array.from(block.children).forEach((child, index) => { 47 child.classList.add(containerClasses[index]); 48 }); 49 50 let loginFlowText = placeholders.auraLoginButton || 'Login'; 51 52 const countrycode = await getConfigValue('country-code'); 53 const countryPrefix = `+${await getCountryIso(countrycode)}`; 54 const maxLength = await getMaxLengthByCountryCode(countrycode); 55 56 if (isLoggedInUser()) { 57 loginFlowText = placeholders.linkYourAura || 'Link your Aura'; 58 } 59 60 const otpLength = 6; 61 62 const mobileForm = document.createElement('div'); 63 mobileForm.innerHTML = ` <form class="aura-contact-form" novalidate=""> 64 <div> 65 <div class="input-field-wrapper"> 66 <div class="input-wrapper"> 67 <label for="login-mobile">${countryPrefix}</label> 68 <input type="tel" id="login-mobile" name="login-mobile" aria-label="${placeholders.mobileNumberInputText || 'Mobile Number'}" placeholder="${placeholders.mobileNumberInputText || 'Mobile Number'}" required maxlength="${maxLength}" minlength="${maxLength}" autocomplete="off"> 69 </div> 70 </div> 71 <div class="aura-buttons"> 72 <button class="secondary-btn" type="submit" data-dismiss="modal"><span>${placeholders.sendOtp || 'Send OTP'}</span></button> 73 <p>${placeholders.alreadyHaveAccount || 'Already have account?'} <a>${loginFlowText}</a></p> 74 </div> 75 </div> 76 </form>`; 77 78 mobileForm.classList.add('aura-contact-container'); 79 block.querySelector('.aura-mobile-number').append(mobileForm); 80 81 const otpForm = document.createElement('div'); 82 otpForm.innerHTML = `<form class="aura-otp-form"> 83 <div class="input-field-wrapper"> 84 <div class="input-wrapper"> 85 <input type="number" id="otp-input" name="otp-input" 86 placeholder="${placeholders.otpPlaceholder || 'One time pin'}" required maxlength="${placeholders.otpLength || otpLength}" oninput="this.value = this.value.slice(0, ${placeholders.otpLength || otpLength});" onkeydown="return event.key !== 'e' && event.key !== 'E';" autocomplete="off"> 87 </div> 88 </div> 89 <div class="otp-success-message"> 90 </div> 91 <div class="aura-buttons"> 92 <button class="secondary-btn" type="submit"><span>${placeholders.verifyOtp || 'Verify'}</span></button> 93 </div> 94 </form>`; 95 96 const auraOtpContainer = block.querySelector('.aura-otp'); 97 otpForm.classList.add('aura-otp-container'); 98 auraOtpContainer.append(otpForm); 99 100 const otpInfoDiv = document.createElement('div'); 101 otpInfoDiv.classList.add('otp-info-text'); 102 otpInfoDiv.innerHTML = `<div> 103 <p>${placeholders.otpInfoText || "Didn't receive your one time pin?"} <a>${placeholders.resendOtp || 'Resend'}</a></p> 104 </div>`; 105 106 auraOtpContainer.appendChild(otpInfoDiv); 107 108 const termsAndConditionText = block.querySelector('.aura-user-
108details > div'); 109 const userDetailsForm = document.createElement('div'); 110 userDetailsForm.innerHTML = `<form class="user-details-form" novalidate=""> 111 <div> 112 <div class="input-field-wrapper"> 113 <div class="input-wrapper mobile-disabled"> 114 <label for="mobile-number">${countryPrefix}</label> 115 <input type="number" id="mobile-number" name="mobile-number" aria-label="${placeholders.mobileNumberInputText || 'Mobile Number'}" placeholder="" pattern="${PHONE_PATTERN}" required autocomplete="off" disabled> 116 </div> 117 </div> 118 <div class="input-field-wrapper"> 119 <div class="input-wrapper"> 120 <input type="text" id="fullname" class="fullname" name="fullname" pattern="${FULLNAME_PATTERN}" placeholder="${placeholders.fullName || 'Full Name'}" aria-label="${placeholders.fullName || 'Full Name'}" required autocomplete="off" /> 121 </div> 122 </div> 123 <div class="input-field-wrapper"> 124 <div class="input-wrapper"> 125 <input type="email" id="email-address" name="email-address" aria-label="${placeholders.email || 'Email'}" placeholder="${placeholders.emailAddress || 'Email Address'}" pattern="${EMAIL_PATTERN}" required autocomplete="off" > 126 </div> 127 </div> 128 </div> 129 <div class="aura-buttons"> 130 <div class="aura-terms-condition"> 131 </div> 132 <button class="secondary-btn" type="submit"><span>${placeholders.auraCompleteRegistration || 'Complete Registration'}</span></button> 133 </div> 134 </form>`; 135 136 if (termsAndConditionText) { 137 userDetailsForm.querySelector('.aura-terms-condition')?.append(termsAndConditionText); 138 } 139 140 const congratulationsFragment = await loadFragment(`/${document.documentElement.lang}/fragments/aura/congratulations-aura`); 141 block.children[block.children.length - 1].replaceWith(congratulationsFragment.querySelector('.block')); 142 block.querySelector('.aura-user-details')?.append(userDetailsForm); 143 block.querySelector('.aura-mobile-number').classList.add('show'); 144} 145 146export async function createJoinAura(reDecorate, eventAction) { 147 const MODAL_DELAY_MILLIS = 300; 148 loadFragment(`/${document.documentElement.lang}/fragments/aura/join-aura`).then(async (fragment) => { 149 dataLayerAuraBlock(eventAction, 'initiated'); 150 const titleDiv = fragment.querySelector('.aura-title'); 151 const modalContent = fragment.querySelector('.aura-join'); 152 modalContent.firstElementChild.remove(); 153 await closeModal('join-aura-dialog'); 154 // Add generic overlay to eliminate flickr effect in background. 155 document.querySelector('.generic-overlay-background')?.classList.add('show'); 156 if (!document.getElementById('join-aura-dialog')) { 157 await createModalFromContent('join-aura-dialog', titleDiv.textContent, modalContent.outerHTML, ['aura-modal', 'join-aura', 'aura'], 'arrow-left-black', false, 'icon-aura-close'); 158 159 const joinAura = document.querySelector('#join-aura-dialog'); 160 161 const arrowIcon = joinAura.querySelector('.join-aura .icon-title-left'); 162 arrowIcon.classList.add('icon-hide'); 163 164 const mobileForm = joinAura.querySelector('.aura-contact-form'); 165 const otpForm = joinAura.querySelector('.aura-otp-form'); 166 const userDetailsForm = joinAura.querySelector('.user-details-form'); 167 const congratsStage = joinAura.querySelector('.aura-congratulations'); 168 const congratsForm = congratsStage.querySelector('.aura-congratulations .aura-buttons'); 169 const linkAura = mobileForm.querySelector('a'); 170 const auraSocialLink = congratsStage.querySelector('.aura-social'); 171 auraSocialLink.addEventListener('click', (event) => { 172 const link = event.target.closest('a'); 173 if (link) { 174 if (link.href?.includes('apple')) { 175 dataLayerAuraBlock('click appstore'); 176 } else { 177 dataLayerAuraBlock('click playstore'); 178 } 179 } 180 }); 181 182 const otpContainer = joinAura.querySelector('.aura-otp'); 183 const defaultOtpInfoText = otpContainer.querySelector('div').textContent; 184 const defaultOtpResendText = otpContainer.querySelector('.aura-otp .otp-info-text p').innerHTML; 185 186 const successMessageDiv = otpContainer.querySelector('.otp-success-message'); 187 successMessageDiv.innerHTML = `<p>${placeholders.resendOtpSendSuccessfully || 'Resend OTP Send Successfully'}</p>`; 188 successMessageDiv.classList.add('hide-message'); 189 190 const countrycode = await getConfigValue('country-code'); 191 const countryPrefix = `${await getCountryIso(countrycode)}`; 192 const disableInlineValidation = await getConfigValue('disable-inline-validation'); 193 194 const forms = [userDetailsForm, otpForm, mobileForm]; 195 // TODO - Use Block-Config(Developer authorable) instead of config(Author authorable). 196 if (disableInlineValidation === 'true') { 197 forms.slice(0, 2).forEach((element) => { 198 debouncedFormValidation(element); 199 decorateIcons(element); 200 }); 201 202 await phonenumberBlurValidation(mobileForm); 203 decorateIcons(mobileForm); 204 } else {
205 forms.forEach((element) => { 206 addFormValidation(element); 207 decorateIcons(element); 208 }); 209 await phonenumberValidation(mobileForm); 210 } 211 212 mobileForm.addEventListener('submit', async (event) => { 213 event.stopPropagation(); 214 event.preventDefault(); 215 216 if (isSubmitting) { 217 return; 218 } 219 220 isSubmitting = true; 221 222 try { 223 const mobile = mobileForm.querySelector('#login-mobile').value; 224 if (mobile === '') { 225 isSubmitting = false; 226 const inputFieldWrapper = mobileForm.querySelector('.input-field-wrapper'); 227 const errorMessage = placeholders.noMobileMessage || `Please enter +${countryPrefix} Mobile Number`; 228 showErrorMessage(inputFieldWrapper, errorMessage); 229 return; 230 } 231 232 // Check if user exists with the provided mobile number and country prefix. 233 const mobileWithPrefix = `${countryPrefix}${mobile}`; 234 auraProfileExistsByPhone = await checkUserExists(mobileWithPrefix, 'mobile'); 235 if (!auraProfileExistsByPhone) { 236 const status = await loginMobile(mobile, countryPrefix); 237 if (status === true) { 238 dataLayerAuraBlock(eventAction, 'mobile'); 239 userDetailsForm.querySelector('#mobile-number').value = mobile; 240 resetOtp(otpContainer, defaultOtpResendText, mobileForm); 241 otpContainer.querySelector('.aura-otp > div').innerHTML = `${defaultOtpInfoText} <span class="mob-number">+${countryPrefix}${mobile}</span>`; 242 nextStage(joinAura); 243 arrowIcon.classList.remove('icon-hide'); 244 joinAura.querySelector('.otp-info-text .disabled')?.classList.remove('disabled'); 245 otpContainer.querySelector('.error-message-container').classList.add('hide'); 246 otpContainer.querySelector('.error-message-container .error-message').textContent = ''; 247 } else { 248 const inputFieldWrapper = mobileForm.querySelector('.input-field-wrapper'); 249 const errorMessage = status.parameters.length > 0 ? status?.parameters[0] : 'Maximum number of attempts reached. Please try after sometime.'; 250 showErrorMessage(inputFieldWrapper, errorMessage); 251 } 252 } else { 253 const inputFieldWrapper = mobileForm.querySelector('.input-field-wrapper'); 254 const errorMessage = placeholders.mobileRegisteredErrorMessage || 'This phone number is already registered. Please sign-in or use a different phone number'; 255 showErrorMessage(inputFieldWrapper, errorMessage); 256 } 257 } catch (error) { 258 console.error(error); 259 } finally { 260 isSubmitting = false; 261 } 262 }); 263 264 otpForm.addEventListener('submit', async (event) => { 265 event.preventDefault(); 266 267 if (isSubmitting) { 268 return; 269 } 270 271 isSubmitting = true; 272 273 try { 274 const mobile = mobileForm.querySelector('#login-mobile').value; 275 const otp = otpForm.querySelector('#otp-input').value; 276 277 if (otp === '') { 278 isSubmitting = false; 279 const inputFieldWrapper = otpForm.querySelector('.input-field-wrapper'); 280 const errorMessage = placeholders.noOtpMessage || 'Please enter One time pin'; 281 showErrorMessage(inputFieldWrapper, errorMessage); 282 return; 283 } 284 285 const status = await verifyOtpApiCall(mobile, countryPrefix, otp); 286 287 if (status === true) { 288 dataLayerAuraBlock(eventAction, 'verify otp'); 289 nextStage(joinAura); 290 arrowIcon.classList.add('icon-hide'); 291 } else { 292 const inputFieldWrapper = otpForm.querySelector('.input-field-wrapper'); 293 const errorMessage = placeholders.incorrectOtpErrorMessage || 'Incorrect OTP'; 294 showErrorMessage(inputFieldWrapper, errorMessage); 295 } 296 } catch (error) { 297 console.error(error); 298 } finally { 299 isSubmitting = false;
300 } 301 }); 302 303 userDetailsForm.addEventListener('submit', async (event) => { 304 event.preventDefault(); 305 if (!validateForm(userDetailsForm) || isSubmitting) { 306 return; 307 } 308 isSubmitting = true; 309 310 try { 311 const email = userDetailsForm.querySelector('#email-address').value; 312 const mobileNumber = mobileForm.querySelector('#login-mobile').value; 313 314 auraProfileExistsByEmail = await checkUserExists(email, 'email'); 315 const mobile = `+${countryPrefix}${mobileNumber}`; 316 const fullname = userDetailsForm.querySelector('#fullname').value; 317 318 dataLayerAuraBlock(eventAction, 'name'); // Call dataLayerAuraBlock for successful name entry. 319 dataLayerAuraBlock(eventAction, 'email'); // Call dataLayerAuraBlock for successful email. 320 321 const nameParts = fullname.trim().split(' '); 322 const firstname = nameParts.slice(0, -1).join(' '); 323 const lastname = nameParts.length > 1 ? nameParts[nameParts.length - 1] : ''; 324 const isVerified = 'Y'; 325 if (!auraProfileExistsByPhone && !auraProfileExistsByEmail) { 326 const status = await userRegister(firstname, lastname, email, mobile, isVerified, true); 327 const response = await getAuraCustomerData(); 328 329 if (['2', 2].includes(response?.apc_link)) { 330 congratsStage.querySelector('.aura-quick-enrolled').remove(); 331 congratsStage.querySelector('.aura-social').remove(); 332 } else { 333 congratsStage.querySelector('.aura-full-enrolled')?.remove(); 334 } 335 if (status?.apc_identifier_number !== null || status === true) { 336 const congratulationsTitle = joinAura.querySelector('.congratulations-title'); 337 joinAura.querySelector('.modal-header > h4').textContent = congratulationsTitle.querySelector('*').textContent; 338 joinAura.querySelector('.congratulations-title').remove(); 339 const refreshAuraEvent = new CustomEvent('EVENT_AURA_JOINED', { 340 detail: { message: 'Aura was Joined!' }, 341 }); 342 window.dispatchEvent(refreshAuraEvent); 343 window.dispatchEvent(new CustomEvent('storage', { detail: CACHE_KEYS.AURA_INFO })); 344 // Call dataLayerAuraBlock for success page. 345 dataLayerAuraBlock(eventAction, 'success'); 346 nextStage(joinAura); 347 } else { 348 const inputFieldWrapper = userDetailsForm.querySelector('.input-field-wrapper:last-child'); 349 const auraRegisterError = sessionStorage.getItem('_aura_register_error'); 350 const errorMessage = auraRegisterError || placeholders.incorrectEmailAddress || 'Please Enter Valid Email ID'; 351 showErrorMessage(inputFieldWrapper, errorMessage); 352 // Call dataLayerAuraBlock for fail page 353 dataLayerAuraBlock(eventAction, 'fail'); 354 } 355 } else { 356 const inputFieldWrapper = userDetailsForm.querySelector('.input-field-wrapper:last-child'); 357 const errorMessage = placeholders.emailRegisteredErrorMessage || 'This email is already registered. Please sign-in or use a different email'; 358 showErrorMessage(inputFieldWrapper, errorMessage); 359 // Call dataLayerAuraBlock for fail page 360 dataLayerAuraBlock(eventAction, 'fail'); 361 } 362 } catch (error) { 363 console.error(error); 364 } finally { 365 isSubmitting = false; 366 } 367 }); 368 369 auraModalRedirection(congratsForm, 'join-aura-dialog', 'join-aura-redirect', reDecorate); 370 371 arrowIcon.addEventListener('click', async (event) => { 372 event.preventDefault(); 373 prevStage(joinAura); 374 arrowIcon.classList.add('icon-hide'); 375 }); 376 377 linkAura.addEventListener('click', async (event) => { 378 // eslint-disable-next-line import/no-cycle 379 const { createLinkAura } = await import('../link-your-aura/link-your-aura.js'); 380 event.preventDefault(); 381 closeModal('join-aura-dialog', true, false); 382 setTimeout(() => { 383 createLinkAura(reDecorate, `SignIn_${event.target.textContent.trim()}`, false); 384 }, MODAL_DELAY_MILLIS); // Adjust the delay (in milliseconds) as needed 385 }); 386 387 submitOnEnter(forms); 388 } 389 390 // close aura card but not remove it 391 closeModal('aura-menu-dialog', false, false); 392 setTimeout(() => { 393 openModal('join-aura-dialog'); 394 }, MODAL_DELAY_MILLIS); // Adjust the delay (in milliseconds) as needed 395 }); 396}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.