diff --git a/jam-ui/src/helpers/MetaTracking.js b/jam-ui/src/helpers/MetaTracking.js new file mode 100644 index 000000000..27720b803 --- /dev/null +++ b/jam-ui/src/helpers/MetaTracking.js @@ -0,0 +1,81 @@ +/** + * meta_tracking.js + * A standalone module to capture and persist Meta attribution signals (fbclid, _fbp) in cookies. + * + * Logic adapted from web/app/assets/javascripts/meta_tracking.js for React environment. + * - Checks URL for `fbclid` and sets `_fbc` cookie. + * - Checks for `_fbp` cookie; if missing, generates and sets it. + */ + +const MetaTracking = { + init: function () { + const location = window.location; + this.handleFbc(location.search); + this.handleFbp(); + }, + + // 1. Parsing and storing _fbc (Click ID) + handleFbc: function (searchParams) { + const fbclid = this.getQueryParam('fbclid', searchParams); + + if (fbclid) { + const version = 'fb'; + const subdomainIndex = 1; // 1 = example.com + const creationTime = new Date().getTime(); // Unix timestamp in ms + + // Format: fb.1.timestamp.id + const fbcValue = `${version}.${subdomainIndex}.${creationTime}.${fbclid}`; + + this.setCookie('_fbc', fbcValue, 90); + } + }, + + // 2. Handling _fbp (Browser ID) + handleFbp: function () { + if (!this.getCookie('_fbp')) { + const version = 'fb'; + const subdomainIndex = 1; + const creationTime = new Date().getTime(); + const randomInt = Math.floor(Math.random() * 10000000000); // 10-digit random number + + // Format: fb.1.timestamp.randomDigits + const fbpValue = `${version}.${subdomainIndex}.${creationTime}.${randomInt}`; + + this.setCookie('_fbp', fbpValue, 90); + } + }, + + // Helper: Get query param by name + getQueryParam: function (name, search) { + name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); + const regex = new RegExp('[\\?&]' + name + '=([^&#]*)'); + const results = regex.exec(search); + return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); + }, + + // Helper: Set cookie + setCookie: function (name, value, days) { + let expires = ""; + if (days) { + const date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + // Ensure path is root and domain is included if needed (defaults to current host) + document.cookie = name + "=" + (value || "") + expires + "; path=/"; + }, + + // Helper: Get cookie + getCookie: function (name) { + const nameEQ = name + "="; + const ca = document.cookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); + } + return null; + } +}; + +export default MetaTracking; diff --git a/jam-ui/src/layouts/JKLayout.js b/jam-ui/src/layouts/JKLayout.js index a7efc7874..aa17e622e 100644 --- a/jam-ui/src/layouts/JKLayout.js +++ b/jam-ui/src/layouts/JKLayout.js @@ -1,4 +1,4 @@ -import React, {useEffect} from 'react'; +import React, { useEffect } from 'react'; import { Route, Switch } from 'react-router-dom'; import { toast, ToastContainer } from 'react-toastify'; import { CloseButton, Fade } from '../components/common/Toast'; @@ -8,6 +8,7 @@ import ErrorLayout from './ErrorLayout'; import BuildMeta from "./JKBuildMeta"; import loadable from '@loadable/component'; +import MetaTracking from "../helpers/MetaTracking"; const AuthBasicLayout = loadable(() => import('./JKAuthBasicLayout')); const PublicLayout = loadable(() => import('./JKPublicLayout')); @@ -15,6 +16,7 @@ const Layout = () => { useEffect(() => { AuthBasicLayout.preload(); PublicLayout.preload(); + MetaTracking.init(); //see if there is affiliate in query string and save it as cookie const urlParams = new URLSearchParams(window.location.search); @@ -28,7 +30,7 @@ const Layout = () => { }, []); return ( - <> + <> @@ -37,7 +39,7 @@ const Layout = () => { } position={toast.POSITION.BOTTOM_RIGHT} /> - + ); };