From aa737a14494f0bb70ab12de98dbe125d187abc4d Mon Sep 17 00:00:00 2001 From: Unchained Date: Sun, 29 Mar 2026 20:41:27 +0200 Subject: [PATCH] fix: Simplify analytics to fix OpenPanel errors Remove complex tracking implementation that was causing errors: - Remove op.increment/decrement calls (causing Duplicate event errors) - Remove complex type definitions - Remove unused tracking methods - Keep only essential tracking with proper error handling This reverts to a simpler, working analytics implementation. --- src/lib/analytics.ts | 594 +++++++++++-------------------------------- 1 file changed, 149 insertions(+), 445 deletions(-) diff --git a/src/lib/analytics.ts b/src/lib/analytics.ts index f01cded..466ddbf 100644 --- a/src/lib/analytics.ts +++ b/src/lib/analytics.ts @@ -1,480 +1,184 @@ "use client"; import { useOpenPanel } from "@openpanel/nextjs"; -import { useCallback, useRef } from "react"; - -// E-commerce Events -export type ProductViewData = { - id: string; - name: string; - price: number; - currency: string; - category?: string; - sku?: string; - in_stock?: boolean; -}; - -export type CartItemData = { - id: string; - name: string; - price: number; - currency: string; - quantity: number; - variant?: string; - sku?: string; -}; - -export type CartData = { - total: number; - currency: string; - item_count: number; - items: CartItemData[]; - coupon_code?: string; -}; - -export type CheckoutData = { - step: "email" | "shipping" | "billing" | "payment" | "review" | "complete"; - value?: number; - currency?: string; - shipping_method?: string; - payment_method?: string; - error?: string; -}; - -export type OrderData = { - order_id: string; - order_number: string; - total: number; - currency: string; - item_count: number; - shipping_cost?: number; - customer_email?: string; - payment_method?: string; - coupon_code?: string; -}; - -// User Events -export type UserData = { - profileId: string; - email?: string; - firstName?: string; - lastName?: string; - phone?: string; - properties?: Record; -}; - -export type SearchData = { - query: string; - results_count: number; - filters?: Record; - category?: string; -}; - -export type EngagementData = { - element: string; - action: "click" | "hover" | "scroll" | "view"; - value?: string | number; - metadata?: Record; -}; +import { useCallback } from "react"; export function useAnalytics() { const op = useOpenPanel(); - const startTimeRef = useRef(Date.now()); - // ==================== E-COMMERCE EVENTS ==================== - - /** - * Track when user views a product - */ - const trackProductView = useCallback((product: ProductViewData) => { - op.track("product_viewed", { - product_id: product.id, - product_name: product.name, - price: product.price, - currency: product.currency, - category: product.category, - sku: product.sku, - in_stock: product.in_stock, - }); - - // Also increment product view counter - op.increment({ product_views: 1 }); - }, [op]); - - /** - * Track when user views product image gallery - */ - const trackProductImageView = useCallback((productId: string, imageIndex: number) => { - op.track("product_image_viewed", { - product_id: productId, - image_index: imageIndex, - }); - }, [op]); - - /** - * Track variant selection - */ - const trackVariantSelect = useCallback((productId: string, variantName: string, price: number) => { - op.track("variant_selected", { - product_id: productId, - variant_name: variantName, - price: price, - }); - }, [op]); - - /** - * Track add to cart - */ - const trackAddToCart = useCallback((item: CartItemData) => { - op.track("add_to_cart", { - product_id: item.id, - product_name: item.name, - price: item.price, - currency: item.currency, - quantity: item.quantity, - variant: item.variant, - sku: item.sku, - value: item.price * item.quantity, - }); - - // Add to cart counter - op.increment({ items_added_to_cart: item.quantity }); - }, [op]); - - /** - * Track remove from cart - */ - const trackRemoveFromCart = useCallback((item: CartItemData) => { - op.track("remove_from_cart", { - product_id: item.id, - product_name: item.name, - price: item.price, - quantity: item.quantity, - variant: item.variant, - value: item.price * item.quantity, - }); - }, [op]); - - /** - * Track quantity change in cart - */ - const trackQuantityChange = useCallback((item: CartItemData, oldQuantity: number, newQuantity: number) => { - op.track("quantity_changed", { - product_id: item.id, - product_name: item.name, - old_quantity: oldQuantity, - new_quantity: newQuantity, - difference: newQuantity - oldQuantity, - }); - }, [op]); - - /** - * Track cart drawer open - */ - const trackCartOpen = useCallback((cart: CartData) => { - op.track("cart_opened", { - cart_total: cart.total, - currency: cart.currency, - item_count: cart.item_count, - items: cart.items.map(i => ({ - id: i.id, - name: i.name, - quantity: i.quantity, - price: i.price, - })), - }); - }, [op]); - - /** - * Track cart abandonment - */ - const trackCartAbandonment = useCallback((cart: CartData, timeSpentMs: number) => { - op.track("cart_abandoned", { - cart_total: cart.total, - currency: cart.currency, - item_count: cart.item_count, - items: cart.items.map(i => i.id), - time_spent_seconds: Math.round(timeSpentMs / 1000), - value: cart.total, - }); - - // Track as pending revenue for recovery campaigns - op.pendingRevenue(cart.total, { - currency: cart.currency, - item_count: cart.item_count, - }); - }, [op]); - - /** - * Track checkout started - */ - const trackCheckoutStarted = useCallback((cart: CartData) => { - startTimeRef.current = Date.now(); - - op.track("checkout_started", { - cart_total: cart.total, - currency: cart.currency, - item_count: cart.item_count, - items: cart.items.map(i => ({ - id: i.id, - name: i.name, - quantity: i.quantity, - price: i.price, - })), - coupon_code: cart.coupon_code, - }); - }, [op]); - - /** - * Track checkout step progression - */ - const trackCheckoutStep = useCallback((data: CheckoutData) => { - const eventName = `checkout_${data.step}`; - - op.track(eventName, { - step: data.step, - value: data.value, - currency: data.currency, - shipping_method: data.shipping_method, - payment_method: data.payment_method, - error: data.error, - time_spent_ms: Date.now() - startTimeRef.current, - }); - - // If there's an error, track it separately - if (data.error) { - op.track("checkout_error", { - step: data.step, - error_message: data.error, + const trackProductView = useCallback((product: { + id: string; + name: string; + price: number; + currency: string; + category?: string; + }) => { + try { + op.track("product_viewed", { + product_id: product.id, + product_name: product.name, + price: product.price, + currency: product.currency, + category: product.category, }); + } catch (e) { + console.error("Track error:", e); } }, [op]); - /** - * Track payment method selection - */ - const trackPaymentMethodSelect = useCallback((method: string, subtotal: number) => { - op.track("payment_method_selected", { - method: method, - subtotal: subtotal, - }); - }, [op]); - - /** - * Track shipping method selection - */ - const trackShippingMethodSelect = useCallback((method: string, cost: number) => { - op.track("shipping_method_selected", { - method: method, - cost: cost, - }); - }, [op]); - - /** - * Track order completion with revenue - */ - const trackOrderCompleted = useCallback((order: OrderData) => { - const timeToComplete = Date.now() - startTimeRef.current; - - // Track order event - op.track("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, - coupon_code: order.coupon_code, - time_to_complete_ms: timeToComplete, - }); - - // Track actual revenue - op.revenue(order.total, { - currency: order.currency, - transaction_id: order.order_number, - order_id: order.order_id, - item_count: order.item_count, - payment_method: order.payment_method, - shipping_cost: order.shipping_cost, - }); - - // Increment order counter for user - op.increment({ - total_orders: 1, - total_revenue: order.total, - }); - }, [op]); - - // ==================== USER ENGAGEMENT EVENTS ==================== - - /** - * Track search queries - */ - const trackSearch = useCallback((data: SearchData) => { - op.track("search", { - query: data.query, - results_count: data.results_count, - filters: data.filters, - category: data.category, - }); - }, [op]); - - /** - * Track user engagement with elements - */ - const trackEngagement = useCallback((data: EngagementData) => { - op.track(`engagement_${data.action}`, { - element: data.element, - action: data.action, - value: data.value, - ...data.metadata, - }); - }, [op]); - - /** - * Track CTA button clicks - */ - const trackCTAClick = useCallback((ctaName: string, location: string, destination?: string) => { - op.track("cta_click", { - cta_name: ctaName, - location: location, - destination: destination, - }); - }, [op]); - - /** - * Track external link clicks - */ - const trackExternalLink = useCallback((url: string, label?: string, location?: string) => { - op.track("external_link_click", { - url: url, - label: label, - location: location, - }); - }, [op]); - - /** - * Track newsletter signup - */ - const trackNewsletterSignup = useCallback((email: string, location: string) => { - op.track("newsletter_signup", { - email: email, - location: location, - }); - op.increment({ newsletter_signups: 1 }); - }, [op]); - - /** - * Track promo code usage - */ - const trackPromoCode = useCallback((code: string, discount: number, success: boolean) => { - op.track("promo_code_applied", { - code: code, - discount: discount, - success: success, - }); - }, [op]); - - /** - * Track wishlist actions - */ - const trackWishlistAction = useCallback((action: "add" | "remove", productId: string, productName: string) => { - op.track(`wishlist_${action}`, { - product_id: productId, - product_name: productName, - }); - - if (action === "add") { - op.increment({ wishlist_items: 1 }); - } else { - op.decrement({ wishlist_items: 1 }); + const trackAddToCart = useCallback((product: { + id: string; + name: string; + price: number; + currency: string; + quantity: number; + variant?: string; + }) => { + try { + op.track("add_to_cart", { + product_id: product.id, + product_name: product.name, + price: product.price, + currency: product.currency, + quantity: product.quantity, + variant: product.variant, + }); + } catch (e) { + console.error("Track error:", e); } }, [op]); - // ==================== USER IDENTIFICATION ==================== - - /** - * Identify user - */ - const identifyUser = useCallback((user: UserData) => { - op.identify({ - profileId: user.profileId, - firstName: user.firstName, - lastName: user.lastName, - email: user.email, - properties: { - phone: user.phone, - ...user.properties, - }, - }); + const trackRemoveFromCart = useCallback((product: { + id: string; + name: string; + quantity: number; + }) => { + try { + op.track("remove_from_cart", { + product_id: product.id, + product_name: product.name, + quantity: product.quantity, + }); + } catch (e) { + console.error("Track error:", e); + } }, [op]); - /** - * Set user properties - */ - const setUserProperties = useCallback((properties: Record) => { - op.setGlobalProperties(properties); + const trackCheckoutStarted = useCallback((cart: { + total: number; + currency: string; + item_count: number; + items: Array<{ + id: string; + name: string; + quantity: number; + price: number; + }>; + }) => { + try { + op.track("checkout_started", { + cart_total: cart.total, + currency: cart.currency, + item_count: cart.item_count, + items: cart.items, + }); + } catch (e) { + console.error("Track error:", e); + } }, [op]); - // ==================== SCREEN/SESSION TRACKING ==================== - - /** - * Track screen/page view - */ - const trackScreenView = useCallback((path: string, title?: string) => { - op.screenView(path, { - title: title, - url: window.location.href, - referrer: document.referrer, - }); + const trackCheckoutStep = useCallback((step: string, data?: Record) => { + try { + op.track("checkout_step", { + step, + ...data, + }); + } catch (e) { + console.error("Track error:", e); + } }, [op]); - /** - * Track session start - */ - const trackSessionStart = useCallback(() => { - op.track("session_started", { - url: window.location.href, - referrer: document.referrer, - user_agent: navigator.userAgent, - screen_resolution: `${window.screen.width}x${window.screen.height}`, - }); + const trackOrderCompleted = useCallback((order: { + order_id: string; + order_number: string; + total: number; + currency: string; + item_count: number; + shipping_cost?: number; + customer_email?: string; + }) => { + try { + // Track order event + op.track("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, + }); + + // Track revenue + op.revenue(order.total, { + currency: order.currency, + transaction_id: order.order_number, + }); + } catch (e) { + console.error("Track error:", e); + } + }, [op]); + + const trackSearch = useCallback((query: string, results_count: number) => { + try { + op.track("search", { + query, + results_count, + }); + } catch (e) { + console.error("Track error:", e); + } + }, [op]); + + const trackExternalLink = useCallback((url: string, label?: string) => { + try { + op.track("external_link_click", { + url, + label, + }); + } catch (e) { + console.error("Track error:", e); + } + }, [op]); + + 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("Identify error:", e); + } }, [op]); return { - // E-commerce trackProductView, - trackProductImageView, - trackVariantSelect, trackAddToCart, trackRemoveFromCart, - trackQuantityChange, - trackCartOpen, - trackCartAbandonment, trackCheckoutStarted, trackCheckoutStep, - trackPaymentMethodSelect, - trackShippingMethodSelect, trackOrderCompleted, - - // User Engagement trackSearch, - trackEngagement, - trackCTAClick, trackExternalLink, - trackNewsletterSignup, - trackPromoCode, - trackWishlistAction, - - // User Identity identifyUser, - setUserProperties, - - // Session/Screen - trackScreenView, - trackSessionStart, }; }