@wix/headless-stores 0.0.36 → 0.0.38

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/{dist/react/ProductsList.d.ts → cjs/dist/react/ProductList.d.ts} +71 -38
  8. package/cjs/dist/react/{ProductsList.js → ProductList.js} +30 -26
  9. package/cjs/dist/react/ProductListFilters.d.ts +244 -0
  10. package/cjs/dist/react/ProductListFilters.js +216 -0
  11. package/cjs/dist/react/ProductListPagination.d.ts +246 -0
  12. package/cjs/dist/react/ProductListPagination.js +207 -0
  13. package/cjs/dist/react/ProductListSort.d.ts +87 -0
  14. package/cjs/dist/react/ProductListSort.js +85 -0
  15. package/cjs/dist/react/ProductModifiers.d.ts +5 -5
  16. package/cjs/dist/react/ProductModifiers.js +10 -10
  17. package/cjs/dist/react/ProductVariantSelector.d.ts +5 -5
  18. package/cjs/dist/react/ProductVariantSelector.js +13 -10
  19. package/cjs/dist/react/SelectedVariant.d.ts +66 -3
  20. package/cjs/dist/react/SelectedVariant.js +106 -7
  21. package/cjs/dist/react/index.d.ts +6 -9
  22. package/cjs/dist/react/index.js +6 -9
  23. package/cjs/dist/services/buy-now-service.d.ts +208 -0
  24. package/cjs/dist/services/buy-now-service.js +132 -1
  25. package/cjs/dist/services/categories-list-service.d.ts +163 -0
  26. package/cjs/dist/services/categories-list-service.js +148 -0
  27. package/cjs/dist/services/category-service.d.ts +115 -70
  28. package/cjs/dist/services/category-service.js +101 -110
  29. package/cjs/dist/services/index.d.ts +6 -7
  30. package/cjs/dist/services/index.js +5 -16
  31. package/cjs/dist/services/pay-now-service.d.ts +146 -0
  32. package/cjs/dist/services/pay-now-service.js +112 -1
  33. package/cjs/dist/services/product-service.d.ts +71 -0
  34. package/cjs/dist/services/product-service.js +47 -0
  35. package/cjs/dist/services/products-list-filters-service.d.ts +292 -0
  36. package/cjs/dist/services/products-list-filters-service.js +446 -0
  37. package/cjs/dist/services/products-list-pagination-service.d.ts +186 -0
  38. package/cjs/dist/services/products-list-pagination-service.js +179 -0
  39. package/cjs/dist/services/products-list-service.d.ts +138 -52
  40. package/cjs/dist/services/products-list-service.js +185 -54
  41. package/cjs/dist/services/products-list-sort-service.d.ts +117 -0
  42. package/cjs/dist/services/products-list-sort-service.js +144 -0
  43. package/cjs/dist/utils/url-params.d.ts +68 -0
  44. package/cjs/dist/utils/url-params.js +72 -4
  45. package/dist/react/Category.d.ts +65 -59
  46. package/dist/react/Category.js +50 -83
  47. package/dist/react/CategoryList.d.ts +184 -0
  48. package/dist/react/CategoryList.js +174 -0
  49. package/dist/react/Product.d.ts +3 -3
  50. package/dist/react/Product.js +6 -6
  51. package/{cjs/dist/react/ProductsList.d.ts → dist/react/ProductList.d.ts} +71 -38
  52. package/dist/react/{ProductsList.js → ProductList.js} +30 -26
  53. package/dist/react/ProductListFilters.d.ts +244 -0
  54. package/dist/react/ProductListFilters.js +216 -0
  55. package/dist/react/ProductListPagination.d.ts +246 -0
  56. package/dist/react/ProductListPagination.js +207 -0
  57. package/dist/react/ProductListSort.d.ts +87 -0
  58. package/dist/react/ProductListSort.js +85 -0
  59. package/dist/react/ProductModifiers.d.ts +5 -5
  60. package/dist/react/ProductModifiers.js +10 -10
  61. package/dist/react/ProductVariantSelector.d.ts +5 -5
  62. package/dist/react/ProductVariantSelector.js +13 -10
  63. package/dist/react/SelectedVariant.d.ts +66 -3
  64. package/dist/react/SelectedVariant.js +106 -7
  65. package/dist/react/index.d.ts +6 -9
  66. package/dist/react/index.js +6 -9
  67. package/dist/services/buy-now-service.d.ts +208 -0
  68. package/dist/services/buy-now-service.js +132 -1
  69. package/dist/services/categories-list-service.d.ts +163 -0
  70. package/dist/services/categories-list-service.js +148 -0
  71. package/dist/services/category-service.d.ts +115 -70
  72. package/dist/services/category-service.js +101 -110
  73. package/dist/services/index.d.ts +6 -7
  74. package/dist/services/index.js +5 -16
  75. package/dist/services/pay-now-service.d.ts +146 -0
  76. package/dist/services/pay-now-service.js +112 -1
  77. package/dist/services/product-service.d.ts +71 -0
  78. package/dist/services/product-service.js +47 -0
  79. package/dist/services/products-list-filters-service.d.ts +292 -0
  80. package/dist/services/products-list-filters-service.js +446 -0
  81. package/dist/services/products-list-pagination-service.d.ts +186 -0
  82. package/dist/services/products-list-pagination-service.js +179 -0
  83. package/dist/services/products-list-service.d.ts +138 -52
  84. package/dist/services/products-list-service.js +185 -54
  85. package/dist/services/products-list-sort-service.d.ts +117 -0
  86. package/dist/services/products-list-sort-service.js +144 -0
  87. package/dist/utils/url-params.d.ts +68 -0
  88. package/dist/utils/url-params.js +72 -4
  89. package/package.json +3 -3
  90. package/cjs/dist/react/Collection.d.ts +0 -294
  91. package/cjs/dist/react/Collection.js +0 -345
  92. package/cjs/dist/react/FilteredCollection.d.ts +0 -299
  93. package/cjs/dist/react/FilteredCollection.js +0 -352
  94. package/cjs/dist/react/ProductActions.d.ts +0 -70
  95. package/cjs/dist/react/ProductActions.js +0 -104
  96. package/cjs/dist/react/RelatedProducts.d.ts +0 -169
  97. package/cjs/dist/react/RelatedProducts.js +0 -180
  98. package/cjs/dist/react/Sort.d.ts +0 -37
  99. package/cjs/dist/react/Sort.js +0 -36
  100. package/cjs/dist/services/catalog-service.d.ts +0 -36
  101. package/cjs/dist/services/catalog-service.js +0 -193
  102. package/cjs/dist/services/collection-service.d.ts +0 -124
  103. package/cjs/dist/services/collection-service.js +0 -628
  104. package/cjs/dist/services/filter-service.d.ts +0 -35
  105. package/cjs/dist/services/filter-service.js +0 -119
  106. package/cjs/dist/services/related-products-service.d.ts +0 -100
  107. package/cjs/dist/services/related-products-service.js +0 -127
  108. package/cjs/dist/services/sort-service.d.ts +0 -20
  109. package/cjs/dist/services/sort-service.js +0 -27
  110. package/dist/react/Collection.d.ts +0 -294
  111. package/dist/react/Collection.js +0 -345
  112. package/dist/react/FilteredCollection.d.ts +0 -299
  113. package/dist/react/FilteredCollection.js +0 -352
  114. package/dist/react/ProductActions.d.ts +0 -70
  115. package/dist/react/ProductActions.js +0 -104
  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
