@wix/headless-stores 0.0.0

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 (150) hide show
  1. package/astro/actions/package.json +4 -0
  2. package/cjs/dist/astro/actions/custom-checkout.d.ts +50 -0
  3. package/cjs/dist/astro/actions/custom-checkout.js +53 -0
  4. package/cjs/dist/astro/actions/index.d.ts +1 -0
  5. package/cjs/dist/astro/actions/index.js +1 -0
  6. package/cjs/dist/data-component-tags.d.ts +8 -0
  7. package/cjs/dist/data-component-tags.js +9 -0
  8. package/cjs/dist/enums/index.d.ts +2 -0
  9. package/cjs/dist/enums/index.js +2 -0
  10. package/cjs/dist/enums/social-platform-enums.d.ts +25 -0
  11. package/cjs/dist/enums/social-platform-enums.js +27 -0
  12. package/cjs/dist/enums/sort-enums.d.ts +17 -0
  13. package/cjs/dist/enums/sort-enums.js +18 -0
  14. package/cjs/dist/react/Category.d.ts +242 -0
  15. package/cjs/dist/react/Category.js +235 -0
  16. package/cjs/dist/react/CategoryList.d.ts +107 -0
  17. package/cjs/dist/react/CategoryList.js +91 -0
  18. package/cjs/dist/react/Choice.d.ts +211 -0
  19. package/cjs/dist/react/Choice.js +213 -0
  20. package/cjs/dist/react/Option.d.ts +242 -0
  21. package/cjs/dist/react/Option.js +346 -0
  22. package/cjs/dist/react/Product.d.ts +1065 -0
  23. package/cjs/dist/react/Product.js +1157 -0
  24. package/cjs/dist/react/ProductList.d.ts +400 -0
  25. package/cjs/dist/react/ProductList.js +368 -0
  26. package/cjs/dist/react/core/CategoryList.d.ts +194 -0
  27. package/cjs/dist/react/core/CategoryList.js +180 -0
  28. package/cjs/dist/react/core/Product.d.ts +225 -0
  29. package/cjs/dist/react/core/Product.js +190 -0
  30. package/cjs/dist/react/core/ProductList.d.ts +235 -0
  31. package/cjs/dist/react/core/ProductList.js +217 -0
  32. package/cjs/dist/react/core/ProductListFilters.d.ts +138 -0
  33. package/cjs/dist/react/core/ProductListFilters.js +242 -0
  34. package/cjs/dist/react/core/ProductListPagination.d.ts +49 -0
  35. package/cjs/dist/react/core/ProductListPagination.js +41 -0
  36. package/cjs/dist/react/core/ProductListSort.d.ts +19 -0
  37. package/cjs/dist/react/core/ProductListSort.js +52 -0
  38. package/cjs/dist/react/core/ProductModifiers.d.ts +416 -0
  39. package/cjs/dist/react/core/ProductModifiers.js +413 -0
  40. package/cjs/dist/react/core/ProductVariantSelector.d.ts +313 -0
  41. package/cjs/dist/react/core/ProductVariantSelector.js +291 -0
  42. package/cjs/dist/react/core/SelectedVariant.d.ts +230 -0
  43. package/cjs/dist/react/core/SelectedVariant.js +269 -0
  44. package/cjs/dist/react/index.d.ts +6 -0
  45. package/cjs/dist/react/index.js +6 -0
  46. package/cjs/dist/react/types.d.ts +8 -0
  47. package/cjs/dist/react/types.js +9 -0
  48. package/cjs/dist/server-actions/custom-checkout-action.d.ts +49 -0
  49. package/cjs/dist/server-actions/custom-checkout-action.js +64 -0
  50. package/cjs/dist/server-actions/index.d.ts +1 -0
  51. package/cjs/dist/server-actions/index.js +1 -0
  52. package/cjs/dist/services/buy-now-service.d.ts +346 -0
  53. package/cjs/dist/services/buy-now-service.js +197 -0
  54. package/cjs/dist/services/categories-list-service.d.ts +164 -0
  55. package/cjs/dist/services/categories-list-service.js +148 -0
  56. package/cjs/dist/services/index.d.ts +5 -0
  57. package/cjs/dist/services/index.js +5 -0
  58. package/cjs/dist/services/pay-now-service.d.ts +214 -0
  59. package/cjs/dist/services/pay-now-service.js +156 -0
  60. package/cjs/dist/services/product-modifiers-service.d.ts +34 -0
  61. package/cjs/dist/services/product-modifiers-service.js +107 -0
  62. package/cjs/dist/services/product-service.d.ts +177 -0
  63. package/cjs/dist/services/product-service.js +190 -0
  64. package/cjs/dist/services/products-list-search-service.d.ts +1 -0
  65. package/cjs/dist/services/products-list-search-service.js +1 -0
  66. package/cjs/dist/services/products-list-service.d.ts +429 -0
  67. package/cjs/dist/services/products-list-service.js +893 -0
  68. package/cjs/dist/services/selected-variant-service.d.ts +66 -0
  69. package/cjs/dist/services/selected-variant-service.js +527 -0
  70. package/cjs/dist/utils/index.d.ts +1 -0
  71. package/cjs/dist/utils/index.js +30 -0
  72. package/cjs/dist/utils/url-params.d.ts +73 -0
  73. package/cjs/dist/utils/url-params.js +114 -0
  74. package/cjs/package.json +3 -0
  75. package/dist/astro/actions/custom-checkout.d.ts +50 -0
  76. package/dist/astro/actions/custom-checkout.js +53 -0
  77. package/dist/astro/actions/index.d.ts +1 -0
  78. package/dist/astro/actions/index.js +1 -0
  79. package/dist/data-component-tags.d.ts +8 -0
  80. package/dist/data-component-tags.js +9 -0
  81. package/dist/enums/index.d.ts +2 -0
  82. package/dist/enums/index.js +2 -0
  83. package/dist/enums/social-platform-enums.d.ts +25 -0
  84. package/dist/enums/social-platform-enums.js +27 -0
  85. package/dist/enums/sort-enums.d.ts +17 -0
  86. package/dist/enums/sort-enums.js +18 -0
  87. package/dist/react/Category.d.ts +242 -0
  88. package/dist/react/Category.js +235 -0
  89. package/dist/react/CategoryList.d.ts +107 -0
  90. package/dist/react/CategoryList.js +91 -0
  91. package/dist/react/Choice.d.ts +211 -0
  92. package/dist/react/Choice.js +213 -0
  93. package/dist/react/Option.d.ts +242 -0
  94. package/dist/react/Option.js +346 -0
  95. package/dist/react/Product.d.ts +1065 -0
  96. package/dist/react/Product.js +1157 -0
  97. package/dist/react/ProductList.d.ts +400 -0
  98. package/dist/react/ProductList.js +368 -0
  99. package/dist/react/core/CategoryList.d.ts +194 -0
  100. package/dist/react/core/CategoryList.js +180 -0
  101. package/dist/react/core/Product.d.ts +225 -0
  102. package/dist/react/core/Product.js +190 -0
  103. package/dist/react/core/ProductList.d.ts +235 -0
  104. package/dist/react/core/ProductList.js +217 -0
  105. package/dist/react/core/ProductListFilters.d.ts +138 -0
  106. package/dist/react/core/ProductListFilters.js +242 -0
  107. package/dist/react/core/ProductListPagination.d.ts +49 -0
  108. package/dist/react/core/ProductListPagination.js +41 -0
  109. package/dist/react/core/ProductListSort.d.ts +19 -0
  110. package/dist/react/core/ProductListSort.js +52 -0
  111. package/dist/react/core/ProductModifiers.d.ts +416 -0
  112. package/dist/react/core/ProductModifiers.js +413 -0
  113. package/dist/react/core/ProductVariantSelector.d.ts +313 -0
  114. package/dist/react/core/ProductVariantSelector.js +291 -0
  115. package/dist/react/core/SelectedVariant.d.ts +230 -0
  116. package/dist/react/core/SelectedVariant.js +269 -0
  117. package/dist/react/index.d.ts +6 -0
  118. package/dist/react/index.js +6 -0
  119. package/dist/react/types.d.ts +8 -0
  120. package/dist/react/types.js +9 -0
  121. package/dist/server-actions/custom-checkout-action.d.ts +49 -0
  122. package/dist/server-actions/custom-checkout-action.js +64 -0
  123. package/dist/server-actions/index.d.ts +1 -0
  124. package/dist/server-actions/index.js +1 -0
  125. package/dist/services/buy-now-service.d.ts +346 -0
  126. package/dist/services/buy-now-service.js +197 -0
  127. package/dist/services/categories-list-service.d.ts +164 -0
  128. package/dist/services/categories-list-service.js +148 -0
  129. package/dist/services/index.d.ts +5 -0
  130. package/dist/services/index.js +5 -0
  131. package/dist/services/pay-now-service.d.ts +214 -0
  132. package/dist/services/pay-now-service.js +156 -0
  133. package/dist/services/product-modifiers-service.d.ts +34 -0
  134. package/dist/services/product-modifiers-service.js +107 -0
  135. package/dist/services/product-service.d.ts +177 -0
  136. package/dist/services/product-service.js +190 -0
  137. package/dist/services/products-list-search-service.d.ts +0 -0
  138. package/dist/services/products-list-search-service.js +1 -0
  139. package/dist/services/products-list-service.d.ts +429 -0
  140. package/dist/services/products-list-service.js +893 -0
  141. package/dist/services/selected-variant-service.d.ts +66 -0
  142. package/dist/services/selected-variant-service.js +527 -0
  143. package/dist/utils/index.d.ts +1 -0
  144. package/dist/utils/index.js +30 -0
  145. package/dist/utils/url-params.d.ts +73 -0
  146. package/dist/utils/url-params.js +114 -0
  147. package/package.json +89 -0
  148. package/react/package.json +4 -0
  149. package/server-actions/package.json +4 -0
  150. package/services/package.json +4 -0
