223 lines
6.1 KiB
TypeScript
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>
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|