@wix/headless-stores 0.0.104 → 0.0.106

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.
Files changed (33) hide show
  1. package/cjs/dist/react/Option.d.ts +1 -0
  2. package/cjs/dist/react/Product.d.ts +51 -4
  3. package/cjs/dist/react/Product.js +95 -15
  4. package/cjs/dist/react/ProductList.d.ts +2 -3
  5. package/cjs/dist/react/core/Product.d.ts +8 -8
  6. package/cjs/dist/react/core/ProductModifiers.d.ts +11 -9
  7. package/cjs/dist/react/core/ProductModifiers.js +6 -3
  8. package/cjs/dist/react/core/ProductVariantSelector.d.ts +13 -9
  9. package/cjs/dist/react/core/ProductVariantSelector.js +14 -8
  10. package/cjs/dist/services/buy-now-service.js +2 -2
  11. package/cjs/dist/services/product-modifiers-service.d.ts +1 -1
  12. package/cjs/dist/services/product-modifiers-service.js +1 -1
  13. package/cjs/dist/services/product-service.d.ts +17 -1
  14. package/cjs/dist/services/product-service.js +151 -1
  15. package/cjs/dist/services/selected-variant-service.d.ts +6 -6
  16. package/cjs/dist/services/selected-variant-service.js +15 -16
  17. package/dist/react/Option.d.ts +1 -0
  18. package/dist/react/Product.d.ts +51 -4
  19. package/dist/react/Product.js +95 -15
  20. package/dist/react/ProductList.d.ts +2 -3
  21. package/dist/react/core/Product.d.ts +8 -8
  22. package/dist/react/core/ProductModifiers.d.ts +11 -9
  23. package/dist/react/core/ProductModifiers.js +6 -3
  24. package/dist/react/core/ProductVariantSelector.d.ts +13 -9
  25. package/dist/react/core/ProductVariantSelector.js +14 -8
  26. package/dist/services/buy-now-service.js +2 -2
  27. package/dist/services/product-modifiers-service.d.ts +1 -1
  28. package/dist/services/product-modifiers-service.js +1 -1
  29. package/dist/services/product-service.d.ts +17 -1
  30. package/dist/services/product-service.js +151 -1
  31. package/dist/services/selected-variant-service.d.ts +6 -6
  32. package/dist/services/selected-variant-service.js +15 -16
  33. package/package.json +8 -10
@@ -1,5 +1,5 @@
1
+ import { productsV3 } from '@wix/stores';
1
2
  import { type Signal } from '@wix/services-definitions/core-services/signals';
2
- import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
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 { productsV3 } from '@wix/stores';
1
2
  import { defineService, implementService } from '@wix/services-definitions';
2
3
  import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
