@wix/headless-stores 0.0.36 → 0.0.37

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 (129) hide show
  1. package/cjs/dist/react/Category.d.ts +65 -59
  2. package/cjs/dist/react/Category.js +50 -83
  3. package/cjs/dist/react/CategoryList.d.ts +184 -0
  4. package/cjs/dist/react/CategoryList.js +174 -0
  5. package/cjs/dist/react/Product.d.ts +3 -3
  6. package/cjs/dist/react/Product.js +6 -6
  7. package/cjs/dist/react/ProductActions.d.ts +1 -1
  8. package/cjs/dist/react/ProductActions.js +2 -2
  9. package/{dist/react/ProductsList.d.ts → cjs/dist/react/ProductList.d.ts} +71 -38
  10. package/cjs/dist/react/{ProductsList.js → ProductList.js} +30 -26
  11. package/cjs/dist/react/ProductListFilters.d.ts +244 -0
  12. package/cjs/dist/react/ProductListFilters.js +216 -0
  13. package/cjs/dist/react/ProductListPagination.d.ts +246 -0
  14. package/cjs/dist/react/ProductListPagination.js +207 -0
  15. package/cjs/dist/react/ProductListSort.d.ts +87 -0
  16. package/cjs/dist/react/ProductListSort.js +85 -0
  17. package/cjs/dist/react/ProductModifiers.d.ts +5 -5
  18. package/cjs/dist/react/ProductModifiers.js +10 -10
  19. package/cjs/dist/react/ProductVariantSelector.d.ts +5 -5
  20. package/cjs/dist/react/ProductVariantSelector.js +13 -10
  21. package/cjs/dist/react/SelectedVariant.d.ts +3 -3
  22. package/cjs/dist/react/SelectedVariant.js +6 -6
  23. package/cjs/dist/react/index.d.ts +7 -9
  24. package/cjs/dist/react/index.js +7 -9
  25. package/cjs/dist/services/buy-now-service.d.ts +208 -0
  26. package/cjs/dist/services/buy-now-service.js +132 -1
  27. package/cjs/dist/services/categories-list-service.d.ts +163 -0
  28. package/cjs/dist/services/categories-list-service.js +148 -0
  29. package/cjs/dist/services/category-service.d.ts +115 -70
  30. package/cjs/dist/services/category-service.js +101 -110
  31. package/cjs/dist/services/index.d.ts +6 -7
  32. package/cjs/dist/services/index.js +5 -16
  33. package/cjs/dist/services/pay-now-service.d.ts +146 -0
  34. package/cjs/dist/services/pay-now-service.js +112 -1
  35. package/cjs/dist/services/product-service.d.ts +71 -0
  36. package/cjs/dist/services/product-service.js +47 -0
  37. package/cjs/dist/services/products-list-filters-service.d.ts +292 -0
  38. package/cjs/dist/services/products-list-filters-service.js +446 -0
  39. package/cjs/dist/services/products-list-pagination-service.d.ts +186 -0
  40. package/cjs/dist/services/products-list-pagination-service.js +179 -0
  41. package/cjs/dist/services/products-list-service.d.ts +138 -52
  42. package/cjs/dist/services/products-list-service.js +98 -51
  43. package/cjs/dist/services/products-list-sort-service.d.ts +117 -0
  44. package/cjs/dist/services/products-list-sort-service.js +144 -0
  45. package/cjs/dist/utils/url-params.d.ts +68 -0
  46. package/cjs/dist/utils/url-params.js +72 -4
  47. package/dist/react/Category.d.ts +65 -59
  48. package/dist/react/Category.js +50 -83
  49. package/dist/react/CategoryList.d.ts +184 -0
  50. package/dist/react/CategoryList.js +174 -0
  51. package/dist/react/Product.d.ts +3 -3
  52. package/dist/react/Product.js +6 -6
  53. package/dist/react/ProductActions.d.ts +1 -1
  54. package/dist/react/ProductActions.js +2 -2
  55. package/{cjs/dist/react/ProductsList.d.ts → dist/react/ProductList.d.ts} +71 -38
  56. package/dist/react/{ProductsList.js → ProductList.js} +30 -26
  57. package/dist/react/ProductListFilters.d.ts +244 -0
  58. package/dist/react/ProductListFilters.js +216 -0
  59. package/dist/react/ProductListPagination.d.ts +246 -0
  60. package/dist/react/ProductListPagination.js +207 -0
  61. package/dist/react/ProductListSort.d.ts +87 -0
  62. package/dist/react/ProductListSort.js +85 -0
  63. package/dist/react/ProductModifiers.d.ts +5 -5
  64. package/dist/react/ProductModifiers.js +10 -10
  65. package/dist/react/ProductVariantSelector.d.ts +5 -5
  66. package/dist/react/ProductVariantSelector.js +13 -10
  67. package/dist/react/SelectedVariant.d.ts +3 -3
  68. package/dist/react/SelectedVariant.js +6 -6
  69. package/dist/react/index.d.ts +7 -9
  70. package/dist/react/index.js +7 -9
  71. package/dist/services/buy-now-service.d.ts +208 -0
  72. package/dist/services/buy-now-service.js +132 -1
  73. package/dist/services/categories-list-service.d.ts +163 -0
  74. package/dist/services/categories-list-service.js +148 -0
  75. package/dist/services/category-service.d.ts +115 -70
  76. package/dist/services/category-service.js +101 -110
  77. package/dist/services/index.d.ts +6 -7
  78. package/dist/services/index.js +5 -16
  79. package/dist/services/pay-now-service.d.ts +146 -0
  80. package/dist/services/pay-now-service.js +112 -1
  81. package/dist/services/product-service.d.ts +71 -0
  82. package/dist/services/product-service.js +47 -0
  83. package/dist/services/products-list-filters-service.d.ts +292 -0
  84. package/dist/services/products-list-filters-service.js +446 -0
  85. package/dist/services/products-list-pagination-service.d.ts +186 -0
  86. package/dist/services/products-list-pagination-service.js +179 -0
  87. package/dist/services/products-list-service.d.ts +138 -52
  88. package/dist/services/products-list-service.js +98 -51
  89. package/dist/services/products-list-sort-service.d.ts +117 -0
  90. package/dist/services/products-list-sort-service.js +144 -0
  91. package/dist/utils/url-params.d.ts +68 -0
  92. package/dist/utils/url-params.js +72 -4
  93. package/package.json +3 -3
  94. package/cjs/dist/react/Collection.d.ts +0 -294
  95. package/cjs/dist/react/Collection.js +0 -345
  96. package/cjs/dist/react/FilteredCollection.d.ts +0 -299
  97. package/cjs/dist/react/FilteredCollection.js +0 -352
  98. package/cjs/dist/react/RelatedProducts.d.ts +0 -169
  99. package/cjs/dist/react/RelatedProducts.js +0 -180
  100. package/cjs/dist/react/Sort.d.ts +0 -37
  101. package/cjs/dist/react/Sort.js +0 -36
  102. package/cjs/dist/services/catalog-service.d.ts +0 -36
  103. package/cjs/dist/services/catalog-service.js +0 -193
  104. package/cjs/dist/services/collection-service.d.ts +0 -124
  105. package/cjs/dist/services/collection-service.js +0 -628
  106. package/cjs/dist/services/filter-service.d.ts +0 -35
  107. package/cjs/dist/services/filter-service.js +0 -119
  108. package/cjs/dist/services/related-products-service.d.ts +0 -100
  109. package/cjs/dist/services/related-products-service.js +0 -127
  110. package/cjs/dist/services/sort-service.d.ts +0 -20
  111. package/cjs/dist/services/sort-service.js +0 -27
  112. package/dist/react/Collection.d.ts +0 -294
  113. package/dist/react/Collection.js +0 -345
  114. package/dist/react/FilteredCollection.d.ts +0 -299
  115. package/dist/react/FilteredCollection.js +0 -352
  116. package/dist/react/RelatedProducts.d.ts +0 -169
  117. package/dist/react/RelatedProducts.js +0 -180
  118. package/dist/react/Sort.d.ts +0 -37
  119. package/dist/react/Sort.js +0 -36
  120. package/dist/services/catalog-service.d.ts +0 -36
  121. package/dist/services/catalog-service.js +0 -193
  122. package/dist/services/collection-service.d.ts +0 -124
  123. package/dist/services/collection-service.js +0 -628
  124. package/dist/services/filter-service.d.ts +0 -35
  125. package/dist/services/filter-service.js +0 -119
  126. package/dist/services/related-products-service.d.ts +0 -100
  127. package/dist/services/related-products-service.js +0 -127
  128. package/dist/services/sort-service.d.ts +0 -20
  129. package/dist/services/sort-service.js +0 -27
