@metodokorexmk/tracking 1.1.0 → 1.2.1
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/dist/index.cjs +45 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +216 -211
- package/dist/index.d.ts +216 -211
- package/dist/index.js +45 -40
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +27 -27
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +54 -54
- package/dist/react/index.d.ts +54 -54
- package/dist/react/index.js +27 -27
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -227,7 +227,7 @@ var LandingTracker = class {
|
|
|
227
227
|
this.listeners = [];
|
|
228
228
|
}
|
|
229
229
|
/**
|
|
230
|
-
*
|
|
230
|
+
* Initializes landing page tracking with the given configuration.
|
|
231
231
|
*/
|
|
232
232
|
init(config) {
|
|
233
233
|
if (typeof window === "undefined") return;
|
|
@@ -263,7 +263,7 @@ var LandingTracker = class {
|
|
|
263
263
|
this.trackSessionStart();
|
|
264
264
|
}
|
|
265
265
|
/**
|
|
266
|
-
*
|
|
266
|
+
* Destroys the tracker and cleans up all observers and listeners.
|
|
267
267
|
*/
|
|
268
268
|
destroy() {
|
|
269
269
|
this.observers.forEach((obs) => obs.disconnect());
|
|
@@ -279,43 +279,43 @@ var LandingTracker = class {
|
|
|
279
279
|
this.sectionTimers.clear();
|
|
280
280
|
}
|
|
281
281
|
// ====================================
|
|
282
|
-
//
|
|
282
|
+
// PUBLIC TRACKING METHODS
|
|
283
283
|
// ====================================
|
|
284
|
-
/**
|
|
284
|
+
/** Track a CTA click */
|
|
285
285
|
trackCTAClick(buttonName, section, additionalData) {
|
|
286
286
|
trackCTAClick(buttonName, section, additionalData);
|
|
287
287
|
this.pushGTMEvent("cta_click", { button_text: buttonName, section });
|
|
288
288
|
}
|
|
289
|
-
/**
|
|
289
|
+
/** Track a conversion */
|
|
290
290
|
trackConversion(type, value, additionalData) {
|
|
291
291
|
trackEvent("Conversion", type, void 0, value, additionalData);
|
|
292
292
|
this.pushGTMEvent("conversion", { conversion_type: type, value });
|
|
293
293
|
}
|
|
294
|
-
/**
|
|
294
|
+
/** Track FAQ expansion */
|
|
295
295
|
trackFAQExpand(question, index) {
|
|
296
296
|
trackEvent("FAQ", "expand", question, index);
|
|
297
297
|
}
|
|
298
|
-
/**
|
|
298
|
+
/** Track social link click */
|
|
299
299
|
trackSocialClick(platform, action) {
|
|
300
300
|
trackEvent("Social", "click", platform, void 0, { social_action: action });
|
|
301
301
|
}
|
|
302
|
-
/**
|
|
302
|
+
/** Track image click */
|
|
303
303
|
trackImageClick(imageName, section) {
|
|
304
304
|
trackEvent("Engagement", "image_click", imageName, void 0, { section });
|
|
305
305
|
}
|
|
306
|
-
/**
|
|
306
|
+
/** Track scroll to a section */
|
|
307
307
|
trackScrollTo(section) {
|
|
308
308
|
trackEvent("Navigation", "scroll_to", section);
|
|
309
309
|
}
|
|
310
|
-
/**
|
|
310
|
+
/** Track content share */
|
|
311
311
|
trackShare(platform, content) {
|
|
312
312
|
trackEvent("Share", "click", platform, void 0, { share_content: content });
|
|
313
313
|
}
|
|
314
|
-
/**
|
|
314
|
+
/** Track file download */
|
|
315
315
|
trackDownload(fileName, fileType) {
|
|
316
316
|
trackEvent("Download", "click", fileName, void 0, { file_type: fileType });
|
|
317
317
|
}
|
|
318
|
-
/**
|
|
318
|
+
/** Get current session data */
|
|
319
319
|
getSessionData() {
|
|
320
320
|
return {
|
|
321
321
|
duration: Math.round((Date.now() - this.sessionStartTime) / 1e3),
|
|
@@ -324,7 +324,7 @@ var LandingTracker = class {
|
|
|
324
324
|
};
|
|
325
325
|
}
|
|
326
326
|
// ====================================
|
|
327
|
-
//
|
|
327
|
+
// PRIVATE METHODS
|
|
328
328
|
// ====================================
|
|
329
329
|
captureInitialData() {
|
|
330
330
|
const utmParams = captureUTMParams();
|
|
@@ -522,7 +522,7 @@ var VideoTracker = class {
|
|
|
522
522
|
this.watchTimeIntervals = /* @__PURE__ */ new Map();
|
|
523
523
|
}
|
|
524
524
|
/**
|
|
525
|
-
*
|
|
525
|
+
* Initializes tracking for a video.
|
|
526
526
|
*/
|
|
527
527
|
initVideo(videoId) {
|
|
528
528
|
if (this.videos.has(videoId)) return;
|
|
@@ -550,7 +550,7 @@ var VideoTracker = class {
|
|
|
550
550
|
});
|
|
551
551
|
}
|
|
552
552
|
/**
|
|
553
|
-
*
|
|
553
|
+
* Tracks a play event.
|
|
554
554
|
*/
|
|
555
555
|
trackPlay(videoId, currentTime = 0) {
|
|
556
556
|
const state = this.getOrCreateState(videoId);
|
|
@@ -566,7 +566,7 @@ var VideoTracker = class {
|
|
|
566
566
|
});
|
|
567
567
|
}
|
|
568
568
|
/**
|
|
569
|
-
*
|
|
569
|
+
* Tracks a pause event.
|
|
570
570
|
*/
|
|
571
571
|
trackPause(videoId, currentTime, duration) {
|
|
572
572
|
const state = this.getOrCreateState(videoId);
|
|
@@ -588,7 +588,7 @@ var VideoTracker = class {
|
|
|
588
588
|
});
|
|
589
589
|
}
|
|
590
590
|
/**
|
|
591
|
-
*
|
|
591
|
+
* Tracks a seek event (timeline jump).
|
|
592
592
|
*/
|
|
593
593
|
trackSeek(videoId, fromTime, toTime) {
|
|
594
594
|
const state = this.getOrCreateState(videoId);
|
|
@@ -603,7 +603,7 @@ var VideoTracker = class {
|
|
|
603
603
|
});
|
|
604
604
|
}
|
|
605
605
|
/**
|
|
606
|
-
*
|
|
606
|
+
* Tracks progress milestones (25%, 50%, 75%).
|
|
607
607
|
*/
|
|
608
608
|
trackProgress(videoId, percentage, currentTime) {
|
|
609
609
|
const state = this.getOrCreateState(videoId);
|
|
@@ -619,7 +619,7 @@ var VideoTracker = class {
|
|
|
619
619
|
}
|
|
620
620
|
}
|
|
621
621
|
/**
|
|
622
|
-
*
|
|
622
|
+
* Tracks video completion (>=95%).
|
|
623
623
|
*/
|
|
624
624
|
trackComplete(videoId, totalDuration) {
|
|
625
625
|
const state = this.getOrCreateState(videoId);
|
|
@@ -636,7 +636,7 @@ var VideoTracker = class {
|
|
|
636
636
|
});
|
|
637
637
|
}
|
|
638
638
|
/**
|
|
639
|
-
*
|
|
639
|
+
* Tracks video end (native ended event).
|
|
640
640
|
*/
|
|
641
641
|
trackEnd(videoId) {
|
|
642
642
|
const state = this.getOrCreateState(videoId);
|
|
@@ -649,7 +649,7 @@ var VideoTracker = class {
|
|
|
649
649
|
});
|
|
650
650
|
}
|
|
651
651
|
/**
|
|
652
|
-
*
|
|
652
|
+
* Tracks playback speed change.
|
|
653
653
|
*/
|
|
654
654
|
trackSpeedChange(videoId, speed) {
|
|
655
655
|
const state = this.getOrCreateState(videoId);
|
|
@@ -660,7 +660,7 @@ var VideoTracker = class {
|
|
|
660
660
|
});
|
|
661
661
|
}
|
|
662
662
|
/**
|
|
663
|
-
*
|
|
663
|
+
* Tracks fullscreen toggle.
|
|
664
664
|
*/
|
|
665
665
|
trackFullscreen(videoId, isFullscreen) {
|
|
666
666
|
trackEvent("Video", isFullscreen ? "fullscreen_enter" : "fullscreen_exit", videoId, void 0, {
|
|
@@ -669,7 +669,7 @@ var VideoTracker = class {
|
|
|
669
669
|
});
|
|
670
670
|
}
|
|
671
671
|
/**
|
|
672
|
-
*
|
|
672
|
+
* Tracks no interaction (user is on the page but not interacting with the video).
|
|
673
673
|
*/
|
|
674
674
|
trackNoInteraction(videoId, timeOnPage) {
|
|
675
675
|
trackEvent("Video", "no_interaction", videoId, void 0, {
|
|
@@ -678,20 +678,20 @@ var VideoTracker = class {
|
|
|
678
678
|
});
|
|
679
679
|
}
|
|
680
680
|
/**
|
|
681
|
-
*
|
|
681
|
+
* Returns the current state of a video.
|
|
682
682
|
*/
|
|
683
683
|
getState(videoId) {
|
|
684
684
|
return this.videos.get(videoId);
|
|
685
685
|
}
|
|
686
686
|
/**
|
|
687
|
-
*
|
|
687
|
+
* Cleans up tracking for a specific video.
|
|
688
688
|
*/
|
|
689
689
|
cleanup(videoId) {
|
|
690
690
|
this.stopWatchTimeTracking(videoId);
|
|
691
691
|
this.videos.delete(videoId);
|
|
692
692
|
}
|
|
693
693
|
/**
|
|
694
|
-
*
|
|
694
|
+
* Cleans up all video tracking.
|
|
695
695
|
*/
|
|
696
696
|
cleanupAll() {
|
|
697
697
|
for (const videoId of this.videos.keys()) {
|
|
@@ -699,7 +699,7 @@ var VideoTracker = class {
|
|
|
699
699
|
}
|
|
700
700
|
this.videos.clear();
|
|
701
701
|
}
|
|
702
|
-
// ---
|
|
702
|
+
// --- Private methods ---
|
|
703
703
|
getOrCreateState(videoId) {
|
|
704
704
|
if (!this.videos.has(videoId)) {
|
|
705
705
|
this.initVideo(videoId);
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +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":["ReactGA","useRef","useEffect","useState"],"mappings":"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","file":"index.cjs","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// ====================================\r\n// FUNNEL — ETAPA 5: QUIZ (Regla 1)\r\n// ====================================\r\n\r\n/**\r\n * Trackea el inicio de un quiz (Regla 1, Etapa 5).\r\n * El quiz funciona como mecanismo de lead scoring implícito.\r\n */\r\nexport const trackQuizStart = (quizName: string): void => {\r\n trackEvent('Quiz', 'started', quizName, undefined, {\r\n quiz_name: quizName,\r\n });\r\n};\r\n\r\n/**\r\n * Trackea una respuesta individual del quiz.\r\n */\r\nexport const trackQuizAnswer = (\r\n quizName: string,\r\n questionIndex: number,\r\n answer: string,\r\n): void => {\r\n trackEvent('Quiz', 'answer', quizName, questionIndex, {\r\n quiz_name: quizName,\r\n question_index: questionIndex,\r\n answer,\r\n });\r\n};\r\n\r\n/**\r\n * Trackea la finalización del quiz (Regla 1, Etapa 5).\r\n */\r\nexport const trackQuizComplete = (\r\n quizName: string,\r\n totalQuestions?: number,\r\n score?: number,\r\n): void => {\r\n trackEvent('Quiz', 'completed', quizName, totalQuestions, {\r\n quiz_name: quizName,\r\n ...(totalQuestions !== undefined && { total_questions: totalQuestions }),\r\n ...(score !== undefined && { score }),\r\n });\r\n};\r\n\r\n// ====================================\r\n// FUNNEL — ETAPA 6: THANK YOU PAGE (Regla 1)\r\n// ====================================\r\n\r\n/**\r\n * Trackea la visita a la Thank You Page (Regla 1, Etapa 6).\r\n * Confirma que el lead completó el flujo del funnel.\r\n */\r\nexport const trackThankYouPageVisit = (source?: string): void => {\r\n trackEvent('Funnel', 'thank_you_page_visit', source);\r\n};\r\n\r\n// ====================================\r\n// FUNNEL — ETAPA 7: WHATSAPP CLICK (Regla 1)\r\n// ====================================\r\n\r\n/**\r\n * Trackea el clic al link de WhatsApp (Regla 1, Etapa 7).\r\n * Complementa markWhatsAppSent() que marca post-hoc.\r\n */\r\nexport const trackWhatsAppClick = (phoneNumber?: string, section?: string): void => {\r\n trackEvent('WhatsApp', 'click', section, undefined, {\r\n ...(phoneNumber && { phone_number: phoneNumber }),\r\n ...(section && { section }),\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"]}
|
|
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":["ReactGA","useRef","useEffect","useState"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Google Analytics 4 core module.\r\n * Initialization, event tracking, and visitor data capture.\r\n * Framework-agnostic (no Next.js, React, etc. dependency).\r\n */\r\n\r\nimport ReactGA from 'react-ga4';\r\nimport type { TrackingConfig, UTMParams, TrackingEventData } from './types';\r\n\r\n// ====================================\r\n// INTERNAL STATE\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// GA TRACKING ID RESOLUTION\r\n// ====================================\r\n\r\n/**\r\n * Returns the current GA Tracking ID.\r\n * Uses `resolveTrackingId` dynamically if configured, otherwise falls back to the static `trackingId`.\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 if resolver function throws\r\n }\r\n }\r\n\r\n return resolvedTrackingId;\r\n};\r\n\r\n// ====================================\r\n// INITIALIZATION\r\n// ====================================\r\n\r\n/**\r\n * Initializes Google Analytics with the provided configuration.\r\n *\r\n * @example\r\n * ```ts\r\n * // With a fixed ID\r\n * initGA({ trackingId: 'G-XXXXXXX' })\r\n *\r\n * // With dynamic resolution\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 * Reinitializes GA with a new Tracking ID.\r\n * Useful when the ID changes dynamically (e.g., after user login).\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 * Returns whether GA has been initialized.\r\n */\r\nexport const isGAInitialized = (): boolean => isInitialized;\r\n\r\n// ====================================\r\n// VISITOR DATA CAPTURE\r\n// ====================================\r\n\r\n/**\r\n * Extracts UTM parameters from the current URL.\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 * Captures the `user_id` from the URL query string (?user_id=xxx).\r\n * Caches it internally to include in all subsequent events.\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 may be unavailable\r\n }\r\n }\r\n\r\n return userId;\r\n};\r\n\r\n/**\r\n * Resolves the user name from multiple sources.\r\n * Priority: URL > in-memory cache > localStorage > null\r\n */\r\nexport const getUserName = (): string | null => {\r\n if (typeof window === 'undefined') return null;\r\n\r\n // 1. From 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. From in-memory cache\r\n if (cachedUserName) return cachedUserName;\r\n\r\n // 3. From 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 may be unavailable\r\n }\r\n\r\n return null;\r\n};\r\n\r\n/**\r\n * Returns the cached user_id.\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 * Manually sets the user_id (e.g., after 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 // Set as GA4 user property\r\n window.gtag?.('set', { user_id: userId });\r\n};\r\n\r\n/**\r\n * Manually sets the 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// PAGE TRACKING\r\n// ====================================\r\n\r\n/**\r\n * Sends a pageview to GA4.\r\n * Automatically includes UTM params and user_id if available.\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 // Append UTM params to the path if present\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 // Send via 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 may fail if GA4 is not loaded (ad-blockers, slow load, etc.)\r\n }\r\n\r\n // Send via native gtag\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 may not be available\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// EVENT TRACKING\r\n// ====================================\r\n\r\n/**\r\n * Sends a custom event to GA4.\r\n * Uses dual dispatch: `window.gtag()` + `ReactGA.event()`.\r\n * Always includes `user_id` and `user_name` as parameters.\r\n *\r\n * @example\r\n * ```ts\r\n * trackEvent('CTA', 'click', 'Hero Button', 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. Send via native gtag (more reliable)\r\n try {\r\n window.gtag?.('event', action, eventData);\r\n } catch {\r\n // ignore\r\n }\r\n\r\n // 2. Send via ReactGA as 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// UTILITIES\r\n// ====================================\r\n\r\n/**\r\n * Resets all internal state (useful for tests and reinitialization).\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 — Vanilla injection (no Next.js dependency).\r\n * Provides pushToDataLayer and injectGTMScript.\r\n */\r\n\r\n/**\r\n * Injects the Google Tag Manager script into the document <head>.\r\n * Framework-agnostic equivalent of Next.js <Script> component.\r\n *\r\n * @param gtmId - GTM container ID (e.g., '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 // Prevent duplicate injection\r\n const existingScript = document.querySelector(`script[data-gtm-id=\"${gtmId}\"]`);\r\n if (existingScript) return;\r\n\r\n // Initialize 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 // Create and inject the 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 // Inject noscript fallback in 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 * Pushes an event to the GTM dataLayer.\r\n *\r\n * @param eventName - Name of the event\r\n * @param data - Additional event data\r\n *\r\n * @example\r\n * ```ts\r\n * pushToDataLayer('page_view', { page_path: '/kin' })\r\n * pushToDataLayer('cta_click', { button_text: 'Join Now', 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 * Generic GA4 event tracking functions.\r\n * Each function wraps `trackEvent()` with a predefined category and action.\r\n */\r\n\r\nimport { trackEvent } from '../core/analytics';\r\nimport type { TrackingEventData } from '../core/types';\r\n\r\n// ====================================\r\n// CTA / BUTTONS\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// FORMS\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// CONVERSIONS\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// SOCIAL MEDIA\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; // Ignore sections viewed for less than 3 seconds\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// NAVIGATION\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// CONTACT\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// SHARING\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// DOWNLOADS\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// ====================================\r\n// FUNNEL — STAGE 5: QUIZ\r\n// ====================================\r\n\r\n/**\r\n * Tracks quiz start (funnel stage 5).\r\n * The quiz serves as an implicit lead scoring mechanism.\r\n */\r\nexport const trackQuizStart = (quizName: string): void => {\r\n trackEvent('Quiz', 'started', quizName, undefined, {\r\n quiz_name: quizName,\r\n });\r\n};\r\n\r\n/**\r\n * Tracks an individual quiz answer.\r\n */\r\nexport const trackQuizAnswer = (\r\n quizName: string,\r\n questionIndex: number,\r\n answer: string,\r\n): void => {\r\n trackEvent('Quiz', 'answer', quizName, questionIndex, {\r\n quiz_name: quizName,\r\n question_index: questionIndex,\r\n answer,\r\n });\r\n};\r\n\r\n/**\r\n * Tracks quiz completion (funnel stage 5).\r\n */\r\nexport const trackQuizComplete = (\r\n quizName: string,\r\n totalQuestions?: number,\r\n score?: number,\r\n): void => {\r\n trackEvent('Quiz', 'completed', quizName, totalQuestions, {\r\n quiz_name: quizName,\r\n ...(totalQuestions !== undefined && { total_questions: totalQuestions }),\r\n ...(score !== undefined && { score }),\r\n });\r\n};\r\n\r\n// ====================================\r\n// FUNNEL — STAGE 6: THANK YOU PAGE\r\n// ====================================\r\n\r\n/**\r\n * Tracks a Thank You Page visit (funnel stage 6).\r\n * Confirms the lead completed the funnel flow.\r\n */\r\nexport const trackThankYouPageVisit = (source?: string): void => {\r\n trackEvent('Funnel', 'thank_you_page_visit', source);\r\n};\r\n\r\n// ====================================\r\n// FUNNEL — STAGE 7: WHATSAPP CLICK\r\n// ====================================\r\n\r\n/**\r\n * Tracks a WhatsApp link click (funnel stage 7).\r\n * Complements markWhatsAppSent() which marks post-hoc.\r\n */\r\nexport const trackWhatsAppClick = (phoneNumber?: string, section?: string): void => {\r\n trackEvent('WhatsApp', 'click', section, undefined, {\r\n ...(phoneNumber && { phone_number: phoneNumber }),\r\n ...(section && { section }),\r\n });\r\n};\r\n","/**\r\n * LandingTracker — Full session orchestrator.\r\n * Initializes and manages scroll tracking, click heatmap, section dwell time,\r\n * form auto-tracking, and session 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 * Initializes landing page tracking with the given configuration.\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 // Capture initial visitor data\r\n this.captureInitialData();\r\n\r\n // Track page load\r\n trackPageView(this.config.pagePath);\r\n\r\n // Inject GTM if configured\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 // Initialize tracking modules\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 // Track session start\r\n this.trackSessionStart();\r\n }\r\n\r\n /**\r\n * Destroys the tracker and cleans up all observers and 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 // PUBLIC TRACKING METHODS\r\n // ====================================\r\n\r\n /** Track a CTA click */\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 /** Track a conversion */\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 /** Track FAQ expansion */\r\n trackFAQExpand(question: string, index: number): void {\r\n trackEvent('FAQ', 'expand', question, index);\r\n }\r\n\r\n /** Track social link click */\r\n trackSocialClick(platform: string, action: string): void {\r\n trackEvent('Social', 'click', platform, undefined, { social_action: action });\r\n }\r\n\r\n /** Track image click */\r\n trackImageClick(imageName: string, section: string): void {\r\n trackEvent('Engagement', 'image_click', imageName, undefined, { section });\r\n }\r\n\r\n /** Track scroll to a section */\r\n trackScrollTo(section: string): void {\r\n trackEvent('Navigation', 'scroll_to', section);\r\n }\r\n\r\n /** Track content 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 /** Track file download */\r\n trackDownload(fileName: string, fileType: string): void {\r\n trackEvent('Download', 'click', fileName, undefined, { file_type: fileType });\r\n }\r\n\r\n /** Get current session data */\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 // PRIVATE METHODS\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 may be unavailable\r\n }\r\n\r\n // Send page load event\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); // Wait for DOM to be fully rendered\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 // Observe all sections\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 to create a 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 * // On unmount:\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 * Initializes full landing page tracking.\r\n * React wrapper for the framework-agnostic LandingTracker.\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 to initialize full landing page tracking.\r\n * Creates a LandingTracker, initializes it, and destroys it on unmount.\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 * // Use tracker for manual events\r\n * const handleClick = () => tracker?.trackCTAClick('Join', '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 — Advanced video tracking for GA4.\r\n * Manages per-video playback state and emits events to Google Analytics.\r\n * Framework-agnostic (no React dependency).\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 * Initializes tracking for a 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 * Tracks a play event.\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 // Start watch time counter\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 * Tracks a pause event.\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 // Stop watch time counter\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 * Tracks a seek event (timeline jump).\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 * Tracks progress 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 // Emit each milestone only once\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 * Tracks video completion (>=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 * Tracks video end (native ended event).\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 * Tracks playback speed change.\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 * Tracks fullscreen toggle.\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 * Tracks no interaction (user is on the page but not interacting with the 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 * Returns the current state of a 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 * Cleans up tracking for a specific video.\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 * Cleans up all video 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 // --- Private methods ---\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 * Singleton VideoTracker instance.\r\n * Use when a single global instance is needed.\r\n */\r\nexport const videoTracker = new VideoTracker();\r\n","/**\r\n * React Hooks: useVideoTracking, useWistiaVideoTracking\r\n * Hooks for integrating video tracking in React components.\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 /** Progress percentage (0-100) */\r\n progress: number;\r\n /** Whether the video is currently playing */\r\n isPlaying: boolean;\r\n /** Current playback time in seconds */\r\n currentTime: number;\r\n /** Total duration in seconds */\r\n duration: number;\r\n}\r\n\r\n/**\r\n * Hook for tracking a native HTML5 <video> element.\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 completed'),\r\n * onProgress: (pct) => console.log(`Progress: ${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 // Cancel no-interaction timer\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 // Completion\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 from previous position\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 /** Current video stats */\r\n stats: VideoStats;\r\n /** Whether the video was completed */\r\n isCompleted: boolean;\r\n}\r\n\r\n/**\r\n * Hook for tracking a Wistia video.\r\n * Polls window._wq until the Wistia SDK is ready.\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>Watched: {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 // Poll until Wistia SDK is available\r\n const interval = setInterval(() => {\r\n if (bindWistia()) {\r\n clearInterval(interval);\r\n }\r\n }, 100);\r\n\r\n // Timeout: stop trying after 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"]}
|