/* bootcamp-waitlist.js — page-local waiting-list form for the three bootcamp pages. * * The user's rule: "You want another timezone / language? Join the waiting list." * The form posts to the same n8n CRM webhook the site's other forms use, with the * site's own footer-message payload shape — area:'message', no `name` (that belongs to * the `area:'quote'` shape of the Custom Quote modal). * * This page does not load /script.js, so the UTM helpers are copied verbatim from it * (same as /static/black-friday.js does). If script.js's helpers change, change these. * * All copy — labels, placeholders, the "sending" label, success and error text — lives in * the page markup, in the page's own language. This file carries no strings. * * A 200 from the webhook means the workflow accepted the request, not that a CRM record * exists (the workflow answers before it runs) — same as the site's other forms. */ var ENDPOINT = 'https://n8n.odoo4projects.com/webhook/ODOO4projects/crm'; (function () { 'use strict'; /* ---- UTM tracking helpers (verbatim from /script.js) ---- */ function getCookie(name) { var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); return match ? decodeURIComponent(match[2]) : null; } function setCookie(name, value, days) { var expires = ''; if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = '; expires=' + date.toUTCString(); } document.cookie = name + '=' + encodeURIComponent(value) + expires + '; path=/; SameSite=Lax'; } function parseUTM() { var urlParams = new URLSearchParams(window.location.search); var utm = {}; var keys = ['source', 'medium', 'campaign']; for (var i = 0; i < keys.length; i++) { var val = urlParams.get('utm_' + keys[i]); if (val) { utm[keys[i]] = val; setCookie('utm_' + keys[i], val, 90); } } return utm; } function getStoredUTM() { var utm = {}; var keys = ['source', 'medium', 'campaign']; for (var i = 0; i < keys.length; i++) { var val = getCookie('utm_' + keys[i]); if (val) utm[keys[i]] = val; } return utm; } function injectUTM(data) { var stored = getStoredUTM(); if (Object.keys(stored).length > 0) { data.utm_source = stored.source || null; data.utm_medium = stored.medium || null; data.utm_campaign = stored.campaign || null; } return data; } window.UTM = { parse: parseUTM, getStored: getStoredUTM, inject: injectUTM }; window.UTM.parse(); /* ---- Waiting list ---- */ function run() { var form = document.getElementById('bcWaitlistForm'); if (!form) return; var emailInput = document.getElementById('bcWaitlistEmail'); var commentInput = document.getElementById('bcWaitlistComment'); var submitBtn = form.querySelector('button[type="submit"]'); var success = document.getElementById('bcWaitlistSuccess'); var error = document.getElementById('bcWaitlistError'); var label = submitBtn ? submitBtn.textContent : ''; /* Both fields are required, exactly as the footer form requires both. */ form.addEventListener('submit', function (e) { e.preventDefault(); var email = emailInput.value.trim(); var comment = commentInput.value.trim(); if (!email || !comment) return; error.style.display = 'none'; submitBtn.textContent = submitBtn.getAttribute('data-sending') || label; submitBtn.disabled = true; fetch(ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(UTM.inject({ email: email, requirements: comment, area: 'message' })) }).then(function (r) { if (!r.ok) throw new Error('bad status ' + r.status); if (window.plausible) window.plausible('Bootcamp Waitlist'); form.style.display = 'none'; success.style.display = 'block'; }).catch(function () { submitBtn.textContent = label; submitBtn.disabled = false; error.style.display = 'block'; }); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', run); } else { run(); } })(); /* ---- Footer contact form ------------------------------------------------------------ * These pages do NOT load /script.js, so its footer-form handler does not run here. This * mirrors /script.js's handler one-for-one — same webhook, same payload shape * ({ email, requirements, area:'message' } wrapped in UTM.inject), same success/failure * behaviour — but reads its two strings from the form's own data-ok / data-err attributes * so the message is in the page's language. Without it a submit would navigate the visitor * to the webhook's raw JSON reply. */ (function () { 'use strict'; function runFooter() { var form = document.getElementById('footerContactForm'); if (!form) return; var emailInput = document.getElementById('footerContactEmail'); var textInput = document.getElementById('footerContactText'); var submitBtn = form.querySelector('.footer-send-btn'); var status = document.getElementById('footerFormStatus'); var okText = form.getAttribute('data-ok') || "Thanks! We'll get back to you shortly."; var errText = form.getAttribute('data-err') || 'Something went wrong. Please try again.'; function showStatus(message, isError) { status.textContent = message; status.classList.toggle('error', !!isError); status.classList.add('visible'); } form.addEventListener('submit', function (e) { e.preventDefault(); var email = emailInput.value.trim(); var text = textInput.value.trim(); if (!email || !text) return; submitBtn.disabled = true; status.classList.remove('visible'); fetch(ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(window.UTM.inject({ email: email, requirements: text, area: 'message' })) }).then(function (r) { if (!r.ok) throw new Error('bad status ' + r.status); form.reset(); submitBtn.disabled = false; showStatus(okText, false); }).catch(function () { submitBtn.disabled = false; showStatus(errText, true); }); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runFooter); } else { runFooter(); } })();