/** * utm.js — trackable campaign links (spec §7 `utm_links` entity). * * Mints a UTM-tagged URL to the public landing page so every ad click is attributable * end-to-end (source / medium / campaign / content / term → destination). Front-end * demo: pure string building, no shortener or backend. */ // Use the running app's origin so the copied link opens the demo landing page; in a // real deployment this resolves to your public site (swap to your domain when hosting). const ORIGIN = (typeof window !== 'undefined' && window.location?.origin) || 'https://lynkeduppro.com'; const BASE = `${ORIGIN}/ads/lp`; // Ad medium by platform (search vs paid social vs messaging). const MEDIUM = { google: 'paid_search', meta: 'paid_social', linkedin: 'paid_social', reddit: 'paid_social', nextdoor: 'paid_social', whatsapp: 'messaging', }; const slug = (s) => (s || '') .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .slice(0, 60); /** * Build the tracked link + its utm_links field breakdown for a campaign. * Returns { url, fields }. */ export function buildUtm(campaign, { creative } = {}) { const source = campaign.platform; const medium = MEDIUM[source] || 'paid'; const name = slug(campaign.name) || campaign.blueprintId || 'campaign'; const content = creative ? (slug(creative.headline) || creative.id) : (campaign.creativeId || 'default'); const term = campaign.serviceType || ''; const params = { c: campaign.blueprintId, src: source, utm_source: source, utm_medium: medium, utm_campaign: name, utm_content: content, utm_term: term, }; const qs = Object.entries(params) .filter(([, v]) => v) .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) .join('&'); return { url: `${BASE}?${qs}`, fields: { campaignId: campaign.id || null, source, medium, campaign: name, content, term, destinationUrl: BASE, }, }; }