"use client"; import React, { useEffect, createContext, useContext } from "react"; // ── TYPES & INTERFACES ──────────────────────────────────────────────────────── export interface UTMParams { utm_source: string | null; utm_medium: string | null; utm_campaign: string | null; utm_content: string | null; utm_term: string | null; referrer: string | null; } export interface IdentifyTraits { userId: string | number; email: string; name?: string; plan?: string; [key: string]: any; } export interface VibnTrackerContextType { getStoredAttribution: () => UTMParams; clearAttribution: () => void; identify: (traits: IdentifyTraits) => void; track: (eventName: string, properties?: Record) => void; } declare global { interface Window { umami?: { identify: (traits: Record) => void; track: (eventName: string, properties?: Record) => void; }; } } // ── UTILITY FUNCTIONS ───────────────────────────────────────────────────────── const COOKIE_EXPIRY_DAYS = 30; function setCookie(name: string, value: string, days: number) { if (typeof document === "undefined") return; const date = new Date(); date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); const expires = "; expires=" + date.toUTCString(); document.cookie = name + "=" + (value || "") + expires + "; path=/; SameSite=Lax"; } function getCookie(name: string): string | null { if (typeof document === "undefined") return null; const nameEQ = name + "="; const ca = document.cookie.split(";"); for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === " ") c = c.substring(1, c.length); if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); } return null; } function deleteCookie(name: string) { if (typeof document === "undefined") return; document.cookie = name + "=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; } // ── CONTEXT PROVIDER ────────────────────────────────────────────────────────── const VibnTrackerContext = createContext(undefined); export function VibnTrackerProvider({ children, umamiWebsiteId, umamiScriptUrl = "https://analytics.vibnai.com/script.js", }: { children: React.ReactNode; umamiWebsiteId?: string; umamiScriptUrl?: string; }) { useEffect(() => { if (typeof window === "undefined") return; // 1. Extract UTMs from active search string const urlParams = new URLSearchParams(window.location.search); const keys: Array = [ "utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term", ]; keys.forEach((key) => { const val = urlParams.get(key); if (val) { // Persist to sessionStorage (for current window lifespans) sessionStorage.setItem(key, val); // Persist to cookie (cross-session backup, used if user closes window and signs up later) setCookie(`vibn_${key}`, val, COOKIE_EXPIRY_DAYS); } }); // 2. Persist browser referrer if (!sessionStorage.getItem("referrer") && !getCookie("vibn_referrer")) { const ref = document.referrer || "direct"; sessionStorage.setItem("referrer", ref); setCookie("vibn_referrer", ref, COOKIE_EXPIRY_DAYS); } // 3. Inject Umami analytics script automatically if website ID provided if (umamiWebsiteId && !document.querySelector(`script[data-website-id="${umamiWebsiteId}"]`)) { const script = document.createElement("script"); script.src = umamiScriptUrl; script.setAttribute("data-website-id", umamiWebsiteId); script.async = true; script.defer = true; document.head.appendChild(script); } }, [umamiWebsiteId, umamiScriptUrl]); const getStoredAttribution = (): UTMParams => { if (typeof window === "undefined") { return { utm_source: null, utm_medium: null, utm_campaign: null, utm_content: null, utm_term: null, referrer: null, }; } return { utm_source: sessionStorage.getItem("utm_source") || getCookie("vibn_utm_source"), utm_medium: sessionStorage.getItem("utm_medium") || getCookie("vibn_utm_medium"), utm_campaign: sessionStorage.getItem("utm_campaign") || getCookie("vibn_utm_campaign"), utm_content: sessionStorage.getItem("utm_content") || getCookie("vibn_utm_content"), utm_term: sessionStorage.getItem("utm_term") || getCookie("vibn_utm_term"), referrer: sessionStorage.getItem("referrer") || getCookie("vibn_referrer"), }; }; const clearAttribution = () => { if (typeof window === "undefined") return; const keys = ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term", "referrer"]; keys.forEach((key) => { sessionStorage.removeItem(key); deleteCookie(`vibn_${key}`); }); }; const identify = (traits: IdentifyTraits) => { if (typeof window !== "undefined" && window.umami) { window.umami.identify(traits); console.log(`[VibnTracker] User identified in Umami: ${traits.email}`); } }; const track = (eventName: string, properties?: Record) => { if (typeof window !== "undefined" && window.umami) { window.umami.track(eventName, properties); console.log(`[VibnTracker] Event tracked: "${eventName}"`, properties || ""); } }; return ( {children} ); } // ── CUSTOM HOOK ─────────────────────────────────────────────────────────────── export function useVibnTracker() { const context = useContext(VibnTrackerContext); if (!context) { throw new Error("useVibnTracker must be used within a VibnTrackerProvider"); } return context; }