|
124 | 124 | id="newsletter-error" |
125 | 125 | class="tw-text-left tw-ml-2 tw-text-red-400 tw-text-sm tw-hidden" |
126 | 126 | ></div> |
| 127 | + |
127 | 128 | <!-- reCAPTCHA v2 Container - Hidden by default --> |
128 | 129 | <div |
129 | 130 | id="newsletter-recaptcha-container" |
|
144 | 145 | </div> |
145 | 146 | </div> |
146 | 147 | </div> |
| 148 | + |
147 | 149 | <div |
148 | 150 | class="tw-px-6 tw-py-2 tw-rounded-full tw-bg-[#7782FF] tw-text-white tw-text-sm tw-font-medium hover:tw-bg-[#6672fa]" |
149 | 151 | > |
|
179 | 181 | // Configuration |
180 | 182 | const NEWSLETTER_CONFIG = window.NEWSLETTER_CONFIG || { |
181 | 183 | RECAPTCHA_SITE_KEY: "6LdvUdMrAAAAAJksqV0YEwNBEBGL2SB90Gebun5n", |
182 | | - NEWSLETTER_ENDPOINT: |
183 | | - "https://5cciazu22tev222enhrbx6w3y40hscvu.lambda-url.us-east-2.on.aws", |
| 184 | + NEWSLETTER_ENDPOINT: "https://5cciazu22tev222enhrbx6w3y40hscvu.lambda-url.us-east-2.on.aws", |
184 | 185 | FALLBACK_ENDPOINT: |
185 | 186 | "https://5cciazu22tev222enhrbx6w3y40hscvu.lambda-url.us-east-2.on.aws", |
186 | 187 | }; |
|
284 | 285 | ) { |
285 | 286 | recaptchaToken = token; |
286 | 287 |
|
287 | | - // // Hide loader immediately on success |
288 | | - // const loader = document.getElementById("newsletter-captcha-loader"); |
289 | | - // if (loader) { |
290 | | - // loader.style.display = "none"; |
291 | | - // } |
| 288 | + // Hide loader, show success state briefly |
| 289 | + const loader = document.getElementById("newsletter-captcha-loader"); |
| 290 | + if (loader) { |
| 291 | + loader.style.display = "flex"; |
| 292 | + } |
292 | 293 |
|
293 | 294 | // Auto-submit after reCAPTCHA completion (like Vue component) |
294 | 295 | setTimeout(async () => { |
|
305 | 306 | const recaptchaContainer = document.getElementById( |
306 | 307 | "newsletter-recaptcha" |
307 | 308 | ); |
308 | | - if (!recaptchaContainer) return; |
309 | | - |
310 | | - // Clear any existing widget content first |
311 | | - recaptchaContainer.innerHTML = ""; |
| 309 | + if (!recaptchaContainer || recaptchaWidgetId !== null) return; |
312 | 310 |
|
313 | 311 | try { |
314 | 312 | recaptchaWidgetId = window.grecaptcha.render(recaptchaContainer, { |
|
318 | 316 | theme: "dark", |
319 | 317 | }); |
320 | 318 | } catch (error) { |
321 | | - // If render fails, reset the widget ID |
322 | | - recaptchaWidgetId = null; |
| 319 | + // Silent error handling |
323 | 320 | } |
324 | 321 | } |
325 | 322 |
|
|
330 | 327 | "newsletter-recaptcha-container" |
331 | 328 | ); |
332 | 329 | const recaptchaDiv = document.getElementById("newsletter-recaptcha"); |
333 | | - const loader = document.getElementById("newsletter-captcha-loader"); |
334 | 330 |
|
335 | 331 | if (container) { |
336 | 332 | container.classList.remove("tw-hidden"); |
337 | 333 | container.classList.add("tw-flex"); |
338 | 334 | } |
339 | 335 |
|
340 | | - // Ensure loader is hidden when showing reCAPTCHA |
341 | | - if (loader) { |
342 | | - loader.style.display = "none"; |
343 | | - } |
344 | | - |
345 | | - // Always try to initialize reCAPTCHA, even if widget exists |
| 336 | + // Wait for DOM update, then render reCAPTCHA |
346 | 337 | setTimeout(() => { |
347 | | - if (recaptchaDiv && window.grecaptcha) { |
348 | | - // Destroy existing widget if it exists |
349 | | - if (recaptchaWidgetId !== null) { |
350 | | - try { |
351 | | - window.grecaptcha.reset(recaptchaWidgetId); |
352 | | - } catch (error) { |
353 | | - // If reset fails, clear the container |
354 | | - recaptchaDiv.innerHTML = ""; |
355 | | - } |
356 | | - recaptchaWidgetId = null; |
357 | | - } |
358 | | - // Always create a new widget |
| 338 | + if (recaptchaDiv && window.grecaptcha && !recaptchaWidgetId) { |
359 | 339 | initializeRecaptcha(); |
360 | 340 | } |
361 | 341 | }, 100); |
|
372 | 352 | "Please complete the reCAPTCHA to proceed.", |
373 | 353 | "error" |
374 | 354 | ); |
375 | | - // Show submit button again |
376 | | - const buttonContainer = subscribeBtn.parentElement; |
377 | | - buttonContainer.style.display = "block"; |
| 355 | + // Re-enable submit button |
378 | 356 | subscribeBtn.disabled = false; |
379 | 357 | subscribeBtn.textContent = "Subscribe"; |
380 | 358 | return; |
381 | 359 | } |
382 | 360 |
|
383 | | - // Hide loader during processing |
384 | | - const loader = document.getElementById("newsletter-captcha-loader"); |
385 | | - if (loader) { |
386 | | - loader.style.display = "none"; |
387 | | - } |
388 | | - |
389 | 361 | try { |
390 | 362 | isSubmitting = true; |
391 | 363 |
|
|
401 | 373 | "Captcha validation failed. Please try again.", |
402 | 374 | "error" |
403 | 375 | ); |
404 | | - // Show submit button again |
405 | | - const buttonContainer = subscribeBtn.parentElement; |
406 | | - buttonContainer.style.display = "block"; |
| 376 | + // Re-enable submit button |
407 | 377 | subscribeBtn.disabled = false; |
408 | 378 | subscribeBtn.textContent = "Subscribe"; |
409 | 379 | return; |
|
432 | 402 | container.classList.remove("tw-flex"); |
433 | 403 | } |
434 | 404 |
|
435 | | - // Completely destroy and recreate reCAPTCHA widget |
436 | | - if (window.grecaptcha && recaptchaWidgetId !== null) { |
437 | | - try { |
438 | | - window.grecaptcha.reset(recaptchaWidgetId); |
439 | | - } catch (error) { |
440 | | - // Silent error handling |
441 | | - } |
| 405 | + if (window.grecaptcha && recaptchaWidgetId) { |
| 406 | + window.grecaptcha.reset(recaptchaWidgetId); |
442 | 407 | } |
443 | | - |
444 | | - // Clear the container and reset widget ID |
445 | | - const recaptchaDiv = document.getElementById( |
446 | | - "newsletter-recaptcha" |
447 | | - ); |
448 | | - if (recaptchaDiv) { |
449 | | - recaptchaDiv.innerHTML = ""; |
450 | | - } |
451 | | - recaptchaWidgetId = null; |
452 | 408 | recaptchaToken = ""; |
453 | 409 |
|
454 | 410 | // Analytics tracking |
|
487 | 443 | container.classList.remove("tw-flex"); |
488 | 444 | } |
489 | 445 |
|
490 | | - // Completely destroy and recreate reCAPTCHA widget on error |
491 | | - if (window.grecaptcha && recaptchaWidgetId !== null) { |
492 | | - try { |
493 | | - window.grecaptcha.reset(recaptchaWidgetId); |
494 | | - } catch (error) { |
495 | | - // Silent error handling |
496 | | - } |
| 446 | + if (window.grecaptcha && recaptchaWidgetId) { |
| 447 | + window.grecaptcha.reset(recaptchaWidgetId); |
497 | 448 | } |
498 | | - |
499 | | - // Clear the container and reset widget ID |
500 | | - const recaptchaDiv = document.getElementById( |
501 | | - "newsletter-recaptcha" |
502 | | - ); |
503 | | - if (recaptchaDiv) { |
504 | | - recaptchaDiv.innerHTML = ""; |
505 | | - } |
506 | | - recaptchaWidgetId = null; |
507 | 449 | recaptchaToken = ""; |
508 | 450 |
|
509 | 451 | // GTM error tracking |
|
517 | 459 | }); |
518 | 460 | } finally { |
519 | 461 | isSubmitting = false; |
520 | | - // Show submit button again |
521 | | - const buttonContainer = subscribeBtn.parentElement; |
522 | | - buttonContainer.style.display = "block"; |
| 462 | + // Re-enable submit button |
523 | 463 | subscribeBtn.disabled = false; |
524 | 464 | subscribeBtn.textContent = "Subscribe"; |
525 | 465 | } |
|
541 | 481 | setTimeout(() => toast.classList.add("tw-hidden"), 300); |
542 | 482 | }, 4000); |
543 | 483 | } |
| 484 | + |
544 | 485 | document.addEventListener("DOMContentLoaded", function () { |
545 | 486 | const form = document.getElementById("newsletter-form"); |
546 | 487 | const emailInput = document.getElementById("newsletter-email"); |
|
550 | 491 | function validateEmail(email) { |
551 | 492 | return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); |
552 | 493 | } |
553 | | - // Initialize reCAPTCHA when API is ready |
554 | 494 |
|
| 495 | + // Initialize reCAPTCHA when API is ready |
555 | 496 | if (window.grecaptcha && window.grecaptcha.render) { |
556 | 497 | isRecaptchaLoaded = true; |
557 | 498 | initializeRecaptcha(); |
558 | 499 | } |
| 500 | + |
559 | 501 | form.addEventListener("submit", async function (e) { |
560 | 502 | e.preventDefault(); // Prevent form submission and URL changes |
561 | 503 | e.stopPropagation(); // Stop event bubbling |
| 504 | + |
562 | 505 | emailError.classList.add("tw-hidden"); |
| 506 | + |
563 | 507 | const email = emailInput.value.trim(); |
564 | 508 | if (!validateEmail(email)) { |
565 | 509 | emailError.textContent = "Invalid email address"; |
566 | 510 | emailError.classList.remove("tw-hidden"); |
567 | 511 | return false; // Prevent further processing |
568 | 512 | } |
569 | 513 |
|
570 | | - // Hide submit button after click |
571 | | - const buttonContainer = subscribeBtn.parentElement; |
572 | | - buttonContainer.style.display = "none"; |
| 514 | + // Disable submit button to prevent multiple submissions |
| 515 | + subscribeBtn.disabled = true; |
| 516 | + subscribeBtn.textContent = "Processing..."; |
| 517 | + |
573 | 518 | try { |
574 | 519 | // Show reCAPTCHA (like Vue component submitForm) |
575 | 520 | submitForm(); |
576 | 521 | } catch (error) { |
577 | 522 | showToastMessage("An error occurred. Please try again.", "error"); |
578 | | - // Show submit button again on error |
579 | | - const buttonContainer = subscribeBtn.parentElement; |
580 | | - buttonContainer.style.display = "block"; |
| 523 | + |
| 524 | + // Re-enable submit button |
581 | 525 | subscribeBtn.disabled = false; |
582 | 526 | subscribeBtn.textContent = "Subscribe"; |
583 | 527 | } |
| 528 | + |
584 | 529 | return false; // Ensure no form submission happens |
585 | 530 | }); |
586 | 531 | }); |
|
1136 | 1081 | } |
1137 | 1082 | } |
1138 | 1083 | </style> |
| 1084 | + |
1139 | 1085 | <!-- reCAPTCHA v2 Script --> |
1140 | 1086 | <script |
1141 | 1087 | src="https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoad&render=explicit" |
|
0 commit comments