@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.
- package/astro/actions/package.json +4 -0
- package/cjs/dist/astro/actions/custom-checkout.d.ts +50 -0
- package/cjs/dist/astro/actions/custom-checkout.js +53 -0
- package/cjs/dist/astro/actions/index.d.ts +1 -0
- package/cjs/dist/astro/actions/index.js +1 -0
- package/cjs/dist/data-component-tags.d.ts +8 -0
- package/cjs/dist/data-component-tags.js +9 -0
- package/cjs/dist/enums/index.d.ts +2 -0
- package/cjs/dist/enums/index.js +2 -0
- package/cjs/dist/enums/social-platform-enums.d.ts +25 -0
- package/cjs/dist/enums/social-platform-enums.js +27 -0
- package/cjs/dist/enums/sort-enums.d.ts +17 -0
- package/cjs/dist/enums/sort-enums.js +18 -0
- package/cjs/dist/react/Category.d.ts +242 -0
- package/cjs/dist/react/Category.js +235 -0
- package/cjs/dist/react/CategoryList.d.ts +107 -0
- package/cjs/dist/react/CategoryList.js +91 -0
- package/cjs/dist/react/Choice.d.ts +211 -0
- package/cjs/dist/react/Choice.js +213 -0
- package/cjs/dist/react/Option.d.ts +242 -0
- package/cjs/dist/react/Option.js +346 -0
- package/cjs/dist/react/Product.d.ts +1065 -0
- package/cjs/dist/react/Product.js +1157 -0
- package/cjs/dist/react/ProductList.d.ts +400 -0
- package/cjs/dist/react/ProductList.js +368 -0
- package/cjs/dist/react/core/CategoryList.d.ts +194 -0
- package/cjs/dist/react/core/CategoryList.js +180 -0
- package/cjs/dist/react/core/Product.d.ts +225 -0
- package/cjs/dist/react/core/Product.js +190 -0
- package/cjs/dist/react/core/ProductList.d.ts +235 -0
- package/cjs/dist/react/core/ProductList.js +217 -0
- package/cjs/dist/react/core/ProductListFilters.d.ts +138 -0
- package/cjs/dist/react/core/ProductListFilters.js +242 -0
- package/cjs/dist/react/core/ProductListPagination.d.ts +49 -0
- package/cjs/dist/react/core/ProductListPagination.js +41 -0
- package/cjs/dist/react/core/ProductListSort.d.ts +19 -0
- package/cjs/dist/react/core/ProductListSort.js +52 -0
- package/cjs/dist/react/core/ProductModifiers.d.ts +416 -0
- package/cjs/dist/react/core/ProductModifiers.js +413 -0
- package/cjs/dist/react/core/ProductVariantSelector.d.ts +313 -0
- package/cjs/dist/react/core/ProductVariantSelector.js +291 -0
- package/cjs/dist/react/core/SelectedVariant.d.ts +230 -0
- package/cjs/dist/react/core/SelectedVariant.js +269 -0
- package/cjs/dist/react/index.d.ts +6 -0
- package/cjs/dist/react/index.js +6 -0
- package/cjs/dist/react/types.d.ts +8 -0
- package/cjs/dist/react/types.js +9 -0
- package/cjs/dist/server-actions/custom-checkout-action.d.ts +49 -0
- package/cjs/dist/server-actions/custom-checkout-action.js +64 -0
- package/cjs/dist/server-actions/index.d.ts +1 -0
- package/cjs/dist/server-actions/index.js +1 -0
- package/cjs/dist/services/buy-now-service.d.ts +346 -0
- package/cjs/dist/services/buy-now-service.js +197 -0
- package/cjs/dist/services/categories-list-service.d.ts +164 -0
- package/cjs/dist/services/categories-list-service.js +148 -0
- package/cjs/dist/services/index.d.ts +5 -0
- package/cjs/dist/services/index.js +5 -0
- package/cjs/dist/services/pay-now-service.d.ts +214 -0
- package/cjs/dist/services/pay-now-service.js +156 -0
- package/cjs/dist/services/product-modifiers-service.d.ts +34 -0
- package/cjs/dist/services/product-modifiers-service.js +107 -0
- package/cjs/dist/services/product-service.d.ts +177 -0
- package/cjs/dist/services/product-service.js +190 -0
- package/cjs/dist/services/products-list-search-service.d.ts +1 -0
- package/cjs/dist/services/products-list-search-service.js +1 -0
- package/cjs/dist/services/products-list-service.d.ts +429 -0
- package/cjs/dist/services/products-list-service.js +893 -0
- package/cjs/dist/services/selected-variant-service.d.ts +66 -0
- package/cjs/dist/services/selected-variant-service.js +527 -0
- package/cjs/dist/utils/index.d.ts +1 -0
- package/cjs/dist/utils/index.js +30 -0
- package/cjs/dist/utils/url-params.d.ts +73 -0
- package/cjs/dist/utils/url-params.js +114 -0
- package/cjs/package.json +3 -0
- package/dist/astro/actions/custom-checkout.d.ts +50 -0
- package/dist/astro/actions/custom-checkout.js +53 -0
- package/dist/astro/actions/index.d.ts +1 -0
- package/dist/astro/actions/index.js +1 -0
- package/dist/data-component-tags.d.ts +8 -0
- package/dist/data-component-tags.js +9 -0
- package/dist/enums/index.d.ts +2 -0
- package/dist/enums/index.js +2 -0
- package/dist/enums/social-platform-enums.d.ts +25 -0
- package/dist/enums/social-platform-enums.js +27 -0
- package/dist/enums/sort-enums.d.ts +17 -0
- package/dist/enums/sort-enums.js +18 -0
- package/dist/react/Category.d.ts +242 -0
- package/dist/react/Category.js +235 -0
- package/dist/react/CategoryList.d.ts +107 -0
- package/dist/react/CategoryList.js +91 -0
- package/dist/react/Choice.d.ts +211 -0
- package/dist/react/Choice.js +213 -0
- package/dist/react/Option.d.ts +242 -0
- package/dist/react/Option.js +346 -0
- package/dist/react/Product.d.ts +1065 -0
- package/dist/react/Product.js +1157 -0
- package/dist/react/ProductList.d.ts +400 -0
- package/dist/react/ProductList.js +368 -0
- package/dist/react/core/CategoryList.d.ts +194 -0
- package/dist/react/core/CategoryList.js +180 -0
- package/dist/react/core/Product.d.ts +225 -0
- package/dist/react/core/Product.js +190 -0
- package/dist/react/core/ProductList.d.ts +235 -0
- package/dist/react/core/ProductList.js +217 -0
- package/dist/react/core/ProductListFilters.d.ts +138 -0
- package/dist/react/core/ProductListFilters.js +242 -0
- package/dist/react/core/ProductListPagination.d.ts +49 -0
- package/dist/react/core/ProductListPagination.js +41 -0
- package/dist/react/core/ProductListSort.d.ts +19 -0
- package/dist/react/core/ProductListSort.js +52 -0
- package/dist/react/core/ProductModifiers.d.ts +416 -0
- package/dist/react/core/ProductModifiers.js +413 -0
- package/dist/react/core/ProductVariantSelector.d.ts +313 -0
- package/dist/react/core/ProductVariantSelector.js +291 -0
- package/dist/react/core/SelectedVariant.d.ts +230 -0
- package/dist/react/core/SelectedVariant.js +269 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +6 -0
- package/dist/react/types.d.ts +8 -0
- package/dist/react/types.js +9 -0
- package/dist/server-actions/custom-checkout-action.d.ts +49 -0
- package/dist/server-actions/custom-checkout-action.js +64 -0
- package/dist/server-actions/index.d.ts +1 -0
- package/dist/server-actions/index.js +1 -0
- package/dist/services/buy-now-service.d.ts +346 -0
- package/dist/services/buy-now-service.js +197 -0
- package/dist/services/categories-list-service.d.ts +164 -0
- package/dist/services/categories-list-service.js +148 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +5 -0
- package/dist/services/pay-now-service.d.ts +214 -0
- package/dist/services/pay-now-service.js +156 -0
- package/dist/services/product-modifiers-service.d.ts +34 -0
- package/dist/services/product-modifiers-service.js +107 -0
- package/dist/services/product-service.d.ts +177 -0
- package/dist/services/product-service.js +190 -0
- package/dist/services/products-list-search-service.d.ts +0 -0
- package/dist/services/products-list-search-service.js +1 -0
- package/dist/services/products-list-service.d.ts +429 -0
- package/dist/services/products-list-service.js +893 -0
- package/dist/services/selected-variant-service.d.ts +66 -0
- package/dist/services/selected-variant-service.js +527 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +30 -0
- package/dist/utils/url-params.d.ts +73 -0
- package/dist/utils/url-params.js +114 -0
- package/package.json +89 -0
- package/react/package.json +4 -0
- package/server-actions/package.json +4 -0
- 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
|
+
}
|