(function () { "use strict"; var DEFAULT_ORDER_FORM_ACTION = "https://supervision.neolis.fr/bqj/neolisweb/v2/order.php"; var ORDER_POST_PREFIX = "order"; function getOrderFormAction() { if (typeof window.ORDER_FORM_ACTION === "string" && window.ORDER_FORM_ACTION.length > 0) { return window.ORDER_FORM_ACTION; } return DEFAULT_ORDER_FORM_ACTION; } function removeDynamicOrderFields(form) { form.querySelectorAll(".js-order-post-field").forEach(function (node) { node.parentNode.removeChild(node); }); } function appendNestedPostFields(form, data, prefix) { if (data === null || data === undefined) { var empty = document.createElement("input"); empty.type = "hidden"; empty.className = "js-order-post-field"; empty.name = prefix; empty.value = ""; form.appendChild(empty); return; } if (typeof data !== "object") { var inp = document.createElement("input"); inp.type = "hidden"; inp.className = "js-order-post-field"; inp.name = prefix; inp.value = String(data); form.appendChild(inp); return; } if (Array.isArray(data)) { data.forEach(function (item, i) { appendNestedPostFields(form, item, prefix + "[" + i + "]"); }); return; } Object.keys(data).forEach(function (key) { appendNestedPostFields(form, data[key], prefix + "[" + key + "]"); }); } /* Sum must stay ≤ default users (3) and ≥ sites (1) when physical */ var DEFAULT_QTYS = ["1", "1", "1"]; var els = { quoteForm: document.getElementById("quote-form"), virtualOnly: document.getElementById("virtual-only"), hardwareSection: document.getElementById("hardware-section"), sitesRow: document.getElementById("sites-row"), qtyInputs: document.querySelectorAll(".js-qty"), totalPositions: document.getElementById("total-positions"), sitesInput: document.getElementById("sites"), numUsers: document.getElementById("num-users"), numGeo: document.getElementById("num-geo"), numPort: document.getElementById("num-port"), btnPersonalizedFooter: document.getElementById("btn-personalized-footer"), modal: document.getElementById("modal-personalized"), modalClose: document.querySelectorAll("[data-modal-close]"), planUnlimited: document.getElementById("plan-unlimited"), planExtra: document.getElementById("plan-extra"), unlimitedOptions: document.getElementById("unlimited-options"), channels: document.getElementById("channels"), packageType: document.getElementById("package-type"), btnOrder: document.getElementById("btn-order"), quoteInitialTotal: document.getElementById("quote-initial-total"), quoteInitialLines: document.getElementById("quote-initial-lines"), quoteMonthlyTotal: document.getElementById("quote-monthly-total"), quoteMonthlyLines: document.getElementById("quote-monthly-lines"), quotePerUserHint: document.getElementById("quote-per-user-hint"), }; if (els.quoteForm) { els.quoteForm.setAttribute("action", getOrderFormAction()); } function formatEuro(n) { var v = Math.round(n * 100) / 100; if (v % 1 === 0) return "€ " + v; return "€ " + v.toFixed(2); } function readFormState() { var qty = function (id) { var el = document.getElementById(id); return el ? el.value : "0"; }; return { users: els.numUsers ? els.numUsers.value : "3", sites: els.sitesInput ? els.sitesInput.value : "1", numGeo: els.numGeo ? els.numGeo.value : "0", numPort: els.numPort ? els.numPort.value : "0", virtualOnly: els.virtualOnly ? els.virtualOnly.checked : true, qtyReception: qty("qty-reception"), qtySecondary: qty("qty-secondary"), qtyWireless: qty("qty-wireless"), planUnlimited: els.planUnlimited ? els.planUnlimited.checked : false, packageType: els.packageType ? els.packageType.value : "", channels: els.channels ? els.channels.value : "", }; } function renderQuote() { if (typeof Pricing === "undefined") return; var state = readFormState(); var initial = Pricing.calculateInitial(state); var monthly = Pricing.calculateMonthly(state); if (els.quoteInitialTotal) { els.quoteInitialTotal.innerHTML = formatEuro(initial.total) + " ht"; } if (els.quoteMonthlyTotal) { els.quoteMonthlyTotal.innerHTML = formatEuro(monthly.total) + " ht"; } if (els.quotePerUserHint) { els.quotePerUserHint.textContent = //"( € ht par mois )"; ""; } function fillList(ul, lines) { if (!ul) return; ul.innerHTML = ""; lines.forEach(function (line) { var li = document.createElement("li"); var main = document.createElement("span"); main.textContent = line.label + ": " + formatEuro(line.amount) + " ht"; li.appendChild(main); if (line.detail) { var sub = document.createElement("span"); sub.className = "sub"; sub.textContent = line.detail; li.appendChild(sub); } ul.appendChild(li); }); } fillList(els.quoteInitialLines, initial.lines); fillList(els.quoteMonthlyLines, monthly.lines); } function sumPositions() { var total = 0; els.qtyInputs.forEach(function (input) { var n = parseInt(input.value, 10); if (!isNaN(n) && n > 0) total += n; }); return total; } function updateTotalPositions() { if (els.totalPositions) { els.totalPositions.textContent = String(sumPositions()); } } function setQtyDefaults() { els.qtyInputs.forEach(function (input, i) { input.value = DEFAULT_QTYS[i] != null ? DEFAULT_QTYS[i] : "0"; }); updateTotalPositions(); } function updateSitesRow() { if (!els.virtualOnly || !els.sitesRow || !els.sitesInput) return; var physical = !els.virtualOnly.checked; els.sitesRow.classList.toggle("hidden", !physical); if (physical) { els.sitesInput.setAttribute("required", "required"); } else { els.sitesInput.removeAttribute("required"); } } function toggleVirtualHardware() { if (!els.virtualOnly || !els.hardwareSection) return; var hide = els.virtualOnly.checked; els.hardwareSection.classList.toggle("hidden", hide); updateSitesRow(); if (hide) { els.qtyInputs.forEach(function (input) { input.value = "0"; }); updateTotalPositions(); } else { setQtyDefaults(); } renderQuote(); } function clampSites() { if (!els.sitesInput) return; var max = parseInt(els.sitesInput.getAttribute("max"), 10) || 5; var v = parseInt(els.sitesInput.value, 10); if (isNaN(v) || v < 1) els.sitesInput.value = "1"; else if (v > max) els.sitesInput.value = String(max); } function clampUsers() { if (!els.numUsers) return; var v = parseInt(els.numUsers.value, 10); var min = parseInt(els.numUsers.getAttribute("min"), 10) || 3; var max = parseInt(els.numUsers.getAttribute("max"), 10) || 200; if (isNaN(v) || v < min) els.numUsers.value = String(min); else if (v > max) els.numUsers.value = String(max); } function clampNumGeo() { if (!els.numGeo) return; var v = parseInt(els.numGeo.value, 10); if (isNaN(v) || v < 1) els.numGeo.value = "1"; } function syncNumPortMax() { if (!els.numGeo || !els.numPort) return; clampNumGeo(); var geo = parseInt(els.numGeo.value, 10) || 1; els.numPort.setAttribute("max", String(geo)); var p = parseInt(els.numPort.value, 10); if (!isNaN(p) && p > geo) els.numPort.value = String(geo); } function getOrderErrors() { var errors = []; var state = readFormState(); var numGeo = parseInt(state.numGeo, 10); var numPort = parseInt(state.numPort, 10); if (isNaN(numGeo) || numGeo < 1) { errors.push("Geographic numbers must be at least 1."); } if (!isNaN(numGeo) && numGeo >= 1 && !isNaN(numPort) && numPort > numGeo) { errors.push("Numbers to wear must be less than or equal to geographic numbers."); } var users = parseInt(state.users, 10); if (isNaN(users) || users < 3 || users > 200) { errors.push("Number of users must be between 3 and 200."); } var accept = document.getElementById("accept-terms"); if (accept && !accept.checked) { errors.push("Please accept the general terms and conditions of sale."); } if (!state.virtualOnly) { var devices = sumPositions(); var sites = parseInt(state.sites, 10) || 1; if (devices < sites) { errors.push("Total physical phones cannot be lower than the number of sites."); } if (devices > users) { errors.push("Total physical phones cannot be greater than the number of users."); } } return errors; } function toggleUnlimitedOptions() { if (!els.planUnlimited || !els.unlimitedOptions) return; els.unlimitedOptions.classList.toggle("hidden", !els.planUnlimited.checked); renderQuote(); } function openModal() { if (els.modal) els.modal.classList.add("is-open"); } function closeModal() { if (els.modal) els.modal.classList.remove("is-open"); } function wireRecalc() { var nodes = [els.numUsers, els.sitesInput, els.numPort, els.channels, els.packageType]; nodes.forEach(function (el) { if (el) { el.addEventListener("input", renderQuote); el.addEventListener("change", renderQuote); } }); if (els.numGeo) { els.numGeo.addEventListener("input", function () { syncNumPortMax(); renderQuote(); }); els.numGeo.addEventListener("change", function () { syncNumPortMax(); renderQuote(); }); els.numGeo.addEventListener("blur", function () { clampNumGeo(); syncNumPortMax(); renderQuote(); }); } if (els.numPort) { els.numPort.addEventListener("blur", function () { syncNumPortMax(); renderQuote(); }); } } if (els.qtyInputs.length) { els.qtyInputs.forEach(function (input) { input.addEventListener("input", function () { updateTotalPositions(); renderQuote(); }); }); } if (els.virtualOnly) { els.virtualOnly.addEventListener("change", toggleVirtualHardware); } if (els.planUnlimited) { els.planUnlimited.addEventListener("change", toggleUnlimitedOptions); } if (els.planExtra) { els.planExtra.addEventListener("change", toggleUnlimitedOptions); } if (els.sitesInput) { els.sitesInput.addEventListener("change", function () { clampSites(); renderQuote(); }); els.sitesInput.addEventListener("blur", function () { clampSites(); renderQuote(); }); } if (els.numUsers) { els.numUsers.addEventListener("blur", function () { clampUsers(); renderQuote(); }); els.numUsers.addEventListener("input", renderQuote); } if (els.btnPersonalizedFooter) els.btnPersonalizedFooter.addEventListener("click", openModal); els.modalClose.forEach(function (btn) { btn.addEventListener("click", closeModal); }); if (els.modal) { els.modal.addEventListener("click", function (e) { if (e.target === els.modal) closeModal(); }); } function validateOrderExtra() { if (els.planUnlimited && els.planUnlimited.checked) { if (!els.channels || !els.channels.value) { return "Please select the number of simultaneous channels for the unlimited plan."; } if (!els.packageType || !els.packageType.value) { return "Please select a package type for the unlimited plan."; } } return ""; } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function hideOrderFeedback() { var el = document.getElementById("order-feedback"); if (!el) return; el.className = "order-feedback hidden"; el.innerHTML = ""; } function showOrderFeedback(type, title, bodyHtml) { var el = document.getElementById("order-feedback"); if (!el) return; el.className = "order-feedback order-feedback--" + type; el.innerHTML = '
' + escapeHtml(title) + "
" + (bodyHtml ? '" + escapeHtml(planErr) + "
"); return; } if (typeof Pricing === "undefined") { showOrderFeedback("error", "Error", "Pricing module is missing. Reload the page.
"); return; } els.quoteForm.action = getOrderFormAction(); removeDynamicOrderFields(els.quoteForm); var postPrefix = typeof window.ORDER_POST_PREFIX === "string" && window.ORDER_POST_PREFIX.length > 0 ? window.ORDER_POST_PREFIX : ORDER_POST_PREFIX; appendNestedPostFields(els.quoteForm, buildStripePayload(), postPrefix); var btn = els.btnOrder; if (btn) { btn.classList.add("is-loading"); btn.disabled = true; } /* Full page navigation to order.php (same as browser form submit). */ els.quoteForm.submit(); }); } var modalSend = document.getElementById("modal-send"); if (modalSend) { modalSend.addEventListener("click", function () { alert("Static demo: connect email / CRM for personalized offer."); closeModal(); }); } wireRecalc(); updateSitesRow(); toggleVirtualHardware(); toggleUnlimitedOptions(); syncNumPortMax(); renderQuote(); })();