@akson/cortex-landing-hooks
Version:
React hooks for landing pages - device detection, API calls, form submission, analytics, and performance
613 lines (608 loc) • 19.2 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/analytics/index.ts
var analytics_exports = {};
__export(analytics_exports, {
GTM_ACTIONS: () => GTM_ACTIONS,
GTM_COMPONENTS: () => GTM_COMPONENTS,
GTM_EVENTS: () => GTM_EVENTS,
useAnalytics: () => useAnalytics,
useAnalyticsCompat: () => useAnalyticsCompat,
useAnalyticsCompatAlias: () => useAnalytics2,
useEcommerceAnalytics: () => useEcommerceAnalytics,
useGTMTracking: () => useGTMTracking
});
module.exports = __toCommonJS(analytics_exports);
// src/analytics/useAnalytics.ts
var import_react = require("react");
function useAnalytics(config = {}) {
const { gtmId, posthogKey, facebookPixelId, customTracker, debug = false } = config;
const [isReady, setIsReady] = (0, import_react.useState)(false);
const [sessionId] = (0, import_react.useState)(() => Math.random().toString(36).substring(2, 15));
(0, import_react.useEffect)(() => {
if (typeof window === "undefined") return;
const checkReady = () => {
const gtmReady = !gtmId || typeof window.gtag === "function";
const posthogReady = !posthogKey || typeof window.posthog === "object";
const facebookReady = !facebookPixelId || typeof window.fbq === "function";
const allReady = gtmReady && posthogReady && facebookReady;
if (allReady !== isReady) {
setIsReady(allReady);
}
};
checkReady();
const interval = setInterval(checkReady, 1e3);
const timeout = setTimeout(() => {
clearInterval(interval);
setIsReady(true);
}, 1e4);
return () => {
clearInterval(interval);
clearTimeout(timeout);
};
}, [gtmId, posthogKey, facebookPixelId, isReady]);
const debugLog = (0, import_react.useCallback)((message, data) => {
if (debug) {
console.log(`[Analytics] ${message}`, data || "");
}
}, [debug]);
const track = (0, import_react.useCallback)((eventName, properties = {}) => {
if (typeof window === "undefined") return;
const eventData = {
...properties,
sessionId,
timestamp: (/* @__PURE__ */ new Date()).toISOString()
};
debugLog(`Tracking event: ${eventName}`, eventData);
if (gtmId && typeof window.gtag === "function") {
window.gtag("event", eventName, eventData);
}
if (posthogKey && typeof window.posthog === "object") {
window.posthog.capture(eventName, eventData);
}
if (facebookPixelId && typeof window.fbq === "function") {
window.fbq("track", eventName, eventData);
}
if (customTracker) {
customTracker({ name: eventName, properties: eventData });
}
}, [gtmId, posthogKey, facebookPixelId, customTracker, sessionId, debugLog]);
const trackPageView = (0, import_react.useCallback)((path, properties = {}) => {
const currentPath = path || (typeof window !== "undefined" ? window.location.pathname : "");
track("page_view", {
...properties,
page_path: currentPath,
page_title: typeof window !== "undefined" ? document.title : "",
page_location: typeof window !== "undefined" ? window.location.href : ""
});
}, [track]);
const trackInteraction = (0, import_react.useCallback)((element, action, properties = {}) => {
track("interaction", {
...properties,
element,
action
});
}, [track]);
const trackConversion = (0, import_react.useCallback)((type, value, properties = {}) => {
track("conversion", {
...properties,
conversion_type: type,
value: value || 0,
currency: properties.currency || "USD"
});
}, [track]);
const identify = (0, import_react.useCallback)((userData) => {
if (typeof window === "undefined") return;
debugLog("Identifying user", userData);
if (posthogKey && typeof window.posthog === "object") {
window.posthog.identify(userData.userId, userData);
}
if (typeof window !== "undefined") {
window.userData = userData;
}
}, [posthogKey, debugLog]);
const collectFormData = (0, import_react.useCallback)(() => {
if (typeof window === "undefined") return null;
const forms = document.querySelectorAll("form");
if (forms.length === 0) return null;
let totalFields = 0;
const filledFields = [];
const emptyFields = [];
forms.forEach((form) => {
const inputs = form.querySelectorAll("input, textarea, select");
inputs.forEach((input) => {
const field = input;
if (field.type === "hidden" || field.type === "submit" || field.type === "button") {
return;
}
const fieldName = field.getAttribute("name") || field.getAttribute("id") || field.getAttribute("placeholder") || "unnamed_field";
totalFields++;
const hasValue = field instanceof HTMLInputElement && (field.type === "checkbox" || field.type === "radio") ? field.checked : Boolean(field.value && field.value.trim() !== "");
if (hasValue) {
filledFields.push(fieldName);
} else {
emptyFields.push(fieldName);
}
});
});
if (totalFields === 0) return null;
return {
completionPercentage: Math.round(filledFields.length / totalFields * 100),
fieldsCompleted: filledFields.length,
totalFields,
filledFields,
emptyFields
};
}, []);
const trackFormStart = (0, import_react.useCallback)((formName, properties = {}) => {
track("form_start", {
...properties,
form_name: formName
});
}, [track]);
const trackFormComplete = (0, import_react.useCallback)((formName, properties = {}) => {
const formData = collectFormData();
track("form_complete", {
...properties,
form_name: formName,
...formData
});
}, [track, collectFormData]);
const trackFormAbandonment = (0, import_react.useCallback)((formName, step, properties = {}) => {
const formData = collectFormData();
track("form_abandonment", {
...properties,
form_name: formName,
abandonment_step: step,
...formData
});
}, [track, collectFormData]);
const getSessionId = (0, import_react.useCallback)(() => sessionId, [sessionId]);
return (0, import_react.useMemo)(() => ({
// Core tracking
track,
trackPageView,
trackInteraction,
trackConversion,
// User identification
identify,
// Form tracking
collectFormData,
trackFormStart,
trackFormComplete,
trackFormAbandonment,
// Utility
getSessionId,
isReady,
debug: debugLog
}), [
track,
trackPageView,
trackInteraction,
trackConversion,
identify,
collectFormData,
trackFormStart,
trackFormComplete,
trackFormAbandonment,
getSessionId,
isReady,
debugLog
]);
}
function useEcommerceAnalytics(config = {}) {
const analytics = useAnalytics(config);
const trackPurchase = (0, import_react.useCallback)((transactionId, items, value, currency = "USD") => {
analytics.track("purchase", {
transaction_id: transactionId,
value,
currency,
items
});
}, [analytics]);
const trackAddToCart = (0, import_react.useCallback)((item, value, currency = "USD") => {
analytics.track("add_to_cart", {
...item,
value: value || item.price || 0,
currency
});
}, [analytics]);
const trackViewItem = (0, import_react.useCallback)((item, value, currency = "USD") => {
analytics.track("view_item", {
...item,
value: value || item.price || 0,
currency
});
}, [analytics]);
const trackBeginCheckout = (0, import_react.useCallback)((items, value, currency = "USD") => {
analytics.track("begin_checkout", {
value,
currency,
items
});
}, [analytics]);
return {
...analytics,
trackPurchase,
trackAddToCart,
trackViewItem,
trackBeginCheckout
};
}
// src/analytics/useAnalyticsCompat.ts
var import_react2 = require("react");
var FUNNEL_STAGES;
var calculateConversionValue;
try {
const analyticsV2 = require("@/app/lib/analytics-v2");
FUNNEL_STAGES = analyticsV2.FUNNEL_STAGES;
calculateConversionValue = analyticsV2.calculateConversionValue;
} catch {
FUNNEL_STAGES = {};
calculateConversionValue = () => 0;
}
var mockAnalytics = {
track: (event, properties) => {
console.log("Analytics track:", event, properties);
},
trackPageView: (properties) => {
console.log("Analytics page view:", properties);
},
trackInteraction: (properties) => {
console.log("Analytics interaction:", properties);
},
trackEcommerce: (properties) => {
console.log("Analytics ecommerce:", properties);
},
trackExperiment: (properties) => {
console.log("Analytics experiment:", properties);
},
trackFeatureFlag: (properties) => {
console.log("Analytics feature flag:", properties);
},
identify: (userId, properties) => {
console.log("Analytics identify:", userId, properties);
},
getSessionId: () => `session_${Date.now()}`,
getFunnelPath: () => [],
getTimeInFunnel: () => 0,
clearSession: () => console.log("Session cleared"),
isReady: true
};
var mockFunnel = {
trackStage: (stage, data) => {
console.log("Funnel stage:", stage, data);
},
trackAbandonment: (stage, reason, data) => {
console.log("Funnel abandonment:", stage, reason, data);
},
trackConversion: (type, value, data) => {
console.log("Funnel conversion:", type, value, data);
}
};
var mockLead = {
sendUserData: (userData) => {
console.log("Lead user data:", userData);
}
};
function useAnalyticsCompat() {
const analytics = mockAnalytics;
const funnel = mockFunnel;
const lead = mockLead;
const trackStage = (0, import_react2.useCallback)((stage, data) => {
const conversionValue = calculateConversionValue ? calculateConversionValue(stage, data) : 0;
const standardizedEvent = FUNNEL_STAGES ? Object.values(FUNNEL_STAGES).find((s) => s === stage) : null;
if (standardizedEvent && typeof standardizedEvent === "string") {
analytics.track(standardizedEvent, {
...data,
value: conversionValue,
funnel_stage: stage
});
} else {
funnel.trackStage(stage, data);
}
}, [analytics, funnel]);
const trackAbandonment = (0, import_react2.useCallback)((lastStage, reason, additionalData) => {
funnel.trackAbandonment(lastStage, reason, additionalData);
}, [funnel]);
const trackConversion = (0, import_react2.useCallback)((type, value, additionalData) => {
funnel.trackConversion(type, value, additionalData);
}, [funnel]);
const collectFormData = (0, import_react2.useCallback)(() => {
if (typeof window === "undefined") return null;
const forms = document.querySelectorAll("form");
if (forms.length === 0) return null;
let totalFields = 0;
const filledFields = [];
const emptyFields = [];
forms.forEach((form) => {
const inputs = form.querySelectorAll("input, textarea, select");
inputs.forEach((input) => {
const field = input;
if (field.type === "hidden" || field.type === "submit" || field.type === "button") {
return;
}
const fieldName = field.getAttribute("name") || field.getAttribute("id") || field.getAttribute("placeholder") || "unnamed_field";
totalFields++;
const hasValue = field instanceof HTMLInputElement && (field.type === "checkbox" || field.type === "radio") ? field.checked : Boolean(field.value && field.value.trim() !== "");
if (hasValue) {
filledFields.push(fieldName);
} else {
emptyFields.push(fieldName);
}
});
});
if (totalFields === 0) return null;
return {
completionPercentage: Math.round(filledFields.length / totalFields * 100),
fieldsCompleted: filledFields.length,
totalFields,
filledFields,
emptyFields
};
}, []);
return (0, import_react2.useMemo)(() => ({
// Funnel tracking
trackStage,
trackAbandonment,
trackConversion,
// Enhanced Conversions
sendUserData: lead.sendUserData,
// Core tracking from @akson
track: analytics.track,
trackPageView: analytics.trackPageView,
trackInteraction: analytics.trackInteraction,
trackEcommerce: analytics.trackEcommerce,
trackExperiment: analytics.trackExperiment,
trackFeatureFlag: analytics.trackFeatureFlag,
identify: analytics.identify,
// Utility functions
getSessionId: analytics.getSessionId,
getFunnelPath: analytics.getFunnelPath,
getTimeInFunnel: analytics.getTimeInFunnel,
clearFunnelState: analytics.clearSession,
collectFormData,
// Status checks
isGTMReady: () => analytics.isReady,
getPostHog: () => typeof window !== "undefined" ? window.posthog : null
}), [
trackStage,
trackAbandonment,
trackConversion,
lead.sendUserData,
analytics,
collectFormData
]);
}
var useAnalytics2 = useAnalyticsCompat;
// src/analytics/useGTMTracking.ts
var import_react3 = require("react");
var DEFAULT_CONFIG = {
enabled: true,
debug: process.env.NODE_ENV === "development",
logEvents: process.env.NODE_ENV === "development"
};
var GTM_EVENTS = {
INTERACTION: "interaction",
CONVERSION: "conversion",
PAGEVIEW: "page_view",
EXPERIMENT: "experiment"
};
var GTM_ACTIONS = {
CLICK: "click",
VIEW: "view",
SUBMIT: "submit",
HOVER: "hover",
FOCUS: "focus",
BLUR: "blur",
START: "start",
STEP: "step",
COMPLETE: "complete",
ABANDON: "abandon",
ADD_TO_CART: "add_to_cart",
CONVERT: "convert",
SCROLL: "scroll"
};
var GTM_COMPONENTS = {
HERO: "hero",
CTA: "cta",
FORM: "form",
WHATSAPP: "whatsapp",
PRODUCT: "product",
NAVIGATION: "navigation",
PAGE: "page"
};
function useGTMTracking(config = {}) {
const finalConfig = { ...DEFAULT_CONFIG, ...config };
(0, import_react3.useEffect)(() => {
if (typeof window !== "undefined") {
window.dataLayer = window.dataLayer || [];
}
}, []);
const track = (0, import_react3.useCallback)(
(event) => {
if (typeof window === "undefined") return;
if (!finalConfig.enabled) {
if (finalConfig.debug) {
console.log("[GTM] Tracking disabled, skipping event:", event);
}
return;
}
if (!event.event) {
console.warn('[GTM] Event missing required "event" field:', event);
return;
}
const enrichedEvent = {
...event,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
url: window.location.href,
user_agent: navigator.userAgent,
viewport: `${window.innerWidth}x${window.innerHeight}`
};
window.dataLayer = window.dataLayer || [];
window.dataLayer.push(enrichedEvent);
if (finalConfig.logEvents) {
console.log("[GTM] Event tracked:", enrichedEvent);
}
},
[finalConfig]
);
const trackInteraction = (0, import_react3.useCallback)(
(component, action, metadata) => {
track({
event: GTM_EVENTS.INTERACTION,
component,
action,
metadata
});
},
[track]
);
const trackConversion = (0, import_react3.useCallback)(
(component, value, metadata) => {
track({
event: GTM_EVENTS.CONVERSION,
component,
value,
metadata
});
},
[track]
);
const trackPageView = (0, import_react3.useCallback)(
(metadata) => {
track({
event: GTM_EVENTS.PAGEVIEW,
metadata
});
},
[track]
);
const trackExperiment = (0, import_react3.useCallback)(
(experimentName, variant, action, metadata) => {
track({
event: GTM_EVENTS.EXPERIMENT,
action,
metadata: {
experiment_name: experimentName,
variant,
...metadata
}
});
},
[track]
);
const trackWhatsApp = (0, import_react3.useCallback)(
(action = GTM_ACTIONS.CLICK, metadata) => {
trackInteraction(GTM_COMPONENTS.WHATSAPP, action, metadata);
},
[trackInteraction]
);
const trackForm = (0, import_react3.useCallback)(
(action, formType, metadata) => {
trackInteraction(GTM_COMPONENTS.FORM, action, {
form_type: formType,
...metadata
});
},
[trackInteraction]
);
const trackCTA = (0, import_react3.useCallback)(
(action = GTM_ACTIONS.CLICK, metadata) => {
trackInteraction(GTM_COMPONENTS.CTA, action, metadata);
},
[trackInteraction]
);
const trackHero = (0, import_react3.useCallback)(
(action = GTM_ACTIONS.VIEW, metadata) => {
trackInteraction(GTM_COMPONENTS.HERO, action, metadata);
},
[trackInteraction]
);
const trackProduct = (0, import_react3.useCallback)(
(action, productId, productName, metadata) => {
trackInteraction(GTM_COMPONENTS.PRODUCT, action, {
product_id: productId,
product_name: productName,
...metadata
});
},
[trackInteraction]
);
const identify = (0, import_react3.useCallback)(
(userId, traits) => {
if (typeof window === "undefined") return;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "identify",
userId,
userProperties: traits
});
if (finalConfig.debug) {
console.log("[GTM] User identified:", userId, traits);
}
},
[finalConfig.debug]
);
const reset = (0, import_react3.useCallback)(() => {
if (typeof window === "undefined") return;
sessionStorage.removeItem("gtm_session_id");
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "reset",
timestamp: (/* @__PURE__ */ new Date()).toISOString()
});
if (finalConfig.debug) {
console.log("[GTM] Session reset");
}
}, [finalConfig.debug]);
return {
// Core function
track,
// Specific tracking functions
trackInteraction,
trackConversion,
trackPageView,
trackExperiment,
// Component-specific functions
trackWhatsApp,
trackForm,
trackCTA,
trackHero,
trackProduct,
// User management
identify,
reset,
// Constants for components to use
ACTIONS: GTM_ACTIONS,
COMPONENTS: GTM_COMPONENTS,
EVENTS: GTM_EVENTS
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
GTM_ACTIONS,
GTM_COMPONENTS,
GTM_EVENTS,
useAnalytics,
useAnalyticsCompat,
useAnalyticsCompatAlias,
useEcommerceAnalytics,
useGTMTracking
});