feat: added desktop sso endpoints
This commit is contained in:
185
vibn-attribution-package/frontend/VibnTracker.tsx
Normal file
185
vibn-attribution-package/frontend/VibnTracker.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
"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<string, any>) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
umami?: {
|
||||
identify: (traits: Record<string, any>) => void;
|
||||
track: (eventName: string, properties?: Record<string, any>) => 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<VibnTrackerContextType | undefined>(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<keyof UTMParams> = [
|
||||
"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<string, any>) => {
|
||||
if (typeof window !== "undefined" && window.umami) {
|
||||
window.umami.track(eventName, properties);
|
||||
console.log(`[VibnTracker] Event tracked: "${eventName}"`, properties || "");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VibnTrackerContext.Provider
|
||||
value={{ getStoredAttribution, clearAttribution, identify, track }}
|
||||
>
|
||||
{children}
|
||||
</VibnTrackerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// ── CUSTOM HOOK ───────────────────────────────────────────────────────────────
|
||||
|
||||
export function useVibnTracker() {
|
||||
const context = useContext(VibnTrackerContext);
|
||||
if (!context) {
|
||||
throw new Error("useVibnTracker must be used within a VibnTrackerProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user