analytics/examples/ecommerce/use-product-tracking.ts
2026-01-29 08:20:58 -08:00

223 lines
6.1 KiB
TypeScript

/**
* useProductTracking - React hook for product page analytics
*
* Provides tracking methods for product detail pages.
*/
import { useCallback, useEffect, useRef } from 'react';
import { useAnalytics } from '../react-spa/analytics-setup';
import {
trackProductView,
trackWishlistAdd,
trackProductShare,
trackReviewSubmit,
type Product,
} from './product-analytics';
import { trackCartAdd, type Cart } from './cart-analytics';
// ─────────────────────────────────────────────────────────────────────────────
// Hook
// ─────────────────────────────────────────────────────────────────────────────
interface UseProductTrackingOptions {
/** Track view automatically on mount */
trackViewOnMount?: boolean;
/** List name if product came from a list */
sourceList?: string;
/** Position in source list */
sourcePosition?: number;
}
export function useProductTracking(
product: Product,
options: UseProductTrackingOptions = {},
) {
const { trackViewOnMount = true, sourceList, sourcePosition } = options;
const { client } = useAnalytics();
const hasTrackedView = useRef(false);
// Track view on mount (once per product)
useEffect(() => {
if (!trackViewOnMount || hasTrackedView.current) return;
if (product.id === hasTrackedView.current) return;
trackProductView(client, product);
hasTrackedView.current = true;
// Track source if came from a list
if (sourceList && sourcePosition !== undefined) {
client.trackEngagement({
type: 'ecommerce',
action: 'product_navigation',
metadata: {
productId: product.id,
fromList: sourceList,
fromPosition: sourcePosition,
},
});
}
}, [client, product, trackViewOnMount, sourceList, sourcePosition]);
/**
* Manually track product view.
* Use when trackViewOnMount is false or for re-tracking.
*/
const trackView = useCallback(() => {
trackProductView(client, product);
}, [client, product]);
/**
* Track add to cart from product page.
*/
const trackAddToCart = useCallback(
(quantity: number, cart: Cart) => {
trackCartAdd(client, product, quantity, cart);
},
[client, product],
);
/**
* Track wishlist addition.
*/
const trackWishlist = useCallback(() => {
trackWishlistAdd(client, product);
}, [client, product]);
/**
* Track product share.
*/
const trackShare = useCallback(
(method: 'email' | 'twitter' | 'facebook' | 'link' | string) => {
trackProductShare(client, product, method);
},
[client, product],
);
/**
* Track review submission.
*/
const trackReview = useCallback(
(rating: number, hasText: boolean) => {
trackReviewSubmit(client, product, rating, hasText);
},
[client, product],
);
/**
* Track variant selection.
*/
const trackVariantSelect = useCallback(
(variantType: string, variantValue: string) => {
client.trackEngagement({
type: 'ecommerce',
action: 'variant_select',
metadata: {
productId: product.id,
productName: product.name,
variantType,
variantValue,
},
});
},
[client, product],
);
/**
* Track image gallery interaction.
*/
const trackImageView = useCallback(
(imageIndex: number, imageType: 'thumbnail' | 'zoom' | 'gallery') => {
client.trackEngagement({
type: 'ecommerce',
action: 'image_view',
metadata: {
productId: product.id,
imageIndex,
imageType,
},
});
},
[client, product],
);
/**
* Track when user reads product details tabs (specs, reviews, etc.)
*/
const trackTabView = useCallback(
(tabName: string) => {
client.trackEngagement({
type: 'ecommerce',
action: 'product_tab_view',
metadata: {
productId: product.id,
tabName,
},
});
},
[client, product],
);
return {
trackView,
trackAddToCart,
trackWishlist,
trackShare,
trackReview,
trackVariantSelect,
trackImageView,
trackTabView,
};
}
// ─────────────────────────────────────────────────────────────────────────────
// Example Usage
// ─────────────────────────────────────────────────────────────────────────────
/**
* @example
* ```tsx
* function ProductPage({ product, cart, onAddToCart }) {
* const tracking = useProductTracking(product, {
* sourceList: 'search_results',
* sourcePosition: 3,
* });
*
* const handleAddToCart = (quantity: number) => {
* onAddToCart(product, quantity);
* tracking.trackAddToCart(quantity, cart);
* };
*
* return (
* <div>
* <ImageGallery
* images={product.images}
* onImageChange={(index) => tracking.trackImageView(index, 'gallery')}
* />
*
* <VariantSelector
* variants={product.variants}
* onSelect={(type, value) => tracking.trackVariantSelect(type, value)}
* />
*
* <AddToCartButton onClick={() => handleAddToCart(1)} />
*
* <WishlistButton onClick={tracking.trackWishlist} />
*
* <ShareButtons
* onShare={(method) => tracking.trackShare(method)}
* />
*
* <ProductTabs
* onTabChange={(tab) => tracking.trackTabView(tab)}
* >
* <Tab name="description">...</Tab>
* <Tab name="specifications">...</Tab>
* <Tab name="reviews">...</Tab>
* </ProductTabs>
* </div>
* );
* }
* ```
*/