@@ -0,0 +1,446 @@
1
+ import { defineService } from "@wix/services-definitions";
2
+ import { implementService } from "@wix/services-definitions";
3
+ import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
4
+ import { productsV3 } from "@wix/stores";
5
+ import { ProductsListServiceDefinition } from "./products-list-service.js";
6
+ import { customizationsV3 } from "@wix/stores";
7
+ /**
8
+ * Enumeration of inventory status types available for filtering.
9
+ * Maps to the Wix Stores API inventory availability statuses.
10
+ *
11
+ * @enum {string}
12
+ */
13
+ export var InventoryStatusType;
14
+ (function (InventoryStatusType) {
15
+ /** Product is in stock and available for purchase */
16
+ InventoryStatusType["IN_STOCK"] = "IN_STOCK";
17
+ /** Product is out of stock */
18
+ InventoryStatusType["OUT_OF_STOCK"] = "OUT_OF_STOCK";
19
+ /** Product is partially out of stock (some variants available) */
20
+ InventoryStatusType["PARTIALLY_OUT_OF_STOCK"] = "PARTIALLY_OUT_OF_STOCK";
21
+ })(InventoryStatusType || (InventoryStatusType = {}));
22
+ /**
23
+ * Loads products list filters service configuration from the Wix Stores API for SSR initialization.
24
+ * This function fetches customization data that will be used to build product filter options.
25
+ *
26
+ * @returns {Promise<ProductsListFiltersServiceConfig>} Promise that resolves to the filters configuration
27
+ *
28
+ * @example
29
+ * ```astro
30
+ * ---
31
+ * // Astro page example - pages/products.astro
32
+ * import { loadProductsListFiltersServiceConfig } from '@wix/stores/services';
33
+ * import { ProductsListFilters } from '@wix/stores/components';
34
+ *
35
+ * // Load filters configuration during SSR
36
+ * const filtersConfig = await loadProductsListFiltersServiceConfig();
37
+ * ---
38
+ *
39
+ * <ProductsListFilters.Root filtersConfig={filtersConfig}>
40
+ * <ProductsListFilters.MinPrice>
41
+ * {({ minPrice, setMinPrice }) => (
42
+ * <input
43
+ * type="number"
44
+ * value={minPrice}
45
+ * onChange={(e) => setMinPrice(parseFloat(e.target.value))}
46
+ * placeholder="Min Price"
47
+ * />
48
+ * )}
49
+ * </ProductsListFilters.MinPrice>
50
+ * </ProductsListFilters.Root>
51
+ * ```
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * // Next.js page example
56
+ * import { GetServerSideProps } from 'next';
57
+ * import { loadProductsListFiltersServiceConfig } from '@wix/stores/services';
58
+ *
59
+ * export const getServerSideProps: GetServerSideProps = async () => {
60
+ * const filtersConfig = await loadProductsListFiltersServiceConfig();
61
+ *
62
+ * return {
63
+ * props: {
64
+ * filtersConfig,
65
+ * },
66
+ * };
67
+ * };
68
+ * ```
69
+ */
70
+ export async function loadProductsListFiltersServiceConfig() {
71
+ const { items: customizations = [] } = await customizationsV3
72
+ .queryCustomizations()
73
+ .find();
74
+ return {
75
+ customizations,
76
+ };
77
+ }
78
+ /**
79
+ * Service definition for the Products List Filters service.
80
+ * This defines the reactive API contract for managing product list filtering capabilities
81
+ * including price, inventory status, and product option filters.
82
+ *
83
+ * @constant
84
+ */
85
+ export const ProductsListFiltersServiceDefinition = defineService("products-list-filters");
86
+ /**
87
+ * Implementation of the Products List Filters service that manages reactive filtering state.
88
+ * This service provides signals for all filter types (price, inventory, product options) and
89
+ * automatically updates the products list search options when filters change.
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * import { ProductsListFiltersService, ProductsListFiltersServiceDefinition } from '@wix/stores/services';
94
+ * import { useService } from '@wix/services-manager-react';
95
+ *
96
+ * function FiltersComponent({ filtersConfig }) {
97
+ * return (
98
+ * <ServiceProvider services={createServicesMap([
99
+ * [ProductsListFiltersServiceDefinition, ProductsListFiltersService.withConfig(filtersConfig)]
100
+ * ])}>
101
+ * <FilterControls />
102
+ * </ServiceProvider>
103
+ * );
104
+ * }
105
+ *
106
+ * function FilterControls() {
107
+ * const filtersService = useService(ProductsListFiltersServiceDefinition);
108
+ * const minPrice = filtersService.minPrice.get();
109
+ * const maxPrice = filtersService.maxPrice.get();
110
+ * const selectedInventoryStatuses = filtersService.selectedInventoryStatuses.get();
111
+ * const availableProductOptions = filtersService.availableProductOptions.get();
112
+ * const isFiltered = filtersService.isFiltered.get();
113
+ *
114
+ * return (
115
+ * <div>
116
+ * <div>
117
+ * <input
118
+ * type="number"
119
+ * value={minPrice}
120
+ * onChange={(e) => filtersService.setMinPrice(parseFloat(e.target.value))}
121
+ * placeholder="Min Price"
122
+ * />
123
+ * <input
124
+ * type="number"
125
+ * value={maxPrice}
126
+ * onChange={(e) => filtersService.setMaxPrice(parseFloat(e.target.value))}
127
+ * placeholder="Max Price"
128
+ * />
129
+ * </div>
130
+ *
131
+ * {availableProductOptions.map(option => (
132
+ * <div key={option.id}>
133
+ * <h4>{option.name}</h4>
134
+ * {option.choices.map(choice => (
135
+ * <label key={choice.id}>
136
+ * <input
137
+ * type="checkbox"
138
+ * onChange={() => filtersService.toggleProductOption(option.id, choice.id)}
139
+ * />
140
+ * {choice.name}
141
+ * </label>
142
+ * ))}
143
+ * </div>
144
+ * ))}
145
+ *
146
+ * {isFiltered && (
147
+ * <button onClick={() => filtersService.reset()}>
148
+ * Clear All Filters
149
+ * </button>
150
+ * )}
151
+ * </div>
152
+ * );
153
+ * }
154
+ * ```
155
+ */
156
+ export const ProductsListFiltersService = implementService.withConfig()(ProductsListFiltersServiceDefinition, ({ getService, config }) => {
157
+ let firstRun = true;
158
+ const signalsService = getService(SignalsServiceDefinition);
159
+ const productsListService = getService(ProductsListServiceDefinition);
160
+ const { customizations } = config;
161
+ const aggregationData = productsListService.aggregations.get()?.results;
162
+ // TODO: use the aggregations to get the available inventory statuses
163
+ // and the available price ranges
164
+ // and the available product options
165
+ // and the available product choices
166
+ const minPriceSignal = signalsService.signal(getMinPrice(productsListService.searchOptions.get()));
167
+ const maxPriceSignal = signalsService.signal(getMaxPrice(productsListService.searchOptions.get()));
168
+ const availableInventoryStatusesSignal = signalsService.signal([
169
+ InventoryStatusType.IN_STOCK,
170
+ InventoryStatusType.OUT_OF_STOCK,
171
+ InventoryStatusType.PARTIALLY_OUT_OF_STOCK,
172
+ ]);
173
+ const selectedInventoryStatusesSignal = signalsService.signal(getSelectedInventoryStatuses(productsListService.searchOptions.get()));
174
+ // TODO: Get product options from aggregations data
175
+ const availableProductOptionsSignal = signalsService.signal(getAvailableProductOptions(aggregationData, customizations));
176
+ const selectedProductOptionsSignal = signalsService.signal(getSelectedProductOptions(productsListService.searchOptions.get()));
177
+ const isFilteredSignal = signalsService.signal(false);
178
+ if (typeof window !== "undefined") {
179
+ signalsService.effect(() => {
180
+ // CRITICAL: Read the signals FIRST to establish dependencies, even on first run
181
+ const minPrice = minPriceSignal.get();
182
+ const maxPrice = maxPriceSignal.get();
183
+ const selectedInventoryStatuses = selectedInventoryStatusesSignal.get();
184
+ const selectedProductOptions = selectedProductOptionsSignal.get();
185
+ if (firstRun) {
186
+ firstRun = false;
187
+ return;
188
+ }
189
+ isFilteredSignal.set(true);
190
+ // Build new search options with updated price filters
191
+ const newSearchOptions = {
192
+ ...productsListService.searchOptions.peek(),
193
+ };
194
+ delete newSearchOptions.cursorPaging?.cursor;
195
+ // Initialize filter if it doesn't exist
196
+ if (!newSearchOptions.filter) {
197
+ newSearchOptions.filter = {};
198
+ }
199
+ else {
200
+ // Copy existing filter to avoid mutation
201
+ newSearchOptions.filter = { ...newSearchOptions.filter };
202
+ }
203
+ // Remove existing price filters
204
+ delete newSearchOptions.filter["actualPriceRange.minValue.amount"];
205
+ delete newSearchOptions.filter["actualPriceRange.maxValue.amount"];
206
+ // Remove existing inventory filter
207
+ delete newSearchOptions.filter["inventory.availabilityStatus"];
208
+ // Remove existing product option filters
209
+ // First, find and remove any existing option filters
210
+ Object.keys(newSearchOptions.filter).forEach((key) => {
211
+ if (key.startsWith("options.")) {
212
+ delete newSearchOptions.filter[key];
213
+ }
214
+ });
215
+ // Add new price filters if they have valid values
216
+ if (minPrice > 0) {
217
+ newSearchOptions.filter["actualPriceRange.minValue.amount"] = { $gte: minPrice };
218
+ }
219
+ if (maxPrice > 0) {
220
+ newSearchOptions.filter["actualPriceRange.maxValue.amount"] = { $lte: maxPrice };
221
+ }
222
+ // Add new inventory filter if there are selected statuses
223
+ if (selectedInventoryStatuses.length > 0) {
224
+ if (selectedInventoryStatuses.length === 1) {
225
+ newSearchOptions.filter["inventory.availabilityStatus"] =
226
+ selectedInventoryStatuses[0];
227
+ }
228
+ else {
229
+ newSearchOptions.filter["inventory.availabilityStatus"] =
230
+ {
231
+ $in: selectedInventoryStatuses,
232
+ };
233
+ }
234
+ }
235
+ // Add new product option filters if there are selected options
236
+ if (selectedProductOptions &&
237
+ Object.keys(selectedProductOptions).length > 0) {
238
+ for (const [optionId, choiceIds] of Object.entries(selectedProductOptions)) {
239
+ if (choiceIds && choiceIds.length > 0) {
240
+ // Handle inventory filter separately
241
+ if (optionId === "inventory-filter") {
242
+ newSearchOptions.filter["inventory.availabilityStatus"] = {
243
+ $in: choiceIds,
244
+ };
245
+ }
246
+ else {
247
+ // Regular product options filter
248
+ newSearchOptions.filter["options.choicesSettings.choices.choiceId"] = {
249
+ $hasSome: choiceIds,
250
+ };
251
+ }
252
+ }
253
+ }
254
+ }
255
+ // Use callback to update search options
256
+ productsListService.setSearchOptions(newSearchOptions);
257
+ });
258
+ }
259
+ return {
260
+ minPrice: minPriceSignal,
261
+ maxPrice: maxPriceSignal,
262
+ availableInventoryStatuses: availableInventoryStatusesSignal,
263
+ selectedInventoryStatuses: selectedInventoryStatusesSignal,
264
+ availableProductOptions: availableProductOptionsSignal,
265
+ selectedProductOptions: selectedProductOptionsSignal,
266
+ setMinPrice: (minPrice) => {
267
+ minPriceSignal.set(minPrice);
268
+ },
269
+ setMaxPrice: (maxPrice) => {
270
+ maxPriceSignal.set(maxPrice);
271
+ },
272
+ toggleInventoryStatus: (status) => {
273
+ const current = selectedInventoryStatusesSignal.get();
274
+ const isSelected = current.includes(status);
275
+ if (isSelected) {
276
+ selectedInventoryStatusesSignal.set(current.filter((s) => s !== status));
277
+ }
278
+ else {
279
+ selectedInventoryStatusesSignal.set([...current, status]);
280
+ }
281
+ },
282
+ toggleProductOption: (optionId, choiceId) => {
283
+ const current = selectedProductOptionsSignal.get();
284
+ const currentChoices = current[optionId] || [];
285
+ const isSelected = currentChoices.includes(choiceId);
286
+ if (isSelected) {
287
+ // Remove the choice
288
+ const newChoices = currentChoices.filter((id) => id !== choiceId);
289
+ if (newChoices.length === 0) {
290
+ const newOptions = { ...current };
291
+ delete newOptions[optionId];
292
+ selectedProductOptionsSignal.set(newOptions);
293
+ }
294
+ else {
295
+ selectedProductOptionsSignal.set({
296
+ ...current,
297
+ [optionId]: newChoices,
298
+ });
299
+ }
300
+ }
301
+ else {
302
+ // Add the choice
303
+ selectedProductOptionsSignal.set({
304
+ ...current,
305
+ [optionId]: [...currentChoices, choiceId],
306
+ });
307
+ }
308
+ },
309
+ isFiltered: isFilteredSignal,
310
+ reset: () => {
311
+ // TODO: reset the filters to the original values from the aggregation data
312
+ minPriceSignal.set(0);
313
+ maxPriceSignal.set(0);
314
+ selectedInventoryStatusesSignal.set([]);
315
+ selectedProductOptionsSignal.set({});
316
+ isFilteredSignal.set(false);
317
+ },
318
+ };
319
+ });
320
+ function getMinPrice(searchOptions) {
321
+ const filter = searchOptions.filter;
322
+ if (!filter)
323
+ return 0;
324
+ const minPriceFilter = filter["actualPriceRange.minValue.amount"];
325
+ if (typeof minPriceFilter === "object" &&
326
+ minPriceFilter !== null &&
327
+ "$gte" in minPriceFilter) {
328
+ return Number(minPriceFilter.$gte) || 0;
329
+ }
330
+ return 0;
331
+ }
332
+ function getMaxPrice(searchOptions) {
333
+ const filter = searchOptions.filter;
334
+ if (!filter)
335
+ return 0;
336
+ const maxPriceFilter = filter["actualPriceRange.maxValue.amount"];
337
+ if (typeof maxPriceFilter === "object" &&
338
+ maxPriceFilter !== null &&
339
+ "$lte" in maxPriceFilter) {
340
+ return Number(maxPriceFilter.$lte) || 0;
341
+ }
342
+ return 0;
343
+ }
344
+ function getSelectedInventoryStatuses(searchOptions) {
345
+ const filter = searchOptions.filter;
346
+ if (!filter)
347
+ return [];
348
+ const inventoryFilter = filter["inventory.availabilityStatus"];
349
+ if (typeof inventoryFilter === "string" && inventoryFilter.length > 0) {
350
+ return [inventoryFilter];
351
+ }
352
+ if (typeof inventoryFilter === "object" &&
353
+ inventoryFilter !== null &&
354
+ "$in" in inventoryFilter) {
355
+ return Array.isArray(inventoryFilter.$in) ? inventoryFilter.$in : [];
356
+ }
357
+ return [];
358
+ }
359
+ function getSelectedProductOptions(searchOptions) {
360
+ const filter = searchOptions.filter;
361
+ if (!filter)
362
+ return {};
363
+ const selectedOptions = {};
364
+ // Look for options.{optionId}.choice filters
365
+ Object.keys(filter).forEach((key) => {
366
+ if (key.startsWith("options.") && key.endsWith(".choice")) {
367
+ const optionId = key.slice(8, -7); // Remove "options." and ".choice"
368
+ const optionFilter = filter[key];
369
+ if (typeof optionFilter === "string" && optionFilter.length > 0) {
370
+ selectedOptions[optionId] = [optionFilter];
371
+ }
372
+ else if (typeof optionFilter === "object" &&
373
+ optionFilter !== null &&
374
+ "$in" in optionFilter &&
375
+ Array.isArray(optionFilter.$in)) {
376
+ selectedOptions[optionId] = optionFilter.$in;
377
+ }
378
+ }
379
+ });
380
+ return selectedOptions;
381
+ }
382
+ function getAvailableProductOptions(aggregationData = [], customizations = []) {
383
+ // Helper function to match aggregation names case-insensitively
384
+ const matchesAggregationName = (name, aggregationNames) => {
385
+ return aggregationNames.some((aggName) => aggName.toLowerCase() === name.toLowerCase());
386
+ };
387
+ // Helper function to sort choices intelligently (numbers first, then alphabetically)
388
+ const sortChoicesIntelligently = (choices) => {
389
+ return [...choices].sort((a, b) => {
390
+ const aIsNumber = /^\d+$/.test(a.name);
391
+ const bIsNumber = /^\d+$/.test(b.name);
392
+ if (aIsNumber && bIsNumber) {
393
+ return parseInt(b.name) - parseInt(a.name);
394
+ }
395
+ if (aIsNumber && !bIsNumber)
396
+ return -1;
397
+ if (!aIsNumber && bIsNumber)
398
+ return 1;
399
+ return a.name.localeCompare(b.name);
400
+ });
401
+ };
402
+ // Extract option names from aggregation data
403
+ const optionNames = [];
404
+ const choiceNames = [];
405
+ // Process aggregation results to extract available option and choice names
406
+ aggregationData.forEach((result) => {
407
+ if (result.name === "optionNames" && result.values?.results) {
408
+ optionNames.push(...result.values.results
409
+ .map((item) => item.value)
410
+ .filter((value) => typeof value === "string"));
411
+ }
412
+ if (result.name === "choiceNames" && result.values?.results) {
413
+ choiceNames.push(...result.values.results
414
+ .map((item) => item.value)
415
+ .filter((value) => typeof value === "string"));
416
+ }
417
+ });
418
+ // Build options by matching customizations with aggregation data
419
+ const options = customizations
420
+ .filter((customization) => customization.name &&
421
+ customization._id &&
422
+ customization.customizationType ===
423
+ customizationsV3.CustomizationType.PRODUCT_OPTION &&
424
+ (optionNames.length === 0 ||
425
+ matchesAggregationName(customization.name, optionNames)))
426
+ .map((customization) => {
427
+ const choices = (customization.choicesSettings?.choices || [])
428
+ .filter((choice) => choice._id &&
429
+ choice.name &&
430
+ (choiceNames.length === 0 ||
431
+ matchesAggregationName(choice.name, choiceNames)))
432
+ .map((choice) => ({
433
+ id: choice._id,
434
+ name: choice.name,
435
+ colorCode: choice.colorCode,
436
+ }));
437
+ return {
438
+ id: customization._id,
439
+ name: customization.name,
440
+ choices: sortChoicesIntelligently(choices),
441
+ optionRenderType: customization.customizationRenderType,
442
+ };
443
+ })
444
+ .filter((option) => option.choices.length > 0);
445
+ return options;
446
+ }
@@ -0,0 +1,186 @@
1
+ import { type Signal } from "@wix/services-definitions/core-services/signals";
2
+ /**
3
+ * Service definition for the Products List Pagination service.
4
+ * This defines the reactive API contract for managing product list pagination state and navigation.
5
+ *
6
+ * @constant
7
+ */
8
+ export declare const ProductsListPaginationServiceDefinition: string & {
9
+ __api: {
10
+ /** Reactive signal containing the current page size limit */
11
+ currentLimit: Signal<number>;
12
+ /** Reactive signal containing the current cursor for pagination */
13
+ currentCursor: Signal<string | null>;
14
+ /** Computed signal indicating if there's a next page available */
15
+ hasNextPage: {
16
+ get: () => boolean;
17
+ };
18
+ /** Computed signal indicating if there's a previous page available */
19
+ hasPrevPage: {
20
+ get: () => boolean;
21
+ };
22
+ /** Function to set the page size limit */
23
+ setLimit: (limit: number) => void;
24
+ /** Function to navigate to the next page */
25
+ nextPage: () => void;
26
+ /** Function to navigate to the previous page */
27
+ prevPage: () => void;
28
+ /** Function to navigate to the first page */
29
+ goToFirstPage: () => void;
30
+ /** Function to load more items (increase the limit) */
31
+ loadMore: (count: number) => void;
32
+ };
33
+ __config: {};
34
+ isServiceDefinition?: boolean;
35
+ } & {
36
+ /** Reactive signal containing the current page size limit */
37
+ currentLimit: Signal<number>;
38
+ /** Reactive signal containing the current cursor for pagination */
39
+ currentCursor: Signal<string | null>;
40
+ /** Computed signal indicating if there's a next page available */
41
+ hasNextPage: {
42
+ get: () => boolean;
43
+ };
44
+ /** Computed signal indicating if there's a previous page available */
45
+ hasPrevPage: {
46
+ get: () => boolean;
47
+ };
48
+ /** Function to set the page size limit */
49
+ setLimit: (limit: number) => void;
50
+ /** Function to navigate to the next page */
51
+ nextPage: () => void;
52
+ /** Function to navigate to the previous page */
53
+ prevPage: () => void;
54
+ /** Function to navigate to the first page */
55
+ goToFirstPage: () => void;
56
+ /** Function to load more items (increase the limit) */
57
+ loadMore: (count: number) => void;
58
+ };
59
+ /**
60
+ * Configuration interface for the Products List Pagination service.
61
+ * Currently empty as this service doesn't require initial configuration.
62
+ *
63
+ * @interface ProductsListPaginationServiceConfig
64
+ */
65
+ export type ProductsListPaginationServiceConfig = {};
66
+ /**
67
+ * Implementation of the Products List Pagination service that manages reactive pagination state.
68
+ * This service provides signals for pagination state and automatically updates the products list
69
+ * search options when pagination settings change. It supports both cursor-based pagination
70
+ * and load-more functionality.
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * import { ProductsListPaginationService, ProductsListPaginationServiceDefinition } from '@wix/stores/services';
75
+ * import { useService } from '@wix/services-manager-react';
76
+ *
77
+ * function PaginationComponent() {
78
+ * return (
79
+ * <ServiceProvider services={createServicesMap([
80
+ * [ProductsListPaginationServiceDefinition, ProductsListPaginationService.withConfig({})]
81
+ * ])}>
82
+ * <PaginationControls />
83
+ * </ServiceProvider>
84
+ * );
85
+ * }
86
+ *
87
+ * function PaginationControls() {
88
+ * const paginationService = useService(ProductsListPaginationServiceDefinition);
89
+ * const currentLimit = paginationService.currentLimit.get();
90
+ * const hasNextPage = paginationService.hasNextPage.get();
91
+ * const hasPrevPage = paginationService.hasPrevPage.get();
92
+ *
93
+ * return (
94
+ * <div>
95
+ * <div>
96
+ * Items per page:
97
+ * <select
98
+ * value={currentLimit}
99
+ * onChange={(e) => paginationService.setLimit(parseInt(e.target.value))}
100
+ * >
101
+ * <option value={12}>12</option>
102
+ * <option value={24}>24</option>
103
+ * <option value={48}>48</option>
104
+ * </select>
105
+ * </div>
106
+ *
107
+ * <div>
108
+ * <button
109
+ * onClick={() => paginationService.goToFirstPage()}
110
+ * disabled={!hasPrevPage}
111
+ * >
112
+ * First
113
+ * </button>
114
+ * <button
115
+ * onClick={() => paginationService.prevPage()}
116
+ * disabled={!hasPrevPage}
117
+ * >
118
+ * Previous
119
+ * </button>
120
+ * <button
121
+ * onClick={() => paginationService.nextPage()}
122
+ * disabled={!hasNextPage}
123
+ * >
124
+ * Next
125
+ * </button>
126
+ * </div>
127
+ *
128
+ * <button onClick={() => paginationService.loadMore(12)}>
129
+ * Load More
130
+ * </button>
131
+ * </div>
132
+ * );
133
+ * }
134
+ * ```
135
+ */
136
+ export declare const ProductsListPaginationService: import("@wix/services-definitions").ServiceFactory<string & {
137
+ __api: {
138
+ /** Reactive signal containing the current page size limit */
139
+ currentLimit: Signal<number>;
140
+ /** Reactive signal containing the current cursor for pagination */
141
+ currentCursor: Signal<string | null>;
142
+ /** Computed signal indicating if there's a next page available */
143
+ hasNextPage: {
144
+ get: () => boolean;
145
+ };
146
+ /** Computed signal indicating if there's a previous page available */
147
+ hasPrevPage: {
148
+ get: () => boolean;
149
+ };
150
+ /** Function to set the page size limit */
151
+ setLimit: (limit: number) => void;
152
+ /** Function to navigate to the next page */
153
+ nextPage: () => void;
154
+ /** Function to navigate to the previous page */
155
+ prevPage: () => void;
156
+ /** Function to navigate to the first page */
157
+ goToFirstPage: () => void;
158
+ /** Function to load more items (increase the limit) */
159
+ loadMore: (count: number) => void;
160
+ };
161
+ __config: {};
162
+ isServiceDefinition?: boolean;
163
+ } & {
164
+ /** Reactive signal containing the current page size limit */
165
+ currentLimit: Signal<number>;
166
+ /** Reactive signal containing the current cursor for pagination */
167
+ currentCursor: Signal<string | null>;
168
+ /** Computed signal indicating if there's a next page available */
169
+ hasNextPage: {
170
+ get: () => boolean;
171
+ };
172
+ /** Computed signal indicating if there's a previous page available */
173
+ hasPrevPage: {
174
+ get: () => boolean;
175
+ };
176
+ /** Function to set the page size limit */
177
+ setLimit: (limit: number) => void;
178
+ /** Function to navigate to the next page */
179
+ nextPage: () => void;
180
+ /** Function to navigate to the previous page */
181
+ prevPage: () => void;
182
+ /** Function to navigate to the first page */
183
+ goToFirstPage: () => void;
184
+ /** Function to load more items (increase the limit) */
185
+ loadMore: (count: number) => void;
186
+ }, ProductsListPaginationServiceConfig>;