"use client"; import { useOpenPanel } from "@openpanel/nextjs"; import { useCallback } from "react"; import { trackRybbitProductView, trackRybbitAddToCart, trackRybbitRemoveFromCart, trackRybbitCheckoutStarted, trackRybbitCheckoutStep, trackRybbitOrderCompleted, trackRybbitSearch, trackRybbitExternalLink, trackRybbitCartView, trackRybbitWishlistAdd, trackRybbitUserLogin, trackRybbitUserRegister, trackRybbitNewsletterSignup, } from "@/lib/services/RybbitService"; export function useAnalytics() { const op = useOpenPanel(); // Helper to track with both OpenPanel and Rybbit const trackDual = useCallback(( eventName: string, openPanelData: Record ) => { // OpenPanel tracking try { op.track(eventName, openPanelData); } catch (e) { console.error("[OpenPanel] Tracking error:", e); } // Rybbit tracking (fire-and-forget) try { switch (eventName) { case "product_viewed": trackRybbitProductView({ id: openPanelData.product_id, name: openPanelData.product_name, price: openPanelData.price, currency: openPanelData.currency, category: openPanelData.category, }); break; case "add_to_cart": trackRybbitAddToCart({ id: openPanelData.product_id, name: openPanelData.product_name, price: openPanelData.price, currency: openPanelData.currency, quantity: openPanelData.quantity, variant: openPanelData.variant, }); break; case "remove_from_cart": trackRybbitRemoveFromCart({ id: openPanelData.product_id, name: openPanelData.product_name, quantity: openPanelData.quantity, }); break; case "cart_view": trackRybbitCartView({ total: openPanelData.cart_total, currency: openPanelData.currency, item_count: openPanelData.item_count, }); break; case "checkout_started": trackRybbitCheckoutStarted({ total: openPanelData.cart_total, currency: openPanelData.currency, item_count: openPanelData.item_count, items: openPanelData.items, }); break; case "checkout_step": trackRybbitCheckoutStep(openPanelData.step, openPanelData); break; case "order_completed": trackRybbitOrderCompleted({ order_id: openPanelData.order_id, order_number: openPanelData.order_number, total: openPanelData.total, currency: openPanelData.currency, item_count: openPanelData.item_count, shipping_cost: openPanelData.shipping_cost, customer_email: openPanelData.customer_email, payment_method: openPanelData.payment_method, }); break; case "search": trackRybbitSearch(openPanelData.query, openPanelData.results_count); break; case "external_link_click": trackRybbitExternalLink(openPanelData.url, openPanelData.label); break; case "wishlist_add": trackRybbitWishlistAdd({ id: openPanelData.product_id, name: openPanelData.product_name, }); break; case "user_login": trackRybbitUserLogin(openPanelData.method); break; case "user_register": trackRybbitUserRegister(openPanelData.method); break; case "newsletter_signup": trackRybbitNewsletterSignup(openPanelData.email, openPanelData.source); break; } } catch (e) { console.warn("[Rybbit] Tracking error:", e); } }, [op]); const trackProductView = useCallback((product: { id: string; name: string; price: number; currency: string; category?: string; }) => { trackDual("product_viewed", { product_id: product.id, product_name: product.name, price: product.price, currency: product.currency, category: product.category, source: "client", }); }, [trackDual]); const trackAddToCart = useCallback((product: { id: string; name: string; price: number; currency: string; quantity: number; variant?: string; }) => { trackDual("add_to_cart", { product_id: product.id, product_name: product.name, price: product.price, currency: product.currency, quantity: product.quantity, variant: product.variant, source: "client", }); }, [trackDual]); const trackRemoveFromCart = useCallback((product: { id: string; name: string; quantity: number; }) => { trackDual("remove_from_cart", { product_id: product.id, product_name: product.name, quantity: product.quantity, source: "client", }); }, [trackDual]); const trackCartView = useCallback((cart: { total: number; currency: string; item_count: number; }) => { trackDual("cart_view", { cart_total: cart.total, currency: cart.currency, item_count: cart.item_count, source: "client", }); }, [trackDual]); const trackCheckoutStarted = useCallback((cart: { total: number; currency: string; item_count: number; items: Array<{ id: string; name: string; quantity: number; price: number; }>; }) => { trackDual("checkout_started", { cart_total: cart.total, currency: cart.currency, item_count: cart.item_count, items: cart.items, source: "client", }); }, [trackDual]); const trackCheckoutStep = useCallback((step: string, data?: Record) => { trackDual("checkout_step", { step, ...data, source: "client", }); }, [trackDual]); 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); // Track with both OpenPanel and Rybbit trackDual("order_completed", { 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, source: "client", }); // OpenPanel revenue tracking try { op.revenue(order.total, { currency: order.currency, transaction_id: order.order_number, source: "client", }); } catch (e) { console.error("[OpenPanel] Revenue tracking error:", e); } // 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); } }, [op, trackDual]); const trackSearch = useCallback((query: string, results_count: number) => { trackDual("search", { query, results_count, source: "client", }); }, [trackDual]); const trackExternalLink = useCallback((url: string, label?: string) => { trackDual("external_link_click", { url, label, source: "client", }); }, [trackDual]); const trackWishlistAdd = useCallback((product: { id: string; name: string; }) => { trackDual("wishlist_add", { product_id: product.id, product_name: product.name, source: "client", }); }, [trackDual]); const trackUserLogin = useCallback((method: string) => { trackDual("user_login", { method, source: "client", }); }, [trackDual]); const trackUserRegister = useCallback((method: string) => { trackDual("user_register", { method, source: "client", }); }, [trackDual]); const trackNewsletterSignup = useCallback((email: string, source: string) => { trackDual("newsletter_signup", { email, source, }); }, [trackDual]); const identifyUser = useCallback((user: { profileId: string; email?: string; firstName?: string; lastName?: string; }) => { try { op.identify({ profileId: user.profileId, firstName: user.firstName, lastName: user.lastName, email: user.email, }); } catch (e) { console.error("[OpenPanel] Identify error:", e); } }, [op]); return { trackProductView, trackAddToCart, trackRemoveFromCart, trackCartView, trackCheckoutStarted, trackCheckoutStep, trackOrderCompleted, trackSearch, trackExternalLink, trackWishlistAdd, trackUserLogin, trackUserRegister, trackNewsletterSignup, identifyUser, }; } export default useAnalytics;