@wix/headless-stores 0.0.104 → 0.0.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/dist/react/Product.d.ts +48 -1
- package/cjs/dist/react/Product.js +89 -9
- package/cjs/dist/services/product-service.d.ts +17 -1
- package/cjs/dist/services/product-service.js +151 -1
- package/dist/react/Product.d.ts +48 -1
- package/dist/react/Product.js +89 -9
- package/dist/services/product-service.d.ts +17 -1
- package/dist/services/product-service.js +151 -1
- package/package.json +5 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { V3Product } from '@wix/auto_sdk_stores_products-v-3';
|
|
2
|
-
import React from 'react';
|
|
3
2
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
3
|
+
import React from 'react';
|
|
4
4
|
import { AsContent } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Context for sharing variant options state between components
|
|
@@ -885,6 +885,53 @@ export declare const Action: {
|
|
|
885
885
|
/** Pre-order action button */
|
|
886
886
|
readonly PreOrder: React.ForwardRefExoticComponent<ProductActionProps & React.RefAttributes<HTMLButtonElement>>;
|
|
887
887
|
};
|
|
888
|
+
/**
|
|
889
|
+
* Props for Product Link component
|
|
890
|
+
*/
|
|
891
|
+
export interface ProductLinkProps {
|
|
892
|
+
/** Whether to render as a child component */
|
|
893
|
+
asChild?: boolean;
|
|
894
|
+
/** Custom render function when using asChild */
|
|
895
|
+
children?: React.ReactNode;
|
|
896
|
+
/** CSS classes to apply to the link */
|
|
897
|
+
className?: string;
|
|
898
|
+
/** List or collection the product is part of (e.g., "Category: T-Shirts") */
|
|
899
|
+
list?: string;
|
|
900
|
+
/** Position of the product within a list or collection */
|
|
901
|
+
position?: string;
|
|
902
|
+
/** Event origin (e.g., "Search Results", "Related Products") */
|
|
903
|
+
origin?: string;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Product link component that automatically tracks ClickProduct events.
|
|
907
|
+
* Wraps a link to the product page and fires analytics when clicked.
|
|
908
|
+
*
|
|
909
|
+
* @component
|
|
910
|
+
* @example
|
|
911
|
+
* ```tsx
|
|
912
|
+
* // Simple usage
|
|
913
|
+
* <Product.Link className="btn-secondary">
|
|
914
|
+
* View Product
|
|
915
|
+
* </Product.Link>
|
|
916
|
+
*
|
|
917
|
+
* // With tracking parameters
|
|
918
|
+
* <Product.Link
|
|
919
|
+
* list="Category: T-Shirts"
|
|
920
|
+
* position="3"
|
|
921
|
+
* className="btn-secondary"
|
|
922
|
+
* >
|
|
923
|
+
* View Product
|
|
924
|
+
* </Product.Link>
|
|
925
|
+
*
|
|
926
|
+
* // asChild with custom link
|
|
927
|
+
* <Product.Link asChild list="Search Results" position="5">
|
|
928
|
+
* <a href="/custom-url" className="custom-link">
|
|
929
|
+
* <Product.Name />
|
|
930
|
+
* </a>
|
|
931
|
+
* </Product.Link>
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
export declare const Link: React.ForwardRefExoticComponent<ProductLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
888
935
|
/**
|
|
889
936
|
* Props for ProductVariantStock component
|
|
890
937
|
*/
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { InventoryAvailabilityStatus } from '@wix/auto_sdk_stores_products-v-3';
|
|
3
|
-
import
|
|
3
|
+
import { Quantity as QuantityComponent } from '@wix/headless-components/react';
|
|
4
4
|
import { Commerce } from '@wix/headless-ecom/react';
|
|
5
|
-
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
6
5
|
import { MediaGallery } from '@wix/headless-media/react';
|
|
7
|
-
import {
|
|
6
|
+
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
7
|
+
import { useService } from '@wix/services-manager-react';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { DataComponentTags } from '../data-component-tags.js';
|
|
10
|
+
import { ProductServiceDefinition, SelectedVariantServiceDefinition, } from '../services/index.js';
|
|
8
11
|
import * as CoreProduct from './core/Product.js';
|
|
9
|
-
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
10
12
|
import * as CoreProductModifiers from './core/ProductModifiers.js';
|
|
13
|
+
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
11
14
|
import * as CoreSelectedVariant from './core/SelectedVariant.js';
|
|
12
15
|
import * as Option from './Option.js';
|
|
13
16
|
import { AsContent } from './types.js';
|
|
14
|
-
import { DataComponentTags } from '../data-component-tags.js';
|
|
15
17
|
const VariantsContext = React.createContext(null);
|
|
16
18
|
/**
|
|
17
19
|
* Hook to access variants context
|
|
@@ -61,6 +63,7 @@ var TestIds;
|
|
|
61
63
|
TestIds["productActionAddToCart"] = "product-action-add-to-cart";
|
|
62
64
|
TestIds["productActionBuyNow"] = "product-action-buy-now";
|
|
63
65
|
TestIds["productActionPreOrder"] = "product-action-can-pre-order";
|
|
66
|
+
TestIds["productLink"] = "product-link";
|
|
64
67
|
TestIds["productQuantity"] = "product-quantity";
|
|
65
68
|
TestIds["productQuantityDecrement"] = "product-quantity-decrement";
|
|
66
69
|
TestIds["productQuantityInput"] = "product-quantity-input";
|
|
@@ -879,6 +882,19 @@ export const ProductVariantSelectorReset = React.forwardRef((props, ref) => {
|
|
|
879
882
|
*/
|
|
880
883
|
export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
881
884
|
const { asChild, children, className, label, loadingState } = props;
|
|
885
|
+
// Get services for tracking
|
|
886
|
+
const productService = useService(ProductServiceDefinition);
|
|
887
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
888
|
+
// Track after successful add to cart
|
|
889
|
+
const handleSuccess = async () => {
|
|
890
|
+
const choices = variantService.selectedChoices.get();
|
|
891
|
+
const quantity = variantService.selectedQuantity.get();
|
|
892
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
893
|
+
productService.reportAddToCartTrackEvent({
|
|
894
|
+
quantity,
|
|
895
|
+
variant: variantString,
|
|
896
|
+
});
|
|
897
|
+
};
|
|
882
898
|
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, canAddToCart, isLoading, addToCart, isPreOrderEnabled, }) => {
|
|
883
899
|
if (isPreOrderEnabled) {
|
|
884
900
|
return null;
|
|
@@ -886,13 +902,13 @@ export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
|
886
902
|
const onClick = addToCart;
|
|
887
903
|
const disabled = !canAddToCart || isLoading;
|
|
888
904
|
if (asChild && children) {
|
|
889
|
-
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
905
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, onSuccess: handleSuccess, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
890
906
|
disabled,
|
|
891
907
|
isLoading,
|
|
892
908
|
onClick,
|
|
893
909
|
} }) }));
|
|
894
910
|
}
|
|
895
|
-
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
911
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, onSuccess: handleSuccess, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
896
912
|
} }));
|
|
897
913
|
});
|
|
898
914
|
/**
|
|
@@ -923,6 +939,19 @@ export const ProductActionBuyNow = React.forwardRef((props, ref) => {
|
|
|
923
939
|
*/
|
|
924
940
|
export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
925
941
|
const { asChild, children, className, label, loadingState } = props;
|
|
942
|
+
// Get services for tracking
|
|
943
|
+
const productService = useService(ProductServiceDefinition);
|
|
944
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
945
|
+
// Track after successful pre-order
|
|
946
|
+
const handleSuccess = async () => {
|
|
947
|
+
const choices = variantService.selectedChoices.get();
|
|
948
|
+
const quantity = variantService.selectedQuantity.get();
|
|
949
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
950
|
+
productService.reportAddToCartTrackEvent({
|
|
951
|
+
quantity,
|
|
952
|
+
variant: variantString,
|
|
953
|
+
});
|
|
954
|
+
};
|
|
926
955
|
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, isLoading, addToCart, isPreOrderEnabled }) => {
|
|
927
956
|
if (!isPreOrderEnabled) {
|
|
928
957
|
return null;
|
|
@@ -931,13 +960,13 @@ export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
|
931
960
|
const onClick = addToCart;
|
|
932
961
|
const disabled = !canPreOrder;
|
|
933
962
|
if (asChild && children) {
|
|
934
|
-
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
963
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, onSuccess: handleSuccess, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
935
964
|
disabled,
|
|
936
965
|
isLoading,
|
|
937
966
|
onClick,
|
|
938
967
|
} }) }));
|
|
939
968
|
}
|
|
940
|
-
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
969
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, onSuccess: handleSuccess, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
941
970
|
} }));
|
|
942
971
|
});
|
|
943
972
|
/**
|
|
@@ -952,6 +981,57 @@ export const Action = {
|
|
|
952
981
|
/** Pre-order action button */
|
|
953
982
|
PreOrder: ProductActionPreOrder,
|
|
954
983
|
};
|
|
984
|
+
/**
|
|
985
|
+
* Product link component that automatically tracks ClickProduct events.
|
|
986
|
+
* Wraps a link to the product page and fires analytics when clicked.
|
|
987
|
+
*
|
|
988
|
+
* @component
|
|
989
|
+
* @example
|
|
990
|
+
* ```tsx
|
|
991
|
+
* // Simple usage
|
|
992
|
+
* <Product.Link className="btn-secondary">
|
|
993
|
+
* View Product
|
|
994
|
+
* </Product.Link>
|
|
995
|
+
*
|
|
996
|
+
* // With tracking parameters
|
|
997
|
+
* <Product.Link
|
|
998
|
+
* list="Category: T-Shirts"
|
|
999
|
+
* position="3"
|
|
1000
|
+
* className="btn-secondary"
|
|
1001
|
+
* >
|
|
1002
|
+
* View Product
|
|
1003
|
+
* </Product.Link>
|
|
1004
|
+
*
|
|
1005
|
+
* // asChild with custom link
|
|
1006
|
+
* <Product.Link asChild list="Search Results" position="5">
|
|
1007
|
+
* <a href="/custom-url" className="custom-link">
|
|
1008
|
+
* <Product.Name />
|
|
1009
|
+
* </a>
|
|
1010
|
+
* </Product.Link>
|
|
1011
|
+
* ```
|
|
1012
|
+
*/
|
|
1013
|
+
export const Link = React.forwardRef((props, ref) => {
|
|
1014
|
+
const { asChild, children, className, list, position, origin } = props;
|
|
1015
|
+
// Get services for tracking
|
|
1016
|
+
const productService = useService(ProductServiceDefinition);
|
|
1017
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
1018
|
+
return (_jsx(CoreProduct.Slug, { children: ({ slug }) => {
|
|
1019
|
+
const handleClick = () => {
|
|
1020
|
+
const choices = variantService.selectedChoices.get();
|
|
1021
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
1022
|
+
productService.reportClickProductTrackEvent({
|
|
1023
|
+
variant: variantString,
|
|
1024
|
+
list,
|
|
1025
|
+
position,
|
|
1026
|
+
origin,
|
|
1027
|
+
});
|
|
1028
|
+
};
|
|
1029
|
+
if (asChild && children) {
|
|
1030
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productLink, customElement: children, customElementProps: { onClick: handleClick } }));
|
|
1031
|
+
}
|
|
1032
|
+
return (_jsx("a", { ref: ref, href: `/${slug}`, onClick: handleClick, className: className, "data-testid": TestIds.productLink, children: children }));
|
|
1033
|
+
} }));
|
|
1034
|
+
});
|
|
955
1035
|
/**
|
|
956
1036
|
* Helper function to determine stock status and label based on availability and can-pre-order settings
|
|
957
1037
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
2
1
|
import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
|
|
2
|
+
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
3
3
|
/**
|
|
4
4
|
* API interface for the Product service, providing reactive product data management.
|
|
5
5
|
* This service handles loading and managing a single product's data, loading state, and errors.
|
|
@@ -15,6 +15,22 @@ export interface ProductServiceAPI {
|
|
|
15
15
|
error: Signal<string | null>;
|
|
16
16
|
/** Function to load a product by its slug */
|
|
17
17
|
loadProduct: (slug: string) => Promise<void>;
|
|
18
|
+
/** Reports a ViewContent analytics event for the current product */
|
|
19
|
+
reportViewContentTrackEvent: () => void;
|
|
20
|
+
/** Reports an AddToCart analytics event for the current product */
|
|
21
|
+
reportAddToCartTrackEvent: (options?: {
|
|
22
|
+
quantity?: number;
|
|
23
|
+
variant?: string;
|
|
24
|
+
origin?: string;
|
|
25
|
+
position?: string;
|
|
26
|
+
}) => void;
|
|
27
|
+
/** Reports a ClickProduct analytics event for the current product */
|
|
28
|
+
reportClickProductTrackEvent: (options?: {
|
|
29
|
+
variant?: string;
|
|
30
|
+
list?: string;
|
|
31
|
+
position?: string;
|
|
32
|
+
origin?: string;
|
|
33
|
+
}) => void;
|
|
18
34
|
}
|
|
19
35
|
/**
|
|
20
36
|
* Service definition for the Product service.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
|
|
1
2
|
import { defineService, implementService } from '@wix/services-definitions';
|
|
2
3
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
3
|
-
import
|
|
4
|
+
import { analytics } from '@wix/site';
|
|
4
5
|
/**
|
|
5
6
|
* Service definition for the Product service.
|
|
6
7
|
* This defines the contract that the ProductService must implement.
|
|
@@ -61,11 +62,160 @@ export const ProductService = implementService.withConfig()(ProductServiceDefini
|
|
|
61
62
|
if (config.productSlug) {
|
|
62
63
|
loadProduct(config.productSlug);
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Reports a ViewContent analytics event for product page tracking.
|
|
67
|
+
* This function automatically formats and sends a standardized ViewContent event
|
|
68
|
+
* to the Wix Analytics service when a user views a product page. It extracts
|
|
69
|
+
* relevant product information (brand, category, price, SKU) and sends it in
|
|
70
|
+
* the proper format for analytics tracking.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* // Track when a product page loads
|
|
75
|
+
* import { useService } from '@wix/services-manager-react';
|
|
76
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
77
|
+
*
|
|
78
|
+
* function ProductPage() {
|
|
79
|
+
* const productService = useService(ProductServiceDefinition);
|
|
80
|
+
*
|
|
81
|
+
* useEffect(() => {
|
|
82
|
+
* productService.reportViewContentTrackEvent();
|
|
83
|
+
* }, []);
|
|
84
|
+
*
|
|
85
|
+
* return <div>Product Page</div>;
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
const reportViewContentTrackEvent = () => {
|
|
90
|
+
const currentProduct = product.get();
|
|
91
|
+
if (!currentProduct)
|
|
92
|
+
return;
|
|
93
|
+
const eventData = {
|
|
94
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
95
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
96
|
+
currency: currentProduct.currency ?? undefined,
|
|
97
|
+
id: currentProduct._id,
|
|
98
|
+
name: currentProduct.name || '',
|
|
99
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
100
|
+
?.amount ?? '0'),
|
|
101
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
102
|
+
};
|
|
103
|
+
analytics.trackEvent('ViewContent', eventData);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Reports an AddToCart analytics event for cart tracking.
|
|
107
|
+
* This function automatically formats and sends a standardized AddToCart event
|
|
108
|
+
* to the Wix Analytics service when a user adds a product to their cart. It extracts
|
|
109
|
+
* relevant product information (brand, category, price, SKU, variant, quantity) and sends
|
|
110
|
+
* it in the proper format for analytics tracking.
|
|
111
|
+
*
|
|
112
|
+
* @param options Additional options for the AddToCart event
|
|
113
|
+
* @param options.quantity The quantity being added to cart (defaults to 1)
|
|
114
|
+
* @param options.variant The selected product variant (e.g., "Large", "Green")
|
|
115
|
+
* @param options.origin Event origin (e.g., "Product Page", "Quick View")
|
|
116
|
+
* @param options.position List or collection position (e.g., "Product Gallery", "Search Results")
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```tsx
|
|
120
|
+
* // Track add to cart from product page
|
|
121
|
+
* import { useService } from '@wix/services-manager-react';
|
|
122
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
123
|
+
*
|
|
124
|
+
* function AddToCartButton() {
|
|
125
|
+
* const productService = useService(ProductServiceDefinition);
|
|
126
|
+
*
|
|
127
|
+
* const handleAddToCart = () => {
|
|
128
|
+
* productService.reportAddToCartTrackEvent({
|
|
129
|
+
* quantity: 1,
|
|
130
|
+
* variant: 'Large Blue'
|
|
131
|
+
* });
|
|
132
|
+
* };
|
|
133
|
+
*
|
|
134
|
+
* return <button onClick={handleAddToCart}>Add to Cart</button>;
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
const reportAddToCartTrackEvent = (options) => {
|
|
139
|
+
const currentProduct = product.get();
|
|
140
|
+
if (!currentProduct)
|
|
141
|
+
return;
|
|
142
|
+
const eventData = {
|
|
143
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
144
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
145
|
+
currency: currentProduct.currency ?? undefined,
|
|
146
|
+
id: currentProduct._id,
|
|
147
|
+
name: currentProduct.name || '',
|
|
148
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
149
|
+
?.amount ?? '0'),
|
|
150
|
+
quantity: options?.quantity ?? 1,
|
|
151
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
152
|
+
variant: options?.variant,
|
|
153
|
+
origin: options?.origin,
|
|
154
|
+
position: options?.position,
|
|
155
|
+
};
|
|
156
|
+
analytics.trackEvent('AddToCart', eventData);
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Reports a ClickProduct analytics event for product click tracking.
|
|
160
|
+
* This function automatically formats and sends a standardized ClickProduct event
|
|
161
|
+
* to the Wix Analytics service when a user clicks on a product link. It extracts
|
|
162
|
+
* relevant product information (brand, category, price, SKU, variant) and sends
|
|
163
|
+
* it in the proper format for analytics tracking.
|
|
164
|
+
*
|
|
165
|
+
* @param options Additional options for the ClickProduct event
|
|
166
|
+
* @param options.variant The selected product variant (e.g., "Large", "Green")
|
|
167
|
+
* @param options.list List or collection the product is part of (e.g., "Category: T-Shirts")
|
|
168
|
+
* @param options.position Position of the product within a list or collection (e.g., "3")
|
|
169
|
+
* @param options.origin Event origin (e.g., "Search Results", "Related Products")
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```tsx
|
|
173
|
+
* // Track product click from category page
|
|
174
|
+
* import { useService } from '@wix/services-manager-react';
|
|
175
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
176
|
+
*
|
|
177
|
+
* function ProductLink() {
|
|
178
|
+
* const productService = useService(ProductServiceDefinition);
|
|
179
|
+
*
|
|
180
|
+
* const handleClick = () => {
|
|
181
|
+
* productService.reportClickProductTrackEvent({
|
|
182
|
+
* list: 'Category: T-Shirts',
|
|
183
|
+
* position: '3'
|
|
184
|
+
* });
|
|
185
|
+
* };
|
|
186
|
+
*
|
|
187
|
+
* return <a onClick={handleClick}>View Product</a>;
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
const reportClickProductTrackEvent = (options) => {
|
|
192
|
+
const currentProduct = product.get();
|
|
193
|
+
if (!currentProduct)
|
|
194
|
+
return;
|
|
195
|
+
const eventData = {
|
|
196
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
197
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
198
|
+
currency: currentProduct.currency ?? undefined,
|
|
199
|
+
id: currentProduct._id,
|
|
200
|
+
name: currentProduct.name || '',
|
|
201
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
202
|
+
?.amount ?? '0'),
|
|
203
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
204
|
+
variant: options?.variant,
|
|
205
|
+
list: options?.list,
|
|
206
|
+
position: options?.position,
|
|
207
|
+
origin: options?.origin,
|
|
208
|
+
};
|
|
209
|
+
analytics.trackEvent('ClickProduct', eventData);
|
|
210
|
+
};
|
|
64
211
|
return {
|
|
65
212
|
product,
|
|
66
213
|
isLoading,
|
|
67
214
|
error,
|
|
68
215
|
loadProduct,
|
|
216
|
+
reportViewContentTrackEvent,
|
|
217
|
+
reportAddToCartTrackEvent,
|
|
218
|
+
reportClickProductTrackEvent,
|
|
69
219
|
};
|
|
70
220
|
});
|
|
71
221
|
/**
|
package/dist/react/Product.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { V3Product } from '@wix/auto_sdk_stores_products-v-3';
|
|
2
|
-
import React from 'react';
|
|
3
2
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
3
|
+
import React from 'react';
|
|
4
4
|
import { AsContent } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Context for sharing variant options state between components
|
|
@@ -885,6 +885,53 @@ export declare const Action: {
|
|
|
885
885
|
/** Pre-order action button */
|
|
886
886
|
readonly PreOrder: React.ForwardRefExoticComponent<ProductActionProps & React.RefAttributes<HTMLButtonElement>>;
|
|
887
887
|
};
|
|
888
|
+
/**
|
|
889
|
+
* Props for Product Link component
|
|
890
|
+
*/
|
|
891
|
+
export interface ProductLinkProps {
|
|
892
|
+
/** Whether to render as a child component */
|
|
893
|
+
asChild?: boolean;
|
|
894
|
+
/** Custom render function when using asChild */
|
|
895
|
+
children?: React.ReactNode;
|
|
896
|
+
/** CSS classes to apply to the link */
|
|
897
|
+
className?: string;
|
|
898
|
+
/** List or collection the product is part of (e.g., "Category: T-Shirts") */
|
|
899
|
+
list?: string;
|
|
900
|
+
/** Position of the product within a list or collection */
|
|
901
|
+
position?: string;
|
|
902
|
+
/** Event origin (e.g., "Search Results", "Related Products") */
|
|
903
|
+
origin?: string;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Product link component that automatically tracks ClickProduct events.
|
|
907
|
+
* Wraps a link to the product page and fires analytics when clicked.
|
|
908
|
+
*
|
|
909
|
+
* @component
|
|
910
|
+
* @example
|
|
911
|
+
* ```tsx
|
|
912
|
+
* // Simple usage
|
|
913
|
+
* <Product.Link className="btn-secondary">
|
|
914
|
+
* View Product
|
|
915
|
+
* </Product.Link>
|
|
916
|
+
*
|
|
917
|
+
* // With tracking parameters
|
|
918
|
+
* <Product.Link
|
|
919
|
+
* list="Category: T-Shirts"
|
|
920
|
+
* position="3"
|
|
921
|
+
* className="btn-secondary"
|
|
922
|
+
* >
|
|
923
|
+
* View Product
|
|
924
|
+
* </Product.Link>
|
|
925
|
+
*
|
|
926
|
+
* // asChild with custom link
|
|
927
|
+
* <Product.Link asChild list="Search Results" position="5">
|
|
928
|
+
* <a href="/custom-url" className="custom-link">
|
|
929
|
+
* <Product.Name />
|
|
930
|
+
* </a>
|
|
931
|
+
* </Product.Link>
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
export declare const Link: React.ForwardRefExoticComponent<ProductLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
888
935
|
/**
|
|
889
936
|
* Props for ProductVariantStock component
|
|
890
937
|
*/
|
package/dist/react/Product.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { InventoryAvailabilityStatus } from '@wix/auto_sdk_stores_products-v-3';
|
|
3
|
-
import
|
|
3
|
+
import { Quantity as QuantityComponent } from '@wix/headless-components/react';
|
|
4
4
|
import { Commerce } from '@wix/headless-ecom/react';
|
|
5
|
-
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
6
5
|
import { MediaGallery } from '@wix/headless-media/react';
|
|
7
|
-
import {
|
|
6
|
+
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
7
|
+
import { useService } from '@wix/services-manager-react';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { DataComponentTags } from '../data-component-tags.js';
|
|
10
|
+
import { ProductServiceDefinition, SelectedVariantServiceDefinition, } from '../services/index.js';
|
|
8
11
|
import * as CoreProduct from './core/Product.js';
|
|
9
|
-
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
10
12
|
import * as CoreProductModifiers from './core/ProductModifiers.js';
|
|
13
|
+
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
11
14
|
import * as CoreSelectedVariant from './core/SelectedVariant.js';
|
|
12
15
|
import * as Option from './Option.js';
|
|
13
16
|
import { AsContent } from './types.js';
|
|
14
|
-
import { DataComponentTags } from '../data-component-tags.js';
|
|
15
17
|
const VariantsContext = React.createContext(null);
|
|
16
18
|
/**
|
|
17
19
|
* Hook to access variants context
|
|
@@ -61,6 +63,7 @@ var TestIds;
|
|
|
61
63
|
TestIds["productActionAddToCart"] = "product-action-add-to-cart";
|
|
62
64
|
TestIds["productActionBuyNow"] = "product-action-buy-now";
|
|
63
65
|
TestIds["productActionPreOrder"] = "product-action-can-pre-order";
|
|
66
|
+
TestIds["productLink"] = "product-link";
|
|
64
67
|
TestIds["productQuantity"] = "product-quantity";
|
|
65
68
|
TestIds["productQuantityDecrement"] = "product-quantity-decrement";
|
|
66
69
|
TestIds["productQuantityInput"] = "product-quantity-input";
|
|
@@ -879,6 +882,19 @@ export const ProductVariantSelectorReset = React.forwardRef((props, ref) => {
|
|
|
879
882
|
*/
|
|
880
883
|
export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
881
884
|
const { asChild, children, className, label, loadingState } = props;
|
|
885
|
+
// Get services for tracking
|
|
886
|
+
const productService = useService(ProductServiceDefinition);
|
|
887
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
888
|
+
// Track after successful add to cart
|
|
889
|
+
const handleSuccess = async () => {
|
|
890
|
+
const choices = variantService.selectedChoices.get();
|
|
891
|
+
const quantity = variantService.selectedQuantity.get();
|
|
892
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
893
|
+
productService.reportAddToCartTrackEvent({
|
|
894
|
+
quantity,
|
|
895
|
+
variant: variantString,
|
|
896
|
+
});
|
|
897
|
+
};
|
|
882
898
|
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, canAddToCart, isLoading, addToCart, isPreOrderEnabled, }) => {
|
|
883
899
|
if (isPreOrderEnabled) {
|
|
884
900
|
return null;
|
|
@@ -886,13 +902,13 @@ export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
|
886
902
|
const onClick = addToCart;
|
|
887
903
|
const disabled = !canAddToCart || isLoading;
|
|
888
904
|
if (asChild && children) {
|
|
889
|
-
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
905
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, onSuccess: handleSuccess, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
890
906
|
disabled,
|
|
891
907
|
isLoading,
|
|
892
908
|
onClick,
|
|
893
909
|
} }) }));
|
|
894
910
|
}
|
|
895
|
-
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
911
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, onSuccess: handleSuccess, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
896
912
|
} }));
|
|
897
913
|
});
|
|
898
914
|
/**
|
|
@@ -923,6 +939,19 @@ export const ProductActionBuyNow = React.forwardRef((props, ref) => {
|
|
|
923
939
|
*/
|
|
924
940
|
export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
925
941
|
const { asChild, children, className, label, loadingState } = props;
|
|
942
|
+
// Get services for tracking
|
|
943
|
+
const productService = useService(ProductServiceDefinition);
|
|
944
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
945
|
+
// Track after successful pre-order
|
|
946
|
+
const handleSuccess = async () => {
|
|
947
|
+
const choices = variantService.selectedChoices.get();
|
|
948
|
+
const quantity = variantService.selectedQuantity.get();
|
|
949
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
950
|
+
productService.reportAddToCartTrackEvent({
|
|
951
|
+
quantity,
|
|
952
|
+
variant: variantString,
|
|
953
|
+
});
|
|
954
|
+
};
|
|
926
955
|
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, isLoading, addToCart, isPreOrderEnabled }) => {
|
|
927
956
|
if (!isPreOrderEnabled) {
|
|
928
957
|
return null;
|
|
@@ -931,13 +960,13 @@ export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
|
931
960
|
const onClick = addToCart;
|
|
932
961
|
const disabled = !canPreOrder;
|
|
933
962
|
if (asChild && children) {
|
|
934
|
-
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
963
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, onSuccess: handleSuccess, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
935
964
|
disabled,
|
|
936
965
|
isLoading,
|
|
937
966
|
onClick,
|
|
938
967
|
} }) }));
|
|
939
968
|
}
|
|
940
|
-
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
969
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, onSuccess: handleSuccess, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
941
970
|
} }));
|
|
942
971
|
});
|
|
943
972
|
/**
|
|
@@ -952,6 +981,57 @@ export const Action = {
|
|
|
952
981
|
/** Pre-order action button */
|
|
953
982
|
PreOrder: ProductActionPreOrder,
|
|
954
983
|
};
|
|
984
|
+
/**
|
|
985
|
+
* Product link component that automatically tracks ClickProduct events.
|
|
986
|
+
* Wraps a link to the product page and fires analytics when clicked.
|
|
987
|
+
*
|
|
988
|
+
* @component
|
|
989
|
+
* @example
|
|
990
|
+
* ```tsx
|
|
991
|
+
* // Simple usage
|
|
992
|
+
* <Product.Link className="btn-secondary">
|
|
993
|
+
* View Product
|
|
994
|
+
* </Product.Link>
|
|
995
|
+
*
|
|
996
|
+
* // With tracking parameters
|
|
997
|
+
* <Product.Link
|
|
998
|
+
* list="Category: T-Shirts"
|
|
999
|
+
* position="3"
|
|
1000
|
+
* className="btn-secondary"
|
|
1001
|
+
* >
|
|
1002
|
+
* View Product
|
|
1003
|
+
* </Product.Link>
|
|
1004
|
+
*
|
|
1005
|
+
* // asChild with custom link
|
|
1006
|
+
* <Product.Link asChild list="Search Results" position="5">
|
|
1007
|
+
* <a href="/custom-url" className="custom-link">
|
|
1008
|
+
* <Product.Name />
|
|
1009
|
+
* </a>
|
|
1010
|
+
* </Product.Link>
|
|
1011
|
+
* ```
|
|
1012
|
+
*/
|
|
1013
|
+
export const Link = React.forwardRef((props, ref) => {
|
|
1014
|
+
const { asChild, children, className, list, position, origin } = props;
|
|
1015
|
+
// Get services for tracking
|
|
1016
|
+
const productService = useService(ProductServiceDefinition);
|
|
1017
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
1018
|
+
return (_jsx(CoreProduct.Slug, { children: ({ slug }) => {
|
|
1019
|
+
const handleClick = () => {
|
|
1020
|
+
const choices = variantService.selectedChoices.get();
|
|
1021
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
1022
|
+
productService.reportClickProductTrackEvent({
|
|
1023
|
+
variant: variantString,
|
|
1024
|
+
list,
|
|
1025
|
+
position,
|
|
1026
|
+
origin,
|
|
1027
|
+
});
|
|
1028
|
+
};
|
|
1029
|
+
if (asChild && children) {
|
|
1030
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productLink, customElement: children, customElementProps: { onClick: handleClick } }));
|
|
1031
|
+
}
|
|
1032
|
+
return (_jsx("a", { ref: ref, href: `/${slug}`, onClick: handleClick, className: className, "data-testid": TestIds.productLink, children: children }));
|
|
1033
|
+
} }));
|
|
1034
|
+
});
|
|
955
1035
|
/**
|
|
956
1036
|
* Helper function to determine stock status and label based on availability and can-pre-order settings
|
|
957
1037
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
2
1
|
import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
|
|
2
|
+
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
3
3
|
/**
|
|
4
4
|
* API interface for the Product service, providing reactive product data management.
|
|
5
5
|
* This service handles loading and managing a single product's data, loading state, and errors.
|
|
@@ -15,6 +15,22 @@ export interface ProductServiceAPI {
|
|
|
15
15
|
error: Signal<string | null>;
|
|
16
16
|
/** Function to load a product by its slug */
|
|
17
17
|
loadProduct: (slug: string) => Promise<void>;
|
|
18
|
+
/** Reports a ViewContent analytics event for the current product */
|
|
19
|
+
reportViewContentTrackEvent: () => void;
|
|
20
|
+
/** Reports an AddToCart analytics event for the current product */
|
|
21
|
+
reportAddToCartTrackEvent: (options?: {
|
|
22
|
+
quantity?: number;
|
|
23
|
+
variant?: string;
|
|
24
|
+
origin?: string;
|
|
25
|
+
position?: string;
|
|
26
|
+
}) => void;
|
|
27
|
+
/** Reports a ClickProduct analytics event for the current product */
|
|
28
|
+
reportClickProductTrackEvent: (options?: {
|
|
29
|
+
variant?: string;
|
|
30
|
+
list?: string;
|
|
31
|
+
position?: string;
|
|
32
|
+
origin?: string;
|
|
33
|
+
}) => void;
|
|
18
34
|
}
|
|
19
35
|
/**
|
|
20
36
|
* Service definition for the Product service.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
|
|
1
2
|
import { defineService, implementService } from '@wix/services-definitions';
|
|
2
3
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
3
|
-
import
|
|
4
|
+
import { analytics } from '@wix/site';
|
|
4
5
|
/**
|
|
5
6
|
* Service definition for the Product service.
|
|
6
7
|
* This defines the contract that the ProductService must implement.
|
|
@@ -61,11 +62,160 @@ export const ProductService = implementService.withConfig()(ProductServiceDefini
|
|
|
61
62
|
if (config.productSlug) {
|
|
62
63
|
loadProduct(config.productSlug);
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Reports a ViewContent analytics event for product page tracking.
|
|
67
|
+
* This function automatically formats and sends a standardized ViewContent event
|
|
68
|
+
* to the Wix Analytics service when a user views a product page. It extracts
|
|
69
|
+
* relevant product information (brand, category, price, SKU) and sends it in
|
|
70
|
+
* the proper format for analytics tracking.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* // Track when a product page loads
|
|
75
|
+
* import { useService } from '@wix/services-manager-react';
|
|
76
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
77
|
+
*
|
|
78
|
+
* function ProductPage() {
|
|
79
|
+
* const productService = useService(ProductServiceDefinition);
|
|
80
|
+
*
|
|
81
|
+
* useEffect(() => {
|
|
82
|
+
* productService.reportViewContentTrackEvent();
|
|
83
|
+
* }, []);
|
|
84
|
+
*
|
|
85
|
+
* return <div>Product Page</div>;
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
const reportViewContentTrackEvent = () => {
|
|
90
|
+
const currentProduct = product.get();
|
|
91
|
+
if (!currentProduct)
|
|
92
|
+
return;
|
|
93
|
+
const eventData = {
|
|
94
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
95
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
96
|
+
currency: currentProduct.currency ?? undefined,
|
|
97
|
+
id: currentProduct._id,
|
|
98
|
+
name: currentProduct.name || '',
|
|
99
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
100
|
+
?.amount ?? '0'),
|
|
101
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
102
|
+
};
|
|
103
|
+
analytics.trackEvent('ViewContent', eventData);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Reports an AddToCart analytics event for cart tracking.
|
|
107
|
+
* This function automatically formats and sends a standardized AddToCart event
|
|
108
|
+
* to the Wix Analytics service when a user adds a product to their cart. It extracts
|
|
109
|
+
* relevant product information (brand, category, price, SKU, variant, quantity) and sends
|
|
110
|
+
* it in the proper format for analytics tracking.
|
|
111
|
+
*
|
|
112
|
+
* @param options Additional options for the AddToCart event
|
|
113
|
+
* @param options.quantity The quantity being added to cart (defaults to 1)
|
|
114
|
+
* @param options.variant The selected product variant (e.g., "Large", "Green")
|
|
115
|
+
* @param options.origin Event origin (e.g., "Product Page", "Quick View")
|
|
116
|
+
* @param options.position List or collection position (e.g., "Product Gallery", "Search Results")
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```tsx
|
|
120
|
+
* // Track add to cart from product page
|
|
121
|
+
* import { useService } from '@wix/services-manager-react';
|
|
122
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
123
|
+
*
|
|
124
|
+
* function AddToCartButton() {
|
|
125
|
+
* const productService = useService(ProductServiceDefinition);
|
|
126
|
+
*
|
|
127
|
+
* const handleAddToCart = () => {
|
|
128
|
+
* productService.reportAddToCartTrackEvent({
|
|
129
|
+
* quantity: 1,
|
|
130
|
+
* variant: 'Large Blue'
|
|
131
|
+
* });
|
|
132
|
+
* };
|
|
133
|
+
*
|
|
134
|
+
* return <button onClick={handleAddToCart}>Add to Cart</button>;
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
const reportAddToCartTrackEvent = (options) => {
|
|
139
|
+
const currentProduct = product.get();
|
|
140
|
+
if (!currentProduct)
|
|
141
|
+
return;
|
|
142
|
+
const eventData = {
|
|
143
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
144
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
145
|
+
currency: currentProduct.currency ?? undefined,
|
|
146
|
+
id: currentProduct._id,
|
|
147
|
+
name: currentProduct.name || '',
|
|
148
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
149
|
+
?.amount ?? '0'),
|
|
150
|
+
quantity: options?.quantity ?? 1,
|
|
151
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
152
|
+
variant: options?.variant,
|
|
153
|
+
origin: options?.origin,
|
|
154
|
+
position: options?.position,
|
|
155
|
+
};
|
|
156
|
+
analytics.trackEvent('AddToCart', eventData);
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Reports a ClickProduct analytics event for product click tracking.
|
|
160
|
+
* This function automatically formats and sends a standardized ClickProduct event
|
|
161
|
+
* to the Wix Analytics service when a user clicks on a product link. It extracts
|
|
162
|
+
* relevant product information (brand, category, price, SKU, variant) and sends
|
|
163
|
+
* it in the proper format for analytics tracking.
|
|
164
|
+
*
|
|
165
|
+
* @param options Additional options for the ClickProduct event
|
|
166
|
+
* @param options.variant The selected product variant (e.g., "Large", "Green")
|
|
167
|
+
* @param options.list List or collection the product is part of (e.g., "Category: T-Shirts")
|
|
168
|
+
* @param options.position Position of the product within a list or collection (e.g., "3")
|
|
169
|
+
* @param options.origin Event origin (e.g., "Search Results", "Related Products")
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```tsx
|
|
173
|
+
* // Track product click from category page
|
|
174
|
+
* import { useService } from '@wix/services-manager-react';
|
|
175
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
176
|
+
*
|
|
177
|
+
* function ProductLink() {
|
|
178
|
+
* const productService = useService(ProductServiceDefinition);
|
|
179
|
+
*
|
|
180
|
+
* const handleClick = () => {
|
|
181
|
+
* productService.reportClickProductTrackEvent({
|
|
182
|
+
* list: 'Category: T-Shirts',
|
|
183
|
+
* position: '3'
|
|
184
|
+
* });
|
|
185
|
+
* };
|
|
186
|
+
*
|
|
187
|
+
* return <a onClick={handleClick}>View Product</a>;
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
const reportClickProductTrackEvent = (options) => {
|
|
192
|
+
const currentProduct = product.get();
|
|
193
|
+
if (!currentProduct)
|
|
194
|
+
return;
|
|
195
|
+
const eventData = {
|
|
196
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
197
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
198
|
+
currency: currentProduct.currency ?? undefined,
|
|
199
|
+
id: currentProduct._id,
|
|
200
|
+
name: currentProduct.name || '',
|
|
201
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
202
|
+
?.amount ?? '0'),
|
|
203
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
204
|
+
variant: options?.variant,
|
|
205
|
+
list: options?.list,
|
|
206
|
+
position: options?.position,
|
|
207
|
+
origin: options?.origin,
|
|
208
|
+
};
|
|
209
|
+
analytics.trackEvent('ClickProduct', eventData);
|
|
210
|
+
};
|
|
64
211
|
return {
|
|
65
212
|
product,
|
|
66
213
|
isLoading,
|
|
67
214
|
error,
|
|
68
215
|
loadProduct,
|
|
216
|
+
reportViewContentTrackEvent,
|
|
217
|
+
reportAddToCartTrackEvent,
|
|
218
|
+
reportClickProductTrackEvent,
|
|
69
219
|
};
|
|
70
220
|
});
|
|
71
221
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-stores",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.105",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -66,12 +66,13 @@
|
|
|
66
66
|
"@wix/auto_sdk_stores_read-only-variants-v-3": "^1.0.23",
|
|
67
67
|
"@wix/ecom": "^1.0.1461",
|
|
68
68
|
"@wix/essentials": "^0.1.24",
|
|
69
|
-
"@wix/headless-ecom": "0.0.
|
|
69
|
+
"@wix/headless-ecom": "0.0.38",
|
|
70
70
|
"@wix/headless-media": "0.0.18",
|
|
71
71
|
"@wix/headless-utils": "0.0.8",
|
|
72
72
|
"@wix/redirects": "^1.0.83",
|
|
73
73
|
"@wix/services-definitions": "^0.1.4",
|
|
74
|
-
"@wix/services-manager-react": "^0.1.26"
|
|
74
|
+
"@wix/services-manager-react": "^0.1.26",
|
|
75
|
+
"@wix/site": "^1.27.0"
|
|
75
76
|
},
|
|
76
77
|
"peerDependencies": {
|
|
77
78
|
"@wix/headless-components": "^0.0.0"
|
|
@@ -86,5 +87,5 @@
|
|
|
86
87
|
"groupId": "com.wixpress.headless-components"
|
|
87
88
|
}
|
|
88
89
|
},
|
|
89
|
-
"falconPackageHash": "
|
|
90
|
+
"falconPackageHash": "2937c978ada9e33f794daf2161a4f84ffe9ea93141e59938401efb95"
|
|
90
91
|
}
|