Public Access
feat(bf): fire the trial conversion behind the same consent gate
Adds the second Google Ads label to /odoo-black-friday/: the free-trial form (#trialForm) now fires AW-18065522009/AtAPCIjjvrAdENn6p6ZD (action 7886320008 "Registrierung") when it is submitted. Reuses the existing gate in static/ads-consent.js rather than a second mechanism: the label is read from the page (data-ads-trial-send-to on #adsConsent), so the script stays generic. The page hands the gate its real submit function via window.o4pAds.trialConversion; the gate fires the conversion and runs the submit from the event callback, with the same 700 ms hard timeout that runs it anyway. Nothing fires and no cookie is set before consent (identical default-deny gate). The buy conversion is untouched (comments only).
This commit is contained in:
1 parent
6d486ad954
commit
7497fbf588
3 files changed
+69
-9
No files matched your search
@@ -1063,7 +1063,9 @@
|
|||||||
|
|
||||||
<!-- Google Ads consent banner — default DENY. The tag loads only after "Accept".
|
<!-- Google Ads consent banner — default DENY. The tag loads only after "Accept".
|
||||||
Markup lives in the page; JS (static/ads-consent.js) reveals it and drives it.
|
Markup lives in the page; JS (static/ads-consent.js) reveals it and drives it.
|
||||||
With JavaScript off it stays hidden and the native buy forms submit untouched.
|
Two conversions are wired here: the buy forms (data-ads-send-to) and the free-trial
|
||||||
|
form (data-ads-trial-send-to). With JavaScript off it stays hidden and both actions
|
||||||
|
(the native buy POSTs, the trial's own submit) run untouched.
|
||||||
See wiki/concepts/conversion-tracking-contract. -->
|
See wiki/concepts/conversion-tracking-contract. -->
|
||||||
<div
|
<div
|
||||||
id="adsConsent"
|
id="adsConsent"
|
||||||
@@ -1072,6 +1074,7 @@
|
|||||||
aria-label="Advertising cookie consent"
|
aria-label="Advertising cookie consent"
|
||||||
data-ads-id="AW-18065522009"
|
data-ads-id="AW-18065522009"
|
||||||
data-ads-send-to="AW-18065522009/vnDUCPDxt7AdENn6p6ZD"
|
data-ads-send-to="AW-18065522009/vnDUCPDxt7AdENn6p6ZD"
|
||||||
|
data-ads-trial-send-to="AW-18065522009/AtAPCIjjvrAdENn6p6ZD"
|
||||||
hidden
|
hidden
|
||||||
>
|
>
|
||||||
<div class="ads-consent-inner">
|
<div class="ads-consent-inner">
|
||||||
|
|||||||
+53
-8
@@ -9,20 +9,24 @@
|
|||||||
* _gcl_au / _gcl_aw. So nothing loads and no request fires until the visitor accepts.
|
* _gcl_au / _gcl_aw. So nothing loads and no request fires until the visitor accepts.
|
||||||
* The default is DENY. Declining genuinely prevents the tag from loading.
|
* The default is DENY. Declining genuinely prevents the tag from loading.
|
||||||
*
|
*
|
||||||
* WHAT THE CONVERSION IS: a CLICK PROXY, not a sale. It fires when a buy form on this page
|
* WHAT THE CONVERSIONS ARE: both are CLICK PROXIES, not sales.
|
||||||
* is submitted. The real purchase is measured on the checkout host (20.odoo4projects.com)
|
* - a buy form on this page (POST /shop/post_basket) -> data-ads-send-to
|
||||||
* by odoo-dev. See wiki/concepts/conversion-tracking-contract. Never label it a sale.
|
* - the free-trial form (#trialForm, the n8n webhook) -> data-ads-trial-send-to
|
||||||
|
* The real purchase is measured on the checkout host (20.odoo4projects.com) by odoo-dev.
|
||||||
|
* See wiki/concepts/conversion-tracking-contract. Never label either a sale.
|
||||||
*
|
*
|
||||||
* SAFETY: a conversion tag must never swallow a buy. On submit we fire the event and submit
|
* SAFETY: a conversion tag must never swallow an action. On submit we fire the event and run the
|
||||||
* the form from the event callback, with a hard timeout that submits anyway. With JavaScript
|
* action from the event callback, with a hard timeout that runs it anyway. For a buy form the
|
||||||
* off the native POST forms submit untouched (no banner, no conversion, no error).
|
* action is form.submit(); for the trial form the page hands us its send function via
|
||||||
|
* window.o4pAds.trialConversion. With JavaScript off the native POST forms submit untouched
|
||||||
|
* (no banner, no conversion, no error) and the trial form does its native thing.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var STORE_KEY = "o4p_ads_consent"; // "granted" | "denied"
|
var STORE_KEY = "o4p_ads_consent"; // "granted" | "denied"
|
||||||
var SUBMIT_FALLBACK_MS = 700; // hard cap: submit even if the tag never answers
|
var SUBMIT_FALLBACK_MS = 700; // hard cap: submit even if the tag never answers
|
||||||
var cfg = null; // { id, sendTo } — read from the banner, never hard-coded
|
var cfg = null; // { id, sendTo, trialSendTo } — from the banner, never hard-coded
|
||||||
|
|
||||||
/* ---- the visitor's stored choice ---- */
|
/* ---- the visitor's stored choice ---- */
|
||||||
function readChoice() {
|
function readChoice() {
|
||||||
@@ -79,7 +83,28 @@
|
|||||||
document.head.appendChild(s);
|
document.head.appendChild(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- fire one conversion, then run the action from the callback or the hard timeout ---- */
|
||||||
|
function fireConversion(sendTo, action) {
|
||||||
|
var done = false;
|
||||||
|
function run() {
|
||||||
|
if (done) return;
|
||||||
|
done = true;
|
||||||
|
action(); // the action always runs: at most SUBMIT_FALLBACK_MS after the event
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
window.gtag("event", "conversion", {
|
||||||
|
send_to: sendTo,
|
||||||
|
event_callback: run,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
window.setTimeout(run, SUBMIT_FALLBACK_MS);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- the conversion proxy, fired on a buy-form submit ---- */
|
/* ---- the conversion proxy, fired on a buy-form submit ---- */
|
||||||
|
/* NOTE: the buy path below is deliberately left byte-identical to what shipped and was
|
||||||
|
* verified on 2026-11-02 — its label, its one mark and its 700 ms fallback are unchanged. */
|
||||||
function onSubmit(e) {
|
function onSubmit(e) {
|
||||||
var form = e.target;
|
var form = e.target;
|
||||||
if (!form || form.tagName !== "FORM") return;
|
if (!form || form.tagName !== "FORM") return;
|
||||||
@@ -141,7 +166,11 @@
|
|||||||
var id = banner.getAttribute("data-ads-id");
|
var id = banner.getAttribute("data-ads-id");
|
||||||
var sendTo = banner.getAttribute("data-ads-send-to");
|
var sendTo = banner.getAttribute("data-ads-send-to");
|
||||||
if (!id || !sendTo) return;
|
if (!id || !sendTo) return;
|
||||||
cfg = { id: id, sendTo: sendTo };
|
cfg = {
|
||||||
|
id: id,
|
||||||
|
sendTo: sendTo,
|
||||||
|
trialSendTo: banner.getAttribute("data-ads-trial-send-to"),
|
||||||
|
};
|
||||||
|
|
||||||
var choice = readChoice();
|
var choice = readChoice();
|
||||||
if (choice === "granted") {
|
if (choice === "granted") {
|
||||||
@@ -165,6 +194,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- the trial conversion: the page hands us its real submit function ---- *
|
||||||
|
* Called by /static/black-friday.js when #trialForm is submitted. With consent we fire the Ads
|
||||||
|
* conversion and run the page's submit from the callback; a blocked/slow tag is covered by the
|
||||||
|
* hard timeout, so the trial always completes. Without consent (or without the tag) we run the
|
||||||
|
* submit immediately and fire nothing — the trial is never gated on the tag.
|
||||||
|
*/
|
||||||
|
window.o4pAds = window.o4pAds || {};
|
||||||
|
window.o4pAds.trialConversion = function (action) {
|
||||||
|
if (typeof action !== "function") return;
|
||||||
|
if (!cfg || !cfg.trialSendTo || !isGranted() || typeof window.gtag !== "function") {
|
||||||
|
action();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireConversion(cfg.trialSendTo, action);
|
||||||
|
};
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", init);
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -241,6 +241,8 @@
|
|||||||
var location = trialLoc ? trialLoc.value : '';
|
var location = trialLoc ? trialLoc.value : '';
|
||||||
if (!email) return;
|
if (!email) return;
|
||||||
var btn = trialForm.querySelector('.btn-brass');
|
var btn = trialForm.querySelector('.btn-brass');
|
||||||
|
|
||||||
|
function sendTrial() {
|
||||||
btn.textContent = 'Sending...';
|
btn.textContent = 'Sending...';
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
fetch('https://n8n.odoo4projects.com/webhook/ODOO4projects/trial', {
|
fetch('https://n8n.odoo4projects.com/webhook/ODOO4projects/trial', {
|
||||||
@@ -260,6 +262,16 @@
|
|||||||
trialForm.appendChild(errorMsg);
|
trialForm.appendChild(errorMsg);
|
||||||
setTimeout(function () { errorMsg.classList.remove('visible'); }, 3000);
|
setTimeout(function () { errorMsg.classList.remove('visible'); }, 3000);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When the consent gate is present it fires the Google Ads trial conversion and runs
|
||||||
|
sendTrial from the tag's callback, with a hard timeout that runs it anyway — so a
|
||||||
|
blocked or slow tag can never swallow the trial. Without the gate, send directly. */
|
||||||
|
if (window.o4pAds && typeof window.o4pAds.trialConversion === 'function') {
|
||||||
|
window.o4pAds.trialConversion(sendTrial);
|
||||||
|
} else {
|
||||||
|
sendTrial();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in new issue
Block a user