@wix/headless-stores 0.0.37 → 0.0.39
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/cjs/dist/react/Category.d.ts +9 -8
- package/cjs/dist/react/CategoryList.d.ts +12 -11
- package/cjs/dist/react/Product.d.ts +5 -4
- package/cjs/dist/react/ProductListFilters.d.ts +9 -5
- package/cjs/dist/react/ProductListFilters.js +14 -5
- package/cjs/dist/react/ProductListPagination.d.ts +14 -12
- package/cjs/dist/react/ProductListSort.d.ts +6 -4
- package/cjs/dist/react/ProductModifiers.d.ts +4 -2
- package/cjs/dist/react/ProductVariantSelector.d.ts +5 -4
- package/cjs/dist/react/SelectedVariant.d.ts +68 -4
- package/cjs/dist/react/SelectedVariant.js +97 -0
- package/cjs/dist/react/index.d.ts +0 -1
- package/cjs/dist/react/index.js +0 -1
- package/cjs/dist/services/products-list-filters-service.d.ts +41 -24
- package/cjs/dist/services/products-list-filters-service.js +150 -92
- package/cjs/dist/services/products-list-service.js +87 -3
- package/dist/react/Category.d.ts +9 -8
- package/dist/react/CategoryList.d.ts +12 -11
- package/dist/react/Product.d.ts +5 -4
- package/dist/react/ProductListFilters.d.ts +9 -5
- package/dist/react/ProductListFilters.js +14 -5
- package/dist/react/ProductListPagination.d.ts +14 -12
- package/dist/react/ProductListSort.d.ts +6 -4
- package/dist/react/ProductModifiers.d.ts +4 -2
- package/dist/react/ProductVariantSelector.d.ts +5 -4
- package/dist/react/SelectedVariant.d.ts +68 -4
- package/dist/react/SelectedVariant.js +97 -0
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -1
- package/dist/services/products-list-filters-service.d.ts +41 -24
- package/dist/services/products-list-filters-service.js +150 -92
- package/dist/services/products-list-service.js +87 -3
- package/package.json +2 -2
- package/cjs/dist/react/ProductActions.d.ts +0 -70
- package/cjs/dist/react/ProductActions.js +0 -104
- package/dist/react/ProductActions.d.ts +0 -70
- package/dist/react/ProductActions.js +0 -104
|
@@ -112,10 +112,14 @@ export declare function loadProductsListFiltersServiceConfig(): Promise<Products
|
|
|
112
112
|
*/
|
|
113
113
|
export declare const ProductsListFiltersServiceDefinition: string & {
|
|
114
114
|
__api: {
|
|
115
|
-
/** Reactive signal containing the minimum price filter value */
|
|
116
|
-
|
|
117
|
-
/** Reactive signal containing the maximum price filter value */
|
|
118
|
-
|
|
115
|
+
/** Reactive signal containing the user's selected minimum price filter value */
|
|
116
|
+
selectedMinPrice: Signal<number>;
|
|
117
|
+
/** Reactive signal containing the user's selected maximum price filter value */
|
|
118
|
+
selectedMaxPrice: Signal<number>;
|
|
119
|
+
/** Reactive signal containing the catalog minimum price (for UI bounds) */
|
|
120
|
+
availableMinPrice: Signal<number>;
|
|
121
|
+
/** Reactive signal containing the catalog maximum price (for UI bounds) */
|
|
122
|
+
availableMaxPrice: Signal<number>;
|
|
119
123
|
/** Reactive signal containing available inventory status options */
|
|
120
124
|
availableInventoryStatuses: Signal<InventoryStatusType[]>;
|
|
121
125
|
/** Reactive signal containing selected inventory status filters */
|
|
@@ -125,9 +129,9 @@ export declare const ProductsListFiltersServiceDefinition: string & {
|
|
|
125
129
|
/** Reactive signal containing selected product option filters */
|
|
126
130
|
selectedProductOptions: Signal<Record<string, string[]>>;
|
|
127
131
|
/** Function to set the minimum price filter */
|
|
128
|
-
|
|
132
|
+
setSelectedMinPrice: (minPrice: number) => void;
|
|
129
133
|
/** Function to set the maximum price filter */
|
|
130
|
-
|
|
134
|
+
setSelectedMaxPrice: (maxPrice: number) => void;
|
|
131
135
|
/** Function to toggle an inventory status filter */
|
|
132
136
|
toggleInventoryStatus: (status: InventoryStatusType) => void;
|
|
133
137
|
/** Function to toggle a product option choice filter */
|
|
@@ -140,10 +144,14 @@ export declare const ProductsListFiltersServiceDefinition: string & {
|
|
|
140
144
|
__config: {};
|
|
141
145
|
isServiceDefinition?: boolean;
|
|
142
146
|
} & {
|
|
143
|
-
/** Reactive signal containing the minimum price filter value */
|
|
144
|
-
|
|
145
|
-
/** Reactive signal containing the maximum price filter value */
|
|
146
|
-
|
|
147
|
+
/** Reactive signal containing the user's selected minimum price filter value */
|
|
148
|
+
selectedMinPrice: Signal<number>;
|
|
149
|
+
/** Reactive signal containing the user's selected maximum price filter value */
|
|
150
|
+
selectedMaxPrice: Signal<number>;
|
|
151
|
+
/** Reactive signal containing the catalog minimum price (for UI bounds) */
|
|
152
|
+
availableMinPrice: Signal<number>;
|
|
153
|
+
/** Reactive signal containing the catalog maximum price (for UI bounds) */
|
|
154
|
+
availableMaxPrice: Signal<number>;
|
|
147
155
|
/** Reactive signal containing available inventory status options */
|
|
148
156
|
availableInventoryStatuses: Signal<InventoryStatusType[]>;
|
|
149
157
|
/** Reactive signal containing selected inventory status filters */
|
|
@@ -153,9 +161,9 @@ export declare const ProductsListFiltersServiceDefinition: string & {
|
|
|
153
161
|
/** Reactive signal containing selected product option filters */
|
|
154
162
|
selectedProductOptions: Signal<Record<string, string[]>>;
|
|
155
163
|
/** Function to set the minimum price filter */
|
|
156
|
-
|
|
164
|
+
setSelectedMinPrice: (minPrice: number) => void;
|
|
157
165
|
/** Function to set the maximum price filter */
|
|
158
|
-
|
|
166
|
+
setSelectedMaxPrice: (maxPrice: number) => void;
|
|
159
167
|
/** Function to toggle an inventory status filter */
|
|
160
168
|
toggleInventoryStatus: (status: InventoryStatusType) => void;
|
|
161
169
|
/** Function to toggle a product option choice filter */
|
|
@@ -233,14 +241,19 @@ export declare const ProductsListFiltersServiceDefinition: string & {
|
|
|
233
241
|
* </div>
|
|
234
242
|
* );
|
|
235
243
|
* }
|
|
244
|
+
* }
|
|
236
245
|
* ```
|
|
237
246
|
*/
|
|
238
247
|
export declare const ProductsListFiltersService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
239
248
|
__api: {
|
|
240
|
-
/** Reactive signal containing the minimum price filter value */
|
|
241
|
-
|
|
242
|
-
/** Reactive signal containing the maximum price filter value */
|
|
243
|
-
|
|
249
|
+
/** Reactive signal containing the user's selected minimum price filter value */
|
|
250
|
+
selectedMinPrice: Signal<number>;
|
|
251
|
+
/** Reactive signal containing the user's selected maximum price filter value */
|
|
252
|
+
selectedMaxPrice: Signal<number>;
|
|
253
|
+
/** Reactive signal containing the catalog minimum price (for UI bounds) */
|
|
254
|
+
availableMinPrice: Signal<number>;
|
|
255
|
+
/** Reactive signal containing the catalog maximum price (for UI bounds) */
|
|
256
|
+
availableMaxPrice: Signal<number>;
|
|
244
257
|
/** Reactive signal containing available inventory status options */
|
|
245
258
|
availableInventoryStatuses: Signal<InventoryStatusType[]>;
|
|
246
259
|
/** Reactive signal containing selected inventory status filters */
|
|
@@ -250,9 +263,9 @@ export declare const ProductsListFiltersService: import("@wix/services-definitio
|
|
|
250
263
|
/** Reactive signal containing selected product option filters */
|
|
251
264
|
selectedProductOptions: Signal<Record<string, string[]>>;
|
|
252
265
|
/** Function to set the minimum price filter */
|
|
253
|
-
|
|
266
|
+
setSelectedMinPrice: (minPrice: number) => void;
|
|
254
267
|
/** Function to set the maximum price filter */
|
|
255
|
-
|
|
268
|
+
setSelectedMaxPrice: (maxPrice: number) => void;
|
|
256
269
|
/** Function to toggle an inventory status filter */
|
|
257
270
|
toggleInventoryStatus: (status: InventoryStatusType) => void;
|
|
258
271
|
/** Function to toggle a product option choice filter */
|
|
@@ -265,10 +278,14 @@ export declare const ProductsListFiltersService: import("@wix/services-definitio
|
|
|
265
278
|
__config: {};
|
|
266
279
|
isServiceDefinition?: boolean;
|
|
267
280
|
} & {
|
|
268
|
-
/** Reactive signal containing the minimum price filter value */
|
|
269
|
-
|
|
270
|
-
/** Reactive signal containing the maximum price filter value */
|
|
271
|
-
|
|
281
|
+
/** Reactive signal containing the user's selected minimum price filter value */
|
|
282
|
+
selectedMinPrice: Signal<number>;
|
|
283
|
+
/** Reactive signal containing the user's selected maximum price filter value */
|
|
284
|
+
selectedMaxPrice: Signal<number>;
|
|
285
|
+
/** Reactive signal containing the catalog minimum price (for UI bounds) */
|
|
286
|
+
availableMinPrice: Signal<number>;
|
|
287
|
+
/** Reactive signal containing the catalog maximum price (for UI bounds) */
|
|
288
|
+
availableMaxPrice: Signal<number>;
|
|
272
289
|
/** Reactive signal containing available inventory status options */
|
|
273
290
|
availableInventoryStatuses: Signal<InventoryStatusType[]>;
|
|
274
291
|
/** Reactive signal containing selected inventory status filters */
|
|
@@ -278,9 +295,9 @@ export declare const ProductsListFiltersService: import("@wix/services-definitio
|
|
|
278
295
|
/** Reactive signal containing selected product option filters */
|
|
279
296
|
selectedProductOptions: Signal<Record<string, string[]>>;
|
|
280
297
|
/** Function to set the minimum price filter */
|
|
281
|
-
|
|
298
|
+
setSelectedMinPrice: (minPrice: number) => void;
|
|
282
299
|
/** Function to set the maximum price filter */
|
|
283
|
-
|
|
300
|
+
setSelectedMaxPrice: (maxPrice: number) => void;
|
|
284
301
|
/** Function to toggle an inventory status filter */
|
|
285
302
|
toggleInventoryStatus: (status: InventoryStatusType) => void;
|
|
286
303
|
/** Function to toggle a product option choice filter */
|
|
@@ -4,6 +4,7 @@ import { SignalsServiceDefinition, } from "@wix/services-definitions/core-servic
|
|
|
4
4
|
import { productsV3 } from "@wix/stores";
|
|
5
5
|
import { ProductsListServiceDefinition } from "./products-list-service.js";
|
|
6
6
|
import { customizationsV3 } from "@wix/stores";
|
|
7
|
+
const PRICE_FILTER_DEBOUNCE_TIME = 300;
|
|
7
8
|
/**
|
|
8
9
|
* Enumeration of inventory status types available for filtering.
|
|
9
10
|
* Maps to the Wix Stores API inventory availability statuses.
|
|
@@ -151,6 +152,7 @@ export const ProductsListFiltersServiceDefinition = defineService("products-list
|
|
|
151
152
|
* </div>
|
|
152
153
|
* );
|
|
153
154
|
* }
|
|
155
|
+
* }
|
|
154
156
|
* ```
|
|
155
157
|
*/
|
|
156
158
|
export const ProductsListFiltersService = implementService.withConfig()(ProductsListFiltersServiceDefinition, ({ getService, config }) => {
|
|
@@ -159,115 +161,72 @@ export const ProductsListFiltersService = implementService.withConfig()(Products
|
|
|
159
161
|
const productsListService = getService(ProductsListServiceDefinition);
|
|
160
162
|
const { customizations } = config;
|
|
161
163
|
const aggregationData = productsListService.aggregations.get()?.results;
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
const
|
|
164
|
+
const currentSearchOptions = productsListService.searchOptions.get();
|
|
165
|
+
// Get the full catalog price range from initial aggregation data (before any filters)
|
|
166
|
+
const catalogPriceRange = getCatalogPriceRange(aggregationData || []);
|
|
167
|
+
// Initialize signals with user's current filter selections (or 0 if no filters)
|
|
168
|
+
const userFilterMinPriceSignal = signalsService.signal(getSelectedMinPrice(currentSearchOptions) ?? catalogPriceRange.minPrice);
|
|
169
|
+
const userFilterMaxPriceSignal = signalsService.signal(getSelectedMaxPrice(currentSearchOptions) ?? catalogPriceRange.maxPrice);
|
|
170
|
+
// Separate signals for the available catalog range (for UI bounds)
|
|
171
|
+
const catalogMinPriceSignal = signalsService.signal(catalogPriceRange.minPrice);
|
|
172
|
+
const catalogMaxPriceSignal = signalsService.signal(catalogPriceRange.maxPrice);
|
|
168
173
|
const availableInventoryStatusesSignal = signalsService.signal([
|
|
169
174
|
InventoryStatusType.IN_STOCK,
|
|
170
175
|
InventoryStatusType.OUT_OF_STOCK,
|
|
171
176
|
InventoryStatusType.PARTIALLY_OUT_OF_STOCK,
|
|
172
177
|
]);
|
|
173
|
-
const selectedInventoryStatusesSignal = signalsService.signal(getSelectedInventoryStatuses(
|
|
178
|
+
const selectedInventoryStatusesSignal = signalsService.signal(getSelectedInventoryStatuses(currentSearchOptions));
|
|
174
179
|
// TODO: Get product options from aggregations data
|
|
175
180
|
const availableProductOptionsSignal = signalsService.signal(getAvailableProductOptions(aggregationData, customizations));
|
|
176
|
-
const selectedProductOptionsSignal = signalsService.signal(getSelectedProductOptions(
|
|
181
|
+
const selectedProductOptionsSignal = signalsService.signal(getSelectedProductOptions(currentSearchOptions));
|
|
177
182
|
const isFilteredSignal = signalsService.signal(false);
|
|
183
|
+
// Debounce timeout IDs for price filters
|
|
184
|
+
let minPriceTimeoutId = null;
|
|
185
|
+
let maxPriceTimeoutId = null;
|
|
178
186
|
if (typeof window !== "undefined") {
|
|
179
187
|
signalsService.effect(() => {
|
|
180
188
|
// CRITICAL: Read the signals FIRST to establish dependencies, even on first run
|
|
181
|
-
const minPrice =
|
|
182
|
-
const maxPrice =
|
|
189
|
+
const minPrice = userFilterMinPriceSignal.get();
|
|
190
|
+
const maxPrice = userFilterMaxPriceSignal.get();
|
|
183
191
|
const selectedInventoryStatuses = selectedInventoryStatusesSignal.get();
|
|
184
192
|
const selectedProductOptions = selectedProductOptionsSignal.get();
|
|
185
193
|
if (firstRun) {
|
|
186
194
|
firstRun = false;
|
|
187
195
|
return;
|
|
188
196
|
}
|
|
189
|
-
isFilteredSignal
|
|
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);
|
|
197
|
+
doFirstRunInit(isFilteredSignal, productsListService, minPrice, maxPrice, selectedInventoryStatuses, selectedProductOptions);
|
|
257
198
|
});
|
|
258
199
|
}
|
|
259
200
|
return {
|
|
260
|
-
|
|
261
|
-
|
|
201
|
+
selectedMinPrice: userFilterMinPriceSignal,
|
|
202
|
+
selectedMaxPrice: userFilterMaxPriceSignal,
|
|
203
|
+
availableMinPrice: catalogMinPriceSignal,
|
|
204
|
+
availableMaxPrice: catalogMaxPriceSignal,
|
|
262
205
|
availableInventoryStatuses: availableInventoryStatusesSignal,
|
|
263
206
|
selectedInventoryStatuses: selectedInventoryStatusesSignal,
|
|
264
207
|
availableProductOptions: availableProductOptionsSignal,
|
|
265
208
|
selectedProductOptions: selectedProductOptionsSignal,
|
|
266
|
-
|
|
267
|
-
|
|
209
|
+
setSelectedMinPrice: (minPrice) => {
|
|
210
|
+
// Clear any existing timeout
|
|
211
|
+
if (minPriceTimeoutId) {
|
|
212
|
+
clearTimeout(minPriceTimeoutId);
|
|
213
|
+
}
|
|
214
|
+
// Set new debounced timeout
|
|
215
|
+
minPriceTimeoutId = setTimeout(() => {
|
|
216
|
+
userFilterMinPriceSignal.set(minPrice);
|
|
217
|
+
minPriceTimeoutId = null;
|
|
218
|
+
}, PRICE_FILTER_DEBOUNCE_TIME);
|
|
268
219
|
},
|
|
269
|
-
|
|
270
|
-
|
|
220
|
+
setSelectedMaxPrice: (maxPrice) => {
|
|
221
|
+
// Clear any existing timeout
|
|
222
|
+
if (maxPriceTimeoutId) {
|
|
223
|
+
clearTimeout(maxPriceTimeoutId);
|
|
224
|
+
}
|
|
225
|
+
// Set new debounced timeout
|
|
226
|
+
maxPriceTimeoutId = setTimeout(() => {
|
|
227
|
+
userFilterMaxPriceSignal.set(maxPrice);
|
|
228
|
+
maxPriceTimeoutId = null;
|
|
229
|
+
}, PRICE_FILTER_DEBOUNCE_TIME);
|
|
271
230
|
},
|
|
272
231
|
toggleInventoryStatus: (status) => {
|
|
273
232
|
const current = selectedInventoryStatusesSignal.get();
|
|
@@ -308,16 +267,100 @@ export const ProductsListFiltersService = implementService.withConfig()(Products
|
|
|
308
267
|
},
|
|
309
268
|
isFiltered: isFilteredSignal,
|
|
310
269
|
reset: () => {
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
270
|
+
// Reset user selections but keep catalog bounds intact
|
|
271
|
+
userFilterMinPriceSignal.set(catalogMinPriceSignal.get());
|
|
272
|
+
userFilterMaxPriceSignal.set(catalogMaxPriceSignal.get());
|
|
314
273
|
selectedInventoryStatusesSignal.set([]);
|
|
315
274
|
selectedProductOptionsSignal.set({});
|
|
316
275
|
isFilteredSignal.set(false);
|
|
317
276
|
},
|
|
318
277
|
};
|
|
319
278
|
});
|
|
320
|
-
function
|
|
279
|
+
function doFirstRunInit(isFilteredSignal, productsListService, minPrice, maxPrice, selectedInventoryStatuses, selectedProductOptions) {
|
|
280
|
+
isFilteredSignal.set(true);
|
|
281
|
+
// Build new search options with updated price filters
|
|
282
|
+
const newSearchOptions = {
|
|
283
|
+
...productsListService.searchOptions.peek(),
|
|
284
|
+
};
|
|
285
|
+
delete newSearchOptions.cursorPaging?.cursor;
|
|
286
|
+
// Initialize filter if it doesn't exist
|
|
287
|
+
if (!newSearchOptions.filter) {
|
|
288
|
+
newSearchOptions.filter = {};
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
// Copy existing filter to avoid mutation
|
|
292
|
+
newSearchOptions.filter = { ...newSearchOptions.filter };
|
|
293
|
+
}
|
|
294
|
+
// Remove existing price filters
|
|
295
|
+
delete newSearchOptions.filter["actualPriceRange.minValue.amount"];
|
|
296
|
+
delete newSearchOptions.filter["actualPriceRange.maxValue.amount"];
|
|
297
|
+
// Remove existing inventory filter
|
|
298
|
+
delete newSearchOptions.filter["inventory.availabilityStatus"];
|
|
299
|
+
// Remove existing product option filters
|
|
300
|
+
// First, find and remove any existing option filters
|
|
301
|
+
Object.keys(newSearchOptions.filter).forEach((key) => {
|
|
302
|
+
if (key.startsWith("options.")) {
|
|
303
|
+
delete newSearchOptions.filter[key];
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
// Add new price filters if they have valid values
|
|
307
|
+
if (minPrice > 0) {
|
|
308
|
+
newSearchOptions.filter["actualPriceRange.minValue.amount"] = {
|
|
309
|
+
$gte: minPrice,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
if (maxPrice > 0) {
|
|
313
|
+
newSearchOptions.filter["actualPriceRange.maxValue.amount"] = {
|
|
314
|
+
$lte: maxPrice,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
// Add new inventory filter if there are selected statuses
|
|
318
|
+
if (selectedInventoryStatuses.length > 0) {
|
|
319
|
+
if (selectedInventoryStatuses.length === 1) {
|
|
320
|
+
newSearchOptions.filter["inventory.availabilityStatus"] =
|
|
321
|
+
selectedInventoryStatuses[0];
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
newSearchOptions.filter["inventory.availabilityStatus"] = {
|
|
325
|
+
$in: selectedInventoryStatuses,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Add new product option filters if there are selected options
|
|
330
|
+
if (selectedProductOptions &&
|
|
331
|
+
Object.keys(selectedProductOptions).length > 0) {
|
|
332
|
+
for (const [optionId, choiceIds] of Object.entries(selectedProductOptions)) {
|
|
333
|
+
if (choiceIds && choiceIds.length > 0) {
|
|
334
|
+
// Handle inventory filter separately
|
|
335
|
+
if (optionId === "inventory-filter") {
|
|
336
|
+
newSearchOptions.filter["inventory.availabilityStatus"] = {
|
|
337
|
+
$in: choiceIds,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Regular product options filter
|
|
342
|
+
newSearchOptions.filter["options.choicesSettings.choices.choiceId"] = {
|
|
343
|
+
$hasSome: choiceIds,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Use callback to update search options
|
|
350
|
+
productsListService.setSearchOptions(newSearchOptions);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Gets the full catalog price range from aggregation data (before any filters applied)
|
|
354
|
+
*/
|
|
355
|
+
function getCatalogPriceRange(aggregationData) {
|
|
356
|
+
const minPrice = getMinPrice(aggregationData);
|
|
357
|
+
const maxPrice = getMaxPrice(aggregationData);
|
|
358
|
+
return { minPrice, maxPrice };
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Gets the user's currently selected minimum price filter from search options
|
|
362
|
+
*/
|
|
363
|
+
function getSelectedMinPrice(searchOptions) {
|
|
321
364
|
const filter = searchOptions.filter;
|
|
322
365
|
if (!filter)
|
|
323
366
|
return 0;
|
|
@@ -325,11 +368,13 @@ function getMinPrice(searchOptions) {
|
|
|
325
368
|
if (typeof minPriceFilter === "object" &&
|
|
326
369
|
minPriceFilter !== null &&
|
|
327
370
|
"$gte" in minPriceFilter) {
|
|
328
|
-
return Number(minPriceFilter.$gte)
|
|
371
|
+
return Number(minPriceFilter.$gte);
|
|
329
372
|
}
|
|
330
|
-
return 0;
|
|
331
373
|
}
|
|
332
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Gets the user's currently selected maximum price filter from search options
|
|
376
|
+
*/
|
|
377
|
+
function getSelectedMaxPrice(searchOptions) {
|
|
333
378
|
const filter = searchOptions.filter;
|
|
334
379
|
if (!filter)
|
|
335
380
|
return 0;
|
|
@@ -337,7 +382,20 @@ function getMaxPrice(searchOptions) {
|
|
|
337
382
|
if (typeof maxPriceFilter === "object" &&
|
|
338
383
|
maxPriceFilter !== null &&
|
|
339
384
|
"$lte" in maxPriceFilter) {
|
|
340
|
-
return Number(maxPriceFilter.$lte)
|
|
385
|
+
return Number(maxPriceFilter.$lte);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function getMinPrice(aggregationData) {
|
|
389
|
+
const minPriceAggregation = aggregationData.find((data) => data.fieldPath === "actualPriceRange.minValue.amount");
|
|
390
|
+
if (minPriceAggregation?.scalar?.value) {
|
|
391
|
+
return Number(minPriceAggregation.scalar.value) || 0;
|
|
392
|
+
}
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
|
+
function getMaxPrice(aggregationData) {
|
|
396
|
+
const maxPriceAggregation = aggregationData.find((data) => data.fieldPath === "actualPriceRange.maxValue.amount");
|
|
397
|
+
if (maxPriceAggregation?.scalar?.value) {
|
|
398
|
+
return Number(maxPriceAggregation.scalar.value) || 0;
|
|
341
399
|
}
|
|
342
400
|
return 0;
|
|
343
401
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineService, implementService } from "@wix/services-definitions";
|
|
2
2
|
import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
|
|
3
|
-
import { productsV3 } from "@wix/stores";
|
|
3
|
+
import { productsV3, readOnlyVariantsV3 } from "@wix/stores";
|
|
4
4
|
/**
|
|
5
5
|
* Loads products list service configuration from the Wix Stores API for SSR initialization.
|
|
6
6
|
* This function is designed to be used during Server-Side Rendering (SSR) to preload
|
|
@@ -83,7 +83,7 @@ import { productsV3 } from "@wix/stores";
|
|
|
83
83
|
* ```
|
|
84
84
|
*/
|
|
85
85
|
export async function loadProductsListServiceConfig(searchOptions) {
|
|
86
|
-
const result = await
|
|
86
|
+
const result = await fetchProducts(searchOptions);
|
|
87
87
|
return {
|
|
88
88
|
products: result.products ?? [],
|
|
89
89
|
searchOptions,
|
|
@@ -91,6 +91,90 @@ export async function loadProductsListServiceConfig(searchOptions) {
|
|
|
91
91
|
aggregations: result.aggregationData,
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Fetches products and their missing variants in one optimized request.
|
|
96
|
+
* This function wraps the standard searchProducts call and automatically
|
|
97
|
+
* fetches missing variant data for all products that need it.
|
|
98
|
+
*
|
|
99
|
+
* @param searchOptions - The search options for querying products
|
|
100
|
+
* @returns Promise that resolves to the search result with complete variant data
|
|
101
|
+
*/
|
|
102
|
+
const fetchProducts = async (searchOptions) => {
|
|
103
|
+
const result = await productsV3.searchProducts(searchOptions);
|
|
104
|
+
// Fetch missing variants for all products in one batch request
|
|
105
|
+
if (result.products) {
|
|
106
|
+
result.products = await fetchMissingVariants(result.products);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Fetches missing variants for all products in one batch request.
|
|
112
|
+
* This function identifies products that need variant data and fetches
|
|
113
|
+
* all variants efficiently using the readOnlyVariantsV3 API.
|
|
114
|
+
*
|
|
115
|
+
* @param products - Array of products that may need variant data
|
|
116
|
+
* @returns Promise that resolves to products with complete variant information
|
|
117
|
+
*/
|
|
118
|
+
const fetchMissingVariants = async (products) => {
|
|
119
|
+
// Find products that need variants (both single and multi-variant products)
|
|
120
|
+
const productsNeedingVariants = products.filter((product) => !product.variantsInfo?.variants &&
|
|
121
|
+
product.variantSummary?.variantCount &&
|
|
122
|
+
product.variantSummary.variantCount > 0);
|
|
123
|
+
if (productsNeedingVariants.length === 0) {
|
|
124
|
+
return products;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const productIds = productsNeedingVariants
|
|
128
|
+
.map((p) => p._id)
|
|
129
|
+
.filter(Boolean);
|
|
130
|
+
if (productIds.length === 0) {
|
|
131
|
+
return products;
|
|
132
|
+
}
|
|
133
|
+
const items = [];
|
|
134
|
+
const res = await readOnlyVariantsV3
|
|
135
|
+
.queryVariants({})
|
|
136
|
+
.in("productData.productId", productIds)
|
|
137
|
+
.limit(100)
|
|
138
|
+
.find();
|
|
139
|
+
items.push(...res.items);
|
|
140
|
+
let nextRes = res;
|
|
141
|
+
while (nextRes.hasNext()) {
|
|
142
|
+
nextRes = await nextRes.next();
|
|
143
|
+
items.push(...nextRes.items);
|
|
144
|
+
}
|
|
145
|
+
const variantsByProductId = new Map();
|
|
146
|
+
items.forEach((item) => {
|
|
147
|
+
const productId = item.productData?.productId;
|
|
148
|
+
if (productId) {
|
|
149
|
+
if (!variantsByProductId.has(productId)) {
|
|
150
|
+
variantsByProductId.set(productId, []);
|
|
151
|
+
}
|
|
152
|
+
variantsByProductId.get(productId).push({
|
|
153
|
+
...item,
|
|
154
|
+
choices: item.optionChoices,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// Update products with their variants
|
|
159
|
+
return products.map((product) => {
|
|
160
|
+
const variants = variantsByProductId.get(product._id || "");
|
|
161
|
+
if (variants && variants.length > 0) {
|
|
162
|
+
return {
|
|
163
|
+
...product,
|
|
164
|
+
variantsInfo: {
|
|
165
|
+
...product.variantsInfo,
|
|
166
|
+
variants,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return product;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
console.error("Failed to fetch missing variants:", error);
|
|
175
|
+
return products;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
94
178
|
/**
|
|
95
179
|
* Service definition for the Products List service.
|
|
96
180
|
* This defines the reactive API contract for managing a list of products with search, pagination, and filtering capabilities.
|
|
@@ -179,7 +263,7 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
|
|
|
179
263
|
},
|
|
180
264
|
}
|
|
181
265
|
: searchOptions;
|
|
182
|
-
const result = await
|
|
266
|
+
const result = await fetchProducts(affectiveSearchOptions);
|
|
183
267
|
productsSignal.set(result.products ?? []);
|
|
184
268
|
pagingMetadataSignal.set(result.pagingMetadata);
|
|
185
269
|
}
|
package/dist/react/Category.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { PropsWithChildren, ReactNode } from "react";
|
|
2
1
|
import { type CategoryServiceConfig } from "../services/category-service.js";
|
|
2
|
+
export interface RootProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
categoryServiceConfig: CategoryServiceConfig;
|
|
5
|
+
}
|
|
3
6
|
/**
|
|
4
7
|
* Root component that provides the Category service context to its children.
|
|
5
8
|
* This component sets up the necessary services for managing category state.
|
|
@@ -24,15 +27,13 @@ import { type CategoryServiceConfig } from "../services/category-service.js";
|
|
|
24
27
|
* }
|
|
25
28
|
* ```
|
|
26
29
|
*/
|
|
27
|
-
export declare function Root(props:
|
|
28
|
-
categoryServiceConfig: CategoryServiceConfig;
|
|
29
|
-
}>): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
export declare function Root(props: RootProps): React.ReactNode;
|
|
30
31
|
/**
|
|
31
32
|
* Props for Name headless component
|
|
32
33
|
*/
|
|
33
34
|
export interface NameProps {
|
|
34
35
|
/** Content to display (can be a render function receiving name data or ReactNode) */
|
|
35
|
-
children: ((props: NameRenderProps) => ReactNode) | ReactNode;
|
|
36
|
+
children: ((props: NameRenderProps) => React.ReactNode) | React.ReactNode;
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
39
|
* Render props for Name component
|
|
@@ -60,13 +61,13 @@ export interface NameRenderProps {
|
|
|
60
61
|
* }
|
|
61
62
|
* ```
|
|
62
63
|
*/
|
|
63
|
-
export declare function Name(props: NameProps): ReactNode;
|
|
64
|
+
export declare function Name(props: NameProps): import("react").ReactNode;
|
|
64
65
|
/**
|
|
65
66
|
* Props for Slug headless component
|
|
66
67
|
*/
|
|
67
68
|
export interface SlugProps {
|
|
68
69
|
/** Content to display (can be a render function receiving slug data or ReactNode) */
|
|
69
|
-
children: ((props: SlugRenderProps) => ReactNode) | ReactNode;
|
|
70
|
+
children: ((props: SlugRenderProps) => React.ReactNode) | React.ReactNode;
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
72
73
|
* Render props for Slug component
|
|
@@ -94,4 +95,4 @@ export interface SlugRenderProps {
|
|
|
94
95
|
* }
|
|
95
96
|
* ```
|
|
96
97
|
*/
|
|
97
|
-
export declare function Slug(props: SlugProps): ReactNode;
|
|
98
|
+
export declare function Slug(props: SlugProps): import("react").ReactNode;
|