UNPKG

@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
"use strict"; 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 });