@metodokorexmk/tracking 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -0
- package/dist/index.cjs +1779 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +869 -0
- package/dist/index.d.ts +869 -0
- package/dist/index.js +1719 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +870 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +201 -0
- package/dist/react/index.d.ts +201 -0
- package/dist/react/index.js +862 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/analytics.ts","../../src/core/gtm.ts","../../src/trackers/events.ts","../../src/orchestrator/landing-tracker.ts","../../src/react/use-landing-tracking.ts","../../src/trackers/video-tracker.ts","../../src/react/use-video-tracking.ts"],"names":["useRef","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Core de Google Analytics 4\r\n * Inicialización, tracking de eventos y captura de datos del visitante.\r\n * Desacoplado de cualquier framework (Next.js, React, etc.)\r\n */\r\n\r\nimport ReactGA from 'react-ga4';\r\nimport type { TrackingConfig, UTMParams, TrackingEventData } from './types';\r\n\r\n// ====================================\r\n// ESTADO INTERNO\r\n// ====================================\r\n\r\nlet isInitialized = false;\r\nlet currentConfig: TrackingConfig = {};\r\nlet resolvedTrackingId: string = '';\r\nlet cachedUserId: string | null = null;\r\nlet cachedUserName: string | null = null;\r\n\r\n// ====================================\r\n// RESOLUCIÓN DE GA TRACKING ID\r\n// ====================================\r\n\r\n/**\r\n * Obtiene el GA Tracking ID actual.\r\n * Si se configuró un `resolveTrackingId`, lo usa dinámicamente.\r\n * Si no, usa el `trackingId` fijo de la config.\r\n */\r\nexport const getGATrackingId = (): string => {\r\n if (typeof window === 'undefined') return resolvedTrackingId || '';\r\n\r\n if (currentConfig.resolveTrackingId) {\r\n try {\r\n const path = window.location.pathname;\r\n return currentConfig.resolveTrackingId(path);\r\n } catch {\r\n // fallback si la función falla\r\n }\r\n }\r\n\r\n return resolvedTrackingId;\r\n};\r\n\r\n// ====================================\r\n// INICIALIZACIÓN\r\n// ====================================\r\n\r\n/**\r\n * Inicializa Google Analytics con la configuración proporcionada.\r\n *\r\n * @example\r\n * ```ts\r\n * // Con ID fijo\r\n * initGA({ trackingId: 'G-XXXXXXX' })\r\n *\r\n * // Con resolución dinámica\r\n * initGA({\r\n * resolveTrackingId: (path) => {\r\n * if (path.startsWith('/kin')) return 'G-KIN-ID'\r\n * return localStorage.getItem('user_GA_id') || 'G-DEFAULT'\r\n * }\r\n * })\r\n * ```\r\n */\r\nexport const initGA = (config: TrackingConfig = {}): void => {\r\n if (typeof window === 'undefined') return;\r\n if (isInitialized) return;\r\n\r\n currentConfig = config;\r\n resolvedTrackingId = config.trackingId || '';\r\n\r\n const trackingId = getGATrackingId();\r\n if (!trackingId) {\r\n if (config.debug) {\r\n // eslint-disable-next-line no-console\r\n console.warn('[KorexTracking] No tracking ID provided. GA4 will not initialize.');\r\n }\r\n return;\r\n }\r\n\r\n try {\r\n ReactGA.initialize(trackingId);\r\n isInitialized = true;\r\n\r\n if (config.anonymizeIp) {\r\n window.gtag?.('config', trackingId, { anonymize_ip: true });\r\n }\r\n\r\n if (config.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[KorexTracking] GA4 initialized with ID: ${trackingId}`);\r\n }\r\n } catch (error) {\r\n if (config.debug) {\r\n // eslint-disable-next-line no-console\r\n console.error('[KorexTracking] Failed to initialize GA4:', error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Reinicializa GA con un nuevo Tracking ID.\r\n * Útil cuando el ID cambia dinámicamente (ej. login del usuario).\r\n */\r\nexport const reinitGA = (newTrackingId: string): void => {\r\n isInitialized = false;\r\n resolvedTrackingId = newTrackingId;\r\n initGA({ ...currentConfig, trackingId: newTrackingId });\r\n};\r\n\r\n/**\r\n * Verificar si GA está inicializado\r\n */\r\nexport const isGAInitialized = (): boolean => isInitialized;\r\n\r\n// ====================================\r\n// CAPTURA DE DATOS DEL VISITANTE\r\n// ====================================\r\n\r\n/**\r\n * Extrae los parámetros UTM de la URL actual.\r\n */\r\nexport const captureUTMParams = (): UTMParams | null => {\r\n if (typeof window === 'undefined') return null;\r\n\r\n const params = new URLSearchParams(window.location.search);\r\n const utmParams: UTMParams = {};\r\n let hasParams = false;\r\n\r\n const utmKeys: (keyof UTMParams)[] = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\r\n\r\n for (const key of utmKeys) {\r\n const value = params.get(key);\r\n if (value) {\r\n utmParams[key] = value;\r\n hasParams = true;\r\n }\r\n }\r\n\r\n return hasParams ? utmParams : null;\r\n};\r\n\r\n/**\r\n * Captura el `user_id` desde la URL (?user_id=xxx).\r\n * Lo cachea internamente para enviarlo en todos los eventos.\r\n */\r\nexport const captureUserIdFromURL = (): string | null => {\r\n if (typeof window === 'undefined') return null;\r\n\r\n const params = new URLSearchParams(window.location.search);\r\n const userId = params.get('user_id') || params.get('userId');\r\n\r\n if (userId) {\r\n cachedUserId = userId;\r\n try {\r\n localStorage.setItem('tracking_user_id', userId);\r\n } catch {\r\n // localStorage puede no estar disponible\r\n }\r\n }\r\n\r\n return userId;\r\n};\r\n\r\n/**\r\n * Resuelve el nombre del usuario desde múltiples fuentes.\r\n * Prioridad: URL > localStorage > null\r\n */\r\nexport const getUserName = (): string | null => {\r\n if (typeof window === 'undefined') return null;\r\n\r\n // 1. Desde URL\r\n const params = new URLSearchParams(window.location.search);\r\n const fromUrl = params.get('user_name') || params.get('userName');\r\n if (fromUrl) {\r\n cachedUserName = fromUrl;\r\n return fromUrl;\r\n }\r\n\r\n // 2. Desde cache interno\r\n if (cachedUserName) return cachedUserName;\r\n\r\n // 3. Desde localStorage\r\n try {\r\n const fromStorage = localStorage.getItem('tracking_user_name');\r\n if (fromStorage) {\r\n cachedUserName = fromStorage;\r\n return fromStorage;\r\n }\r\n\r\n const userInfo = localStorage.getItem('userInfo');\r\n if (userInfo) {\r\n const parsed = JSON.parse(userInfo);\r\n const name = parsed?.name || parsed?.userName || parsed?.fullName;\r\n if (name) {\r\n cachedUserName = name;\r\n return name;\r\n }\r\n }\r\n } catch {\r\n // localStorage puede no estar disponible\r\n }\r\n\r\n return null;\r\n};\r\n\r\n/**\r\n * Obtiene el user_id cacheado.\r\n */\r\nexport const getUserId = (): string | null => {\r\n if (cachedUserId) return cachedUserId;\r\n\r\n if (typeof window === 'undefined') return null;\r\n\r\n try {\r\n return localStorage.getItem('tracking_user_id');\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\n/**\r\n * Establece manualmente el user_id (ej. después de login).\r\n */\r\nexport const setUserId = (userId: string): void => {\r\n cachedUserId = userId;\r\n try {\r\n localStorage.setItem('tracking_user_id', userId);\r\n } catch {\r\n // ignore\r\n }\r\n // Configurar en GA4 como user property\r\n window.gtag?.('set', { user_id: userId });\r\n};\r\n\r\n/**\r\n * Establece manualmente el user_name.\r\n */\r\nexport const setUserName = (userName: string): void => {\r\n cachedUserName = userName;\r\n try {\r\n localStorage.setItem('tracking_user_name', userName);\r\n } catch {\r\n // ignore\r\n }\r\n};\r\n\r\n// ====================================\r\n// TRACKING DE PÁGINAS\r\n// ====================================\r\n\r\n/**\r\n * Envía un pageview a GA4.\r\n * Automáticamente incluye UTM params y user_id si están disponibles.\r\n */\r\nexport const trackPageView = (path: string): void => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const userId = getUserId();\r\n const utmParams = captureUTMParams();\r\n\r\n // Construir path con UTMs si existen\r\n let pageWithParams = path;\r\n if (utmParams) {\r\n const searchParams = new URLSearchParams();\r\n Object.entries(utmParams).forEach(([key, value]) => {\r\n if (value) searchParams.set(key, value);\r\n });\r\n const paramString = searchParams.toString();\r\n if (paramString) {\r\n pageWithParams = `${path}${path.includes('?') ? '&' : '?'}${paramString}`;\r\n }\r\n }\r\n\r\n // Enviar por ReactGA\r\n try {\r\n ReactGA.send({\r\n hitType: 'pageview',\r\n page: pageWithParams,\r\n ...(userId && { user_id: userId }),\r\n });\r\n } catch {\r\n // ReactGA puede fallar si GA4 no está cargado (ad-blockers, carga lenta, etc.)\r\n }\r\n\r\n // Enviar por gtag nativo\r\n try {\r\n window.gtag?.('event', 'page_view', {\r\n page_path: pageWithParams,\r\n page_title: document.title,\r\n ...(userId && { user_id: userId }),\r\n });\r\n } catch {\r\n // gtag puede no estar disponible\r\n }\r\n\r\n if (currentConfig.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[KorexTracking] PageView: ${pageWithParams}`, { userId });\r\n }\r\n};\r\n\r\n// ====================================\r\n// TRACKING DE EVENTOS\r\n// ====================================\r\n\r\n/**\r\n * Envía un evento personalizado a GA4.\r\n * Usa envío dual: `window.gtag()` + `ReactGA.event()`.\r\n * Siempre incluye `user_id` y `user_name` como parámetros.\r\n *\r\n * @example\r\n * ```ts\r\n * trackEvent('CTA', 'click', 'Botón Hero', undefined, { section: 'hero' })\r\n * ```\r\n */\r\nexport const trackEvent = (category: string, action: string, label?: string, value?: number, additionalData?: TrackingEventData): void => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const userId = getUserId();\r\n const userName = getUserName();\r\n\r\n const eventData: TrackingEventData = {\r\n event_category: category,\r\n event_label: label || '',\r\n ...(value !== undefined && { value }),\r\n ...(userId && { user_id: userId }),\r\n ...(userName && { user_name: userName }),\r\n timestamp: Date.now(),\r\n ...additionalData,\r\n };\r\n\r\n // 1. Enviar por gtag nativo (más fiable)\r\n try {\r\n window.gtag?.('event', action, eventData);\r\n } catch {\r\n // ignore\r\n }\r\n\r\n // 2. Enviar por ReactGA como fallback\r\n try {\r\n ReactGA.event({\r\n category,\r\n action,\r\n label: label || undefined,\r\n value: value || undefined,\r\n ...(userId && { user_id: userId }),\r\n });\r\n } catch {\r\n // ignore\r\n }\r\n\r\n if (currentConfig.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[KorexTracking] Event: ${category}/${action}`, eventData);\r\n }\r\n};\r\n\r\n// ====================================\r\n// UTILIDADES\r\n// ====================================\r\n\r\n/**\r\n * Resetea el estado interno (útil para tests y re-inicialización).\r\n */\r\nexport const resetAnalytics = (): void => {\r\n isInitialized = false;\r\n currentConfig = {};\r\n resolvedTrackingId = '';\r\n cachedUserId = null;\r\n cachedUserName = null;\r\n};\r\n","/**\r\n * Google Tag Manager — Carga e inyección vanilla (sin Next.js)\r\n * Proporciona pushToDataLayer e injectGTMScript.\r\n */\r\n\r\n/**\r\n * Inyecta el script de Google Tag Manager en el <head> del documento.\r\n * Equivalente al componente <Script> de Next.js, pero framework-agnostic.\r\n *\r\n * @param gtmId - ID de GTM (ej: 'GTM-5GMQNFMN')\r\n *\r\n * @example\r\n * ```ts\r\n * injectGTMScript('GTM-5GMQNFMN')\r\n * ```\r\n */\r\nexport const injectGTMScript = (gtmId: string): void => {\r\n if (typeof window === 'undefined') return;\r\n if (!gtmId) return;\r\n\r\n // Evitar inyección duplicada\r\n const existingScript = document.querySelector(`script[data-gtm-id=\"${gtmId}\"]`);\r\n if (existingScript) return;\r\n\r\n // Inicializar dataLayer\r\n window.dataLayer = window.dataLayer || [];\r\n window.dataLayer.push({\r\n 'gtm.start': new Date().getTime(),\r\n event: 'gtm.js',\r\n });\r\n\r\n // Crear e inyectar el script\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;\r\n script.setAttribute('data-gtm-id', gtmId);\r\n\r\n const firstScript = document.getElementsByTagName('script')[0];\r\n if (firstScript?.parentNode) {\r\n firstScript.parentNode.insertBefore(script, firstScript);\r\n } else {\r\n document.head.appendChild(script);\r\n }\r\n\r\n // Inyectar noscript fallback en body\r\n if (document.body) {\r\n const noscript = document.createElement('noscript');\r\n const iframe = document.createElement('iframe');\r\n iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;\r\n iframe.height = '0';\r\n iframe.width = '0';\r\n iframe.style.display = 'none';\r\n iframe.style.visibility = 'hidden';\r\n noscript.appendChild(iframe);\r\n document.body.insertBefore(noscript, document.body.firstChild);\r\n }\r\n};\r\n\r\n/**\r\n * Envía un evento al dataLayer de GTM.\r\n *\r\n * @param eventName - Nombre del evento\r\n * @param data - Datos adicionales del evento\r\n *\r\n * @example\r\n * ```ts\r\n * pushToDataLayer('page_view', { page_path: '/kin' })\r\n * pushToDataLayer('cta_click', { button_text: 'Únete', section: 'hero' })\r\n * ```\r\n */\r\nexport const pushToDataLayer = (eventName: string, data?: Record<string, unknown>): void => {\r\n if (typeof window === 'undefined') return;\r\n\r\n window.dataLayer = window.dataLayer || [];\r\n window.dataLayer.push({\r\n event: eventName,\r\n ...data,\r\n });\r\n};\r\n","/**\r\n * Funciones genéricas de tracking de eventos para GA4.\r\n * Cada función es una abstracción de `trackEvent()` con categoría y acción predefinidas.\r\n */\r\n\r\nimport { trackEvent } from '../core/analytics';\r\nimport type { TrackingEventData } from '../core/types';\r\n\r\n// ====================================\r\n// CTA / BOTONES\r\n// ====================================\r\n\r\nexport const trackCTAClick = (buttonName: string, section: string, additionalData?: TrackingEventData): void => {\r\n trackEvent('CTA', 'click', buttonName, undefined, {\r\n button_name: buttonName,\r\n section,\r\n ...additionalData,\r\n });\r\n};\r\n\r\n// ====================================\r\n// FORMULARIOS\r\n// ====================================\r\n\r\nexport const trackFormStart = (formName: string): void => {\r\n trackEvent('Form', 'started', formName, undefined, {\r\n form_name: formName,\r\n timestamp: Date.now(),\r\n });\r\n};\r\n\r\nexport const trackFormFieldComplete = (formName: string, fieldName: string): void => {\r\n trackEvent('Form', 'field_completed', formName, undefined, {\r\n form_name: formName,\r\n field_name: fieldName,\r\n });\r\n};\r\n\r\nexport const trackFormSubmit = (formName: string, success: boolean, additionalData?: TrackingEventData): void => {\r\n trackEvent('Form', success ? 'submit_success' : 'submit_error', formName, undefined, {\r\n form_name: formName,\r\n success,\r\n ...additionalData,\r\n });\r\n};\r\n\r\nexport const trackFormValidationError = (formName: string, fieldName: string, errorMessage: string): void => {\r\n trackEvent('Error', 'validation', formName, undefined, {\r\n form_name: formName,\r\n field_name: fieldName,\r\n error_message: errorMessage,\r\n });\r\n};\r\n\r\n// ====================================\r\n// CONVERSIONES\r\n// ====================================\r\n\r\nexport const trackConversion = (conversionType: string, value?: number, additionalData?: TrackingEventData): void => {\r\n trackEvent('Conversion', conversionType, undefined, value, {\r\n conversion_type: conversionType,\r\n ...additionalData,\r\n });\r\n};\r\n\r\n// ====================================\r\n// REDES SOCIALES\r\n// ====================================\r\n\r\nexport const trackSocialClick = (platform: string, action: string, additionalData?: TrackingEventData): void => {\r\n trackEvent('Social', 'click', platform, undefined, {\r\n social_platform: platform,\r\n social_action: action,\r\n ...additionalData,\r\n });\r\n};\r\n\r\n// ====================================\r\n// FAQ\r\n// ====================================\r\n\r\nexport const trackFAQExpand = (question: string, index: number): void => {\r\n trackEvent('FAQ', 'expand', question, index, {\r\n question,\r\n question_index: index,\r\n });\r\n};\r\n\r\n// ====================================\r\n// ENGAGEMENT\r\n// ====================================\r\n\r\nexport const trackImageClick = (imageName: string, section: string): void => {\r\n trackEvent('Engagement', 'image_click', imageName, undefined, {\r\n image_name: imageName,\r\n section,\r\n });\r\n};\r\n\r\nexport const trackTimeInSection = (sectionName: string, seconds: number): void => {\r\n if (seconds < 3) return; // Ignorar secciones vistas menos de 3 segundos\r\n\r\n trackEvent('Engagement', 'time_in_section', sectionName, Math.round(seconds), {\r\n section_name: sectionName,\r\n time_seconds: Math.round(seconds),\r\n });\r\n};\r\n\r\n// ====================================\r\n// NAVEGACIÓN\r\n// ====================================\r\n\r\nexport const trackSectionClick = (section: string): void => {\r\n trackEvent('Navigation', 'section_click', section);\r\n};\r\n\r\nexport const trackScrollTo = (section: string): void => {\r\n trackEvent('Navigation', 'scroll_to', section);\r\n};\r\n\r\n// ====================================\r\n// PRICING\r\n// ====================================\r\n\r\nexport const trackPricingCardClick = (productName: string, price: number | string, additionalData?: TrackingEventData): void => {\r\n trackEvent('Pricing', 'card_click', productName, undefined, {\r\n product_name: productName,\r\n price: String(price),\r\n ...additionalData,\r\n });\r\n};\r\n\r\n// ====================================\r\n// CONTACTO\r\n// ====================================\r\n\r\nexport const trackContactClick = (method: string): void => {\r\n trackEvent('Contact', 'click', method, undefined, {\r\n contact_method: method,\r\n });\r\n};\r\n\r\n// ====================================\r\n// COMPARTIR\r\n// ====================================\r\n\r\nexport const trackShare = (platform: string, content: string): void => {\r\n trackEvent('Share', 'click', platform, undefined, {\r\n share_platform: platform,\r\n share_content: content,\r\n });\r\n};\r\n\r\n// ====================================\r\n// DESCARGAS\r\n// ====================================\r\n\r\nexport const trackDownload = (fileName: string, fileType: string): void => {\r\n trackEvent('Download', 'click', fileName, undefined, {\r\n file_name: fileName,\r\n file_type: fileType,\r\n });\r\n};\r\n","/**\r\n * LandingTracker — Orquestador de sesión completa\r\n * Inicializa y gestiona scroll tracking, click heatmap, section dwell time,\r\n * form auto-tracking y sesión start/end.\r\n * Framework-agnostic.\r\n */\r\n\r\nimport { trackEvent, captureUTMParams, captureUserIdFromURL, getUserName, trackPageView } from '../core/analytics';\r\nimport { pushToDataLayer, injectGTMScript } from '../core/gtm';\r\nimport { trackCTAClick, trackFormStart, trackFormSubmit, trackTimeInSection } from '../trackers/events';\r\nimport type { LandingTrackerConfig, ClickData } from '../core/types';\r\n\r\nexport class LandingTracker {\r\n private config: LandingTrackerConfig | null = null;\r\n private isInitialized = false;\r\n private sessionStartTime: number = 0;\r\n private scrollMilestonesReached = new Set<number>();\r\n private clicks: ClickData[] = [];\r\n private sectionTimers: Map<string, number> = new Map();\r\n private observers: IntersectionObserver[] = [];\r\n private listeners: Array<{ target: EventTarget; event: string; handler: EventListener }> = [];\r\n\r\n /**\r\n * Inicializa el tracking de la landing con la configuración dada.\r\n */\r\n init(config: LandingTrackerConfig): void {\r\n if (typeof window === 'undefined') return;\r\n if (this.isInitialized) return;\r\n\r\n this.config = {\r\n enableScrollTracking: true,\r\n enableCTATracking: true,\r\n enableTimeTracking: true,\r\n enableSectionTracking: true,\r\n enableFormTracking: true,\r\n enableHeatmap: false,\r\n eventSuffix: '',\r\n ...config,\r\n };\r\n\r\n this.isInitialized = true;\r\n this.sessionStartTime = Date.now();\r\n\r\n // Capturar datos iniciales del visitante\r\n this.captureInitialData();\r\n\r\n // Trackear carga de página\r\n trackPageView(this.config.pagePath);\r\n\r\n // Inyectar GTM si se proporcionó\r\n if (this.config.gtmId) {\r\n injectGTMScript(this.config.gtmId);\r\n pushToDataLayer(this.getEventName('page_load'), {\r\n page_path: this.config.pagePath,\r\n page_title: this.config.pageName,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n // Inicializar módulos de tracking\r\n if (this.config.enableScrollTracking) this.initScrollTracking();\r\n if (this.config.enableCTATracking) this.initCTATracking();\r\n if (this.config.enableTimeTracking) this.initTimeTracking();\r\n if (this.config.enableSectionTracking) this.initSectionTracking();\r\n if (this.config.enableFormTracking) this.initFormTracking();\r\n if (this.config.enableHeatmap) this.initClickHeatmap();\r\n\r\n // Trackear inicio de sesión\r\n this.trackSessionStart();\r\n }\r\n\r\n /**\r\n * Destruye el tracker y limpia todos los observers y listeners.\r\n */\r\n destroy(): void {\r\n // Cleanup observers\r\n this.observers.forEach((obs) => obs.disconnect());\r\n this.observers = [];\r\n\r\n // Cleanup event listeners\r\n this.listeners.forEach(({ target, event, handler }) => {\r\n target.removeEventListener(event, handler);\r\n });\r\n this.listeners = [];\r\n\r\n this.isInitialized = false;\r\n this.config = null;\r\n this.scrollMilestonesReached.clear();\r\n this.clicks = [];\r\n this.sectionTimers.clear();\r\n }\r\n\r\n // ====================================\r\n // MÉTODOS PÚBLICOS DE TRACKING\r\n // ====================================\r\n\r\n /** Trackear click en un CTA */\r\n trackCTAClick(buttonName: string, section: string, additionalData?: Record<string, unknown>): void {\r\n trackCTAClick(buttonName, section, additionalData);\r\n this.pushGTMEvent('cta_click', { button_text: buttonName, section });\r\n }\r\n\r\n /** Trackear conversión */\r\n trackConversion(type: string, value?: number, additionalData?: Record<string, unknown>): void {\r\n trackEvent('Conversion', type, undefined, value, additionalData);\r\n this.pushGTMEvent('conversion', { conversion_type: type, value });\r\n }\r\n\r\n /** Trackear click en FAQs */\r\n trackFAQExpand(question: string, index: number): void {\r\n trackEvent('FAQ', 'expand', question, index);\r\n }\r\n\r\n /** Trackear click social */\r\n trackSocialClick(platform: string, action: string): void {\r\n trackEvent('Social', 'click', platform, undefined, { social_action: action });\r\n }\r\n\r\n /** Trackear click en imagen */\r\n trackImageClick(imageName: string, section: string): void {\r\n trackEvent('Engagement', 'image_click', imageName, undefined, { section });\r\n }\r\n\r\n /** Trackear scroll a sección */\r\n trackScrollTo(section: string): void {\r\n trackEvent('Navigation', 'scroll_to', section);\r\n }\r\n\r\n /** Trackear share */\r\n trackShare(platform: string, content: string): void {\r\n trackEvent('Share', 'click', platform, undefined, { share_content: content });\r\n }\r\n\r\n /** Trackear descarga */\r\n trackDownload(fileName: string, fileType: string): void {\r\n trackEvent('Download', 'click', fileName, undefined, { file_type: fileType });\r\n }\r\n\r\n /** Obtener datos de la sesión actual */\r\n getSessionData(): { duration: number; clicks: number; scrollMilestones: number[] } {\r\n return {\r\n duration: Math.round((Date.now() - this.sessionStartTime) / 1000),\r\n clicks: this.clicks.length,\r\n scrollMilestones: Array.from(this.scrollMilestonesReached),\r\n };\r\n }\r\n\r\n // ====================================\r\n // MÉTODOS PRIVADOS\r\n // ====================================\r\n\r\n private captureInitialData(): void {\r\n const utmParams = captureUTMParams();\r\n const userId = captureUserIdFromURL();\r\n const userName = getUserName();\r\n\r\n const prefix = this.config!.pagePath.replace('/', '');\r\n\r\n try {\r\n localStorage.setItem(`${prefix}_entry_time`, new Date().toISOString());\r\n if (utmParams) {\r\n localStorage.setItem(`${prefix}_utm_params`, JSON.stringify(utmParams));\r\n }\r\n if (userId) {\r\n localStorage.setItem(`${prefix}_user_id`, userId);\r\n }\r\n } catch {\r\n // localStorage puede no estar disponible\r\n }\r\n\r\n // Enviar evento de carga\r\n trackEvent('Landing', 'page_load', this.config!.pageName, undefined, {\r\n page_path: this.config!.pagePath,\r\n user_id: userId,\r\n user_name: userName,\r\n utm_params: utmParams,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n private trackSessionStart(): void {\r\n trackEvent('Session', 'start', this.config!.pageName, undefined, {\r\n page_path: this.config!.pagePath,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n private getEventName(baseName: string): string {\r\n const suffix = this.config?.eventSuffix || '';\r\n return suffix ? `${baseName}${suffix}` : baseName;\r\n }\r\n\r\n private pushGTMEvent(eventName: string, data?: Record<string, unknown>): void {\r\n if (this.config?.gtmId) {\r\n pushToDataLayer(this.getEventName(eventName), {\r\n page_path: this.config.pagePath,\r\n ...data,\r\n });\r\n }\r\n }\r\n\r\n private addListener(target: EventTarget, event: string, handler: EventListener): void {\r\n target.addEventListener(event, handler);\r\n this.listeners.push({ target, event, handler });\r\n }\r\n\r\n // --- Scroll Tracking ---\r\n private initScrollTracking(): void {\r\n let scrollTimeout: ReturnType<typeof setTimeout>;\r\n\r\n const handler = (): void => {\r\n clearTimeout(scrollTimeout);\r\n scrollTimeout = setTimeout(() => {\r\n const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;\r\n if (scrollHeight <= 0) return;\r\n\r\n const pct = Math.round((window.scrollY / scrollHeight) * 100);\r\n\r\n for (const milestone of [25, 50, 75, 100]) {\r\n if (pct >= milestone && !this.scrollMilestonesReached.has(milestone)) {\r\n this.scrollMilestonesReached.add(milestone);\r\n\r\n trackEvent('Scroll', 'milestone', `${milestone}%`, milestone, {\r\n page_path: this.config!.pagePath,\r\n scroll_percentage: milestone,\r\n });\r\n\r\n this.pushGTMEvent('scroll_milestone', { scroll_percentage: milestone });\r\n }\r\n }\r\n }, 100);\r\n };\r\n\r\n this.addListener(window, 'scroll', handler as EventListener);\r\n }\r\n\r\n // --- CTA Tracking ---\r\n private initCTATracking(): void {\r\n setTimeout(() => {\r\n const ctaButtons = document.querySelectorAll('button, a[href*=\"#\"], [data-cta]');\r\n ctaButtons.forEach((button) => {\r\n const handler = (): void => {\r\n const el = button as HTMLElement;\r\n const text = el.textContent?.trim() || el.getAttribute('data-cta') || 'Unknown CTA';\r\n const section = el.closest('section')?.id || el.closest('[data-section]')?.getAttribute('data-section') || 'unknown';\r\n\r\n this.trackCTAClick(text, section);\r\n };\r\n this.addListener(button, 'click', handler as EventListener);\r\n });\r\n }, 2000); // Esperar a que el DOM esté completo\r\n }\r\n\r\n // --- Time Tracking (page exit) ---\r\n private initTimeTracking(): void {\r\n const handler = (): void => {\r\n const duration = Math.round((Date.now() - this.sessionStartTime) / 1000);\r\n\r\n trackEvent('Session', 'end', this.config!.pageName, duration, {\r\n session_duration: duration,\r\n page_path: this.config!.pagePath,\r\n exit_timestamp: Date.now(),\r\n });\r\n\r\n this.pushGTMEvent('page_exit', {\r\n session_duration: duration,\r\n exit_timestamp: Date.now(),\r\n });\r\n };\r\n\r\n this.addListener(window, 'beforeunload', handler as EventListener);\r\n }\r\n\r\n // --- Section Dwell Time (IntersectionObserver) ---\r\n private initSectionTracking(): void {\r\n const sectionEntryTimes = new Map<string, number>();\r\n\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n const sectionId = (entry.target as HTMLElement).id || (entry.target as HTMLElement).getAttribute('data-section') || 'unknown';\r\n\r\n if (entry.isIntersecting) {\r\n sectionEntryTimes.set(sectionId, Date.now());\r\n } else {\r\n const entryTime = sectionEntryTimes.get(sectionId);\r\n if (entryTime) {\r\n const seconds = (Date.now() - entryTime) / 1000;\r\n trackTimeInSection(sectionId, seconds);\r\n sectionEntryTimes.delete(sectionId);\r\n }\r\n }\r\n });\r\n },\r\n { threshold: 0.5 },\r\n );\r\n\r\n // Observar todas las secciones\r\n setTimeout(() => {\r\n const sections = document.querySelectorAll('section, [data-section]');\r\n sections.forEach((section) => observer.observe(section));\r\n }, 1000);\r\n\r\n this.observers.push(observer);\r\n }\r\n\r\n // --- Form Auto-Tracking ---\r\n private initFormTracking(): void {\r\n setTimeout(() => {\r\n const forms = document.querySelectorAll('form');\r\n forms.forEach((form) => {\r\n const formName = form.getAttribute('name') || form.id || 'unknown-form';\r\n\r\n // Track form focus (start)\r\n let started = false;\r\n const inputs = form.querySelectorAll('input, textarea, select');\r\n inputs.forEach((input) => {\r\n this.addListener(input, 'focus', (() => {\r\n if (!started) {\r\n started = true;\r\n trackFormStart(formName);\r\n }\r\n }) as EventListener);\r\n });\r\n\r\n // Track form submit\r\n this.addListener(form, 'submit', ((_e: Event) => {\r\n trackFormSubmit(formName, true, {\r\n page_path: this.config!.pagePath,\r\n });\r\n this.pushGTMEvent('form_submit', { form_name: formName });\r\n }) as EventListener);\r\n });\r\n }, 2000);\r\n }\r\n\r\n // --- Click Heatmap ---\r\n private initClickHeatmap(): void {\r\n const handler = (e: Event): void => {\r\n const mouseEvent = e as MouseEvent;\r\n const target = mouseEvent.target as HTMLElement;\r\n\r\n const clickData: ClickData = {\r\n x: mouseEvent.clientX,\r\n y: mouseEvent.clientY,\r\n element: target.tagName.toLowerCase(),\r\n section: target.closest('section')?.id || 'unknown',\r\n timestamp: Date.now(),\r\n viewportWidth: window.innerWidth,\r\n viewportHeight: window.innerHeight,\r\n };\r\n\r\n this.clicks.push(clickData);\r\n };\r\n\r\n this.addListener(document, 'click', handler as EventListener);\r\n }\r\n}\r\n\r\n/**\r\n * Factory function para crear un LandingTracker.\r\n *\r\n * @example\r\n * ```ts\r\n * const tracker = createLandingTracker({\r\n * pagePath: '/kin',\r\n * pageName: 'Kin Landing Page',\r\n * gtmId: 'GTM-5GMQNFMN',\r\n * })\r\n * // Al desmontar:\r\n * tracker.destroy()\r\n * ```\r\n */\r\nexport const createLandingTracker = (config: LandingTrackerConfig): LandingTracker => {\r\n const tracker = new LandingTracker();\r\n tracker.init(config);\r\n return tracker;\r\n};\r\n","/**\r\n * React Hook: useLandingPageTracking\r\n * Inicializa el tracking completo de una landing page.\r\n * Wrapper React del LandingTracker framework-agnostic.\r\n */\r\n\r\n'use client';\r\n\r\nimport { useEffect, useRef } from 'react';\r\nimport { LandingTracker } from '../orchestrator/landing-tracker';\r\nimport type { LandingTrackerConfig } from '../core/types';\r\n\r\n/**\r\n * Hook para inicializar tracking completo de una landing page.\r\n * Crea un LandingTracker, lo inicializa y lo destruye al desmontar.\r\n *\r\n * @example\r\n * ```tsx\r\n * function KinLanding() {\r\n * const tracker = useLandingPageTracking({\r\n * pagePath: '/kin',\r\n * pageName: 'Kin Landing Page',\r\n * gtmId: 'GTM-5GMQNFMN',\r\n * })\r\n *\r\n * // Usar tracker para eventos manuales\r\n * const handleClick = () => tracker?.trackCTAClick('Unirse', 'hero')\r\n * }\r\n * ```\r\n */\r\nexport function useLandingPageTracking(config: LandingTrackerConfig): LandingTracker | null {\r\n const trackerRef = useRef<LandingTracker | null>(null);\r\n const initializedRef = useRef(false);\r\n\r\n useEffect(() => {\r\n if (initializedRef.current) return;\r\n initializedRef.current = true;\r\n\r\n const tracker = new LandingTracker();\r\n tracker.init(config);\r\n trackerRef.current = tracker;\r\n\r\n return () => {\r\n tracker.destroy();\r\n trackerRef.current = null;\r\n };\r\n }, [config.pagePath]);\r\n\r\n return trackerRef.current;\r\n}\r\n","/**\r\n * VideoTracker — Tracking avanzado de video para GA4\r\n * Gestiona el estado de reproducción por video y emite eventos a Google Analytics.\r\n * Framework-agnostic (no requiere React).\r\n */\r\n\r\nimport { trackEvent } from '../core/analytics';\r\nimport type { VideoTrackingState } from '../core/types';\r\n\r\nexport class VideoTracker {\r\n private videos: Map<string, VideoTrackingState> = new Map();\r\n private watchTimeIntervals: Map<string, ReturnType<typeof setInterval>> = new Map();\r\n\r\n /**\r\n * Inicializa el tracking para un video.\r\n */\r\n initVideo(videoId: string): void {\r\n if (this.videos.has(videoId)) return;\r\n\r\n const state: VideoTrackingState = {\r\n videoId,\r\n startTime: Date.now(),\r\n totalWatchTime: 0,\r\n playCount: 0,\r\n pauseCount: 0,\r\n seekCount: 0,\r\n completionPercentage: 0,\r\n lastPlayTimestamp: 0,\r\n isPlaying: false,\r\n currentTime: 0,\r\n duration: 0,\r\n pauseTimes: [],\r\n seekEvents: [],\r\n progressMilestones: new Set(),\r\n playbackSpeed: 1,\r\n };\r\n\r\n this.videos.set(videoId, state);\r\n\r\n trackEvent('Video', 'init', videoId, undefined, {\r\n video_id: videoId,\r\n timestamp: Date.now(),\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de evento play.\r\n */\r\n trackPlay(videoId: string, currentTime: number = 0): void {\r\n const state = this.getOrCreateState(videoId);\r\n\r\n state.playCount++;\r\n state.isPlaying = true;\r\n state.lastPlayTimestamp = Date.now();\r\n state.currentTime = currentTime;\r\n\r\n // Iniciar conteo de tiempo de reproducción\r\n this.startWatchTimeTracking(videoId);\r\n\r\n trackEvent('Video', 'play', videoId, undefined, {\r\n video_id: videoId,\r\n play_count: state.playCount,\r\n current_time: Math.round(currentTime),\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de evento pause.\r\n */\r\n trackPause(videoId: string, currentTime: number, duration?: number): void {\r\n const state = this.getOrCreateState(videoId);\r\n\r\n state.pauseCount++;\r\n state.isPlaying = false;\r\n state.currentTime = currentTime;\r\n state.pauseTimes.push(currentTime);\r\n\r\n if (duration) {\r\n state.duration = duration;\r\n state.completionPercentage = Math.round((currentTime / duration) * 100);\r\n }\r\n\r\n // Detener conteo de tiempo\r\n this.stopWatchTimeTracking(videoId);\r\n\r\n trackEvent('Video', 'pause', videoId, undefined, {\r\n video_id: videoId,\r\n pause_count: state.pauseCount,\r\n current_time: Math.round(currentTime),\r\n completion_percentage: state.completionPercentage,\r\n total_watch_time: Math.round(state.totalWatchTime),\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de evento seek (saltar en el timeline).\r\n */\r\n trackSeek(videoId: string, fromTime: number, toTime: number): void {\r\n const state = this.getOrCreateState(videoId);\r\n\r\n state.seekCount++;\r\n state.seekEvents.push({ from: fromTime, to: toTime });\r\n\r\n trackEvent('Video', 'seek', videoId, undefined, {\r\n video_id: videoId,\r\n seek_count: state.seekCount,\r\n from_time: Math.round(fromTime),\r\n to_time: Math.round(toTime),\r\n skip_duration: Math.round(toTime - fromTime),\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de progreso (milestones: 25%, 50%, 75%).\r\n */\r\n trackProgress(videoId: string, percentage: number, currentTime: number): void {\r\n const state = this.getOrCreateState(videoId);\r\n const milestone = Math.floor(percentage / 25) * 25;\r\n\r\n // Solo emitir cada milestone una vez\r\n if (milestone > 0 && milestone < 100 && !state.progressMilestones.has(milestone)) {\r\n state.progressMilestones.add(milestone);\r\n\r\n trackEvent('Video', 'progress', videoId, milestone, {\r\n video_id: videoId,\r\n progress_percentage: milestone,\r\n current_time: Math.round(currentTime),\r\n total_watch_time: Math.round(state.totalWatchTime),\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Tracking de completación del video (≥95%).\r\n */\r\n trackComplete(videoId: string, totalDuration: number): void {\r\n const state = this.getOrCreateState(videoId);\r\n\r\n state.completionPercentage = 100;\r\n state.duration = totalDuration;\r\n this.stopWatchTimeTracking(videoId);\r\n\r\n trackEvent('Video', 'complete', videoId, undefined, {\r\n video_id: videoId,\r\n total_duration: Math.round(totalDuration),\r\n total_watch_time: Math.round(state.totalWatchTime),\r\n play_count: state.playCount,\r\n pause_count: state.pauseCount,\r\n seek_count: state.seekCount,\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de fin del video (evento ended nativo).\r\n */\r\n trackEnd(videoId: string): void {\r\n const state = this.getOrCreateState(videoId);\r\n\r\n state.isPlaying = false;\r\n this.stopWatchTimeTracking(videoId);\r\n\r\n trackEvent('Video', 'end', videoId, undefined, {\r\n video_id: videoId,\r\n total_watch_time: Math.round(state.totalWatchTime),\r\n play_count: state.playCount,\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de cambio de velocidad de reproducción.\r\n */\r\n trackSpeedChange(videoId: string, speed: number): void {\r\n const state = this.getOrCreateState(videoId);\r\n state.playbackSpeed = speed;\r\n\r\n trackEvent('Video', 'speed_change', videoId, undefined, {\r\n video_id: videoId,\r\n playback_speed: speed,\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de pantalla completa.\r\n */\r\n trackFullscreen(videoId: string, isFullscreen: boolean): void {\r\n trackEvent('Video', isFullscreen ? 'fullscreen_enter' : 'fullscreen_exit', videoId, undefined, {\r\n video_id: videoId,\r\n is_fullscreen: isFullscreen,\r\n });\r\n }\r\n\r\n /**\r\n * Tracking de no interacción (el usuario está en la página pero no interactúa con el video).\r\n */\r\n trackNoInteraction(videoId: string, timeOnPage: number): void {\r\n trackEvent('Video', 'no_interaction', videoId, undefined, {\r\n video_id: videoId,\r\n time_on_page: Math.round(timeOnPage),\r\n });\r\n }\r\n\r\n /**\r\n * Obtiene el estado actual de un video.\r\n */\r\n getState(videoId: string): VideoTrackingState | undefined {\r\n return this.videos.get(videoId);\r\n }\r\n\r\n /**\r\n * Limpia el tracking de un video específico.\r\n */\r\n cleanup(videoId: string): void {\r\n this.stopWatchTimeTracking(videoId);\r\n this.videos.delete(videoId);\r\n }\r\n\r\n /**\r\n * Limpia todo el tracking.\r\n */\r\n cleanupAll(): void {\r\n for (const videoId of this.videos.keys()) {\r\n this.stopWatchTimeTracking(videoId);\r\n }\r\n this.videos.clear();\r\n }\r\n\r\n // --- Métodos privados ---\r\n\r\n private getOrCreateState(videoId: string): VideoTrackingState {\r\n if (!this.videos.has(videoId)) {\r\n this.initVideo(videoId);\r\n }\r\n return this.videos.get(videoId)!;\r\n }\r\n\r\n private startWatchTimeTracking(videoId: string): void {\r\n this.stopWatchTimeTracking(videoId);\r\n\r\n const interval = setInterval(() => {\r\n const state = this.videos.get(videoId);\r\n if (state?.isPlaying) {\r\n state.totalWatchTime += 1;\r\n }\r\n }, 1000);\r\n\r\n this.watchTimeIntervals.set(videoId, interval);\r\n }\r\n\r\n private stopWatchTimeTracking(videoId: string): void {\r\n const interval = this.watchTimeIntervals.get(videoId);\r\n if (interval) {\r\n clearInterval(interval);\r\n this.watchTimeIntervals.delete(videoId);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Instancia singleton del VideoTracker.\r\n * Usar cuando se necesita una sola instancia global.\r\n */\r\nexport const videoTracker = new VideoTracker();\r\n","/**\r\n * React Hooks: useVideoTracking, useWistiaVideoTracking\r\n * Hooks para integrar tracking de video en componentes React.\r\n */\r\n\r\n'use client';\r\n\r\nimport { useEffect, useRef, useState } from 'react';\r\nimport { videoTracker } from '../trackers/video-tracker';\r\nimport type { UseVideoTrackingOptions, VideoStats } from '../core/types';\r\n\r\n// ====================================\r\n// HTML5 Video Tracking Hook\r\n// ====================================\r\n\r\ninterface VideoTrackingReturn {\r\n /** Porcentaje de progreso (0-100) */\r\n progress: number;\r\n /** Si el video se está reproduciendo */\r\n isPlaying: boolean;\r\n /** Tiempo actual en segundos */\r\n currentTime: number;\r\n /** Duración total en segundos */\r\n duration: number;\r\n}\r\n\r\n/**\r\n * Hook para tracking de un elemento <video> HTML5 nativo.\r\n *\r\n * @example\r\n * ```tsx\r\n * function VideoPlayer() {\r\n * const videoRef = useRef<HTMLVideoElement>(null)\r\n * const { progress, isPlaying } = useVideoTracking({\r\n * videoId: 'hero-video',\r\n * videoElement: videoRef.current,\r\n * onComplete: () => console.log('Video completado'),\r\n * onProgress: (pct) => console.log(`Progreso: ${pct}%`),\r\n * })\r\n *\r\n * return <video ref={videoRef} src=\"/video.mp4\" />\r\n * }\r\n * ```\r\n */\r\nexport function useVideoTracking({ videoId, videoElement, onComplete, onProgress }: UseVideoTrackingOptions): VideoTrackingReturn {\r\n const [progress, setProgress] = useState(0);\r\n const [isPlaying, setIsPlaying] = useState(false);\r\n const [currentTime, setCurrentTime] = useState(0);\r\n const [duration, setDuration] = useState(0);\r\n const milestonesRef = useRef(new Set<number>());\r\n const noInteractionTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n useEffect(() => {\r\n if (!videoElement) return;\r\n\r\n videoTracker.initVideo(videoId);\r\n\r\n const handlePlay = (): void => {\r\n setIsPlaying(true);\r\n videoTracker.trackPlay(videoId, videoElement.currentTime);\r\n\r\n // Cancelar timer de no interacción\r\n if (noInteractionTimerRef.current) {\r\n clearTimeout(noInteractionTimerRef.current);\r\n }\r\n };\r\n\r\n const handlePause = (): void => {\r\n setIsPlaying(false);\r\n videoTracker.trackPause(videoId, videoElement.currentTime, videoElement.duration);\r\n };\r\n\r\n const handleTimeUpdate = (): void => {\r\n const ct = videoElement.currentTime;\r\n const dur = videoElement.duration || 0;\r\n const pct = dur > 0 ? Math.round((ct / dur) * 100) : 0;\r\n\r\n setCurrentTime(ct);\r\n setDuration(dur);\r\n setProgress(pct);\r\n\r\n // Milestones\r\n for (const milestone of [25, 50, 75]) {\r\n if (pct >= milestone && !milestonesRef.current.has(milestone)) {\r\n milestonesRef.current.add(milestone);\r\n videoTracker.trackProgress(videoId, pct, ct);\r\n onProgress?.(milestone);\r\n }\r\n }\r\n\r\n // Completación\r\n if (pct >= 95 && !milestonesRef.current.has(100)) {\r\n milestonesRef.current.add(100);\r\n videoTracker.trackComplete(videoId, dur);\r\n onComplete?.();\r\n }\r\n };\r\n\r\n const handleSeeking = (): void => {\r\n // Track seek con el tiempo anterior\r\n const state = videoTracker.getState(videoId);\r\n if (state) {\r\n videoTracker.trackSeek(videoId, state.currentTime, videoElement.currentTime);\r\n }\r\n };\r\n\r\n const handleEnded = (): void => {\r\n setIsPlaying(false);\r\n videoTracker.trackEnd(videoId);\r\n };\r\n\r\n const handleRateChange = (): void => {\r\n videoTracker.trackSpeedChange(videoId, videoElement.playbackRate);\r\n };\r\n\r\n // Bind events\r\n videoElement.addEventListener('play', handlePlay);\r\n videoElement.addEventListener('pause', handlePause);\r\n videoElement.addEventListener('timeupdate', handleTimeUpdate);\r\n videoElement.addEventListener('seeking', handleSeeking);\r\n videoElement.addEventListener('ended', handleEnded);\r\n videoElement.addEventListener('ratechange', handleRateChange);\r\n\r\n // No interaction timer (30s)\r\n noInteractionTimerRef.current = setTimeout(() => {\r\n if (!videoElement.paused) return;\r\n videoTracker.trackNoInteraction(videoId, 30);\r\n }, 30000);\r\n\r\n return () => {\r\n videoElement.removeEventListener('play', handlePlay);\r\n videoElement.removeEventListener('pause', handlePause);\r\n videoElement.removeEventListener('timeupdate', handleTimeUpdate);\r\n videoElement.removeEventListener('seeking', handleSeeking);\r\n videoElement.removeEventListener('ended', handleEnded);\r\n videoElement.removeEventListener('ratechange', handleRateChange);\r\n\r\n if (noInteractionTimerRef.current) {\r\n clearTimeout(noInteractionTimerRef.current);\r\n }\r\n\r\n videoTracker.cleanup(videoId);\r\n };\r\n }, [videoId, videoElement, onComplete, onProgress]);\r\n\r\n return { progress, isPlaying, currentTime, duration };\r\n}\r\n\r\n// ====================================\r\n// Wistia Video Tracking Hook\r\n// ====================================\r\n\r\ninterface WistiaTrackingReturn {\r\n /** Estadísticas actuales del video */\r\n stats: VideoStats;\r\n /** Si el video fue completado */\r\n isCompleted: boolean;\r\n}\r\n\r\n/**\r\n * Hook para tracking de un video Wistia.\r\n * Sondea window._wq hasta que el SDK de Wistia esté listo.\r\n *\r\n * @example\r\n * ```tsx\r\n * function WistiaPlayer({ mediaId }: { mediaId: string }) {\r\n * const { stats, isCompleted } = useWistiaVideoTracking(mediaId)\r\n * return <div>Visto: {stats.timeWatched}s {isCompleted && '✅'}</div>\r\n * }\r\n * ```\r\n */\r\nexport function useWistiaVideoTracking(mediaId: string): WistiaTrackingReturn {\r\n const [stats, setStats] = useState<VideoStats>({\r\n timeWatched: 0,\r\n completed: false,\r\n lastUpdate: Date.now(),\r\n });\r\n const boundRef = useRef(false);\r\n\r\n useEffect(() => {\r\n if (!mediaId || boundRef.current) return;\r\n\r\n const bindWistia = (): boolean => {\r\n if (!window._wq) return false;\r\n\r\n boundRef.current = true;\r\n\r\n window._wq.push({\r\n id: mediaId,\r\n onReady: (video: unknown) => {\r\n const v = video as {\r\n time: () => number;\r\n duration: () => number;\r\n bind: (event: string, cb: () => void) => void;\r\n };\r\n const update = (): void => {\r\n const t = v.time();\r\n const d = v.duration();\r\n const pct = d > 0 ? Math.round((t / d) * 100) : 0;\r\n\r\n setStats({\r\n timeWatched: Math.round(t),\r\n completed: pct >= 95,\r\n lastUpdate: Date.now(),\r\n });\r\n };\r\n\r\n v.bind('play', update);\r\n v.bind('pause', update);\r\n v.bind('secondchange', update);\r\n v.bind('end', () => {\r\n setStats({\r\n timeWatched: Math.round(v.duration()),\r\n completed: true,\r\n lastUpdate: Date.now(),\r\n });\r\n });\r\n },\r\n });\r\n\r\n return true;\r\n };\r\n\r\n // Polling hasta que Wistia esté disponible\r\n const interval = setInterval(() => {\r\n if (bindWistia()) {\r\n clearInterval(interval);\r\n }\r\n }, 100);\r\n\r\n // Timeout: dejar de intentar después de 15s\r\n const timeout = setTimeout(() => clearInterval(interval), 15000);\r\n\r\n return () => {\r\n clearInterval(interval);\r\n clearTimeout(timeout);\r\n };\r\n }, [mediaId]);\r\n\r\n return {\r\n stats,\r\n isCompleted: stats.completed,\r\n };\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metodokorexmk/tracking",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Sistema de tracking reutilizable para landing pages con GA4, GTM y video tracking multi-plataforma",
|
|
5
|
+
"author": "MetodoKorexMK",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "./dist/index.cjs",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"require": {
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"default": "./dist/index.cjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"./react": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./dist/react/index.d.ts",
|
|
28
|
+
"default": "./dist/react/index.js"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./dist/react/index.d.cts",
|
|
32
|
+
"default": "./dist/react/index.cjs"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"dev": "tsup --watch",
|
|
43
|
+
"test": "vitest run --reporter=verbose",
|
|
44
|
+
"test:watch": "vitest",
|
|
45
|
+
"test:coverage": "vitest run --coverage",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"lint": "eslint \"src/**/*.ts\" \"__tests__/**/*.ts\"",
|
|
48
|
+
"lint:fix": "eslint \"src/**/*.ts\" \"__tests__/**/*.ts\" --fix",
|
|
49
|
+
"format": "prettier --write \"src/**/*.ts\" \"__tests__/**/*.ts\"",
|
|
50
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"__tests__/**/*.ts\"",
|
|
51
|
+
"prepublishOnly": "npm run lint && npm run test && npm run build"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"react": ">=18.0.0",
|
|
55
|
+
"react-ga4": ">=2.0.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"react": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/react": "^19.0.0",
|
|
64
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
65
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
66
|
+
"eslint": "^10.0.2",
|
|
67
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
68
|
+
"eslint-config-airbnb-typescript": "^18.0.0",
|
|
69
|
+
"eslint-config-prettier": "^10.1.8",
|
|
70
|
+
"eslint-plugin-import": "^2.32.0",
|
|
71
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
72
|
+
"jsdom": "^26.0.0",
|
|
73
|
+
"prettier": "^3.8.1",
|
|
74
|
+
"react": "^19.0.0",
|
|
75
|
+
"react-ga4": "^2.1.0",
|
|
76
|
+
"tsup": "^8.0.0",
|
|
77
|
+
"typescript": "^5.7.0",
|
|
78
|
+
"vitest": "^3.0.0"
|
|
79
|
+
},
|
|
80
|
+
"keywords": [
|
|
81
|
+
"tracking",
|
|
82
|
+
"google-analytics",
|
|
83
|
+
"ga4",
|
|
84
|
+
"gtm",
|
|
85
|
+
"google-tag-manager",
|
|
86
|
+
"video-tracking",
|
|
87
|
+
"wistia",
|
|
88
|
+
"voomly",
|
|
89
|
+
"landing-page",
|
|
90
|
+
"analytics"
|
|
91
|
+
]
|
|
92
|
+
}
|