Skip to content

Commit cb25c94

Browse files
committed
added listeners
1 parent 8649c34 commit cb25c94

5 files changed

Lines changed: 55 additions & 1 deletion

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { browser } from '$app/environment';
2+
3+
/**
4+
* GA4 custom event for partner / referral outbound links.
5+
* @see https://developers.google.com/analytics/devguides/collection/ga4/events
6+
* Custom params (partner, product, …) may need to be registered as custom dimensions in GA4 to appear in standard reports; they still show in DebugView / BigQuery.
7+
*/
8+
const EVENT_NAME = 'affiliate_click';
9+
10+
function onDocumentClickCapture(e: MouseEvent): void {
11+
const target = e.target;
12+
if (!(target instanceof Node)) return;
13+
const el = (target as Element).closest?.('a.affiliate-link');
14+
if (!(el instanceof HTMLAnchorElement)) return;
15+
const g = (window as Window & { gtag?: (...args: unknown[]) => void }).gtag;
16+
if (typeof g !== 'function') return;
17+
18+
const linkText = (el.textContent ?? '')
19+
.trim()
20+
.replace(/\s+/g, ' ')
21+
.slice(0, 120);
22+
23+
g('event', EVENT_NAME, {
24+
partner: el.dataset.partner ?? '',
25+
product: el.dataset.product ?? '',
26+
link_url: el.href,
27+
link_text: linkText
28+
});
29+
}
30+
31+
/** Register capture-phase listener; returns cleanup for $effect. */
32+
export function registerAffiliateLinkClickTracking(): () => void {
33+
if (!browser) return () => {};
34+
document.addEventListener('click', onDocumentClickCapture, true);
35+
return () => document.removeEventListener('click', onDocumentClickCapture, true);
36+
}

bitext/src/lib/components/partners/PartnerBannerPreply.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
title="Preply — language tutors"
1010
{href}
1111
ctaLabel="Open Preply"
12+
partner="preply"
13+
product="language_tutors"
1214
toneClass="border-l-[#FF7AAC] bg-[#FF7AAC]/10 dark:bg-[#FF7AAC]/15"
1315
>
1416
<p class="m-0">

bitext/src/lib/components/partners/PartnerBannerRailway.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
title="Railway - easy deployment"
99
{href}
1010
ctaLabel="Open Railway"
11+
partner="railway"
12+
product="hosting"
1113
toneClass="border-l-[#853bce] bg-[#853bce]/10 dark:bg-[#853bce]/15"
1214
>
1315
<p class="m-0">

bitext/src/lib/components/partners/PartnerBannerShell.svelte

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@
77
href,
88
ctaLabel,
99
toneClass,
10+
partner,
11+
product,
1012
children
1113
}: {
1214
title: string;
1315
href: string;
1416
ctaLabel: string;
1517
/** Left accent bar + pale tinted surface (light/dark), e.g. border-l-[#hex] bg-[#hex]/10 */
1618
toneClass: string;
19+
/** GA4 `affiliate_click` — stable slug, e.g. preply */
20+
partner: string;
21+
/** GA4 `affiliate_click` — offer/category slug, e.g. language_tutors */
22+
product: string;
1723
children: Snippet;
1824
} = $props();
1925
@@ -54,8 +60,10 @@
5460
{@render children()}
5561
</div>
5662
<a
57-
class="{linkClass} mt-1 max-sm:mt-2 sm:col-start-2 sm:row-start-1 sm:mt-0 sm:justify-self-end sm:text-right"
63+
class="affiliate-link {linkClass} mt-1 max-sm:mt-2 sm:col-start-2 sm:row-start-1 sm:mt-0 sm:justify-self-end sm:text-right"
5864
href={href}
65+
data-partner={partner}
66+
data-product={product}
5967
target="_blank"
6068
rel="noopener noreferrer sponsored"
6169
>

bitext/src/routes/+layout.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { browser } from '$app/environment';
33
import { afterNavigate } from '$app/navigation';
44
import '../app.css';
5+
import { registerAffiliateLinkClickTracking } from '$lib/analytics/affiliate-link-tracking.js';
56
import { GA_MEASUREMENT_ID } from '$lib/brand.js';
67
import { flowbiteTheme } from '$lib/flowbite-theme.js';
78
import { settingsStore } from '$lib/state/settings.svelte.js';
@@ -25,6 +26,11 @@
2526
document.documentElement.classList.toggle('dark', isDark);
2627
});
2728
29+
$effect(() => {
30+
if (!browser) return;
31+
return registerAffiliateLinkClickTracking();
32+
});
33+
2834
/** SPA navigations: initial `enter` is already counted by the snippet in app.html */
2935
afterNavigate(({ to, type }) => {
3036
if (!browser || type === 'enter' || !to) return;

0 commit comments

Comments
 (0)