3
- import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
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
  /**
@@ -1,5 +1,5 @@
1
1
  import { type Signal, type ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
2
- import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
2
+ import { productsV3 } from '@wix/stores';
3
3
  export interface SelectedVariantServiceAPI {
4
4
  selectedQuantity: Signal<number>;
5
5
  selectedChoices: Signal<Record<string, string>>;
@@ -39,15 +39,15 @@ export interface SelectedVariantServiceAPI {
39
39
  decrementQuantity: () => void;
40
40
  selectVariantById: (id: string) => void;
41
41
  resetSelections: () => void;
42
- getAvailableChoicesForOption: (optionName: string) => string[];
43
- getChoiceInfo: (optionName: string, choiceValue: string) => {
42
+ getAvailableChoicesForOption: (optionKey: string) => string[];
43
+ getChoiceInfo: (optionKey: string, choiceValue: string) => {
44
44
  isAvailable: boolean;
45
45
  isInStock: boolean;
46
46
  isPreOrderEnabled: boolean;
47
47
  };
48
- isChoiceAvailable: (optionName: string, choiceValue: string) => boolean;
49
- isChoiceInStock: (optionName: string, choiceValue: string) => boolean;
50
- isChoicePreOrderEnabled: (optionName: string, choiceValue: string) => boolean;
48
+ isChoiceAvailable: (optionKey: string, choiceValue: string) => boolean;
49
+ isChoiceInStock: (optionKey: string, choiceValue: string) => boolean;
50
+ isChoicePreOrderEnabled: (optionKey: string, choiceValue: string) => boolean;
51
51
  hasAnySelections: () => boolean;
52
52
  IsAllVariantsAreOutOfStock: () => boolean;
53
53
  }
@@ -1,7 +1,6 @@
1
1
  import { defineService, implementService } from '@wix/services-definitions';
2
2
  import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
3
- import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
4
- import * as inventoryItemsV3 from '@wix/auto_sdk_stores_inventory-items-v-3';
3
+ import { inventoryItemsV3, productsV3 } from '@wix/stores';
5
4
  import { CurrentCartServiceDefinition } from '@wix/headless-ecom/services';
6
5
  import { MediaGalleryServiceDefinition } from '@wix/headless-media/services';
7
6
  import { ProductServiceDefinition } from './product-service.js';
@@ -202,8 +201,8 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
202
201
  if (Object.keys(choices).length !==
203
202
  Object.keys(variantChoices).length)
204
203
  return false;
205
- return Object.entries(choices).every(([optionName, optionValue]) => {
206
- return variantChoices[optionName] === optionValue;
204
+ return Object.entries(choices).every(([optionKey, optionValue]) => {
205
+ return variantChoices[optionKey] === optionValue;
207
206
  });
208
207
  }) || null);
209
208
  }));
@@ -410,7 +409,7 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
410
409
  selectedQuantity.set(newQuantity);
411
410
  };
412
411
  // New methods for smart variant selection
413
- const getAvailableChoicesForOption = (optionName) => {
412
+ const getAvailableChoicesForOption = (optionKey) => {
414
413
  const currentChoices = selectedChoices.get();
415
414
  const variantsList = variants.get();
416
415
  // Get all possible choices for this option that result in valid variants
@@ -419,21 +418,21 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
419
418
  const variantChoices = processVariantChoices(variant);
420
419
  // Check if this variant matches all currently selected choices (except for the option we're checking)
421
420
  const matchesOtherChoices = Object.entries(currentChoices)
422
- .filter(([key]) => key !== optionName)
421
+ .filter(([key]) => key !== optionKey)
423
422
  .every(([key, value]) => variantChoices[key] === value);
424
- if (matchesOtherChoices && variantChoices[optionName]) {
425
- availableChoices.add(variantChoices[optionName]);
423
+ if (matchesOtherChoices && variantChoices[optionKey]) {
424
+ availableChoices.add(variantChoices[optionKey]);
426
425
  }
427
426
  });
428
427
  return Array.from(availableChoices);
429
428
  };
430
429
  // Core method that provides both availability and stock info efficiently
431
- const getChoiceInfo = (optionName, choiceValue) => {
430
+ const getChoiceInfo = (optionKey, choiceValue) => {
432
431
  // Create hypothetical choices with this choice selected
433
432
  const currentChoices = selectedChoices.get();
434
433
  const hypotheticalChoices = {
435
434
  ...currentChoices,
436
- [optionName]: choiceValue,
435
+ [optionKey]: choiceValue,
437
436
  };
438
437
  // Get all variants and find one that matches these choices
439
438
  const variantsList = variants.get();
@@ -459,14 +458,14 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
459
458
  return { isAvailable, isInStock, isPreOrderEnabled };
460
459
  };
461
460
  // Simplified methods using the core getChoiceInfo
462
- const isChoiceAvailable = (optionName, choiceValue) => {
463
- return getChoiceInfo(optionName, choiceValue).isAvailable;
461
+ const isChoiceAvailable = (optionKey, choiceValue) => {
462
+ return getChoiceInfo(optionKey, choiceValue).isAvailable;
464
463
  };
465
- const isChoiceInStock = (optionName, choiceValue) => {
466
- return getChoiceInfo(optionName, choiceValue).isInStock;
464
+ const isChoiceInStock = (optionKey, choiceValue) => {
465
+ return getChoiceInfo(optionKey, choiceValue).isInStock;
467
466
  };
468
- const isChoicePreOrderEnabled = (optionName, choiceValue) => {
469
- return getChoiceInfo(optionName, choiceValue).isPreOrderEnabled;
467
+ const isChoicePreOrderEnabled = (optionKey, choiceValue) => {
468
+ return getChoiceInfo(optionKey, choiceValue).isPreOrderEnabled;
470
469
  };
471
470
  const hasAnySelections = () => {
472
471
  const currentChoices = selectedChoices.get();
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { AsChildChildren } from '@wix/headless-utils/react';
3
3
  export interface Option {
4
4
  name: string;
5
+ key?: string;
5
6
  choices?: any[];
6
7
  hasChoices?: boolean;
7
8
  type?: string;
@@ -1,6 +1,6 @@
1
- import type { V3Product } from '@wix/auto_sdk_stores_products-v-3';
2
- import React from 'react';
1
+ import { productsV3 } from '@wix/stores';
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
@@ -37,7 +37,7 @@ export type StockStatus = BasicStockStatus | 'can-pre-order';
37
37
  */
