So we can see whether a discount helps the CTA (clicks) AND the sale (paid orders), per arm: 1. pw-analytics.js: include the ?code= (the price-test arm) on the campaign-click Umami event, and fire it when EITHER a utm OR a code is present (coupon links often carry code with no utm, so those human clicks were being dropped). Also strip a stray @TrackLink suffix Listmonk glues onto the last query value. 2. coupon_ab_scoreboard.py: one report combining HUMAN clicks (Umami, already bot-filtered) and PAID conversions (compliance_orders) per arm. Discount arms map by code->pct (campaign-daily:<date>:<pct> marker); the no-code control arm is recovered by re-hashing customer_email the same way the builder bucketed it. Prints clicks, paid orders, revenue, and click->order per arm.
139 lines
5.9 KiB
JavaScript
139 lines
5.9 KiB
JavaScript
/**
|
|
* Performance West — Umami custom event tracking.
|
|
* Loaded after the Umami script on all pages.
|
|
* Uses umami.track() for conversion funnel events.
|
|
*/
|
|
(function() {
|
|
// Wait for umami to be available
|
|
function track(name, data) {
|
|
if (window.umami) {
|
|
window.umami.track(name, data || {});
|
|
}
|
|
}
|
|
|
|
// Expose globally for inline usage
|
|
window.pwTrack = track;
|
|
|
|
// ── Auto-track common interactions ────────────────────────────────────
|
|
|
|
// Track all CTA button clicks with data-track attribute
|
|
document.addEventListener("click", function(e) {
|
|
var el = e.target.closest("[data-track]");
|
|
if (el) {
|
|
track(el.getAttribute("data-track"), {
|
|
label: el.textContent.trim().slice(0, 50),
|
|
href: el.href || "",
|
|
page: location.pathname,
|
|
});
|
|
}
|
|
});
|
|
|
|
// ── FCC Compliance Checker events ─────────────────────────────────────
|
|
|
|
// Check if we're on the compliance checker page
|
|
if (location.pathname.indexOf("/tools/fcc-compliance-check") === 0) {
|
|
// Track when check starts (form submit / button click)
|
|
var checkBtn = document.querySelector("#check-btn, [onclick*='runCheck'], button[type='submit']");
|
|
if (checkBtn) {
|
|
checkBtn.addEventListener("click", function() {
|
|
var frn = (document.querySelector("#frn-input, input[name='frn']") || {}).value || "";
|
|
track("compliance-check-start", { frn: frn });
|
|
});
|
|
}
|
|
|
|
// Track when results load (observe DOM for results container)
|
|
var observer = new MutationObserver(function(mutations) {
|
|
for (var i = 0; i < mutations.length; i++) {
|
|
var target = mutations[i].target;
|
|
if (target.id === "checks-container" || target.id === "results") {
|
|
var issues = document.querySelectorAll(".check-fail, .issue-card, [data-severity]").length;
|
|
var frn = (document.querySelector("#frn-input, input[name='frn']") || {}).value || "";
|
|
track("compliance-check-complete", { frn: frn, issues: issues });
|
|
observer.disconnect();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
var resultsEl = document.getElementById("checks-container") || document.getElementById("results");
|
|
if (resultsEl) {
|
|
observer.observe(resultsEl, { childList: true, subtree: true });
|
|
}
|
|
|
|
// Track "Order" CTA clicks from results
|
|
document.addEventListener("click", function(e) {
|
|
var btn = e.target.closest("a[href*='/order/'], button[data-slug]");
|
|
if (btn && (btn.closest("#checks-container") || btn.closest("#results") || btn.closest(".cta"))) {
|
|
var slug = btn.getAttribute("data-slug") || btn.href.split("/order/")[1] || "";
|
|
track("order-cta-click", {
|
|
slug: slug.split("/")[0] || slug.split("?")[0],
|
|
source: "compliance-checker",
|
|
page: location.pathname,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// ── Order / Checkout pages ────────────────────────────────────────────
|
|
|
|
if (location.pathname.indexOf("/order/") === 0) {
|
|
// Track checkout page view with service info
|
|
var slug = location.pathname.replace("/order/", "").replace(/\/$/, "");
|
|
track("checkout-page-view", { slug: slug, referrer: document.referrer });
|
|
|
|
// Track payment button click
|
|
document.addEventListener("click", function(e) {
|
|
var btn = e.target.closest("#pay-btn, #submit-btn, .checkout-btn, button[type='submit']");
|
|
if (btn && (btn.textContent.indexOf("Pay") >= 0 || btn.textContent.indexOf("Checkout") >= 0 || btn.textContent.indexOf("Place Order") >= 0)) {
|
|
track("checkout-start", { slug: slug, page: location.pathname });
|
|
}
|
|
});
|
|
}
|
|
|
|
// ── eSign portal ──────────────────────────────────────────────────────
|
|
|
|
if (location.pathname.indexOf("/portal/esign") === 0 || location.pathname.indexOf("/portal/sign") === 0) {
|
|
track("esign-opened", { page: location.pathname });
|
|
|
|
document.addEventListener("click", function(e) {
|
|
var btn = e.target.closest("#submit-btn, .submit-btn");
|
|
if (btn && btn.textContent.indexOf("Submit") >= 0) {
|
|
track("esign-submitted", { page: location.pathname });
|
|
}
|
|
});
|
|
}
|
|
|
|
// ── Email campaign click attribution ──────────────────────────────────
|
|
|
|
var params = new URLSearchParams(location.search);
|
|
var utmSource = params.get("utm_source");
|
|
var utmCampaign = params.get("utm_campaign");
|
|
// Strip a stray "@TrackLink" suffix Listmonk can leave glued onto the last
|
|
// query value (e.g. utm_campaign=deficiency@TrackLink), so the arm/campaign
|
|
// reads cleanly in analytics.
|
|
var clean = function (v) { return (v || "").split("@TrackLink")[0]; };
|
|
// The price-test arm rides in ?code= (BGQAX=20%, MLTGA=30%, the 40% code; the
|
|
// full-price control arm has no code). Coupon links frequently carry code with
|
|
// NO utm, so fire campaign-click whenever EITHER a utm OR a code is present,
|
|
// and always include the code so each A/B/C arm is attributable per click.
|
|
var couponCode = clean(params.get("code"));
|
|
if (utmSource || utmCampaign || couponCode) {
|
|
track("campaign-click", {
|
|
source: clean(utmSource),
|
|
campaign: clean(utmCampaign),
|
|
medium: clean(params.get("utm_medium")),
|
|
code: couponCode,
|
|
page: location.pathname,
|
|
});
|
|
}
|
|
|
|
// ── Contact form submission ───────────────────────────────────────────
|
|
|
|
if (location.pathname.indexOf("/contact") === 0) {
|
|
document.addEventListener("submit", function(e) {
|
|
if (e.target.closest("form")) {
|
|
track("contact-form-submit", { page: location.pathname });
|
|
}
|
|
});
|
|
}
|
|
|
|
})();
|