Files
manoon-headless/src/lib/analytics.ts
Unchained eb711fbf1a
Some checks failed
Build and Deploy / build (push) Has been cancelled
feat(popup): add email capture popup with Mautic integration
- Email capture popup with scroll (10%) and exit intent triggers
- First name field and full tracking (UTM, device, time on page)
- Mautic API integration for contact creation
- GeoIP detection for country/region
- 4 locale support (sr, en, de, fr)
- Mautic tracking script in layout
2026-04-03 20:44:15 +02:00

233 lines
5.9 KiB
TypeScript

"use client";
import { useCallback } from "react";
import {
trackRybbitProductView,
trackRybbitAddToCart,
trackRybbitRemoveFromCart,
trackRybbitCheckoutStarted,
trackRybbitCheckoutStep,
trackRybbitOrderCompleted,
trackRybbitSearch,
trackRybbitExternalLink,
trackRybbitCartView,
trackRybbitWishlistAdd,
trackRybbitUserLogin,
trackRybbitUserRegister,
trackRybbitNewsletterSignup,
trackRybbitEvent,
} from "@/lib/services/RybbitService";
export function useAnalytics() {
const trackProductView = useCallback((product: {
id: string;
name: string;
price: number;
currency: string;
category?: string;
}) => {
trackRybbitProductView({
id: product.id,
name: product.name,
price: product.price,
currency: product.currency,
category: product.category,
});
}, []);
const trackAddToCart = useCallback((product: {
id: string;
name: string;
price: number;
currency: string;
quantity: number;
variant?: string;
}) => {
trackRybbitAddToCart({
id: product.id,
name: product.name,
price: product.price,
currency: product.currency,
quantity: product.quantity,
variant: product.variant,
});
}, []);
const trackRemoveFromCart = useCallback((product: {
id: string;
name: string;
quantity: number;
}) => {
trackRybbitRemoveFromCart({
id: product.id,
name: product.name,
quantity: product.quantity,
});
}, []);
const trackCartView = useCallback((cart: {
total: number;
currency: string;
item_count: number;
}) => {
trackRybbitCartView({
total: cart.total,
currency: cart.currency,
item_count: cart.item_count,
});
}, []);
const trackCheckoutStarted = useCallback((cart: {
total: number;
currency: string;
item_count: number;
items: Array<{
id: string;
name: string;
quantity: number;
price: number;
}>;
}) => {
trackRybbitCheckoutStarted({
total: cart.total,
currency: cart.currency,
item_count: cart.item_count,
items: cart.items,
});
}, []);
const trackCheckoutStep = useCallback((step: string, data?: Record<string, unknown>) => {
trackRybbitCheckoutStep(step, data);
}, []);
const trackOrderCompleted = useCallback(async (order: {
order_id: string;
order_number: string;
total: number;
currency: string;
item_count: number;
shipping_cost?: number;
customer_email?: string;
payment_method?: string;
}) => {
console.log("[Analytics] Tracking order:", order.order_number);
// Rybbit tracking
trackRybbitOrderCompleted({
order_id: order.order_id,
order_number: order.order_number,
total: order.total,
currency: order.currency,
item_count: order.item_count,
shipping_cost: order.shipping_cost,
customer_email: order.customer_email,
payment_method: order.payment_method,
});
// Server-side tracking for reliability
try {
const response = await fetch("/api/analytics/track-order", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
orderId: order.order_id,
orderNumber: order.order_number,
total: order.total,
currency: order.currency,
itemCount: order.item_count,
customerEmail: order.customer_email,
paymentMethod: order.payment_method,
shippingCost: order.shipping_cost,
}),
});
if (!response.ok) {
console.error("[Server Analytics] Failed:", await response.text());
}
} catch (e) {
console.error("[Server Analytics] API call failed:", e);
}
}, []);
const trackSearch = useCallback((query: string, results_count: number) => {
trackRybbitSearch(query, results_count);
}, []);
const trackExternalLink = useCallback((url: string, label?: string) => {
trackRybbitExternalLink(url, label);
}, []);
const trackWishlistAdd = useCallback((product: {
id: string;
name: string;
}) => {
trackRybbitWishlistAdd({
id: product.id,
name: product.name,
});
}, []);
const trackUserLogin = useCallback((method: string) => {
trackRybbitUserLogin(method);
}, []);
const trackUserRegister = useCallback((method: string) => {
trackRybbitUserRegister(method);
}, []);
const trackNewsletterSignup = useCallback((email: string, source: string) => {
trackRybbitNewsletterSignup(email, source);
}, []);
// Popup tracking functions
const trackPopupView = useCallback((data: { trigger: string; locale: string; country?: string }) => {
trackRybbitEvent("popup_view", data);
}, []);
const trackPopupSubmit = useCallback((data: { trigger: string; locale: string; country?: string }) => {
trackRybbitEvent("popup_submit", data);
}, []);
const trackPopupCtaClick = useCallback((data: { locale: string }) => {
trackRybbitEvent("popup_cta_click", data);
}, []);
const trackPopupDismiss = useCallback((data: { trigger: string; locale: string }) => {
trackRybbitEvent("popup_dismiss", data);
}, []);
// No-op placeholder for identifyUser (OpenPanel removed)
const identifyUser = useCallback((_user: {
profileId: string;
email?: string;
firstName?: string;
lastName?: string;
}) => {
// OpenPanel was removed - this is now a no-op
// User identification is handled by Rybbit automatically via cookies
}, []);
return {
trackProductView,
trackAddToCart,
trackRemoveFromCart,
trackCartView,
trackCheckoutStarted,
trackCheckoutStep,
trackOrderCompleted,
trackSearch,
trackExternalLink,
trackWishlistAdd,
trackUserLogin,
trackUserRegister,
trackNewsletterSignup,
trackPopupView,
trackPopupSubmit,
trackPopupCtaClick,
trackPopupDismiss,
identifyUser,
};
}
export default useAnalytics;