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} />
- >
+ >
);
};