@@ -0,0 +1,66 @@
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';
3
+ export interface SelectedVariantServiceAPI {
4
+ selectedQuantity: Signal<number>;
5
+ selectedChoices: Signal<Record<string, string>>;
6
+ selectedVariantId: ReadOnlySignal<string | null>;
7
+ currentVariant: ReadOnlySignal<productsV3.Variant | null>;
8
+ currentPrice: ReadOnlySignal<string>;
9
+ currentCompareAtPrice: ReadOnlySignal<string | null>;
10
+ isInStock: ReadOnlySignal<boolean>;
11
+ isPreOrderEnabled: ReadOnlySignal<boolean>;
12
+ preOrderMessage: ReadOnlySignal<string | null>;
13
+ isLoading: Signal<boolean>;
14
+ error: Signal<string | null>;
15
+ variants: Signal<productsV3.Variant[]>;
16
+ options: Signal<Record<string, string[]>>;
17
+ basePrice: Signal<number>;
18
+ discountPrice: Signal<number | null>;
19
+ isOnSale: Signal<boolean | null>;
20
+ quantityAvailable: Signal<number | null>;
21
+ trackQuantity: Signal<boolean>;
22
+ productId: Signal<string>;
23
+ ribbonLabel: Signal<string | null>;
24
+ product: ReadOnlySignal<productsV3.V3Product | null>;
25
+ productOptions: ReadOnlySignal<productsV3.ConnectedOption[]>;
26
+ currency: ReadOnlySignal<string>;
27
+ selectedVariant: () => productsV3.Variant | null;
28
+ finalPrice: () => number;
29
+ isLowStock: () => boolean;
30
+ setSelectedChoices: (choices: Record<string, string>) => void;
31
+ createLineItems: (quantity?: number, modifiers?: Record<string, any>) => Array<{
32
+ catalogReference: any;
33
+ quantity: number;
34
+ }>;
35
+ addToCart: (quantity?: number, modifiers?: Record<string, any>) => Promise<void>;
36
+ setOption: (group: string, value: string) => void;
37
+ setSelectedQuantity: (quantity: number) => void;
38
+ incrementQuantity: () => void;
39
+ decrementQuantity: () => void;
40
+ selectVariantById: (id: string) => void;
41
+ resetSelections: () => void;
42
+ getAvailableChoicesForOption: (optionName: string) => string[];
43
+ getChoiceInfo: (optionName: string, choiceValue: string) => {
44
+ isAvailable: boolean;
45
+ isInStock: boolean;
46
+ isPreOrderEnabled: boolean;
47
+ };
48
+ isChoiceAvailable: (optionName: string, choiceValue: string) => boolean;
49
+ isChoiceInStock: (optionName: string, choiceValue: string) => boolean;
50
+ isChoicePreOrderEnabled: (optionName: string, choiceValue: string) => boolean;
51
+ hasAnySelections: () => boolean;
52
+ IsAllVariantsAreOutOfStock: () => boolean;
53
+ }
54
+ export interface SelectedVariantServiceConfig {
55
+ fetchInventoryData?: boolean;
56
+ }
57
+ export declare const SelectedVariantServiceDefinition: string & {
58
+ __api: SelectedVariantServiceAPI;
59
+ __config: {};
60
+ isServiceDefinition?: boolean;
61
+ } & SelectedVariantServiceAPI;
62
+ export declare const SelectedVariantService: import("@wix/services-definitions").ServiceFactory<string & {
63
+ __api: SelectedVariantServiceAPI;
64
+ __config: {};
65
+ isServiceDefinition?: boolean;
66
+ } & SelectedVariantServiceAPI, SelectedVariantServiceConfig>;
@@ -0,0 +1,527 @@
1
+ import { defineService, implementService } from '@wix/services-definitions';
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';
5
+ import { CurrentCartServiceDefinition } from '@wix/headless-ecom/services';
6
+ import { MediaGalleryServiceDefinition } from '@wix/headless-media/services';
7
+ import { ProductServiceDefinition } from './product-service.js';
8
+ export const SelectedVariantServiceDefinition = defineService('selectedVariant');
9
+ export const SelectedVariantService = implementService.withConfig()(SelectedVariantServiceDefinition, ({ getService, config: { fetchInventoryData = true } }) => {
10
+ const mediaService = getService(MediaGalleryServiceDefinition);
11
+ const signalsService = getService(SignalsServiceDefinition);
12
+ const cartService = getService(CurrentCartServiceDefinition);
13
+ const productService = getService(ProductServiceDefinition);
14
+ const selectedChoices = signalsService.signal({});
15
+ const preOrderMessage = signalsService.signal(null);
16
+ const initialProduct = productService.product.get();
17
+ signalsService.effect(() => {
18
+ const product = productService.product.get();
19
+ const selectedChoicesValue = selectedChoices.get() || {};
20
+ let mediaToDisplay = [];
21
+ const productItemsImages = product?.media?.itemsInfo?.items
22
+ ?.map((item) => item)
23
+ .filter(Boolean) ?? [];
24
+ if (productItemsImages.length) {
25
+ mediaToDisplay = productItemsImages;
26
+ }
27
+ else if (product?.media?.main) {
28
+ mediaToDisplay = [product.media.main];
29
+ }
30
+ // Get images based on selected choices if available
31
+ let selectedChoicesImages = [];
32
+ Object.keys(selectedChoicesValue).forEach((choiceKey) => {
33
+ const productOption = product?.options
34
+ ?.find((option) => option.name === choiceKey)
35
+ ?.choicesSettings?.choices?.find((choice) => choice.name === selectedChoicesValue[choiceKey]);
36
+ if (productOption) {
37
+ selectedChoicesImages.push(...(productOption.linkedMedia ?? []));
38
+ }
39
+ });
40
+ if (selectedChoicesImages?.length) {
41
+ mediaToDisplay = selectedChoicesImages;
42
+ }
43
+ mediaService.setMediaToDisplay(mediaToDisplay ?? []);
44
+ });
45
+ const parsePrice = (amount) => {
46
+ if (!amount)
47
+ return 0;
48
+ const parsed = parseFloat(amount);
49
+ return isNaN(parsed) ? 0 : parsed;
50
+ };
51
+ const processVariantChoices = (variant) => {
52
+ const choices = {};
53
+ if (variant.choices) {
54
+ for (const choice of variant.choices) {
55
+ if (choice.optionChoiceNames?.optionName &&
56
+ choice.optionChoiceNames?.choiceName) {
57
+ choices[choice.optionChoiceNames.optionName] =
58
+ choice.optionChoiceNames.choiceName;
59
+ }
60
+ }
61
+ }
62
+ return choices;
63
+ };
64
+ const findVariantByChoices = (variants, selectedChoices) => {
65
+ return (variants.find((variant) => {
66
+ const variantChoices = processVariantChoices(variant);
67
+ const choiceKeys = Object.keys(selectedChoices);
68
+ return choiceKeys.every((key) => variantChoices[key] === selectedChoices[key]);
69
+ }) || null);
70
+ };
71
+ const updateInventoryItemData = async (variantId, inStock, preOrderEnabled) => {
72
+ if (!variantId) {
73
+ preOrderMessage.set(null);
74
+ return;
75
+ }
76
+ try {
77
+ // Use the correct Wix inventoryItemsV3.queryInventoryItems() API
78
+ const queryResult = await inventoryItemsV3
79
+ .queryInventoryItems()
80
+ .eq('variantId', variantId)
81
+ .find();
82
+ const inventoryItem = queryResult.items?.[0];
83
+ const isTrackingQuantity = inventoryItem?.trackQuantity ?? false;
84
+ trackQuantity.set(isTrackingQuantity);
85
+ preOrderMessage.set(inventoryItem?.preorderInfo?.message || null);
86
+ if (!inventoryItem || !isTrackingQuantity) {
87
+ quantityAvailable.set(null);
88
+ return;
89
+ }
90
+ if (inStock && inventoryItem?.quantity) {
91
+ quantityAvailable.set(inventoryItem.quantity);
92
+ }
93
+ else if (preOrderEnabled && inventoryItem.preorderInfo?.limit) {
94
+ quantityAvailable.set(inventoryItem.preorderInfo.limit);
95
+ }
96
+ else {
97
+ quantityAvailable.set(null);
98
+ }
99
+ }
100
+ catch (error) {
101
+ console.error('Failed to fetch inventory quantity:', error);
102
+ // Fallback on error
103
+ quantityAvailable.set(null);
104
+ trackQuantity.set(false);
105
+ }
106
+ };
107
+ const updateDataFromVariant = (variant) => {
108
+ if (variant) {
109
+ const inStock = variant.inventoryStatus?.inStock ?? true;
110
+ const preOrderEnabled = variant.inventoryStatus?.preorderEnabled ?? false;
111
+ // update the quantity available, tracking indication and pre-order message from the inventory API
112
+ // only if fetchInventoryData is enabled (defaults to true for backward compatibility)
113
+ if (fetchInventoryData) {
114
+ updateInventoryItemData(variant._id, inStock, preOrderEnabled);
115
+ }
116
+ else {
117
+ // Set default values when inventory fetching is disabled
118
+ quantityAvailable.set(null);
119
+ trackQuantity.set(false);
120
+ preOrderMessage.set(null);
121
+ }
122
+ }
123
+ else {
124
+ quantityAvailable.set(0);
125
+ trackQuantity.set(false);
126
+ preOrderMessage.set(null);
127
+ }
128
+ };
129
+ const isLoading = signalsService.signal(false);
130
+ const error = signalsService.signal(null);
131
+ const variants = signalsService.signal([]);
132
+ const options = signalsService.signal({});
133
+ const basePrice = signalsService.signal(0);
134
+ const discountPrice = signalsService.signal(null);
135
+ const isOnSale = signalsService.signal(null);
136
+ const quantityAvailable = signalsService.signal(null);
137
+ const trackQuantity = signalsService.signal(false);
138
+ const selectedQuantity = signalsService.signal(1);
139
+ const productId = signalsService.signal('');
140
+ const ribbonLabel = signalsService.signal(null);
141
+ const v3Product = signalsService.signal(initialProduct);
142
+ const init = (currentProduct) => {
143
+ if (currentProduct) {
144
+ v3Product.set(currentProduct);
145
+ productId.set(currentProduct._id || '');
146
+ ribbonLabel.set(currentProduct.ribbon?.name || null);
147
+ const actualPrice = currentProduct.actualPriceRange?.minValue?.amount;
148
+ const compareAtPrice = currentProduct.compareAtPriceRange?.minValue?.amount;
149
+ basePrice.set(parsePrice(actualPrice));
150
+ discountPrice.set(compareAtPrice ? parsePrice(compareAtPrice) : null);
151
+ isOnSale.set(!!compareAtPrice &&
152
+ parsePrice(compareAtPrice) > parsePrice(actualPrice));
153
+ if (currentProduct.options) {
154
+ const optionsMap = {};
155
+ currentProduct.options.forEach((option) => {
156
+ if (option.name && option.choicesSettings?.choices) {
157
+ optionsMap[option.name] = option.choicesSettings.choices.map((choice) => choice.name || '');
158
+ }
159
+ });
160
+ options.set(optionsMap);
161
+ }
162
+ if (currentProduct.variantSummary.variantCount > 0) {
163
+ variants.set(currentProduct.variantsInfo?.variants || []);
164
+ if (currentProduct.variantsInfo?.variants?.length) {
165
+ updateDataFromVariant(currentProduct.variantsInfo?.variants[0] || null);
166
+ }
167
+ }
168
+ else {
169
+ const singleVariant = {
170
+ _id: 'default',
171
+ visible: true,
172
+ choices: [],
173
+ price: {
174
+ actualPrice: currentProduct.actualPriceRange?.minValue,
175
+ compareAtPrice: currentProduct.compareAtPriceRange?.minValue,
176
+ },
177
+ inventoryStatus: {
178
+ inStock: currentProduct.inventory?.availabilityStatus ===
179
+ productsV3.InventoryAvailabilityStatus.IN_STOCK ||
180
+ currentProduct.inventory?.availabilityStatus ===
181
+ productsV3.InventoryAvailabilityStatus
182
+ .PARTIALLY_OUT_OF_STOCK,
183
+ preorderEnabled: currentProduct.inventory?.preorderStatus === 'ENABLED',
184
+ },
185
+ };
186
+ variants.set([singleVariant]);
187
+ updateDataFromVariant(singleVariant);
188
+ }
189
+ }
190
+ };
191
+ signalsService.effect(() => {
192
+ const currentProduct = productService.product.get();
193
+ init(currentProduct);
194
+ });
195
+ const currentVariant = signalsService.computed((() => {
196
+ const choices = selectedChoices.get();
197
+ const variantsList = variants.get();
198
+ if (!variantsList || variantsList.length === 0)
199
+ return null;
200
+ return (variantsList.find((variant) => {
201
+ const variantChoices = processVariantChoices(variant);
202
+ if (Object.keys(choices).length !==
203
+ Object.keys(variantChoices).length)
204
+ return false;
205
+ return Object.entries(choices).every(([optionName, optionValue]) => {
206
+ return variantChoices[optionName] === optionValue;
207
+ });
208
+ }) || null);
209
+ }));
210
+ const selectedVariantId = signalsService.computed(() => {
211
+ const variant = currentVariant.get();
212
+ return variant?._id || null;
213
+ });
214
+ const currentPrice = signalsService.computed(() => {
215
+ const variant = currentVariant.get();
216
+ const prod = v3Product.get();
217
+ // Try to get formatted amount first (if fields worked)
218
+ if (variant?.price?.actualPrice?.formattedAmount) {
219
+ return variant.price.actualPrice.formattedAmount;
220
+ }
221
+ if (prod?.actualPriceRange?.minValue?.formattedAmount) {
222
+ return prod.actualPriceRange.minValue.formattedAmount;
223
+ }
224
+ // Fallback: create our own formatted price from amount
225
+ let rawAmount = null;
226
+ if (variant?.price?.actualPrice?.amount) {
227
+ rawAmount = variant.price.actualPrice.amount;
228
+ }
229
+ else if (prod?.actualPriceRange?.minValue?.amount) {
230
+ rawAmount = prod.actualPriceRange.minValue.amount;
231
+ }
232
+ return rawAmount ? `$${rawAmount}` : '';
233
+ });
234
+ const currentCompareAtPrice = signalsService.computed(() => {
235
+ const variant = currentVariant.get();
236
+ const prod = v3Product.get();
237
+ // Try to get formatted compare-at price first
238
+ if (variant?.price?.compareAtPrice?.formattedAmount) {
239
+ return variant.price.compareAtPrice.formattedAmount;
240
+ }
241
+ if (prod?.compareAtPriceRange?.minValue?.formattedAmount) {
242
+ return prod.compareAtPriceRange.minValue.formattedAmount;
243
+ }
244
+ // Fallback: create our own formatted price from amount
245
+ let rawAmount = null;
246
+ if (variant?.price?.compareAtPrice?.amount) {
247
+ rawAmount = variant.price.compareAtPrice.amount;
248
+ }
249
+ else if (prod?.compareAtPriceRange?.minValue?.amount) {
250
+ rawAmount = prod.compareAtPriceRange.minValue.amount;
251
+ }
252
+ return rawAmount && rawAmount !== '0' ? `$${rawAmount}` : null;
253
+ });
254
+ const isInStock = signalsService.computed(() => {
255
+ const variant = currentVariant.get();
256
+ if (variant) {
257
+ return variant.inventoryStatus?.inStock ?? false;
258
+ }
259
+ else {
260
+ return false;
261
+ }
262
+ });
263
+ const isPreOrderEnabled = signalsService.computed(() => {
264
+ const variant = currentVariant.get();
265
+ return variant?.inventoryStatus?.preorderEnabled ?? false;
266
+ });
267
+ const product = v3Product;
268
+ const productOptions = signalsService.computed(() => {
269
+ const prod = v3Product.get();
270
+ return prod?.options || [];
271
+ });
272
+ const currency = signalsService.computed(() => {
273
+ const prod = v3Product.get();
274
+ return prod?.currency || 'USD';
275
+ });
276
+ const selectedVariant = () => {
277
+ const variantId = selectedVariantId.get();
278
+ const variantsList = variants.get();
279
+ return variantsList.find((v) => v._id === variantId) || null;
280
+ };
281
+ const finalPrice = () => {
282
+ const discount = discountPrice.get();
283
+ const base = basePrice.get();
284
+ return discount !== null ? discount : base;
285
+ };
286
+ const isLowStock = () => {
287
+ // Note: Currently always returns false as inventory quantity tracking
288
+ // is handled separately by the inventory service
289
+ return false;
290
+ };
291
+ const setSelectedChoices = (choices) => {
292
+ selectedChoices.set(choices);
293
+ selectedQuantity.set(1); // Reset quantity when choices change
294
+ const matchingVariant = findVariantByChoices(variants.get(), choices);
295
+ updateDataFromVariant(matchingVariant);
296
+ };
297
+ const createLineItems = (quantity = 1, modifiers) => {
298
+ const prod = v3Product.get();
299
+ const variant = currentVariant.get();
300
+ if (!prod?._id) {
301
+ throw new Error('Product not found');
302
+ }
303
+ // Build catalog reference with modifiers if provided
304
+ const catalogReference = {
305
+ catalogItemId: prod._id,
306
+ appId: '215238eb-22a5-4c36-9e7b-e7c08025e04e',
307
+ options: variant?._id && variant._id !== 'default'
308
+ ? {
309
+ variantId: variant._id,
310
+ preOrderRequested: !!variant?.inventoryStatus?.preorderEnabled,
311
+ }
312
+ : undefined,
313
+ };
314
+ // Transform and add modifiers to catalog reference if they exist
315
+ if (modifiers && Object.keys(modifiers).length > 0) {
316
+ const options = {};
317
+ const customTextFields = {};
318
+ // Get product modifiers to determine types and keys
319
+ const productModifiers = prod.modifiers || [];
320
+ Object.values(modifiers).forEach((modifierValue) => {
321
+ const modifierName = modifierValue.modifierName;
322
+ const productModifier = productModifiers.find((m) => m.name === modifierName);
323
+ if (!productModifier)
324
+ return;
325
+ const renderType = productModifier.modifierRenderType;
326
+ if (renderType === productsV3.ModifierRenderType.TEXT_CHOICES ||
327
+ renderType === productsV3.ModifierRenderType.SWATCH_CHOICES) {
328
+ // For choice modifiers, use the modifier key and choice value
329
+ const modifierKey = productModifier.key || modifierName;
330
+ if (modifierValue.choiceValue) {
331
+ options[modifierKey] = modifierValue.choiceValue;
332
+ }
333
+ }
334
+ else if (renderType === productsV3.ModifierRenderType.FREE_TEXT) {
335
+ // For free text modifiers, use the freeTextSettings key
336
+ const freeTextKey = productModifier.freeTextSettings?.key || modifierName;
337
+ if (modifierValue.freeTextValue) {
338
+ customTextFields[freeTextKey] = modifierValue.freeTextValue;
339
+ }
340
+ }
341
+ });
342
+ // Add formatted modifiers to catalog reference
343
+ if (Object.keys(options).length > 0) {
344
+ catalogReference.options = {
345
+ ...catalogReference.options,
346
+ options,
347
+ };
348
+ }
349
+ if (Object.keys(customTextFields).length > 0) {
350
+ catalogReference.options = {
351
+ ...catalogReference.options,
352
+ customTextFields,
353
+ };
354
+ }
355
+ }
356
+ return [
357
+ {
358
+ catalogReference,
359
+ quantity,
360
+ },
361
+ ];
362
+ };
363
+ const addToCart = async (quantity = 1, modifiers) => {
364
+ try {
365
+ isLoading.set(true);
366
+ error.set(null);
367
+ const lineItems = createLineItems(quantity, modifiers);
368
+ await cartService.addToCart(lineItems);
369
+ }
370
+ catch (err) {
371
+ error.set(err instanceof Error ? err.message : 'Failed to add to cart');
372
+ }
373
+ finally {
374
+ isLoading.set(false);
375
+ }
376
+ };
377
+ const setOption = (group, value) => {
378
+ const currentChoices = selectedChoices.get();
379
+ const newChoices = { ...currentChoices, [group]: value };
380
+ setSelectedChoices(newChoices);
381
+ };
382
+ const selectVariantById = (id) => {
383
+ const variantsList = variants.get();
384
+ const variant = variantsList.find((v) => v._id === id);
385
+ if (variant) {
386
+ const variantChoices = processVariantChoices(variant);
387
+ selectedChoices.set(variantChoices);
388
+ updateDataFromVariant(variant);
389
+ }
390
+ };
391
+ const resetSelections = () => {
392
+ selectedChoices.set({});
393
+ selectedQuantity.set(1); // Reset quantity when resetting selections
394
+ };
395
+ // Quantity management methods
396
+ const setSelectedQuantity = (quantity) => {
397
+ const maxQuantity = quantityAvailable.get();
398
+ const validQuantity = Math.max(1, Math.min(quantity, maxQuantity || 999));
399
+ selectedQuantity.set(validQuantity);
400
+ };
401
+ const incrementQuantity = () => {
402
+ const current = selectedQuantity.get();
403
+ const maxQuantity = quantityAvailable.get();
404
+ const newQuantity = Math.min(current + 1, maxQuantity || 999);
405
+ selectedQuantity.set(newQuantity);
406
+ };
407
+ const decrementQuantity = () => {
408
+ const current = selectedQuantity.get();
409
+ const newQuantity = Math.max(1, current - 1);
410
+ selectedQuantity.set(newQuantity);
411
+ };
412
+ // New methods for smart variant selection
413
+ const getAvailableChoicesForOption = (optionName) => {
414
+ const currentChoices = selectedChoices.get();
415
+ const variantsList = variants.get();
416
+ // Get all possible choices for this option that result in valid variants
417
+ const availableChoices = new Set();
418
+ variantsList.forEach((variant) => {
419
+ const variantChoices = processVariantChoices(variant);
420
+ // Check if this variant matches all currently selected choices (except for the option we're checking)
421
+ const matchesOtherChoices = Object.entries(currentChoices)
422
+ .filter(([key]) => key !== optionName)
423
+ .every(([key, value]) => variantChoices[key] === value);
424
+ if (matchesOtherChoices && variantChoices[optionName]) {
425
+ availableChoices.add(variantChoices[optionName]);
426
+ }
427
+ });
428
+ return Array.from(availableChoices);
429
+ };
430
+ // Core method that provides both availability and stock info efficiently
431
+ const getChoiceInfo = (optionName, choiceValue) => {
432
+ // Create hypothetical choices with this choice selected
433
+ const currentChoices = selectedChoices.get();
434
+ const hypotheticalChoices = {
435
+ ...currentChoices,
436
+ [optionName]: choiceValue,
437
+ };
438
+ // Get all variants and find one that matches these choices
439
+ const variantsList = variants.get();
440
+ const matchingVariants = variantsList.filter((variant) => {
441
+ if (!variant.choices)
442
+ return false;
443
+ const variantChoices = {};
444
+ for (const choice of variant.choices) {
445
+ if (choice.optionChoiceNames?.optionName &&
446
+ choice.optionChoiceNames?.choiceName) {
447
+ variantChoices[choice.optionChoiceNames.optionName] =
448
+ choice.optionChoiceNames.choiceName;
449
+ }
450
+ }
451
+ // Check if this variant matches our hypothetical choices
452
+ return Object.entries(hypotheticalChoices).every(([key, value]) => variantChoices[key] === value);
453
+ });
454
+ const isAvailable = matchingVariants.length > 0;
455
+ // Check if ANY of the matching variants are in stock
456
+ const isInStock = matchingVariants.some((variant) => variant.inventoryStatus?.inStock === true);
457
+ // Check if ANY of the matching variants have pre-order enabled
458
+ const isPreOrderEnabled = matchingVariants.some((variant) => variant.inventoryStatus?.preorderEnabled === true);
459
+ return { isAvailable, isInStock, isPreOrderEnabled };
460
+ };
461
+ // Simplified methods using the core getChoiceInfo
462
+ const isChoiceAvailable = (optionName, choiceValue) => {
463
+ return getChoiceInfo(optionName, choiceValue).isAvailable;
464
+ };
465
+ const isChoiceInStock = (optionName, choiceValue) => {
466
+ return getChoiceInfo(optionName, choiceValue).isInStock;
467
+ };
468
+ const isChoicePreOrderEnabled = (optionName, choiceValue) => {
469
+ return getChoiceInfo(optionName, choiceValue).isPreOrderEnabled;
470
+ };
471
+ const hasAnySelections = () => {
472
+ const currentChoices = selectedChoices.get();
473
+ return Object.keys(currentChoices).length > 0;
474
+ };
475
+ const IsAllVariantsAreOutOfStock = () => {
476
+ const variantsList = variants.get();
477
+ // All variants must be out of stock AND none should have preorder enabled
478
+ return (variantsList?.every((variant) => !variant.inventoryStatus?.inStock &&
479
+ !variant.inventoryStatus?.preorderEnabled) ?? true);
480
+ };
481
+ return {
482
+ selectedChoices,
483
+ selectedVariantId,
484
+ currentVariant,
485
+ currentPrice,
486
+ currentCompareAtPrice,
487
+ isInStock,
488
+ isPreOrderEnabled,
489
+ preOrderMessage,
490
+ isLoading,
491
+ error,
492
+ variants,
493
+ options,
494
+ basePrice,
495
+ discountPrice,
496
+ isOnSale,
497
+ quantityAvailable,
498
+ trackQuantity,
499
+ selectedQuantity,
500
+ productId,
501
+ ribbonLabel,
502
+ setSelectedChoices,
503
+ createLineItems,
504
+ addToCart,
505
+ setOption,
506
+ selectVariantById,
507
+ resetSelections,
508
+ // New methods for smart variant selection
509
+ getAvailableChoicesForOption,
510
+ getChoiceInfo,
511
+ isChoiceAvailable,
512
+ isChoiceInStock,
513
+ isChoicePreOrderEnabled,
514
+ hasAnySelections,
515
+ // Quantity management methods
516
+ setSelectedQuantity,
517
+ incrementQuantity,
518
+ decrementQuantity,
519
+ selectedVariant,
520
+ finalPrice,
521
+ isLowStock,
522
+ product,
523
+ productOptions,
524
+ currency,
525
+ IsAllVariantsAreOutOfStock,
526
+ };
527
+ });
@@ -0,0 +1 @@
1
+ export declare function getCheckoutUrlForProduct(productId: string, variantId?: string): Promise<string>;
@@ -0,0 +1,30 @@
1
+ import { checkout } from '@wix/ecom';
2
+ import { redirects } from '@wix/redirects';
3
+ const CATLOG_APP_ID_V3 = '215238eb-22a5-4c36-9e7b-e7c08025e04e';
4
+ export async function getCheckoutUrlForProduct(productId, variantId) {
5
+ const checkoutResult = await checkout.createCheckout({
6
+ lineItems: [
7
+ {
8
+ catalogReference: {
9
+ catalogItemId: productId,
10
+ appId: CATLOG_APP_ID_V3,
11
+ options: {
12
+ variantId,
13
+ },
14
+ },
15
+ quantity: 1,
16
+ },
17
+ ],
18
+ channelType: checkout.ChannelType.WEB,
19
+ });
20
+ if (!checkoutResult._id) {
21
+ throw new Error('Failed to create checkout');
22
+ }
23
+ const { redirectSession } = await redirects.createRedirectSession({
24
+ ecomCheckout: { checkoutId: checkoutResult._id },
25
+ callbacks: {
26
+ postFlowUrl: window.location.href,
27
+ },
28
+ });
29
+ return redirectSession?.fullUrl;
30
+ }