feat(datasheet): Product-page links carry UTM attribution and report the click

Both Buy renderers (picker hover panel + Part Inspector) now append
utm_source=velxio&utm_medium=simulator&utm_campaign=datasheet with the
component id in utm_content, so vendors can attribute the referral.
trackProductPageClick fires the GA4 event and dispatches a
'velxio:product-page-click' CustomEvent — an overlay build can persist
the click server-side without this tree knowing about it.
This commit is contained in:
David Montero Crespo 2026-08-09 08:16:47 +02:00
parent 75842e3668
commit 4b9fa3f0d0
4 changed files with 52 additions and 3 deletions

View File

@ -24,7 +24,8 @@ import { useTranslation } from 'react-i18next';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import type { PropertyDescriptor } from '../types/component-metadata'; import type { PropertyDescriptor } from '../types/component-metadata';
import { loadDoc, type ComponentDoc } from './componentDocs'; import { loadDoc, productPageHref, type ComponentDoc } from './componentDocs';
import { trackProductPageClick } from '../utils/analytics';
import './ComponentInfoPanel.css'; import './ComponentInfoPanel.css';
export interface PanelData { export interface PanelData {
@ -168,7 +169,13 @@ export const ComponentInfoBody: React.FC<{
{buyHref && ( {buyHref && (
<div className="cip-footer"> <div className="cip-footer">
<a className="cip-buy" href={buyHref} target="_blank" rel="noopener noreferrer"> <a
className="cip-buy"
href={productPageHref(buyHref, data.id)}
target="_blank"
rel="noopener noreferrer"
onClick={() => trackProductPageClick(data.id, doc?.brand, buyHref)}
>
<svg <svg
width="14" width="14"
height="14" height="14"

View File

@ -56,6 +56,25 @@ export function registerComponentDoc(id: string, raw: string): void {
cache.delete(id); cache.delete(id);
} }
/**
* Decorate a datasheet Buy/Product-page URL with UTM attribution so the
* vendor sees the referral came from Velxio. `componentId` lands in
* utm_content so partner dashboards can tell WHICH part drove the visit.
* Existing query params on the vendor URL are preserved.
*/
export function productPageHref(url: string, componentId: string): string {
try {
const u = new URL(url);
u.searchParams.set('utm_source', 'velxio');
u.searchParams.set('utm_medium', 'simulator');
u.searchParams.set('utm_campaign', 'datasheet');
u.searchParams.set('utm_content', componentId);
return u.toString();
} catch {
return url;
}
}
/** /**
* Split an optional `---`-delimited front-matter block off the top of a * Split an optional `---`-delimited front-matter block off the top of a
* Markdown string. Only a leading block counts, so table separators * Markdown string. Only a leading block counts, so table separators

View File

@ -48,6 +48,8 @@ import {
safeHref, safeHref,
type PanelData, type PanelData,
} from '../ComponentInfoPanel'; } from '../ComponentInfoPanel';
import { productPageHref } from '../componentDocs';
import { trackProductPageClick } from '../../utils/analytics';
import { import {
layoutInspectorPins, layoutInspectorPins,
type InspectorPinInput, type InspectorPinInput,
@ -813,10 +815,13 @@ export const PartInspectorDialog: React.FC<PartInspectorDialogProps> = ({
<div className="pid-actions-right"> <div className="pid-actions-right">
<a <a
className="pid-buy-button" className="pid-buy-button"
href={buyHref} href={productPageHref(buyHref, componentMetadata.id)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
title={buyHref} title={buyHref}
onClick={() =>
trackProductPageClick(componentMetadata.id, doc?.brand, buyHref)
}
> >
<svg <svg
width="14" width="14"

View File

@ -110,6 +110,24 @@ export function trackVisitDiscord(): void {
fireEvent('visit_discord', { event_category: 'external_link' }); fireEvent('visit_discord', { event_category: 'external_link' });
} }
/**
* Fired when a user clicks a datasheet "Product page" link. Besides the GA4
* event, dispatches a DOM CustomEvent so an overlay build can persist the
* click server-side (partner attribution) without this tree knowing about it.
*/
export function trackProductPageClick(componentId: string, brand?: string, href?: string): void {
fireEvent('product_page_click', {
event_category: 'external_link',
component_type: componentId,
brand: brand ?? '(none)',
});
window.dispatchEvent(
new CustomEvent('velxio:product-page-click', {
detail: { componentId, brand: brand ?? null, href: href ?? null },
}),
);
}
// ── CTA / Conversion ──────────────────────────────────────────────────────── // ── CTA / Conversion ────────────────────────────────────────────────────────
/** Fired when a user clicks a CTA button on a landing/SEO page. */ /** Fired when a user clicks a CTA button on a landing/SEO page. */