@akson/cortex-landing-hooks
Version:
React hooks for landing pages - device detection, API calls, form submission, analytics, and performance
584 lines (579 loc) • 17.4 kB
JavaScript
import {
__require
} from "./chunk-Y6FXYEAI.mjs";
// src/analytics/useAnalytics.ts
import { useCallback, useEffect, useMemo, useState } from "react";
function useAnalytics(config = {}) {
const { gtmId, posthogKey, facebookPixelId, customTracker, debug = false } = config;
const [isReady, setIsReady] = useState(false);
const [sessionId] = useState(() => Math.random().toString(36).substring(2, 15));
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 = useCallback((message, data) => {
if (debug) {
console.log(`[Analytics] ${message}`, data || "");
}
}, [debug]);
const track = 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 = 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 = useCallback((element, action, properties = {}) => {
track("interaction", {
...properties,
element,
action
});
}, [track]);
const trackConversion = useCallback((type, value, properties = {}) => {
track("conversion", {
...properties,
conversion_type: type,
value: value || 0,
currency: properties.currency || "USD"
});
}, [track]);
const identify = 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 = 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 = useCallback((formName, properties = {}) => {
track("form_start", {
...properties,
form_name: formName
});
}, [track]);
const trackFormComplete = useCallback((formName, properties = {}) => {
const formData = collectFormData();
track("form_complete", {
...properties,
form_name: formName,
...formData
});
}, [track, collectFormData]);
const trackFormAbandonment = useCallback((formName, step, properties = {}) => {
const formData = collectFormData();
track("form_abandonment", {
...properties,
form_name: formName,
abandonment_step: step,
...formData
});
}, [track, collectFormData]);
const getSessionId = useCallback(() => sessionId, [sessionId]);
return 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 = useCallback((transactionId, items, value, currency = "USD") => {
analytics.track("purchase", {
transaction_id: transactionId,
value,
currency,
items
});
}, [analytics]);
const trackAddToCart = useCallback((item, value, currency = "USD") => {
analytics.track("add_to_cart", {
...item,
value: value || item.price || 0,
currency
});
}, [analytics]);
const trackViewItem = useCallback((item, value, currency = "USD") => {
analytics.track("view_item", {
...item,
value: value || item.price || 0,
currency
});
}, [analytics]);
const trackBeginCheckout = useCallback((items, value, currency = "USD") => {
analytics.track("begin_checkout", {
value,
currency,
items
});
}, [analytics]);
return {
...analytics,
trackPurchase,
trackAddToCart,
trackViewItem,
trackBeginCheckout
};
}
// src/analytics/useAnalyticsCompat.ts
import { useCallback as useCallback2, useMemo as useMemo2 } from "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 = useCallback2((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 = useCallback2((lastStage, reason, additionalData) => {
funnel.trackAbandonment(lastStage, reason, additionalData);
}, [funnel]);
const trackConversion = useCallback2((type, value, additionalData) => {
funnel.trackConversion(type, value, additionalData);
}, [funnel]);
const collectFormData = useCallback2(() => {
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 useMemo2(() => ({
// 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
import { useCallback as useCallback3, useEffect as useEffect2 } from "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 };
useEffect2(() => {
if (typeof window !== "undefined") {
window.dataLayer = window.dataLayer || [];
}
}, []);
const track = useCallback3(
(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 = useCallback3(
(component, action, metadata) => {
track({
event: GTM_EVENTS.INTERACTION,
component,
action,
metadata
});
},
[track]
);
const trackConversion = useCallback3(
(component, value, metadata) => {
track({
event: GTM_EVENTS.CONVERSION,
component,
value,
metadata
});
},
[track]
);
const trackPageView = useCallback3(
(metadata) => {
track({
event: GTM_EVENTS.PAGEVIEW,
metadata
});
},
[track]
);
const trackExperiment = useCallback3(
(experimentName, variant, action, metadata) => {
track({
event: GTM_EVENTS.EXPERIMENT,
action,
metadata: {
experiment_name: experimentName,
variant,
...metadata
}
});
},
[track]
);
const trackWhatsApp = useCallback3(
(action = GTM_ACTIONS.CLICK, metadata) => {
trackInteraction(GTM_COMPONENTS.WHATSAPP, action, metadata);
},
[trackInteraction]
);
const trackForm = useCallback3(
(action, formType, metadata) => {
trackInteraction(GTM_COMPONENTS.FORM, action, {
form_type: formType,
...metadata
});
},
[trackInteraction]
);
const trackCTA = useCallback3(
(action = GTM_ACTIONS.CLICK, metadata) => {
trackInteraction(GTM_COMPONENTS.CTA, action, metadata);
},
[trackInteraction]
);
const trackHero = useCallback3(
(action = GTM_ACTIONS.VIEW, metadata) => {
trackInteraction(GTM_COMPONENTS.HERO, action, metadata);
},
[trackInteraction]
);
const trackProduct = useCallback3(
(action, productId, productName, metadata) => {
trackInteraction(GTM_COMPONENTS.PRODUCT, action, {
product_id: productId,
product_name: productName,
...metadata
});
},
[trackInteraction]
);
const identify = useCallback3(
(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 = useCallback3(() => {
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
};
}
export {
useAnalytics,
useEcommerceAnalytics,
useAnalyticsCompat,
useAnalytics2,
GTM_EVENTS,
GTM_ACTIONS,
GTM_COMPONENTS,
useGTMTracking
};