38
38
  export interface ProductRootProps {
39
39
  children: React.ReactNode;
40
- product: V3Product;
40
+ product: productsV3.V3Product;
41
41
  selectedVariant?: any;
42
42
  }
43
43
  /**
@@ -268,7 +268,7 @@ export interface RawProps {
268
268
  asChild?: boolean;
269
269
  /** Custom render function when using asChild */
270
270
  children?: AsChildChildren<{
271
- product: V3Product;
271
+ product: productsV3.V3Product;
272
272
  }>;
273
273
  /** CSS classes to apply to the default element */
274
274
  className?: string;
@@ -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
- import { InventoryAvailabilityStatus } from '@wix/auto_sdk_stores_products-v-3';
3
- import React from 'react';
2
+ import { productsV3 } from '@wix/stores';
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 { Quantity as QuantityComponent } from '@wix/headless-components/react';
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";
@@ -403,15 +406,15 @@ export const Stock = React.forwardRef((props, ref) => {
403
406
  let status;
404
407
  let label;
405
408
  switch (availabilityStatus) {
406
- case InventoryAvailabilityStatus.IN_STOCK:
409
+ case productsV3.InventoryAvailabilityStatus.IN_STOCK:
407
410
  status = 'in-stock';
408
411
  label = finalLabels.inStock;
409
412
  break;
410
- case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
413
+ case productsV3.InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
411
414
  status = 'limited-stock';
412
415
  label = finalLabels.limitedStock;
413
416
  break;
414
- case InventoryAvailabilityStatus.OUT_OF_STOCK:
417
+ case productsV3.InventoryAvailabilityStatus.OUT_OF_STOCK:
415
418
  default:
416
419
  status = 'out-of-stock';
417
420
  label = finalLabels.outOfStock;
@@ -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
  */
@@ -966,12 +1046,12 @@ function getStockStatusMessage(inStock, isPreOrderEnabled, availabilityStatus, l
966
1046
  // Handle stock status based on availability
967
1047
  if (inStock) {
968
1048
  switch (availabilityStatus) {
969
- case InventoryAvailabilityStatus.IN_STOCK:
1049
+ case productsV3.InventoryAvailabilityStatus.IN_STOCK:
970
1050
  return {
971
1051
  status: 'in-stock',
972
1052
  label: labels.inStock,
973
1053
  };
974
- case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
1054
+ case productsV3.InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
975
1055
  return {
976
1056
  status: 'limited-stock',
977
1057
  label: labels.limitedStock,
@@ -1,4 +1,3 @@
1
- import type { V3Product } from '@wix/auto_sdk_stores_products-v-3';
2
1
  import { Sort as SortPrimitive, GenericListTotalsRenderProps, GenericListLoadMoreRenderProps, ListVariant, GenericListRepeaterRenderProps } from '@wix/headless-components/react';
3
2
  import React from 'react';
4
3
  import type { ProductsListServiceConfig } from '../services/products-list-service.js';
@@ -9,7 +8,7 @@ import { AsChildChildren } from '@wix/headless-utils/react';
9
8
  */
10
9
  export interface ProductListRootProps {
11
10
  children: React.ReactNode;
12
- products?: V3Product[];
11
+ products?: productsV3.V3Product[];
13
12
  productsListConfig?: ProductsListServiceConfig;
14
13
  className?: string;
15
14
  variant?: ListVariant;
@@ -95,7 +94,7 @@ export declare const Products: React.ForwardRefExoticComponent<ProductsProps & R
95
94
  /**
96
95
  * Render props for ProductRepeater asChild pattern
97
96
  */
98
- export type ProductRepeaterRenderProps = GenericListRepeaterRenderProps<V3Product>;
97
+ export type ProductRepeaterRenderProps = GenericListRepeaterRenderProps<productsV3.V3Product>;
99
98
  /**
100
99
  * Props for ProductList ProductRepeater component
101
100
  */
@@ -1,5 +1,5 @@
1
1
  import { ProductServiceConfig } from '../../services/product-service.js';
2
- import type { V3Product, ProductMedia } from '@wix/auto_sdk_stores_products-v-3';
2
+ import { productsV3 } from '@wix/stores';
3
3
  export interface RootProps {
4
4
  children: React.ReactNode;
5
5
  productServiceConfig: ProductServiceConfig;
@@ -81,18 +81,18 @@ export interface ProductDescriptionProps {
81
81
  */
82
82
  export interface ProductDescriptionRenderProps {
83
83
  /** Product description using the RICOS (Rich Content Object) format. See https://dev.wix.com/docs/ricos/api-reference/ricos-document */
84
- description: NonNullable<V3Product['description']>;
84
+ description: NonNullable<productsV3.V3Product['description']>;
85
85
  /** Product description with plain html */
86
- plainDescription: NonNullable<V3Product['plainDescription']>;
86
+ plainDescription: NonNullable<productsV3.V3Product['plainDescription']>;
87
87
  }
88
88
  /**
89
89
  * Render props for ProductDescription component
90
90
  */
91
91
  export interface ProductDescriptionRenderProps {
92
92
  /** Product description using the RICOS (Rich Content Object) format. See https://dev.wix.com/docs/ricos/api-reference/ricos-document */
93
- description: NonNullable<V3Product['description']>;
93
+ description: NonNullable<productsV3.V3Product['description']>;
94
94
  /** Product description with plain html */
95
- plainDescription: NonNullable<V3Product['plainDescription']>;
95
+ plainDescription: NonNullable<productsV3.V3Product['plainDescription']>;
96
96
  }
97
97
  /**
98
98
  * Headless component for product description display
@@ -129,15 +129,15 @@ export interface ProductMediaProps {
129
129
  children: (props: ProductMediaRenderProps) => React.ReactNode;
130
130
  }
131
131
  export interface ProductMediaRenderProps {
132
- mediaItems: ProductMedia[];
133
- mainMedia?: ProductMedia;
132
+ mediaItems: productsV3.ProductMedia[];
133
+ mainMedia?: productsV3.ProductMedia;
134
134
  }
135
135
  export declare function Media(props: ProductMediaProps): import("react").ReactNode;
136
136
  export interface ProductProps {
137
137
  children: (props: ProductRenderProps) => React.ReactNode;
138
138
  }
139
139
  export interface ProductRenderProps {
140
- product: V3Product;
140
+ product: productsV3.V3Product;
141
141
  }
142
142
  export declare function Content(props: ProductProps): import("react").ReactNode;
143
143
  export interface LoadingProps {