@@ -1,628 +0,0 @@
1
- import { defineService, implementService } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition } from "@wix/services-definitions/core-services/signals";
3
- import { productsV3, readOnlyVariantsV3 } from "@wix/stores";
4
- import { FilterServiceDefinition } from "./filter-service.js";
5
- import { CategoryServiceDefinition } from "./category-service.js";
6
- import { SortServiceDefinition } from "./sort-service.js";
7
- import { CatalogServiceDefinition } from "./catalog-service.js";
8
- import { URLParamsUtils } from "../utils/url-params.js";
9
- import { SortType } from "../enums/sort-enums.js";
10
- const { SortDirection } = productsV3;
11
- const searchProducts = async (searchOptions) => {
12
- const searchParams = {
13
- filter: searchOptions.search?.filter,
14
- sort: searchOptions.search?.sort,
15
- ...(searchOptions.paging && { cursorPaging: searchOptions.paging }),
16
- };
17
- const options = {
18
- fields: searchOptions.fields || [],
19
- };
20
- const result = await productsV3.searchProducts(searchParams, options);
21
- // Fetch missing variants for all products in one batch request
22
- if (result.products) {
23
- result.products = await fetchMissingVariants(result.products);
24
- }
25
- return result;
26
- };
27
- // Helper to build search options with supported filters
28
- const buildSearchOptions = (filters, selectedCategory, sortBy) => {
29
- const searchOptions = {
30
- search: {},
31
- fields: [
32
- "DESCRIPTION",
33
- "DIRECT_CATEGORIES_INFO",
34
- "BREADCRUMBS_INFO",
35
- "INFO_SECTION",
36
- "MEDIA_ITEMS_INFO",
37
- "PLAIN_DESCRIPTION",
38
- "THUMBNAIL",
39
- "URL",
40
- "VARIANT_OPTION_CHOICE_NAMES",
41
- "WEIGHT_MEASUREMENT_UNIT_INFO",
42
- ],
43
- };
44
- // Build filter conditions array
45
- const filterConditions = [];
46
- // Add category filter if selected
47
- if (selectedCategory) {
48
- filterConditions.push({
49
- "allCategoriesInfo.categories": {
50
- $matchItems: [
51
- {
52
- id: {
53
- $in: [selectedCategory],
54
- },
55
- },
56
- ],
57
- },
58
- });
59
- }
60
- // Add price range filter if provided
61
- if (filters?.priceRange) {
62
- const { min, max } = filters.priceRange;
63
- if (min > 0) {
64
- filterConditions.push({
65
- "actualPriceRange.minValue.amount": { $gte: min.toString() },
66
- });
67
- }
68
- if (max > 0 && max < 999999) {
69
- filterConditions.push({
70
- "actualPriceRange.maxValue.amount": { $lte: max.toString() },
71
- });
72
- }
73
- }
74
- // Add product options filter if provided
75
- if (filters?.selectedOptions &&
76
- Object.keys(filters.selectedOptions).length > 0) {
77
- for (const [optionId, choiceIds] of Object.entries(filters.selectedOptions)) {
78
- if (choiceIds && choiceIds.length > 0) {
79
- // Handle inventory filter separately
80
- if (optionId === "inventory-filter") {
81
- filterConditions.push({
82
- "inventory.availabilityStatus": {
83
- $in: choiceIds,
84
- },
85
- });
86
- }
87
- else {
88
- // Regular product options filter
89
- filterConditions.push({
90
- "options.choicesSettings.choices.choiceId": {
91
- $hasSome: choiceIds,
92
- },
93
- });
94
- }
95
- }
96
- }
97
- }
98
- // Apply filters
99
- if (filterConditions.length > 0) {
100
- if (filterConditions.length === 1) {
101
- // Single condition - no need for $and wrapper
102
- searchOptions.search.filter = filterConditions[0];
103
- }
104
- else {
105
- // Multiple conditions - use $and
106
- searchOptions.search.filter = {
107
- $and: filterConditions,
108
- };
109
- }
110
- }
111
- // Add sort if provided
112
- if (sortBy) {
113
- switch (sortBy) {
114
- case SortType.NAME_ASC:
115
- searchOptions.search.sort = [
116
- { fieldName: "name", order: SortDirection.ASC },
117
- ];
118
- break;
119
- case SortType.NAME_DESC:
120
- searchOptions.search.sort = [
121
- { fieldName: "name", order: SortDirection.DESC },
122
- ];
123
- break;
124
- case SortType.PRICE_ASC:
125
- searchOptions.search.sort = [
126
- {
127
- fieldName: "actualPriceRange.minValue.amount",
128
- order: SortDirection.ASC,
129
- },
130
- ];
131
- break;
132
- case SortType.PRICE_DESC:
133
- searchOptions.search.sort = [
134
- {
135
- fieldName: "actualPriceRange.minValue.amount",
136
- order: SortDirection.DESC,
137
- },
138
- ];
139
- break;
140
- case SortType.RECOMMENDED:
141
- searchOptions.search.sort = [
142
- {
143
- fieldName: "allCategoriesInfo.categories.index",
144
- selectItemsBy: [
145
- {
146
- "allCategoriesInfo.categories.id": selectedCategory,
147
- },
148
- ],
149
- },
150
- ];
151
- break;
152
- }
153
- }
154
- return searchOptions;
155
- };
156
- export const CollectionServiceDefinition = defineService("collection");
157
- export const CollectionService = implementService.withConfig()(CollectionServiceDefinition, ({ getService, config }) => {
158
- const signalsService = getService(SignalsServiceDefinition);
159
- const collectionFilters = getService(FilterServiceDefinition);
160
- const categoryService = getService(CategoryServiceDefinition);
161
- const sortService = getService(SortServiceDefinition);
162
- const catalogService = getService(CatalogServiceDefinition);
163
- const hasMoreProducts = signalsService.signal((config.initialHasMore ?? true));
164
- let nextCursor = config.initialCursor;
165
- const initialProducts = config.initialProducts || [];
166
- // Signal declarations
167
- const productsList = signalsService.signal(initialProducts);
168
- const isLoading = signalsService.signal(false);
169
- const error = signalsService.signal(null);
170
- const totalProducts = signalsService.signal(initialProducts.length);
171
- const hasProducts = signalsService.signal((initialProducts.length > 0));
172
- const pageSize = config.pageSize || 12;
173
- let allProducts = initialProducts;
174
- // Debouncing mechanism to prevent multiple simultaneous refreshes
175
- let refreshTimeout = null;
176
- let isRefreshing = false;
177
- let isInitializingCatalogData = true;
178
- const loadMore = async () => {
179
- // Don't load more if there are no more products available
180
- if (!hasMoreProducts.get()) {
181
- return;
182
- }
183
- try {
184
- isLoading.set(true);
185
- error.set(null);
186
- // For loadMore, use no filters or sorting to work with cursor pagination
187
- const searchOptions = buildSearchOptions(undefined, undefined, undefined);
188
- // Add pagination
189
- searchOptions.paging = { limit: pageSize };
190
- if (nextCursor) {
191
- searchOptions.paging.cursor = nextCursor;
192
- }
193
- const currentProducts = productsList.get();
194
- const productResults = await searchProducts(searchOptions);
195
- // Update cursor for next pagination
196
- nextCursor =
197
- productResults.pagingMetadata?.cursors?.next || undefined;
198
- // Check if there are more products to load
199
- const hasMore = Boolean(nextCursor &&
200
- productResults.products &&
201
- productResults.products.length === pageSize);
202
- hasMoreProducts.set(hasMore);
203
- // Update allProducts with the new data
204
- allProducts = [...allProducts, ...(productResults.products || [])];
205
- // Add new products to the list
206
- const additionalProducts = productResults.products || [];
207
- productsList.set([...currentProducts, ...additionalProducts]);
208
- totalProducts.set(currentProducts.length + additionalProducts.length);
209
- hasProducts.set(currentProducts.length + additionalProducts.length > 0);
210
- }
211
- catch (err) {
212
- error.set(err instanceof Error ? err.message : "Failed to load more products");
213
- }
214
- finally {
215
- isLoading.set(false);
216
- }
217
- };
218
- const refresh = async (setTotalProducts = true) => {
219
- if (isRefreshing)
220
- return;
221
- try {
222
- isRefreshing = true;
223
- isLoading.set(true);
224
- error.set(null);
225
- const filters = collectionFilters.currentFilters.get();
226
- const selectedCategory = categoryService.selectedCategory.get();
227
- const sortBy = sortService.currentSort.get();
228
- // Use regular search for all sorting options including recommended
229
- const searchOptions = buildSearchOptions(filters, selectedCategory, sortBy);
230
- // Add pagination
231
- searchOptions.paging = { limit: pageSize };
232
- const productResults = await searchProducts(searchOptions);
233
- const isPriceSort = sortBy === SortType.PRICE_ASC || sortBy === SortType.PRICE_DESC;
234
- if (isPriceSort) {
235
- productResults.products = productResults.products?.sort((a, b) => {
236
- const aPrice = Number(a.actualPriceRange?.minValue?.amount) || 0;
237
- const bPrice = Number(b.actualPriceRange?.minValue?.amount) || 0;
238
- return sortBy === SortType.PRICE_ASC
239
- ? aPrice - bPrice
240
- : bPrice - aPrice;
241
- });
242
- }
243
- // Reset pagination state
244
- nextCursor =
245
- productResults.pagingMetadata?.cursors?.next || undefined;
246
- const hasMore = Boolean(productResults.pagingMetadata?.cursors?.next &&
247
- productResults.products &&
248
- productResults.products.length === pageSize);
249
- hasMoreProducts.set(hasMore);
250
- // Update allProducts with the new data
251
- allProducts = productResults.products || [];
252
- // All filtering is handled server-side
253
- productsList.set(allProducts);
254
- if (setTotalProducts) {
255
- totalProducts.set(allProducts.length);
256
- }
257
- hasProducts.set(allProducts.length > 0);
258
- }
259
- catch (err) {
260
- error.set(err instanceof Error ? err.message : "Failed to refresh products");
261
- }
262
- finally {
263
- isLoading.set(false);
264
- isRefreshing = false;
265
- }
266
- };
267
- // Debounced refresh function
268
- const debouncedRefresh = async (setTotalProducts = true) => {
269
- if (refreshTimeout) {
270
- clearTimeout(refreshTimeout);
271
- }
272
- return new Promise((resolve) => {
273
- refreshTimeout = setTimeout(async () => {
274
- await refresh(setTotalProducts);
275
- resolve();
276
- }, 50); // 50ms debounce delay
277
- });
278
- };
279
- // Refresh with server-side filtering when any filters change
280
- signalsService.effect(() => {
281
- collectionFilters.currentFilters.get();
282
- // Skip refresh during catalog data initialization to prevent double API calls
283
- if (isInitializingCatalogData) {
284
- return;
285
- }
286
- // All filtering (categories, price, options) is now handled server-side
287
- debouncedRefresh(false);
288
- });
289
- // Initialize catalog data when the service starts
290
- const initializeCatalogData = async () => {
291
- isInitializingCatalogData = true; // Set flag BEFORE loading
292
- const selectedCategory = categoryService.selectedCategory.get();
293
- // Load catalog data from the combined catalog service
294
- await catalogService.loadCatalogData(selectedCategory || undefined);
295
- // Reset flag to allow filter changes to trigger refreshes
296
- isInitializingCatalogData = false;
297
- };
298
- signalsService.effect(() => {
299
- sortService.currentSort.get();
300
- debouncedRefresh(false);
301
- });
302
- signalsService.effect(() => {
303
- categoryService.selectedCategory.get();
304
- debouncedRefresh(true).then(async () => {
305
- await initializeCatalogData();
306
- });
307
- });
308
- return {
309
- products: productsList,
310
- isLoading,
311
- error,
312
- totalProducts,
313
- hasProducts,
314
- hasMoreProducts,
315
- loadMore,
316
- refresh: debouncedRefresh,
317
- };
318
- });
319
- // Helper function to parse URL parameters
320
- function parseURLParams(searchParams, products = []) {
321
- const defaultFilters = {
322
- priceRange: { min: 0, max: 0 },
323
- selectedOptions: {},
324
- };
325
- if (!searchParams) {
326
- return {
327
- initialSort: SortType.NEWEST,
328
- initialFilters: defaultFilters,
329
- };
330
- }
331
- const urlParams = URLParamsUtils.parseSearchParams(searchParams);
332
- // Parse sort parameter
333
- const sortMap = {
334
- name_asc: SortType.NAME_ASC,
335
- name_desc: SortType.NAME_DESC,
336
- price_asc: SortType.PRICE_ASC,
337
- price_desc: SortType.PRICE_DESC,
338
- recommended: SortType.RECOMMENDED,
339
- };
340
- const initialSort = sortMap[urlParams["sort"]] || SortType.NEWEST;
341
- // Check if there are any filter parameters (excluding sort)
342
- const filterParams = Object.keys(urlParams).filter((key) => key !== "sort");
343
- if (filterParams.length === 0 || products.length === 0) {
344
- return { initialSort, initialFilters: defaultFilters };
345
- }
346
- // Calculate available price range from products
347
- const priceRange = calculatePriceRange(products);
348
- const initialFilters = {
349
- priceRange,
350
- selectedOptions: {},
351
- };
352
- // Apply price filters from URL
353
- if (urlParams["minPrice"]) {
354
- const min = parseFloat(urlParams["minPrice"]);
355
- if (!isNaN(min))
356
- initialFilters.priceRange.min = min;
357
- }
358
- if (urlParams["maxPrice"]) {
359
- const max = parseFloat(urlParams["maxPrice"]);
360
- if (!isNaN(max))
361
- initialFilters.priceRange.max = max;
362
- }
363
- // Parse option filters
364
- const optionsMap = buildOptionsMap(products);
365
- parseOptionFilters(urlParams, optionsMap, initialFilters);
366
- // Parse inventory filter from 'availability' URL parameter
367
- if (urlParams["availability"]) {
368
- const availabilityValues = Array.isArray(urlParams["availability"])
369
- ? urlParams["availability"]
370
- : [urlParams["availability"]];
371
- const inventoryStatusValues = availabilityValues.map((value) => value.replace(/\s+/g, "_").toUpperCase());
372
- initialFilters.selectedOptions["inventory-filter"] = inventoryStatusValues;
373
- }
374
- return { initialSort, initialFilters };
375
- }
376
- // Helper function to calculate price range from products
377
- function calculatePriceRange(products) {
378
- if (products.length === 0) {
379
- return { min: 0, max: 1000 };
380
- }
381
- let minPrice = Infinity;
382
- let maxPrice = 0;
383
- products.forEach((product) => {
384
- const min = parseFloat(product.actualPriceRange?.minValue?.amount || "0");
385
- const max = parseFloat(product.actualPriceRange?.maxValue?.amount || "0");
386
- if (min > 0)
387
- minPrice = Math.min(minPrice, min);
388
- if (max > 0)
389
- maxPrice = Math.max(maxPrice, max);
390
- });
391
- // If no valid prices found, return default range
392
- if (minPrice === Infinity) {
393
- minPrice = 0;
394
- maxPrice = 1000;
395
- }
396
- return { min: minPrice, max: maxPrice };
397
- }
398
- // Helper function to build options map from products
399
- function buildOptionsMap(products) {
400
- const optionsMap = new Map();
401
- products.forEach((product) => {
402
- product.options?.forEach((option) => {
403
- if (!option._id || !option.name)
404
- return;
405
- if (!optionsMap.has(option.name)) {
406
- optionsMap.set(option.name, { id: option._id, choices: [] });
407
- }
408
- const optionData = optionsMap.get(option.name);
409
- option.choicesSettings?.choices?.forEach((choice) => {
410
- if (choice.choiceId &&
411
- choice.name &&
412
- !optionData.choices.find((c) => c.id === choice.choiceId)) {
413
- optionData.choices.push({ id: choice.choiceId, name: choice.name });
414
- }
415
- });
416
- });
417
- });
418
- return optionsMap;
419
- }
420
- // Helper function to parse option filters from URL parameters
421
- function parseOptionFilters(urlParams, optionsMap, filters) {
422
- Object.entries(urlParams).forEach(([key, value]) => {
423
- if (["sort", "minPrice", "maxPrice"].includes(key))
424
- return;
425
- const option = optionsMap.get(key);
426
- if (option) {
427
- const values = Array.isArray(value) ? value : [value];
428
- const matchingChoices = option.choices.filter((choice) => values.includes(choice.name));
429
- if (matchingChoices.length > 0) {
430
- filters.selectedOptions[option.id] = matchingChoices.map((c) => c.id);
431
- }
432
- }
433
- });
434
- }
435
- /**
436
- * Loads collection service configuration from the Wix Products API for SSR initialization.
437
- * This function is designed to be used during Server-Side Rendering (SSR) to preload
438
- * initial products data, categories, filters, and sorting that will be used to configure the CollectionService.
439
- * Fetches products for a specific category, parses URL parameters for filters and sorting, and returns initial state.
440
- *
441
- * @param categoryId Optional category ID to filter products by
442
- * @param searchParams Optional URLSearchParams for initial filters and sorting
443
- * @param preloadedCategories Optional pre-loaded categories to avoid redundant API calls
444
- * @returns Promise that resolves to CollectionServiceConfig with initial data
445
- *
446
- * @example
447
- * ```astro
448
- * ---
449
- * // Astro page example - pages/products/[categorySlug].astro
450
- * import { loadCollectionServiceConfig } from '@wix/stores/services';
451
- * import { Collection } from '@wix/stores/components';
452
- *
453
- * // Get category from URL params
454
- * const { categorySlug } = Astro.params;
455
- * const categoryId = categorySlug === 'all' ? undefined : categorySlug;
456
- *
457
- * // Load collection data during SSR
458
- * const collectionConfig = await loadCollectionServiceConfig(
459
- * categoryId,
460
- * Astro.url.searchParams
461
- * );
462
- * ---
463
- *
464
- * <Collection.Root collectionConfig={collectionConfig}>
465
- * <Collection.Grid>
466
- * {({ products, isLoading }) => (
467
- * <div>
468
- * {isLoading ? 'Loading...' : `${products.length} products`}
469
- * </div>
470
- * )}
471
- * </Collection.Grid>
472
- * </Collection.Root>
473
- * ```
474
- *
475
- * @example
476
- * ```tsx
477
- * // Next.js page example - pages/products/[categorySlug].tsx
478
- * import { GetServerSideProps } from 'next';
479
- * import { loadCollectionServiceConfig } from '@wix/stores/services';
480
- * import { Collection } from '@wix/stores/components';
481
- *
482
- * interface ProductsPageProps {
483
- * collectionConfig: Awaited<ReturnType<typeof loadCollectionServiceConfig>>;
484
- * }
485
- *
486
- * export const getServerSideProps: GetServerSideProps<ProductsPageProps> = async ({ params, query }) => {
487
- * const categorySlug = params?.categorySlug as string;
488
- * const categoryId = categorySlug === 'all' ? undefined : categorySlug;
489
- *
490
- * // Convert Next.js query to URLSearchParams
491
- * const searchParams = new URLSearchParams();
492
- * Object.entries(query).forEach(([key, value]) => {
493
- * if (typeof value === 'string') searchParams.set(key, value);
494
- * });
495
- *
496
- * // Load collection data during SSR
497
- * const collectionConfig = await loadCollectionServiceConfig(
498
- * categoryId,
499
- * searchParams
500
- * );
501
- *
502
- * return {
503
- * props: {
504
- * collectionConfig,
505
- * },
506
- * };
507
- * };
508
- *
509
- * export default function ProductsPage({ collectionConfig }: ProductsPageProps) {
510
- * return (
511
- * <Collection.Root collectionConfig={collectionConfig}>
512
- * <Collection.Grid>
513
- * {({ products, isLoading }) => (
514
- * <div>
515
- * {isLoading ? 'Loading...' : `${products.length} products`}
516
- * </div>
517
- * )}
518
- * </Collection.Grid>
519
- * </Collection.Root>
520
- * );
521
- * }
522
- * ```
523
- */
524
- export async function loadCollectionServiceConfig(categoryId, searchParams, preloadedCategories) {
525
- try {
526
- // Use pre-loaded categories if provided, otherwise load them
527
- let categories;
528
- if (preloadedCategories) {
529
- categories = preloadedCategories;
530
- }
531
- else {
532
- const { loadCategoriesConfig } = await import("./category-service.js");
533
- const categoriesConfig = await loadCategoriesConfig();
534
- categories = categoriesConfig.categories;
535
- }
536
- // Build search options with category filter
537
- const searchOptions = buildSearchOptions(undefined, categoryId, undefined);
538
- const pageSize = 12;
539
- searchOptions.paging = { limit: pageSize };
540
- const productResults = await searchProducts(searchOptions);
541
- // Parse URL parameters for initial state
542
- const { initialSort, initialFilters } = parseURLParams(searchParams, productResults.products || []);
543
- return {
544
- initialProducts: productResults.products || [],
545
- pageSize,
546
- initialCursor: productResults.pagingMetadata?.cursors?.next || undefined,
547
- initialHasMore: Boolean(productResults.pagingMetadata?.cursors?.next &&
548
- productResults.products &&
549
- productResults.products.length === pageSize),
550
- initialSort,
551
- initialFilters,
552
- categories,
553
- };
554
- }
555
- catch (error) {
556
- console.warn("Failed to load initial products:", error);
557
- const { initialSort, initialFilters } = parseURLParams(searchParams);
558
- return {
559
- initialProducts: [],
560
- pageSize: 12,
561
- initialHasMore: false,
562
- initialSort,
563
- initialFilters,
564
- categories: [],
565
- };
566
- }
567
- }
568
- // Add function to fetch missing variants for all products in one request
569
- const fetchMissingVariants = async (products) => {
570
- // Find products that need variants (both single and multi-variant products)
571
- const productsNeedingVariants = products.filter((product) => !product.variantsInfo?.variants &&
572
- product.variantSummary?.variantCount &&
573
- product.variantSummary.variantCount > 0);
574
- if (productsNeedingVariants.length === 0) {
575
- return products;
576
- }
577
- try {
578
- const productIds = productsNeedingVariants
579
- .map((p) => p._id)
580
- .filter(Boolean);
581
- if (productIds.length === 0) {
582
- return products;
583
- }
584
- const items = [];
585
- const res = await readOnlyVariantsV3
586
- .queryVariants({})
587
- .in("productData.productId", productIds)
588
- .limit(100)
589
- .find();
590
- items.push(...res.items);
591
- let nextRes = res;
592
- while (nextRes.hasNext()) {
593
- nextRes = await nextRes.next();
594
- items.push(...nextRes.items);
595
- }
596
- const variantsByProductId = new Map();
597
- items.forEach((item) => {
598
- const productId = item.productData?.productId;
599
- if (productId) {
600
- if (!variantsByProductId.has(productId)) {
601
- variantsByProductId.set(productId, []);
602
- }
603
- variantsByProductId.get(productId).push({
604
- ...item,
605
- choices: item.optionChoices,
606
- });
607
- }
608
- });
609
- // Update products with their variants
610
- return products.map((product) => {
611
- const variants = variantsByProductId.get(product._id || "");
612
- if (variants && variants.length > 0) {
613
- return {
614
- ...product,
615
- variantsInfo: {
616
- ...product.variantsInfo,
617
- variants,
618
- },
619
- };
620
- }
621
- return product;
622
- });
623
- }
624
- catch (error) {
625
- console.error("Failed to fetch missing variants:", error);
626
- return products;
627
- }
628
- };
@@ -1,35 +0,0 @@
1
- import type { Signal, ReadOnlySignal } from "@wix/services-definitions/core-services/signals";
2
- import { type ProductOption } from "./catalog-service.js";
3
- export interface PriceRange {
4
- min: number;
5
- max: number;
6
- }
7
- export interface AvailableOptions {
8
- productOptions: ProductOption[];
9
- priceRange: PriceRange;
10
- }
11
- export type FilterSelectedOptions = Record<string, string[]>;
12
- export interface Filter {
13
- priceRange: PriceRange;
14
- selectedOptions: FilterSelectedOptions;
15
- }
16
- export interface FilterServiceAPI {
17
- currentFilters: Signal<Filter>;
18
- applyFilters: (filters: Filter) => Promise<void>;
19
- clearFilters: () => Promise<void>;
20
- availableOptions: ReadOnlySignal<AvailableOptions>;
21
- isFullyLoaded: ReadOnlySignal<boolean>;
22
- }
23
- export declare const FilterServiceDefinition: string & {
24
- __api: FilterServiceAPI;
25
- __config: {};
26
- isServiceDefinition?: boolean;
27
- } & FilterServiceAPI;
28
- export declare const defaultFilter: Filter;
29
- export declare const FilterService: import("@wix/services-definitions").ServiceFactory<string & {
30
- __api: FilterServiceAPI;
31
- __config: {};
32
- isServiceDefinition?: boolean;
33
- } & FilterServiceAPI, {
34
- initialFilters?: Filter;
35
- }>;