feat(analytics): add OpenPanel tracking to storefront
Add comprehensive OpenPanel analytics tracking: - Install @openpanel/nextjs SDK - Add OpenPanelComponent to root layout for automatic page views - Create useAnalytics hook for tracking custom events - Track checkout funnel: started, shipping step, order completed - Track product views and add-to-cart events - Identify users on order completion - Add NEXT_PUBLIC_OPENPANEL_CLIENT_ID to environment
This commit is contained in:
154
src/lib/analytics.ts
Normal file
154
src/lib/analytics.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
"use client";
|
||||
|
||||
import { useOpenPanel } from "@openpanel/nextjs";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export function useAnalytics() {
|
||||
const op = useOpenPanel();
|
||||
|
||||
// Page views are tracked automatically by OpenPanelComponent
|
||||
// but we can track specific events manually
|
||||
|
||||
const trackProductView = useCallback((product: {
|
||||
id: string;
|
||||
name: string;
|
||||
price: number;
|
||||
currency: string;
|
||||
category?: string;
|
||||
}) => {
|
||||
op.track("product_viewed", {
|
||||
product_id: product.id,
|
||||
product_name: product.name,
|
||||
price: product.price,
|
||||
currency: product.currency,
|
||||
category: product.category,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackAddToCart = useCallback((product: {
|
||||
id: string;
|
||||
name: string;
|
||||
price: number;
|
||||
currency: string;
|
||||
quantity: number;
|
||||
variant?: string;
|
||||
}) => {
|
||||
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,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackRemoveFromCart = useCallback((product: {
|
||||
id: string;
|
||||
name: string;
|
||||
quantity: number;
|
||||
}) => {
|
||||
op.track("remove_from_cart", {
|
||||
product_id: product.id,
|
||||
product_name: product.name,
|
||||
quantity: product.quantity,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackCheckoutStarted = useCallback((cart: {
|
||||
total: number;
|
||||
currency: string;
|
||||
item_count: number;
|
||||
items: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
}>;
|
||||
}) => {
|
||||
op.track("checkout_started", {
|
||||
cart_total: cart.total,
|
||||
currency: cart.currency,
|
||||
item_count: cart.item_count,
|
||||
items: cart.items,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackCheckoutStep = useCallback((step: string, data?: Record<string, unknown>) => {
|
||||
op.track("checkout_step", {
|
||||
step,
|
||||
...data,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackOrderCompleted = useCallback((order: {
|
||||
order_id: string;
|
||||
order_number: string;
|
||||
total: number;
|
||||
currency: string;
|
||||
item_count: number;
|
||||
shipping_cost?: number;
|
||||
customer_email?: string;
|
||||
}) => {
|
||||
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,
|
||||
});
|
||||
|
||||
// Also track revenue for analytics
|
||||
op.track("purchase", {
|
||||
transaction_id: order.order_number,
|
||||
value: order.total,
|
||||
currency: order.currency,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackSearch = useCallback((query: string, results_count: number) => {
|
||||
op.track("search", {
|
||||
query,
|
||||
results_count,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const trackExternalLink = useCallback((url: string, label?: string) => {
|
||||
op.track("external_link_click", {
|
||||
url,
|
||||
label,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
const identifyUser = useCallback((user: {
|
||||
profileId: string;
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
properties?: Record<string, unknown>;
|
||||
}) => {
|
||||
op.identify({
|
||||
profileId: user.profileId,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
email: user.email,
|
||||
properties: user.properties,
|
||||
});
|
||||
}, [op]);
|
||||
|
||||
return {
|
||||
trackProductView,
|
||||
trackAddToCart,
|
||||
trackRemoveFromCart,
|
||||
trackCheckoutStarted,
|
||||
trackCheckoutStep,
|
||||
trackOrderCompleted,
|
||||
trackSearch,
|
||||
trackExternalLink,
|
||||
identifyUser,
|
||||
};
|
||||
}
|
||||
|
||||
export default useAnalytics;
|
||||
Reference in New Issue
Block a user