@wix/headless-stores 0.0.108 → 0.0.109

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.
@@ -18,7 +18,7 @@ function getInventoryStatusLabel(status) {
18
18
  return String(status);
19
19
  }
20
20
  }
21
- function buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice) {
21
+ function buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice, currency) {
22
22
  // Build consolidated filter options using search field names
23
23
  const filterOptions = [
24
24
  // Price range - use a logical key that maps to both min/max fields
@@ -28,7 +28,18 @@ function buildSearchFilterData(availableOptions, availableInventoryStatuses, ava
28
28
  type: 'range',
29
29
  displayType: 'range',
30
30
  validValues: [availableMinPrice, availableMaxPrice],
31
- valueFormatter: (value) => `$${value}`,
31
+ valueFormatter: (value) => {
32
+ try {
33
+ return new Intl.NumberFormat(undefined, {
34
+ style: 'currency',
35
+ currency,
36
+ }).format(Number(value));
37
+ }
38
+ catch {
39
+ // Fallback if currency code is invalid
40
+ return `${currency} ${Number(value).toFixed(2)}`;
41
+ }
42
+ },
32
43
  fieldName: [
33
44
  'actualPriceRange.minValue.amount',
34
45
  'actualPriceRange.maxValue.amount',
@@ -148,12 +159,13 @@ function AllFilters(props) {
148
159
  const availableInventoryStatuses = listService.availableInventoryStatuses.get();
149
160
  const availableMinPrice = listService.minPrice.get();
150
161
  const availableMaxPrice = listService.maxPrice.get();
162
+ const currency = listService.currency.get();
151
163
  // Get filter state
152
164
  const resetFilters = listService.resetFilter;
153
165
  const isFiltered = listService.isFiltered().get();
154
166
  // Build filter options and handlers
155
167
  const searchFilterData = useMemo(() => {
156
- const { filterOptions } = buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice);
168
+ const { filterOptions } = buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice, currency);
157
169
  const updateFilter = (newFilter) => {
158
170
  listService.setFilter(newFilter);
159
171
  };
@@ -169,6 +181,7 @@ function AllFilters(props) {
169
181
  availableInventoryStatuses,
170
182
  availableMinPrice,
171
183
  availableMaxPrice,
184
+ currency,
172
185
  currentFilter,
173
186
  resetFilters,
174
187
  isFiltered,
@@ -229,6 +229,7 @@ export const ProductService = implementService.withConfig()(ProductServiceDefini
229
229
  const loadProductBySlug = async (slug) => {
230
230
  const productResponse = await productsV3.getProductBySlug(slug, {
231
231
  fields: [
232
+ 'CURRENCY',
232
233
  'DESCRIPTION',
233
234
  'DIRECT_CATEGORIES_INFO',
234
235
  'BREADCRUMBS_INFO',
@@ -189,6 +189,8 @@ export declare const ProductsListServiceDefinition: string & {
189
189
  minPrice: Signal<number>;
190
190
  /** Reactive signal containing the maximum price of the products */
191
191
  maxPrice: Signal<number>;
192
+ /** Reactive signal containing the currency code from products */
193
+ currency: ReadOnlySignal<string>;
192
194
  /** Reactive signal containing the available inventory statuses */
193
195
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
194
196
  /** Reactive signal containing the available product options */
@@ -227,6 +229,8 @@ export declare const ProductsListServiceDefinition: string & {
227
229
  minPrice: Signal<number>;
228
230
  /** Reactive signal containing the maximum price of the products */
229
231
  maxPrice: Signal<number>;
232
+ /** Reactive signal containing the currency code from products */
233
+ currency: ReadOnlySignal<string>;
230
234
  /** Reactive signal containing the available inventory statuses */
231
235
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
232
236
  /** Reactive signal containing the available product options */
@@ -318,6 +322,8 @@ export declare const ProductListService: import("@wix/services-definitions").Ser
318
322
  minPrice: Signal<number>;
319
323
  /** Reactive signal containing the maximum price of the products */
320
324
  maxPrice: Signal<number>;
325
+ /** Reactive signal containing the currency code from products */
326
+ currency: ReadOnlySignal<string>;
321
327
  /** Reactive signal containing the available inventory statuses */
322
328
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
323
329
  /** Reactive signal containing the available product options */
@@ -356,6 +362,8 @@ export declare const ProductListService: import("@wix/services-definitions").Ser
356
362
  minPrice: Signal<number>;
357
363
  /** Reactive signal containing the maximum price of the products */
358
364
  maxPrice: Signal<number>;
365
+ /** Reactive signal containing the currency code from products */
366
+ currency: ReadOnlySignal<string>;
359
367
  /** Reactive signal containing the available inventory statuses */
360
368
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
361
369
  /** Reactive signal containing the available product options */
@@ -160,7 +160,9 @@ export async function loadProductsListServiceConfig(input) {
160
160
  * @returns Promise that resolves to the search result with complete variant data
161
161
  */
162
162
  const fetchProducts = async (searchOptions) => {
163
- const result = await productsV3.searchProducts(searchOptions);
163
+ const result = await productsV3.searchProducts(searchOptions, {
164
+ fields: [productsV3.RequestedFields.CURRENCY],
165
+ });
164
166
  // Fetch missing variants for all products in one batch request
165
167
  if (result.products) {
166
168
  result.products = await fetchMissingVariants(result.products);
@@ -192,7 +194,9 @@ const fetchMissingVariants = async (products) => {
192
194
  }
193
195
  const items = [];
194
196
  const res = await readOnlyVariantsV3
195
- .queryVariants()
197
+ .queryVariants({
198
+ fields: [readOnlyVariantsV3.RequestedFields.CURRENCY],
199
+ })
196
200
  .in('productData.productId', productIds)
197
201
  .limit(DEFAULT_QUERY_LIMIT)
198
202
  .find();
@@ -311,6 +315,11 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
311
315
  InventoryStatusType.PARTIALLY_OUT_OF_STOCK,
312
316
  ]);
313
317
  const aggregationsSignal = signalsService.signal(config.aggregations);
318
+ // Extract currency from the first product, fallback to USD
319
+ const currencySignal = signalsService.computed(() => {
320
+ const products = productsSignal.get();
321
+ return products[0]?.currency || 'USD';
322
+ });
314
323
  const isLoadingSignal = signalsService.signal(false);
315
324
  const errorSignal = signalsService.signal(null);
316
325
  if (typeof window !== 'undefined') {
@@ -367,6 +376,7 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
367
376
  /* Metadata for products list */
368
377
  minPrice: minPriceSignal,
369
378
  maxPrice: maxPriceSignal,
379
+ currency: currencySignal,
370
380
  availableInventoryStatuses: availableInventoryStatusesSignal,
371
381
  availableProductOptions: availableProductOptionsSignal,
372
382
  /* End of Metadata for products list */
@@ -213,42 +213,29 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
213
213
  const currentPrice = signalsService.computed(() => {
214
214
  const variant = currentVariant.get();
215
215
  const prod = v3Product.get();
216
- // Try to get formatted amount first (if fields worked)
217
- if (variant?.price?.actualPrice?.formattedAmount) {
218
- return variant.price.actualPrice.formattedAmount;
219
- }
220
- if (prod?.actualPriceRange?.minValue?.formattedAmount) {
221
- return prod.actualPriceRange.minValue.formattedAmount;
222
- }
223
- // Fallback: create our own formatted price from amount
224
- let rawAmount = null;
225
- if (variant?.price?.actualPrice?.amount) {
226
- rawAmount = variant.price.actualPrice.amount;
227
- }
228
- else if (prod?.actualPriceRange?.minValue?.amount) {
229
- rawAmount = prod.actualPriceRange.minValue.amount;
230
- }
231
- return rawAmount ? `$${rawAmount}` : '';
216
+ // Use formatted amount from variant or product
217
+ // Note: formattedAmount is returned when CURRENCY field is requested from the API
218
+ return (variant?.price?.actualPrice?.formattedAmount ||
219
+ prod?.actualPriceRange?.minValue?.formattedAmount ||
220
+ variant?.price?.actualPrice?.amount ||
221
+ prod?.actualPriceRange?.minValue?.amount ||
222
+ '');
232
223
  });
233
224
  const currentCompareAtPrice = signalsService.computed(() => {
234
225
  const variant = currentVariant.get();
235
226
  const prod = v3Product.get();
236
- // Try to get formatted compare-at price first
237
- if (variant?.price?.compareAtPrice?.formattedAmount) {
238
- return variant.price.compareAtPrice.formattedAmount;
239
- }
240
- if (prod?.compareAtPriceRange?.minValue?.formattedAmount) {
241
- return prod.compareAtPriceRange.minValue.formattedAmount;
242
- }
243
- // Fallback: create our own formatted price from amount
244
- let rawAmount = null;
245
- if (variant?.price?.compareAtPrice?.amount) {
246
- rawAmount = variant.price.compareAtPrice.amount;
227
+ // Check variant compare-at price - validate amount first, then use formatted if available
228
+ // Note: formattedAmount is returned when CURRENCY field is requested from the API
229
+ const variantCompareAt = variant?.price?.compareAtPrice;
230
+ if (variantCompareAt?.amount && Number(variantCompareAt.amount) > 0) {
231
+ return variantCompareAt.formattedAmount || variantCompareAt.amount;
247
232
  }
248
- else if (prod?.compareAtPriceRange?.minValue?.amount) {
249
- rawAmount = prod.compareAtPriceRange.minValue.amount;
233
+ // Check product compare-at price range - validate amount first, then use formatted if available
234
+ const productCompareAt = prod?.compareAtPriceRange?.minValue;
235
+ if (productCompareAt?.amount && Number(productCompareAt.amount) > 0) {
236
+ return productCompareAt.formattedAmount || productCompareAt.amount;
250
237
  }
251
- return rawAmount && rawAmount !== '0' ? `$${rawAmount}` : null;
238
+ return null;
252
239
  });
253
240
  const isInStock = signalsService.computed(() => {
254
241
  const variant = currentVariant.get();
@@ -18,7 +18,7 @@ function getInventoryStatusLabel(status) {
18
18
  return String(status);
19
19
  }
20
20
  }
21
- function buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice) {
21
+ function buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice, currency) {
22
22
  // Build consolidated filter options using search field names
23
23
  const filterOptions = [
24
24
  // Price range - use a logical key that maps to both min/max fields
@@ -28,7 +28,18 @@ function buildSearchFilterData(availableOptions, availableInventoryStatuses, ava
28
28
  type: 'range',
29
29
  displayType: 'range',
30
30
  validValues: [availableMinPrice, availableMaxPrice],
31
- valueFormatter: (value) => `$${value}`,
31
+ valueFormatter: (value) => {
32
+ try {
33
+ return new Intl.NumberFormat(undefined, {
34
+ style: 'currency',
35
+ currency,
36
+ }).format(Number(value));
37
+ }
38
+ catch {
39
+ // Fallback if currency code is invalid
40
+ return `${currency} ${Number(value).toFixed(2)}`;
41
+ }
42
+ },
32
43
  fieldName: [
33
44
  'actualPriceRange.minValue.amount',
34
45
  'actualPriceRange.maxValue.amount',
@@ -148,12 +159,13 @@ function AllFilters(props) {
148
159
  const availableInventoryStatuses = listService.availableInventoryStatuses.get();
149
160
  const availableMinPrice = listService.minPrice.get();
150
161
  const availableMaxPrice = listService.maxPrice.get();
162
+ const currency = listService.currency.get();
151
163
  // Get filter state
152
164
  const resetFilters = listService.resetFilter;
153
165
  const isFiltered = listService.isFiltered().get();
154
166
  // Build filter options and handlers
155
167
  const searchFilterData = useMemo(() => {
156
- const { filterOptions } = buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice);
168
+ const { filterOptions } = buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice, currency);
157
169
  const updateFilter = (newFilter) => {
158
170
  listService.setFilter(newFilter);
159
171
  };
@@ -169,6 +181,7 @@ function AllFilters(props) {
169
181
  availableInventoryStatuses,
170
182
  availableMinPrice,
171
183
  availableMaxPrice,
184
+ currency,
172
185
  currentFilter,
173
186
  resetFilters,
174
187
  isFiltered,
@@ -229,6 +229,7 @@ export const ProductService = implementService.withConfig()(ProductServiceDefini
229
229
  const loadProductBySlug = async (slug) => {
230
230
  const productResponse = await productsV3.getProductBySlug(slug, {
231
231
  fields: [
232
+ 'CURRENCY',
232
233
  'DESCRIPTION',
233
234
  'DIRECT_CATEGORIES_INFO',
234
235
  'BREADCRUMBS_INFO',
@@ -189,6 +189,8 @@ export declare const ProductsListServiceDefinition: string & {
189
189
  minPrice: Signal<number>;
190
190
  /** Reactive signal containing the maximum price of the products */
191
191
  maxPrice: Signal<number>;
192
+ /** Reactive signal containing the currency code from products */
193
+ currency: ReadOnlySignal<string>;
192
194
  /** Reactive signal containing the available inventory statuses */
193
195
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
194
196
  /** Reactive signal containing the available product options */
@@ -227,6 +229,8 @@ export declare const ProductsListServiceDefinition: string & {
227
229
  minPrice: Signal<number>;
228
230
  /** Reactive signal containing the maximum price of the products */
229
231
  maxPrice: Signal<number>;
232
+ /** Reactive signal containing the currency code from products */
233
+ currency: ReadOnlySignal<string>;
230
234
  /** Reactive signal containing the available inventory statuses */
231
235
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
232
236
  /** Reactive signal containing the available product options */
@@ -318,6 +322,8 @@ export declare const ProductListService: import("@wix/services-definitions").Ser
318
322
  minPrice: Signal<number>;
319
323
  /** Reactive signal containing the maximum price of the products */
320
324
  maxPrice: Signal<number>;
325
+ /** Reactive signal containing the currency code from products */
326
+ currency: ReadOnlySignal<string>;
321
327
  /** Reactive signal containing the available inventory statuses */
322
328
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
323
329
  /** Reactive signal containing the available product options */
@@ -356,6 +362,8 @@ export declare const ProductListService: import("@wix/services-definitions").Ser
356
362
  minPrice: Signal<number>;
357
363
  /** Reactive signal containing the maximum price of the products */
358
364
  maxPrice: Signal<number>;
365
+ /** Reactive signal containing the currency code from products */
366
+ currency: ReadOnlySignal<string>;
359
367
  /** Reactive signal containing the available inventory statuses */
360
368
  availableInventoryStatuses: Signal<InventoryStatusType[]>;
361
369
  /** Reactive signal containing the available product options */
@@ -160,7 +160,9 @@ export async function loadProductsListServiceConfig(input) {
160
160
  * @returns Promise that resolves to the search result with complete variant data
161
161
  */
162
162
  const fetchProducts = async (searchOptions) => {
163
- const result = await productsV3.searchProducts(searchOptions);
163
+ const result = await productsV3.searchProducts(searchOptions, {
164
+ fields: [productsV3.RequestedFields.CURRENCY],
165
+ });
164
166
  // Fetch missing variants for all products in one batch request
165
167
  if (result.products) {
166
168
  result.products = await fetchMissingVariants(result.products);
@@ -192,7 +194,9 @@ const fetchMissingVariants = async (products) => {
192
194
  }
193
195
  const items = [];
194
196
  const res = await readOnlyVariantsV3
195
- .queryVariants()
197
+ .queryVariants({
198
+ fields: [readOnlyVariantsV3.RequestedFields.CURRENCY],
199
+ })
196
200
  .in('productData.productId', productIds)
197
201
  .limit(DEFAULT_QUERY_LIMIT)
198
202
  .find();
@@ -311,6 +315,11 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
311
315
  InventoryStatusType.PARTIALLY_OUT_OF_STOCK,
312
316
  ]);
313
317
  const aggregationsSignal = signalsService.signal(config.aggregations);
318
+ // Extract currency from the first product, fallback to USD
319
+ const currencySignal = signalsService.computed(() => {
320
+ const products = productsSignal.get();
321
+ return products[0]?.currency || 'USD';
322
+ });
314
323
  const isLoadingSignal = signalsService.signal(false);
315
324
  const errorSignal = signalsService.signal(null);
316
325
  if (typeof window !== 'undefined') {
@@ -367,6 +376,7 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
367
376
  /* Metadata for products list */
368
377
  minPrice: minPriceSignal,
369
378
  maxPrice: maxPriceSignal,
379
+ currency: currencySignal,
370
380
  availableInventoryStatuses: availableInventoryStatusesSignal,
371
381
  availableProductOptions: availableProductOptionsSignal,
372
382
  /* End of Metadata for products list */
@@ -213,42 +213,29 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
213
213
  const currentPrice = signalsService.computed(() => {
214
214
  const variant = currentVariant.get();
215
215
  const prod = v3Product.get();
216
- // Try to get formatted amount first (if fields worked)
217
- if (variant?.price?.actualPrice?.formattedAmount) {
218
- return variant.price.actualPrice.formattedAmount;
219
- }
220
- if (prod?.actualPriceRange?.minValue?.formattedAmount) {
221
- return prod.actualPriceRange.minValue.formattedAmount;
222
- }
223
- // Fallback: create our own formatted price from amount
224
- let rawAmount = null;
225
- if (variant?.price?.actualPrice?.amount) {
226
- rawAmount = variant.price.actualPrice.amount;
227
- }
228
- else if (prod?.actualPriceRange?.minValue?.amount) {
229
- rawAmount = prod.actualPriceRange.minValue.amount;
230
- }
231
- return rawAmount ? `$${rawAmount}` : '';
216
+ // Use formatted amount from variant or product
217
+ // Note: formattedAmount is returned when CURRENCY field is requested from the API
218
+ return (variant?.price?.actualPrice?.formattedAmount ||
219
+ prod?.actualPriceRange?.minValue?.formattedAmount ||
220
+ variant?.price?.actualPrice?.amount ||
221
+ prod?.actualPriceRange?.minValue?.amount ||
222
+ '');
232
223
  });
233
224
  const currentCompareAtPrice = signalsService.computed(() => {
234
225
  const variant = currentVariant.get();
235
226
  const prod = v3Product.get();
236
- // Try to get formatted compare-at price first
237
- if (variant?.price?.compareAtPrice?.formattedAmount) {
238
- return variant.price.compareAtPrice.formattedAmount;
239
- }
240
- if (prod?.compareAtPriceRange?.minValue?.formattedAmount) {
241
- return prod.compareAtPriceRange.minValue.formattedAmount;
242
- }
243
- // Fallback: create our own formatted price from amount
244
- let rawAmount = null;
245
- if (variant?.price?.compareAtPrice?.amount) {
246
- rawAmount = variant.price.compareAtPrice.amount;
227
+ // Check variant compare-at price - validate amount first, then use formatted if available
228
+ // Note: formattedAmount is returned when CURRENCY field is requested from the API
229
+ const variantCompareAt = variant?.price?.compareAtPrice;
230
+ if (variantCompareAt?.amount && Number(variantCompareAt.amount) > 0) {
231
+ return variantCompareAt.formattedAmount || variantCompareAt.amount;
247
232
  }
248
- else if (prod?.compareAtPriceRange?.minValue?.amount) {
249
- rawAmount = prod.compareAtPriceRange.minValue.amount;
233
+ // Check product compare-at price range - validate amount first, then use formatted if available
234
+ const productCompareAt = prod?.compareAtPriceRange?.minValue;
235
+ if (productCompareAt?.amount && Number(productCompareAt.amount) > 0) {
236
+ return productCompareAt.formattedAmount || productCompareAt.amount;
250
237
  }
251
- return rawAmount && rawAmount !== '0' ? `$${rawAmount}` : null;
238
+ return null;
252
239
  });
253
240
  const isInStock = signalsService.computed(() => {
254
241
  const variant = currentVariant.get();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-stores",
3
- "version": "0.0.108",
3
+ "version": "0.0.109",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -84,5 +84,5 @@
84
84
  "groupId": "com.wixpress.headless-components"
85
85
  }
86
86
  },
87
- "falconPackageHash": "7013cd7c1973422f48cae8374b85a9de720206371b13ba3f0a1cee21"
87
+ "falconPackageHash": "3d07adc0e9bdf4773a8183a89ef8dc5763762f312cb9ff8d53994733"
88
88
  }