@jay-framework/wix-stores 0.15.4 → 0.15.5
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/dist/actions/get-variant-stock.jay-action +7 -0
- package/dist/actions/search-products.jay-action +12 -0
- package/dist/contracts/product-search.jay-contract +63 -1
- package/dist/contracts/product-search.jay-contract.d.ts +35 -1
- package/dist/index.client.js +131 -38
- package/dist/index.d.ts +91 -77
- package/dist/index.js +189 -24
- package/package.json +18 -16
- package/plugin.yaml +2 -0
|
@@ -11,6 +11,9 @@ inputSchema:
|
|
|
11
11
|
minPrice?: number
|
|
12
12
|
maxPrice?: number
|
|
13
13
|
categoryIds?: string[]
|
|
14
|
+
optionFilters?:
|
|
15
|
+
- optionName: string
|
|
16
|
+
choiceNames: string[]
|
|
14
17
|
sortBy?: enum(relevance | price_asc | price_desc | name_asc | name_desc | newest)
|
|
15
18
|
cursor?: string
|
|
16
19
|
pageSize?: number
|
|
@@ -31,3 +34,12 @@ outputSchema:
|
|
|
31
34
|
maxValue?: number
|
|
32
35
|
productCount: number
|
|
33
36
|
isSelected: boolean
|
|
37
|
+
optionFilters?:
|
|
38
|
+
- optionId: string
|
|
39
|
+
optionName: string
|
|
40
|
+
optionRenderType: enum(TEXT_CHOICES | SWATCH_CHOICES)
|
|
41
|
+
choices:
|
|
42
|
+
- choiceId: string
|
|
43
|
+
choiceName: string
|
|
44
|
+
colorCode: string
|
|
45
|
+
productCount: number
|
|
@@ -180,7 +180,69 @@ tags:
|
|
|
180
180
|
dataType: boolean
|
|
181
181
|
elementType: HTMLInputElement
|
|
182
182
|
description: Show only in-stock products checkbox
|
|
183
|
-
|
|
183
|
+
|
|
184
|
+
# Option-based filters (e.g., Color, Size)
|
|
185
|
+
- tag: optionFilters
|
|
186
|
+
type: sub-contract
|
|
187
|
+
repeated: true
|
|
188
|
+
trackBy: optionId
|
|
189
|
+
phase: fast+interactive
|
|
190
|
+
description: Filter by product options (e.g., Color, Size)
|
|
191
|
+
tags:
|
|
192
|
+
- tag: optionId
|
|
193
|
+
type: data
|
|
194
|
+
dataType: string
|
|
195
|
+
description: Customization ID
|
|
196
|
+
|
|
197
|
+
- tag: optionName
|
|
198
|
+
type: data
|
|
199
|
+
dataType: string
|
|
200
|
+
description: Option display name (e.g., Color, Size)
|
|
201
|
+
|
|
202
|
+
- tag: optionRenderType
|
|
203
|
+
type: variant
|
|
204
|
+
dataType: enum (TEXT_CHOICES | SWATCH_CHOICES)
|
|
205
|
+
description: How to render the option choices
|
|
206
|
+
|
|
207
|
+
- tag: choices
|
|
208
|
+
type: sub-contract
|
|
209
|
+
repeated: true
|
|
210
|
+
trackBy: choiceId
|
|
211
|
+
description: Available choices for this option
|
|
212
|
+
tags:
|
|
213
|
+
- tag: choiceId
|
|
214
|
+
type: data
|
|
215
|
+
dataType: string
|
|
216
|
+
description: Choice ID
|
|
217
|
+
|
|
218
|
+
- tag: choiceName
|
|
219
|
+
type: data
|
|
220
|
+
dataType: string
|
|
221
|
+
description: Choice display name (e.g., Red, Large)
|
|
222
|
+
|
|
223
|
+
- tag: colorCode
|
|
224
|
+
type: data
|
|
225
|
+
dataType: string
|
|
226
|
+
description: HEX color code (for swatch rendering)
|
|
227
|
+
|
|
228
|
+
- tag: productCount
|
|
229
|
+
type: data
|
|
230
|
+
dataType: number
|
|
231
|
+
phase: fast+interactive
|
|
232
|
+
description: Number of products with this choice in current results
|
|
233
|
+
|
|
234
|
+
- tag: isSelected
|
|
235
|
+
type: [data, interactive]
|
|
236
|
+
dataType: boolean
|
|
237
|
+
elementType: HTMLInputElement
|
|
238
|
+
description: Checkbox to toggle this choice filter
|
|
239
|
+
|
|
240
|
+
- tag: isDisabled
|
|
241
|
+
type: data
|
|
242
|
+
dataType: boolean
|
|
243
|
+
phase: fast+interactive
|
|
244
|
+
description: Whether this choice has no matching products (count=0)
|
|
245
|
+
|
|
184
246
|
- tag: clearFilters
|
|
185
247
|
type: interactive
|
|
186
248
|
elementType: HTMLButtonElement
|
|
@@ -30,10 +30,32 @@ export interface CategoryFilterOfFilterOfProductSearchViewState {
|
|
|
30
30
|
categories: Array<CategoryOfCategoryFilterOfFilterOfProductSearchViewState>
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export enum OptionRenderType {
|
|
34
|
+
TEXT_CHOICES,
|
|
35
|
+
SWATCH_CHOICES
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ChoiceOfOptionFilterOfFilterOfProductSearchViewState {
|
|
39
|
+
choiceId: string,
|
|
40
|
+
choiceName: string,
|
|
41
|
+
colorCode: string,
|
|
42
|
+
productCount: number,
|
|
43
|
+
isSelected: boolean,
|
|
44
|
+
isDisabled: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface OptionFilterOfFilterOfProductSearchViewState {
|
|
48
|
+
optionId: string,
|
|
49
|
+
optionName: string,
|
|
50
|
+
optionRenderType: OptionRenderType,
|
|
51
|
+
choices: Array<ChoiceOfOptionFilterOfFilterOfProductSearchViewState>
|
|
52
|
+
}
|
|
53
|
+
|
|
33
54
|
export interface FilterOfProductSearchViewState {
|
|
34
55
|
priceRange: PriceRangeOfFilterOfProductSearchViewState,
|
|
35
56
|
categoryFilter: CategoryFilterOfFilterOfProductSearchViewState,
|
|
36
|
-
inStockOnly: boolean
|
|
57
|
+
inStockOnly: boolean,
|
|
58
|
+
optionFilters: Array<OptionFilterOfFilterOfProductSearchViewState>
|
|
37
59
|
}
|
|
38
60
|
|
|
39
61
|
export enum CurrentSort {
|
|
@@ -141,6 +163,7 @@ export type ProductSearchFastViewState = Pick<ProductSearchViewState, 'searchExp
|
|
|
141
163
|
categoryFilter: {
|
|
142
164
|
categories: Array<Pick<ProductSearchViewState['filters']['categoryFilter']['categories'][number], 'categoryId' | 'isSelected'>>;
|
|
143
165
|
};
|
|
166
|
+
optionFilters: Array<ProductSearchViewState['filters']['optionFilters'][number]>;
|
|
144
167
|
};
|
|
145
168
|
sortBy: ProductSearchViewState['sortBy'];
|
|
146
169
|
suggestions: Array<ProductSearchViewState['suggestions'][number]>;
|
|
@@ -153,6 +176,7 @@ export type ProductSearchInteractiveViewState = Pick<ProductSearchViewState, 'se
|
|
|
153
176
|
categoryFilter: {
|
|
154
177
|
categories: Array<Pick<ProductSearchViewState['filters']['categoryFilter']['categories'][number], 'categoryId' | 'isSelected'>>;
|
|
155
178
|
};
|
|
179
|
+
optionFilters: Array<ProductSearchViewState['filters']['optionFilters'][number]>;
|
|
156
180
|
};
|
|
157
181
|
sortBy: ProductSearchViewState['sortBy'];
|
|
158
182
|
suggestions: Array<ProductSearchViewState['suggestions'][number]>;
|
|
@@ -179,6 +203,11 @@ export interface ProductSearchRefs {
|
|
|
179
203
|
categories: {
|
|
180
204
|
isSelected: HTMLElementCollectionProxy<CategoryOfCategoryFilterOfFilterOfProductSearchViewState, HTMLInputElement>
|
|
181
205
|
}
|
|
206
|
+
},
|
|
207
|
+
optionFilters: {
|
|
208
|
+
choices: {
|
|
209
|
+
isSelected: HTMLElementCollectionProxy<ChoiceOfOptionFilterOfFilterOfProductSearchViewState, HTMLInputElement>
|
|
210
|
+
}
|
|
182
211
|
}
|
|
183
212
|
},
|
|
184
213
|
sortBy: {
|
|
@@ -210,6 +239,11 @@ export interface ProductSearchRepeatedRefs {
|
|
|
210
239
|
categories: {
|
|
211
240
|
isSelected: HTMLElementCollectionProxy<CategoryOfCategoryFilterOfFilterOfProductSearchViewState, HTMLInputElement>
|
|
212
241
|
}
|
|
242
|
+
},
|
|
243
|
+
optionFilters: {
|
|
244
|
+
choices: {
|
|
245
|
+
isSelected: HTMLElementCollectionProxy<ChoiceOfOptionFilterOfFilterOfProductSearchViewState, HTMLInputElement>
|
|
246
|
+
}
|
|
213
247
|
}
|
|
214
248
|
},
|
|
215
249
|
sortBy: {
|
package/dist/index.client.js
CHANGED
|
@@ -313,6 +313,11 @@ function ProductPageInteractive(props, refs, viewStateSignals, fastCarryForward,
|
|
|
313
313
|
};
|
|
314
314
|
}
|
|
315
315
|
const productPage = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(ProductPageInteractive);
|
|
316
|
+
var OptionRenderType = /* @__PURE__ */ ((OptionRenderType2) => {
|
|
317
|
+
OptionRenderType2[OptionRenderType2["TEXT_CHOICES"] = 0] = "TEXT_CHOICES";
|
|
318
|
+
OptionRenderType2[OptionRenderType2["SWATCH_CHOICES"] = 1] = "SWATCH_CHOICES";
|
|
319
|
+
return OptionRenderType2;
|
|
320
|
+
})(OptionRenderType || {});
|
|
316
321
|
var CurrentSort = /* @__PURE__ */ ((CurrentSort2) => {
|
|
317
322
|
CurrentSort2[CurrentSort2["relevance"] = 0] = "relevance";
|
|
318
323
|
CurrentSort2[CurrentSort2["priceAsc"] = 1] = "priceAsc";
|
|
@@ -369,6 +374,15 @@ function updateUrlFilters(searchTerm, filters, sort, categories) {
|
|
|
369
374
|
}
|
|
370
375
|
if (filters.inStockOnly)
|
|
371
376
|
params.set("inStock", "1");
|
|
377
|
+
const optSegments = [];
|
|
378
|
+
for (const opt of filters.optionFilters || []) {
|
|
379
|
+
const selected = opt.choices.filter((c) => c.isSelected).map((c) => encodeURIComponent(c.choiceName));
|
|
380
|
+
if (selected.length > 0) {
|
|
381
|
+
optSegments.push(`${encodeURIComponent(opt.optionName)}:${selected.join(",")}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (optSegments.length > 0)
|
|
385
|
+
params.set("opt", optSegments.join(";"));
|
|
372
386
|
if (sort !== CurrentSort.relevance) {
|
|
373
387
|
const sortNames = {
|
|
374
388
|
[CurrentSort.priceAsc]: "priceAsc",
|
|
@@ -384,6 +398,30 @@ function updateUrlFilters(searchTerm, filters, sort, categories) {
|
|
|
384
398
|
const query = params.toString();
|
|
385
399
|
window.history.replaceState(null, "", query ? `?${query}` : window.location.pathname);
|
|
386
400
|
}
|
|
401
|
+
function buildOptionFiltersViewState(baseOptionFilters, filteredResult, optionSelections) {
|
|
402
|
+
const filteredChoiceCounts = /* @__PURE__ */ new Map();
|
|
403
|
+
for (const opt of filteredResult.optionFilters || []) {
|
|
404
|
+
for (const ch of opt.choices) {
|
|
405
|
+
filteredChoiceCounts.set(ch.choiceName.toLowerCase(), ch.productCount);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return baseOptionFilters.map((opt) => ({
|
|
409
|
+
optionId: opt.optionId,
|
|
410
|
+
optionName: opt.optionName,
|
|
411
|
+
optionRenderType: opt.optionRenderType === "SWATCH_CHOICES" ? OptionRenderType.SWATCH_CHOICES : OptionRenderType.TEXT_CHOICES,
|
|
412
|
+
choices: opt.choices.map((ch) => {
|
|
413
|
+
const count = filteredChoiceCounts.get(ch.choiceName.toLowerCase()) ?? 0;
|
|
414
|
+
return {
|
|
415
|
+
choiceId: ch.choiceId,
|
|
416
|
+
choiceName: ch.choiceName,
|
|
417
|
+
colorCode: ch.colorCode,
|
|
418
|
+
productCount: count,
|
|
419
|
+
isSelected: optionSelections.get(opt.optionName)?.has(ch.choiceName) ?? false,
|
|
420
|
+
isDisabled: count === 0
|
|
421
|
+
};
|
|
422
|
+
})
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
387
425
|
function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForward, storesContext) {
|
|
388
426
|
const baseCategoryId = fastCarryForward.baseCategoryId;
|
|
389
427
|
const variantStockCache = {};
|
|
@@ -394,19 +432,41 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
394
432
|
let debounceTimeout = null;
|
|
395
433
|
let searchVersion = 0;
|
|
396
434
|
const DEBOUNCE_MS = 300;
|
|
435
|
+
const [latestSearchResult, setLatestSearchResult] = createSignal(null);
|
|
436
|
+
const mergedFilters = createMemo(() => {
|
|
437
|
+
const f = filters();
|
|
438
|
+
const result = latestSearchResult();
|
|
439
|
+
if (!result)
|
|
440
|
+
return f;
|
|
441
|
+
const selections = /* @__PURE__ */ new Map();
|
|
442
|
+
for (const opt of f.optionFilters || []) {
|
|
443
|
+
const selected = opt.choices.filter((c) => c.isSelected).map((c) => c.choiceName);
|
|
444
|
+
if (selected.length > 0)
|
|
445
|
+
selections.set(opt.optionName, new Set(selected));
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
...f,
|
|
449
|
+
optionFilters: buildOptionFiltersViewState(fastCarryForward.baseOptionFilters, result, selections)
|
|
450
|
+
};
|
|
451
|
+
});
|
|
397
452
|
const performSearch = async (version, searchTerm, currentFilters, currentSort) => {
|
|
398
453
|
setIsSearching(true);
|
|
399
454
|
setHasSearched(true);
|
|
400
455
|
try {
|
|
401
456
|
const userSelectedCategoryIds = currentFilters.categoryFilter.categories.filter((c) => c.isSelected).map((c) => c.categoryId);
|
|
402
457
|
const categoryIds = baseCategoryId ? [baseCategoryId, ...userSelectedCategoryIds] : userSelectedCategoryIds;
|
|
458
|
+
const activeOptionFilters = (currentFilters.optionFilters || []).map((opt) => ({
|
|
459
|
+
optionName: opt.optionName,
|
|
460
|
+
choiceNames: opt.choices.filter((c) => c.isSelected).map((c) => c.choiceName)
|
|
461
|
+
})).filter((o) => o.choiceNames.length > 0);
|
|
403
462
|
const result = await searchProducts({
|
|
404
463
|
query: searchTerm || "",
|
|
405
464
|
filters: {
|
|
406
465
|
minPrice: currentFilters.priceRange.minPrice || void 0,
|
|
407
466
|
maxPrice: currentFilters.priceRange.maxPrice || void 0,
|
|
408
467
|
categoryIds,
|
|
409
|
-
inStockOnly: currentFilters.inStockOnly
|
|
468
|
+
inStockOnly: currentFilters.inStockOnly,
|
|
469
|
+
optionFilters: activeOptionFilters.length > 0 ? activeOptionFilters : void 0
|
|
410
470
|
},
|
|
411
471
|
sortBy: mapSortToAction(currentSort),
|
|
412
472
|
// No cursor = start from beginning
|
|
@@ -421,6 +481,7 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
421
481
|
setLoadedCount(result.products.length);
|
|
422
482
|
setHasMore(result.hasMore);
|
|
423
483
|
setHasResults(result.products.length > 0);
|
|
484
|
+
setLatestSearchResult(result);
|
|
424
485
|
currentCursor = result.nextCursor;
|
|
425
486
|
updateUrlFilters(searchTerm, currentFilters, currentSort, fastCarryForward.categories);
|
|
426
487
|
} catch (error) {
|
|
@@ -443,13 +504,18 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
443
504
|
const searchTerm = submittedSearchTerm();
|
|
444
505
|
const userSelectedCategoryIds = currentFilters.categoryFilter.categories.filter((c) => c.isSelected).map((c) => c.categoryId);
|
|
445
506
|
const categoryIds = baseCategoryId ? [baseCategoryId, ...userSelectedCategoryIds] : userSelectedCategoryIds;
|
|
507
|
+
const activeOptionFilters = (currentFilters.optionFilters || []).map((opt) => ({
|
|
508
|
+
optionName: opt.optionName,
|
|
509
|
+
choiceNames: opt.choices.filter((c) => c.isSelected).map((c) => c.choiceName)
|
|
510
|
+
})).filter((o) => o.choiceNames.length > 0);
|
|
446
511
|
const result = await searchProducts({
|
|
447
512
|
query: searchTerm || "",
|
|
448
513
|
filters: {
|
|
449
514
|
minPrice: currentFilters.priceRange.minPrice || void 0,
|
|
450
515
|
maxPrice: currentFilters.priceRange.maxPrice || void 0,
|
|
451
516
|
categoryIds,
|
|
452
|
-
inStockOnly: currentFilters.inStockOnly
|
|
517
|
+
inStockOnly: currentFilters.inStockOnly,
|
|
518
|
+
optionFilters: activeOptionFilters.length > 0 ? activeOptionFilters : void 0
|
|
453
519
|
},
|
|
454
520
|
sortBy: mapSortToAction(currentSort),
|
|
455
521
|
cursor: currentCursor,
|
|
@@ -564,6 +630,24 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
564
630
|
const isChecked = event.target.checked;
|
|
565
631
|
setFilters(patch(filters(), [{ op: REPLACE, path: ["inStockOnly"], value: isChecked }]));
|
|
566
632
|
});
|
|
633
|
+
refs.filters.optionFilters.choices.isSelected.oninput(({ event, coordinate }) => {
|
|
634
|
+
const [optionId, choiceId] = coordinate;
|
|
635
|
+
const currentFilters = filters();
|
|
636
|
+
const optionIndex = currentFilters.optionFilters.findIndex((o) => o.optionId === optionId);
|
|
637
|
+
if (optionIndex === -1)
|
|
638
|
+
return;
|
|
639
|
+
const choiceIndex = currentFilters.optionFilters[optionIndex].choices.findIndex((c) => c.choiceId === choiceId);
|
|
640
|
+
if (choiceIndex === -1)
|
|
641
|
+
return;
|
|
642
|
+
const isChecked = event.target.checked;
|
|
643
|
+
setFilters(patch(currentFilters, [
|
|
644
|
+
{
|
|
645
|
+
op: REPLACE,
|
|
646
|
+
path: ["optionFilters", optionIndex, "choices", choiceIndex, "isSelected"],
|
|
647
|
+
value: isChecked
|
|
648
|
+
}
|
|
649
|
+
]));
|
|
650
|
+
});
|
|
567
651
|
refs.filters.clearFilters.onclick(() => {
|
|
568
652
|
const currentFilters = filters();
|
|
569
653
|
const clearedCategories = currentFilters.categoryFilter.categories.map((cat) => ({
|
|
@@ -575,6 +659,10 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
575
659
|
isSelected: i === 0
|
|
576
660
|
// First one is "Show all"
|
|
577
661
|
}));
|
|
662
|
+
const clearedOptionFilters = (currentFilters.optionFilters || []).map((opt) => ({
|
|
663
|
+
...opt,
|
|
664
|
+
choices: opt.choices.map((ch) => ({ ...ch, isSelected: false }))
|
|
665
|
+
}));
|
|
578
666
|
setFilters({
|
|
579
667
|
priceRange: {
|
|
580
668
|
minPrice: 0,
|
|
@@ -584,7 +672,8 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
584
672
|
ranges: clearedRanges
|
|
585
673
|
},
|
|
586
674
|
categoryFilter: { categories: clearedCategories },
|
|
587
|
-
inStockOnly: false
|
|
675
|
+
inStockOnly: false,
|
|
676
|
+
optionFilters: clearedOptionFilters
|
|
588
677
|
});
|
|
589
678
|
});
|
|
590
679
|
refs.loadMoreButton.onclick(() => {
|
|
@@ -617,48 +706,48 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
617
706
|
]));
|
|
618
707
|
}
|
|
619
708
|
});
|
|
709
|
+
const variantStockLoading = /* @__PURE__ */ new Set();
|
|
620
710
|
const loadVariantStock = async (productId) => {
|
|
621
|
-
if (variantStockCache[productId])
|
|
622
|
-
return;
|
|
623
|
-
const stockMap = await getVariantStock({ productId });
|
|
624
|
-
variantStockCache[productId] = stockMap;
|
|
625
|
-
const currentResults = searchResults();
|
|
626
|
-
const productIndex = currentResults.findIndex((p) => p._id === productId);
|
|
627
|
-
if (productIndex === -1)
|
|
628
|
-
return;
|
|
629
|
-
const product = currentResults[productIndex];
|
|
630
|
-
if (product.quickAddType !== QuickAddType.COLOR_AND_TEXT_OPTIONS)
|
|
711
|
+
if (variantStockCache[productId] || variantStockLoading.has(productId))
|
|
631
712
|
return;
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
713
|
+
variantStockLoading.add(productId);
|
|
714
|
+
try {
|
|
715
|
+
const currentResults = searchResults();
|
|
716
|
+
const productIndex = currentResults.findIndex((p) => p._id === productId);
|
|
717
|
+
if (productIndex === -1)
|
|
718
|
+
return;
|
|
719
|
+
const product = currentResults[productIndex];
|
|
720
|
+
if (product?.quickAddType !== QuickAddType.COLOR_AND_TEXT_OPTIONS)
|
|
721
|
+
return;
|
|
722
|
+
const stockMap = await getVariantStock({ productId });
|
|
723
|
+
variantStockCache[productId] = stockMap;
|
|
724
|
+
const selectedColor = product.quickOption?.choices?.find((c) => c.isSelected);
|
|
725
|
+
const textChoices = product.secondQuickOption?.choices;
|
|
726
|
+
if (!selectedColor || !textChoices)
|
|
727
|
+
return;
|
|
728
|
+
const colorStock = stockMap[selectedColor.choiceId];
|
|
729
|
+
const updatedTextChoices = textChoices.map((c) => ({
|
|
730
|
+
...c,
|
|
731
|
+
inStock: colorStock?.[c.choiceId] ?? false
|
|
732
|
+
}));
|
|
733
|
+
setSearchResults(patch(searchResults(), [
|
|
734
|
+
{
|
|
735
|
+
op: REPLACE,
|
|
736
|
+
path: [productIndex, "secondQuickOption", "choices"],
|
|
737
|
+
value: updatedTextChoices
|
|
738
|
+
}
|
|
739
|
+
]));
|
|
740
|
+
} finally {
|
|
741
|
+
variantStockLoading.delete(productId);
|
|
742
|
+
}
|
|
648
743
|
};
|
|
649
744
|
refs.searchResults.productLink.onmouseenter(({ coordinate }) => {
|
|
650
745
|
const [productId] = coordinate;
|
|
651
|
-
|
|
652
|
-
if (product?.quickAddType === QuickAddType.COLOR_AND_TEXT_OPTIONS) {
|
|
653
|
-
loadVariantStock(productId);
|
|
654
|
-
}
|
|
746
|
+
loadVariantStock(productId);
|
|
655
747
|
});
|
|
656
748
|
refs.searchResults.quickOption.choices.choiceButton.onmouseenter(({ coordinate }) => {
|
|
657
749
|
const [productId] = coordinate;
|
|
658
|
-
|
|
659
|
-
if (product?.quickAddType === QuickAddType.COLOR_AND_TEXT_OPTIONS) {
|
|
660
|
-
loadVariantStock(productId);
|
|
661
|
-
}
|
|
750
|
+
loadVariantStock(productId);
|
|
662
751
|
});
|
|
663
752
|
refs.searchResults.quickOption.choices.choiceButton.onclick(async ({ coordinate }) => {
|
|
664
753
|
const [productId, choiceId] = coordinate;
|
|
@@ -763,6 +852,10 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
763
852
|
]));
|
|
764
853
|
}
|
|
765
854
|
});
|
|
855
|
+
refs.searchResults.secondQuickOption.choices.choiceButton.onmouseenter(({ coordinate }) => {
|
|
856
|
+
const [productId] = coordinate;
|
|
857
|
+
loadVariantStock(productId);
|
|
858
|
+
});
|
|
766
859
|
refs.searchResults.viewOptionsButton.onclick(({ coordinate }) => {
|
|
767
860
|
const [productId] = coordinate;
|
|
768
861
|
const product = searchResults().find((p) => p._id === productId);
|
|
@@ -780,7 +873,7 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
|
|
|
780
873
|
hasResults: hasResults(),
|
|
781
874
|
hasSuggestions: hasSuggestions(),
|
|
782
875
|
suggestions: suggestions(),
|
|
783
|
-
filters:
|
|
876
|
+
filters: mergedFilters(),
|
|
784
877
|
sortBy: sortBy(),
|
|
785
878
|
hasMore: hasMore(),
|
|
786
879
|
loadedCount: loadedCount(),
|
package/dist/index.d.ts
CHANGED
|
@@ -7,14 +7,14 @@ import * as _jay_framework_runtime from '@jay-framework/runtime';
|
|
|
7
7
|
import { HTMLElementCollectionProxy, HTMLElementProxy } from '@jay-framework/runtime';
|
|
8
8
|
import { WixClient } from '@wix/sdk';
|
|
9
9
|
import { BuildDescriptors } from '@wix/sdk-types';
|
|
10
|
-
import { productsV3, inventoryItemsV3 } from '@wix/stores';
|
|
10
|
+
import { productsV3, inventoryItemsV3, customizationsV3 } from '@wix/stores';
|
|
11
11
|
import { categories } from '@wix/categories';
|
|
12
|
-
import {
|
|
12
|
+
import { Customization } from '@wix/auto_sdk_stores_customizations-v-3';
|
|
13
13
|
import { Getter } from '@jay-framework/reactive';
|
|
14
14
|
import { OptionChoice } from '@wix/auto_sdk_stores_products-v-3';
|
|
15
15
|
import { PluginSetupContext, PluginSetupResult, PluginReferencesContext, PluginReferencesResult } from '@jay-framework/stack-server-runtime';
|
|
16
16
|
|
|
17
|
-
declare enum OptionRenderType$
|
|
17
|
+
declare enum OptionRenderType$2 {
|
|
18
18
|
TEXT_CHOICES,
|
|
19
19
|
COLOR_SWATCH_CHOICES
|
|
20
20
|
}
|
|
@@ -36,7 +36,7 @@ interface ChoiceOfProductOptionsViewState {
|
|
|
36
36
|
interface ProductOptionsViewState {
|
|
37
37
|
_id: string,
|
|
38
38
|
name: string,
|
|
39
|
-
optionRenderType: OptionRenderType$
|
|
39
|
+
optionRenderType: OptionRenderType$2,
|
|
40
40
|
choices: Array<ChoiceOfProductOptionsViewState>
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -164,10 +164,32 @@ interface CategoryFilterOfFilterOfProductSearchViewState {
|
|
|
164
164
|
categories: Array<CategoryOfCategoryFilterOfFilterOfProductSearchViewState>
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
declare enum OptionRenderType$1 {
|
|
168
|
+
TEXT_CHOICES,
|
|
169
|
+
SWATCH_CHOICES
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface ChoiceOfOptionFilterOfFilterOfProductSearchViewState {
|
|
173
|
+
choiceId: string,
|
|
174
|
+
choiceName: string,
|
|
175
|
+
colorCode: string,
|
|
176
|
+
productCount: number,
|
|
177
|
+
isSelected: boolean,
|
|
178
|
+
isDisabled: boolean
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface OptionFilterOfFilterOfProductSearchViewState {
|
|
182
|
+
optionId: string,
|
|
183
|
+
optionName: string,
|
|
184
|
+
optionRenderType: OptionRenderType$1,
|
|
185
|
+
choices: Array<ChoiceOfOptionFilterOfFilterOfProductSearchViewState>
|
|
186
|
+
}
|
|
187
|
+
|
|
167
188
|
interface FilterOfProductSearchViewState {
|
|
168
189
|
priceRange: PriceRangeOfFilterOfProductSearchViewState,
|
|
169
190
|
categoryFilter: CategoryFilterOfFilterOfProductSearchViewState,
|
|
170
|
-
inStockOnly: boolean
|
|
191
|
+
inStockOnly: boolean,
|
|
192
|
+
optionFilters: Array<OptionFilterOfFilterOfProductSearchViewState>
|
|
171
193
|
}
|
|
172
194
|
|
|
173
195
|
declare enum CurrentSort {
|
|
@@ -275,6 +297,7 @@ type ProductSearchFastViewState = Pick<ProductSearchViewState, 'searchExpression
|
|
|
275
297
|
categoryFilter: {
|
|
276
298
|
categories: Array<Pick<ProductSearchViewState['filters']['categoryFilter']['categories'][number], 'categoryId' | 'isSelected'>>;
|
|
277
299
|
};
|
|
300
|
+
optionFilters: Array<ProductSearchViewState['filters']['optionFilters'][number]>;
|
|
278
301
|
};
|
|
279
302
|
sortBy: ProductSearchViewState['sortBy'];
|
|
280
303
|
suggestions: Array<ProductSearchViewState['suggestions'][number]>;
|
|
@@ -287,6 +310,7 @@ type ProductSearchInteractiveViewState = Pick<ProductSearchViewState, 'searchExp
|
|
|
287
310
|
categoryFilter: {
|
|
288
311
|
categories: Array<Pick<ProductSearchViewState['filters']['categoryFilter']['categories'][number], 'categoryId' | 'isSelected'>>;
|
|
289
312
|
};
|
|
313
|
+
optionFilters: Array<ProductSearchViewState['filters']['optionFilters'][number]>;
|
|
290
314
|
};
|
|
291
315
|
sortBy: ProductSearchViewState['sortBy'];
|
|
292
316
|
suggestions: Array<ProductSearchViewState['suggestions'][number]>;
|
|
@@ -313,6 +337,11 @@ interface ProductSearchRefs {
|
|
|
313
337
|
categories: {
|
|
314
338
|
isSelected: HTMLElementCollectionProxy<CategoryOfCategoryFilterOfFilterOfProductSearchViewState, HTMLInputElement>
|
|
315
339
|
}
|
|
340
|
+
},
|
|
341
|
+
optionFilters: {
|
|
342
|
+
choices: {
|
|
343
|
+
isSelected: HTMLElementCollectionProxy<ChoiceOfOptionFilterOfFilterOfProductSearchViewState, HTMLInputElement>
|
|
344
|
+
}
|
|
316
345
|
}
|
|
317
346
|
},
|
|
318
347
|
sortBy: {
|
|
@@ -390,14 +419,15 @@ interface WixStoresService {
|
|
|
390
419
|
products: BuildDescriptors<typeof productsV3, {}>;
|
|
391
420
|
categories: BuildDescriptors<typeof categories, {}>;
|
|
392
421
|
inventory: BuildDescriptors<typeof inventoryItemsV3, {}>;
|
|
393
|
-
|
|
394
|
-
cart: BuildDescriptors<typeof currentCart, {}>;
|
|
422
|
+
customizations: BuildDescriptors<typeof customizationsV3, {}>;
|
|
395
423
|
/** URL templates for building canonical links */
|
|
396
424
|
urls: UrlTemplates;
|
|
397
425
|
/** Slug of the fallback category for pages without category context */
|
|
398
426
|
defaultCategory: string | null;
|
|
399
427
|
/** Get the cached category tree. Lazily built on first call. */
|
|
400
428
|
getCategoryTree(): Promise<CategoryTree>;
|
|
429
|
+
/** Get cached product customizations (options with choices). Lazily loaded. */
|
|
430
|
+
getCustomizations(): Promise<Customization[]>;
|
|
401
431
|
}
|
|
402
432
|
/**
|
|
403
433
|
* Server service marker for Wix Stores.
|
|
@@ -473,6 +503,53 @@ interface WixStoresContext {
|
|
|
473
503
|
*/
|
|
474
504
|
declare const WIX_STORES_CONTEXT: _jay_framework_runtime.ContextMarker<WixStoresContext>;
|
|
475
505
|
|
|
506
|
+
interface SearchProductsInput {
|
|
507
|
+
query: string;
|
|
508
|
+
filters?: {
|
|
509
|
+
inStockOnly?: boolean;
|
|
510
|
+
minPrice?: number;
|
|
511
|
+
maxPrice?: number;
|
|
512
|
+
categoryIds?: Array<string>;
|
|
513
|
+
optionFilters?: Array<{
|
|
514
|
+
optionName: string;
|
|
515
|
+
choiceNames: Array<string>;
|
|
516
|
+
}>;
|
|
517
|
+
};
|
|
518
|
+
sortBy?: 'relevance' | 'price_asc' | 'price_desc' | 'name_asc' | 'name_desc' | 'newest';
|
|
519
|
+
cursor?: string;
|
|
520
|
+
pageSize?: number;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
interface SearchProductsOutput {
|
|
524
|
+
products: Array<ProductCardViewState>;
|
|
525
|
+
totalCount: number;
|
|
526
|
+
nextCursor?: string;
|
|
527
|
+
hasMore: boolean;
|
|
528
|
+
priceAggregation?: {
|
|
529
|
+
minBound: number;
|
|
530
|
+
maxBound: number;
|
|
531
|
+
ranges: Array<{
|
|
532
|
+
rangeId: string;
|
|
533
|
+
label: string;
|
|
534
|
+
minValue?: number;
|
|
535
|
+
maxValue?: number;
|
|
536
|
+
productCount: number;
|
|
537
|
+
isSelected: boolean;
|
|
538
|
+
}>;
|
|
539
|
+
};
|
|
540
|
+
optionFilters?: Array<{
|
|
541
|
+
optionId: string;
|
|
542
|
+
optionName: string;
|
|
543
|
+
optionRenderType: 'TEXT_CHOICES' | 'SWATCH_CHOICES';
|
|
544
|
+
choices: Array<{
|
|
545
|
+
choiceId: string;
|
|
546
|
+
choiceName: string;
|
|
547
|
+
colorCode: string;
|
|
548
|
+
productCount: number;
|
|
549
|
+
}>;
|
|
550
|
+
}>;
|
|
551
|
+
}
|
|
552
|
+
|
|
476
553
|
/**
|
|
477
554
|
* URL parameters for product search routes.
|
|
478
555
|
* Supports: category (prefix slug), subcategory (sub-category slug).
|
|
@@ -500,6 +577,10 @@ interface SearchSlowCarryForward {
|
|
|
500
577
|
categories: CategoryInfos;
|
|
501
578
|
/** Root category ID when scoped to a category prefix (always applied, hidden from UI) */
|
|
502
579
|
baseCategoryId: string | null;
|
|
580
|
+
/** Pre-loaded product results from slow phase (used when no query params) */
|
|
581
|
+
preloadedResult: SearchProductsOutput | null;
|
|
582
|
+
/** Base option filters from unfiltered search (static list, counts updated per search) */
|
|
583
|
+
baseOptionFilters: SearchProductsOutput['optionFilters'];
|
|
503
584
|
}
|
|
504
585
|
/**
|
|
505
586
|
* Data carried forward from fast rendering to interactive phase
|
|
@@ -510,6 +591,8 @@ interface SearchFastCarryForward {
|
|
|
510
591
|
categories: CategoryInfos;
|
|
511
592
|
/** Root category ID when scoped to a category prefix (always applied, hidden from UI) */
|
|
512
593
|
baseCategoryId: string | null;
|
|
594
|
+
/** Base option filters from unfiltered search (static list structure) */
|
|
595
|
+
baseOptionFilters: SearchProductsOutput['optionFilters'];
|
|
513
596
|
}
|
|
514
597
|
declare const productSearch: _jay_framework_fullstack_component.JayStackComponentDefinition<ProductSearchRefs, ProductSearchSlowViewState, ProductSearchFastViewState, ProductSearchInteractiveViewState, [SearchSlowCarryForward, WixStoresService], [Signals<ProductSearchFastViewState>, SearchFastCarryForward, WixStoresContext], PageProps & ProductSearchParams, ProductSearchParams, _jay_framework_component.JayComponentCore<PageProps & ProductSearchParams, ProductSearchInteractiveViewState>>;
|
|
515
598
|
|
|
@@ -857,75 +940,6 @@ declare const categoryList: _jay_framework_fullstack_component.JayStackComponent
|
|
|
857
940
|
withInteractive(comp: _jay_framework_component.ComponentConstructor<PageProps, CategoryListRefs, CategoryListInteractiveViewState, [], _jay_framework_component.JayComponentCore<PageProps, CategoryListInteractiveViewState>>): _jay_framework_fullstack_component.JayStackComponentDefinition<CategoryListRefs, CategoryListSlowViewState, CategoryListFastViewState, CategoryListInteractiveViewState, [Record<string, never>, WixStoresService], [], PageProps, {}, _jay_framework_component.JayComponentCore<PageProps, CategoryListInteractiveViewState>>;
|
|
858
941
|
};
|
|
859
942
|
|
|
860
|
-
/**
|
|
861
|
-
* Sort options for product search
|
|
862
|
-
*/
|
|
863
|
-
type ProductSortField = 'relevance' | 'price_asc' | 'price_desc' | 'name_asc' | 'name_desc' | 'newest';
|
|
864
|
-
/**
|
|
865
|
-
* Product search filters
|
|
866
|
-
*/
|
|
867
|
-
interface ProductSearchFilters {
|
|
868
|
-
/** Only show products in stock */
|
|
869
|
-
inStockOnly?: boolean;
|
|
870
|
-
/** Minimum price filter */
|
|
871
|
-
minPrice?: number;
|
|
872
|
-
/** Maximum price filter */
|
|
873
|
-
maxPrice?: number;
|
|
874
|
-
/** Filter by category IDs */
|
|
875
|
-
categoryIds?: string[];
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* Price range bucket for aggregation
|
|
879
|
-
*/
|
|
880
|
-
interface PriceRangeBucket {
|
|
881
|
-
rangeId: string;
|
|
882
|
-
label: string;
|
|
883
|
-
minValue: number | null;
|
|
884
|
-
maxValue: number | null;
|
|
885
|
-
productCount: number;
|
|
886
|
-
isSelected: boolean;
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* Price aggregation data from search
|
|
890
|
-
*/
|
|
891
|
-
interface PriceAggregationData {
|
|
892
|
-
/** Minimum price across all products */
|
|
893
|
-
minBound: number;
|
|
894
|
-
/** Maximum price across all products */
|
|
895
|
-
maxBound: number;
|
|
896
|
-
/** Price range buckets with product counts */
|
|
897
|
-
ranges: PriceRangeBucket[];
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* Input for searchProducts action
|
|
901
|
-
*/
|
|
902
|
-
interface SearchProductsInput {
|
|
903
|
-
/** Search query text */
|
|
904
|
-
query: string;
|
|
905
|
-
/** Filters to apply */
|
|
906
|
-
filters?: ProductSearchFilters;
|
|
907
|
-
/** Sort order */
|
|
908
|
-
sortBy?: ProductSortField;
|
|
909
|
-
/** Cursor for pagination (from previous response's nextCursor) */
|
|
910
|
-
cursor?: string;
|
|
911
|
-
/** Items per page (default: 12) */
|
|
912
|
-
pageSize?: number;
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Output for searchProducts action
|
|
916
|
-
*/
|
|
917
|
-
interface SearchProductsOutput {
|
|
918
|
-
/** List of matching products */
|
|
919
|
-
products: ProductCardViewState[];
|
|
920
|
-
/** Total number of matching products */
|
|
921
|
-
totalCount: number;
|
|
922
|
-
/** Cursor for next page (null if no more results) */
|
|
923
|
-
nextCursor: string | null;
|
|
924
|
-
/** Whether there are more results */
|
|
925
|
-
hasMore: boolean;
|
|
926
|
-
/** Price aggregation data (bounds and ranges) */
|
|
927
|
-
priceAggregation?: PriceAggregationData;
|
|
928
|
-
}
|
|
929
943
|
/**
|
|
930
944
|
* Input for getProductBySlug action
|
|
931
945
|
*/
|
|
@@ -1007,4 +1021,4 @@ declare function setupWixStores(ctx: PluginSetupContext): Promise<PluginSetupRes
|
|
|
1007
1021
|
*/
|
|
1008
1022
|
declare function generateWixStoresReferences(ctx: PluginReferencesContext): Promise<PluginReferencesResult>;
|
|
1009
1023
|
|
|
1010
|
-
export { type CategoryTree, type GetProductBySlugInput, type
|
|
1024
|
+
export { type CategoryTree, type GetProductBySlugInput, type ProductPageParams, type ProductSearchParams, type SearchSortOption, WIX_STORES_CONTEXT, WIX_STORES_SERVICE_MARKER, type WixStoresContext, type WixStoresInitData, type WixStoresService, type WixStoresServiceOptions, buildCategoryUrl, buildProductUrl, categoryList, findCategoryImage, findRootCategoryId, findRootCategorySlug, generateWixStoresReferences, getCategories, getProductBySlug, getVariantStock, init, productPage, productSearch, provideWixStoresService, searchProducts, setupWixStores };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { getCurrentCartClient } from "@jay-framework/wix-cart";
|
|
2
1
|
import { WIX_CART_CONTEXT, WIX_CART_SERVICE, cartIndicator, cartPage, provideWixCartContext, provideWixCartService } from "@jay-framework/wix-cart";
|
|
3
2
|
import { createJayService, makeJayQuery, ActionError, makeJayStackComponent, RenderPipeline, makeJayInit } from "@jay-framework/fullstack-component";
|
|
4
|
-
import { inventoryItemsV3, productsV3 } from "@wix/stores";
|
|
3
|
+
import { customizationsV3, inventoryItemsV3, productsV3 } from "@wix/stores";
|
|
5
4
|
import { categories } from "@wix/categories";
|
|
6
5
|
import { registerService, getService } from "@jay-framework/stack-server-runtime";
|
|
7
6
|
import { createJayContext } from "@jay-framework/runtime";
|
|
@@ -10,6 +9,11 @@ import { WIX_CLIENT_SERVICE } from "@jay-framework/wix-server-client";
|
|
|
10
9
|
import * as fs from "fs";
|
|
11
10
|
import * as path from "path";
|
|
12
11
|
import * as yaml from "js-yaml";
|
|
12
|
+
var OptionRenderType$2 = /* @__PURE__ */ ((OptionRenderType2) => {
|
|
13
|
+
OptionRenderType2[OptionRenderType2["TEXT_CHOICES"] = 0] = "TEXT_CHOICES";
|
|
14
|
+
OptionRenderType2[OptionRenderType2["SWATCH_CHOICES"] = 1] = "SWATCH_CHOICES";
|
|
15
|
+
return OptionRenderType2;
|
|
16
|
+
})(OptionRenderType$2 || {});
|
|
13
17
|
var CurrentSort = /* @__PURE__ */ ((CurrentSort2) => {
|
|
14
18
|
CurrentSort2[CurrentSort2["relevance"] = 0] = "relevance";
|
|
15
19
|
CurrentSort2[CurrentSort2["priceAsc"] = 1] = "priceAsc";
|
|
@@ -22,7 +26,8 @@ var CurrentSort = /* @__PURE__ */ ((CurrentSort2) => {
|
|
|
22
26
|
const instances = {
|
|
23
27
|
productsV3ClientInstance: void 0,
|
|
24
28
|
categoriesClientInstance: void 0,
|
|
25
|
-
inventoryV3ClientInstance: void 0
|
|
29
|
+
inventoryV3ClientInstance: void 0,
|
|
30
|
+
customizationsV3ClientInstance: void 0
|
|
26
31
|
};
|
|
27
32
|
function getProductsV3Client(wixClient) {
|
|
28
33
|
if (!instances.productsV3ClientInstance) {
|
|
@@ -42,15 +47,23 @@ function getInventoryClient(wixClient) {
|
|
|
42
47
|
}
|
|
43
48
|
return instances.inventoryV3ClientInstance;
|
|
44
49
|
}
|
|
50
|
+
function getCustomizationsV3Client(wixClient) {
|
|
51
|
+
if (!instances.customizationsV3ClientInstance) {
|
|
52
|
+
instances.customizationsV3ClientInstance = wixClient.use(customizationsV3);
|
|
53
|
+
}
|
|
54
|
+
return instances.customizationsV3ClientInstance;
|
|
55
|
+
}
|
|
45
56
|
const WIX_STORES_SERVICE_MARKER = createJayService("Wix Store Service");
|
|
46
57
|
function provideWixStoresService(wixClient, options) {
|
|
47
58
|
let cachedTree = null;
|
|
59
|
+
let cachedCustomizations = null;
|
|
48
60
|
const categoriesClient = getCategoriesClient(wixClient);
|
|
61
|
+
const customizationsClient = getCustomizationsV3Client(wixClient);
|
|
49
62
|
const service = {
|
|
50
63
|
products: getProductsV3Client(wixClient),
|
|
51
64
|
categories: categoriesClient,
|
|
52
65
|
inventory: getInventoryClient(wixClient),
|
|
53
|
-
|
|
66
|
+
customizations: customizationsClient,
|
|
54
67
|
urls: options?.urls ?? { product: "/products/{slug}", category: null },
|
|
55
68
|
defaultCategory: options?.defaultCategory ?? null,
|
|
56
69
|
async getCategoryTree() {
|
|
@@ -86,6 +99,17 @@ function provideWixStoresService(wixClient, options) {
|
|
|
86
99
|
}
|
|
87
100
|
cachedTree = { slugMap, parentMap, rootIds, imageMap };
|
|
88
101
|
return cachedTree;
|
|
102
|
+
},
|
|
103
|
+
async getCustomizations() {
|
|
104
|
+
if (cachedCustomizations) return cachedCustomizations;
|
|
105
|
+
try {
|
|
106
|
+
const result = await customizationsClient.queryCustomizations().eq("customizationType", "PRODUCT_OPTION").limit(100).find();
|
|
107
|
+
cachedCustomizations = result.items || [];
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("[wix-stores] Failed to load customizations:", error);
|
|
110
|
+
cachedCustomizations = [];
|
|
111
|
+
}
|
|
112
|
+
return cachedCustomizations;
|
|
89
113
|
}
|
|
90
114
|
};
|
|
91
115
|
registerService(WIX_STORES_SERVICE_MARKER, service);
|
|
@@ -329,8 +353,8 @@ function buildVariantStockMap(product) {
|
|
|
329
353
|
const options = product.options;
|
|
330
354
|
const variants = product.variantsInfo?.variants;
|
|
331
355
|
if (!options || options.length !== 2 || !variants) return stockMap;
|
|
332
|
-
const colorOption = options.find((o) => o.optionRenderType === "
|
|
333
|
-
const textOption = options.find((o) => o.optionRenderType
|
|
356
|
+
const colorOption = options.find((o) => o.optionRenderType === "SWATCH_CHOICES");
|
|
357
|
+
const textOption = options.find((o) => o.optionRenderType === "TEXT_CHOICES");
|
|
334
358
|
if (!colorOption || !textOption) return stockMap;
|
|
335
359
|
const colorOptionId = colorOption._id || "";
|
|
336
360
|
const textOptionId = textOption._id || "";
|
|
@@ -428,6 +452,31 @@ const PRICE_BUCKETS = PRICE_BUCKET_BOUNDARIES.slice(0, -1).map((from, i) => ({
|
|
|
428
452
|
to: PRICE_BUCKET_BOUNDARIES[i + 1]
|
|
429
453
|
}));
|
|
430
454
|
PRICE_BUCKETS.push({ from: PRICE_BUCKET_BOUNDARIES[PRICE_BUCKET_BOUNDARIES.length - 1] });
|
|
455
|
+
function getAvailableProductOptions(aggResults, customizations) {
|
|
456
|
+
const optionNamesAgg = aggResults.find((a) => a.name === "optionNames")?.values;
|
|
457
|
+
const choiceNamesAgg = aggResults.find((a) => a.name === "choiceNames")?.values;
|
|
458
|
+
if (!optionNamesAgg?.results?.length || !choiceNamesAgg?.results?.length) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
const optionNames = new Set(optionNamesAgg.results.map((e) => e.value));
|
|
462
|
+
const choiceCounts = new Map(
|
|
463
|
+
choiceNamesAgg.results.map((e) => [e.value.toLowerCase(), e.count ?? 0])
|
|
464
|
+
);
|
|
465
|
+
return customizations.filter(
|
|
466
|
+
(c) => c.customizationType === "PRODUCT_OPTION" && c.name && optionNames.has(c.name) && (c.customizationRenderType === "TEXT_CHOICES" || c.customizationRenderType === "SWATCH_CHOICES")
|
|
467
|
+
).map((c) => ({
|
|
468
|
+
optionId: c._id || "",
|
|
469
|
+
optionName: c.name || "",
|
|
470
|
+
optionRenderType: c.customizationRenderType,
|
|
471
|
+
// Sort by product count descending (most used choices first)
|
|
472
|
+
choices: (c.choicesSettings?.choices || []).filter((ch) => ch.name && choiceCounts.has(ch.name.toLowerCase())).map((ch) => ({
|
|
473
|
+
choiceId: ch._id || "",
|
|
474
|
+
choiceName: ch.name || "",
|
|
475
|
+
colorCode: ch.colorCode || "",
|
|
476
|
+
productCount: choiceCounts.get(ch.name.toLowerCase()) ?? 0
|
|
477
|
+
})).sort((a, b) => b.productCount - a.productCount)
|
|
478
|
+
})).filter((o) => o.choices.length > 0);
|
|
479
|
+
}
|
|
431
480
|
const searchProducts = makeJayQuery("wixStores.searchProducts").withServices(WIX_STORES_SERVICE_MARKER).withHandler(
|
|
432
481
|
async (input, wixStores) => {
|
|
433
482
|
const { query, filters = {}, sortBy = "relevance", cursor, pageSize = 12 } = input;
|
|
@@ -460,6 +509,22 @@ const searchProducts = makeJayQuery("wixStores.searchProducts").withServices(WIX
|
|
|
460
509
|
}
|
|
461
510
|
});
|
|
462
511
|
}
|
|
512
|
+
if (filters.optionFilters && filters.optionFilters.length > 0) {
|
|
513
|
+
for (const optFilter of filters.optionFilters) {
|
|
514
|
+
if (optFilter.choiceNames.length > 0) {
|
|
515
|
+
filterConditions.push({
|
|
516
|
+
$and: [
|
|
517
|
+
{ "options.name": { $hasSome: [optFilter.optionName] } },
|
|
518
|
+
{
|
|
519
|
+
"options.choicesSettings.choices.name": {
|
|
520
|
+
$hasSome: optFilter.choiceNames
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
]
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
463
528
|
const filter = filterConditions.length === 1 ? filterConditions[0] : { $and: filterConditions };
|
|
464
529
|
const sort = [];
|
|
465
530
|
switch (sortBy) {
|
|
@@ -513,6 +578,28 @@ const searchProducts = makeJayQuery("wixStores.searchProducts").withServices(WIX
|
|
|
513
578
|
name: "max-price",
|
|
514
579
|
type: "SCALAR",
|
|
515
580
|
scalar: { type: "MAX" }
|
|
581
|
+
},
|
|
582
|
+
// Option names for option-based filtering
|
|
583
|
+
{
|
|
584
|
+
fieldPath: "options.name",
|
|
585
|
+
name: "optionNames",
|
|
586
|
+
type: "VALUE",
|
|
587
|
+
value: {
|
|
588
|
+
limit: 20,
|
|
589
|
+
sortType: "VALUE",
|
|
590
|
+
sortDirection: "DESC"
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
// Choice names for option-based filtering
|
|
594
|
+
{
|
|
595
|
+
fieldPath: "options.choicesSettings.choices.name",
|
|
596
|
+
name: "choiceNames",
|
|
597
|
+
type: "VALUE",
|
|
598
|
+
value: {
|
|
599
|
+
limit: 50,
|
|
600
|
+
sortType: "COUNT",
|
|
601
|
+
sortDirection: "DESC"
|
|
602
|
+
}
|
|
516
603
|
}
|
|
517
604
|
];
|
|
518
605
|
const searchResult = await wixStores.products.searchProducts(
|
|
@@ -567,6 +654,8 @@ const searchProducts = makeJayQuery("wixStores.searchProducts").withServices(WIX
|
|
|
567
654
|
};
|
|
568
655
|
});
|
|
569
656
|
priceRanges.push(...bucketRanges);
|
|
657
|
+
const customizations = await wixStores.getCustomizations();
|
|
658
|
+
const optionFilters = getAvailableProductOptions(aggResults, customizations);
|
|
570
659
|
const tree = await wixStores.getCategoryTree();
|
|
571
660
|
const mappedProducts = products.map(
|
|
572
661
|
(p) => mapProductToCard(p, wixStores.urls, tree)
|
|
@@ -580,7 +669,8 @@ const searchProducts = makeJayQuery("wixStores.searchProducts").withServices(WIX
|
|
|
580
669
|
minBound,
|
|
581
670
|
maxBound,
|
|
582
671
|
ranges: priceRanges
|
|
583
|
-
}
|
|
672
|
+
},
|
|
673
|
+
optionFilters
|
|
584
674
|
};
|
|
585
675
|
} catch (error) {
|
|
586
676
|
console.error("[wixStores.searchProducts] Search failed:", error);
|
|
@@ -666,13 +756,28 @@ function mapSortToAction(sort) {
|
|
|
666
756
|
function parseUrlFilters(url) {
|
|
667
757
|
try {
|
|
668
758
|
const params = new URL(url, "http://x").searchParams;
|
|
759
|
+
const optionSelections = /* @__PURE__ */ new Map();
|
|
760
|
+
const optParam = params.get("opt");
|
|
761
|
+
if (optParam) {
|
|
762
|
+
for (const segment of optParam.split(";")) {
|
|
763
|
+
const colonIdx = segment.indexOf(":");
|
|
764
|
+
if (colonIdx === -1)
|
|
765
|
+
continue;
|
|
766
|
+
const name = decodeURIComponent(segment.slice(0, colonIdx));
|
|
767
|
+
const choices = segment.slice(colonIdx + 1).split(",").map((c) => decodeURIComponent(c)).filter(Boolean);
|
|
768
|
+
if (choices.length > 0) {
|
|
769
|
+
optionSelections.set(name, new Set(choices));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
669
773
|
return {
|
|
670
774
|
searchTerm: params.get("q") || "",
|
|
671
775
|
selectedCategorySlugs: params.get("cat")?.split(",").filter(Boolean) || [],
|
|
672
776
|
minPrice: params.has("min") ? Number(params.get("min")) : null,
|
|
673
777
|
maxPrice: params.has("max") ? Number(params.get("max")) : null,
|
|
674
778
|
inStockOnly: params.get("inStock") === "1",
|
|
675
|
-
sort: params.get("sort") || "relevance"
|
|
779
|
+
sort: params.get("sort") || "relevance",
|
|
780
|
+
optionSelections
|
|
676
781
|
};
|
|
677
782
|
} catch {
|
|
678
783
|
return {
|
|
@@ -681,7 +786,8 @@ function parseUrlFilters(url) {
|
|
|
681
786
|
minPrice: null,
|
|
682
787
|
maxPrice: null,
|
|
683
788
|
inStockOnly: false,
|
|
684
|
-
sort: "relevance"
|
|
789
|
+
sort: "relevance",
|
|
790
|
+
optionSelections: /* @__PURE__ */ new Map()
|
|
685
791
|
};
|
|
686
792
|
}
|
|
687
793
|
}
|
|
@@ -696,6 +802,30 @@ function parseSortParam(sort) {
|
|
|
696
802
|
};
|
|
697
803
|
return sortMap[sort] ?? CurrentSort.relevance;
|
|
698
804
|
}
|
|
805
|
+
function buildOptionFiltersViewState(baseOptionFilters, filteredResult, optionSelections) {
|
|
806
|
+
const filteredChoiceCounts = /* @__PURE__ */ new Map();
|
|
807
|
+
for (const opt of filteredResult.optionFilters || []) {
|
|
808
|
+
for (const ch of opt.choices) {
|
|
809
|
+
filteredChoiceCounts.set(ch.choiceName.toLowerCase(), ch.productCount);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return baseOptionFilters.map((opt) => ({
|
|
813
|
+
optionId: opt.optionId,
|
|
814
|
+
optionName: opt.optionName,
|
|
815
|
+
optionRenderType: opt.optionRenderType === "SWATCH_CHOICES" ? OptionRenderType$2.SWATCH_CHOICES : OptionRenderType$2.TEXT_CHOICES,
|
|
816
|
+
choices: opt.choices.map((ch) => {
|
|
817
|
+
const count = filteredChoiceCounts.get(ch.choiceName.toLowerCase()) ?? 0;
|
|
818
|
+
return {
|
|
819
|
+
choiceId: ch.choiceId,
|
|
820
|
+
choiceName: ch.choiceName,
|
|
821
|
+
colorCode: ch.colorCode,
|
|
822
|
+
productCount: count,
|
|
823
|
+
isSelected: optionSelections.get(opt.optionName)?.has(ch.choiceName) ?? false,
|
|
824
|
+
isDisabled: count === 0
|
|
825
|
+
};
|
|
826
|
+
})
|
|
827
|
+
}));
|
|
828
|
+
}
|
|
699
829
|
const EMPTY_CATEGORY_HEADER = {
|
|
700
830
|
name: "",
|
|
701
831
|
description: "",
|
|
@@ -801,18 +931,35 @@ async function renderSlowlyChanging$2(props, wixStores) {
|
|
|
801
931
|
if (baseCategoryId) {
|
|
802
932
|
query = query.eq("parentCategory.id", baseCategoryId);
|
|
803
933
|
}
|
|
804
|
-
const
|
|
805
|
-
|
|
934
|
+
const baseCategoryIds = baseCategoryId ? [baseCategoryId] : [];
|
|
935
|
+
const [categoriesResult, productsResult] = await Promise.all([
|
|
936
|
+
query.find(),
|
|
937
|
+
searchProducts({
|
|
938
|
+
query: "",
|
|
939
|
+
filters: {
|
|
940
|
+
categoryIds: baseCategoryIds.length > 0 ? baseCategoryIds : void 0
|
|
941
|
+
},
|
|
942
|
+
pageSize: PAGE_SIZE
|
|
943
|
+
})
|
|
944
|
+
]);
|
|
945
|
+
return {
|
|
946
|
+
categories: categoriesResult.items || [],
|
|
947
|
+
productsResult
|
|
948
|
+
};
|
|
806
949
|
}).recover((error) => {
|
|
807
|
-
console.error("Failed to load categories:", error);
|
|
808
|
-
return Pipeline.ok(
|
|
809
|
-
|
|
950
|
+
console.error("Failed to load categories/products:", error);
|
|
951
|
+
return Pipeline.ok({
|
|
952
|
+
categories: [],
|
|
953
|
+
productsResult: null
|
|
954
|
+
});
|
|
955
|
+
}).toPhaseOutput(({ categories: categories2, productsResult }) => {
|
|
810
956
|
const categoryInfos = categories2.map((cat) => ({
|
|
811
957
|
categoryId: cat._id || "",
|
|
812
958
|
categoryName: cat.name || "",
|
|
813
959
|
categorySlug: cat.slug || "",
|
|
814
960
|
categoryUrl: buildCategoryUrl(wixStores.urls, tree, cat.slug || "", cat._id || "") ?? ""
|
|
815
961
|
}));
|
|
962
|
+
const baseOptionFilters = productsResult?.optionFilters || [];
|
|
816
963
|
return {
|
|
817
964
|
viewState: {
|
|
818
965
|
searchFields: "name,description,sku",
|
|
@@ -829,7 +976,9 @@ async function renderSlowlyChanging$2(props, wixStores) {
|
|
|
829
976
|
searchFields: "name,description,sku",
|
|
830
977
|
fuzzySearch: true,
|
|
831
978
|
categories: categoryInfos,
|
|
832
|
-
baseCategoryId
|
|
979
|
+
baseCategoryId,
|
|
980
|
+
preloadedResult: productsResult,
|
|
981
|
+
baseOptionFilters
|
|
833
982
|
}
|
|
834
983
|
};
|
|
835
984
|
});
|
|
@@ -839,7 +988,15 @@ async function renderFastChanging$1(props, slowCarryForward, _wixStores) {
|
|
|
839
988
|
const urlFilters = parseUrlFilters(props.url);
|
|
840
989
|
const initialSort = parseSortParam(urlFilters.sort);
|
|
841
990
|
const initialCategoryIds = urlFilters.selectedCategorySlugs.map((slug) => slowCarryForward.categories.find((c) => c.categorySlug === slug)?.categoryId).filter(Boolean);
|
|
991
|
+
const initialOptionFilters = [];
|
|
992
|
+
for (const [optionName, choiceNames] of urlFilters.optionSelections) {
|
|
993
|
+
initialOptionFilters.push({ optionName, choiceNames: [...choiceNames] });
|
|
994
|
+
}
|
|
995
|
+
const hasActiveFilters = !!urlFilters.searchTerm || initialCategoryIds.length > 0 || urlFilters.minPrice !== null || urlFilters.maxPrice !== null || urlFilters.inStockOnly || initialSort !== CurrentSort.relevance || initialOptionFilters.length > 0;
|
|
842
996
|
return Pipeline.try(async () => {
|
|
997
|
+
if (!hasActiveFilters && slowCarryForward.preloadedResult) {
|
|
998
|
+
return slowCarryForward.preloadedResult;
|
|
999
|
+
}
|
|
843
1000
|
const baseCategoryIds = slowCarryForward.baseCategoryId ? [slowCarryForward.baseCategoryId, ...initialCategoryIds] : initialCategoryIds;
|
|
844
1001
|
const result = await searchProducts({
|
|
845
1002
|
query: urlFilters.searchTerm || "",
|
|
@@ -847,7 +1004,8 @@ async function renderFastChanging$1(props, slowCarryForward, _wixStores) {
|
|
|
847
1004
|
categoryIds: baseCategoryIds.length > 0 ? baseCategoryIds : void 0,
|
|
848
1005
|
minPrice: urlFilters.minPrice ?? void 0,
|
|
849
1006
|
maxPrice: urlFilters.maxPrice ?? void 0,
|
|
850
|
-
inStockOnly: urlFilters.inStockOnly || void 0
|
|
1007
|
+
inStockOnly: urlFilters.inStockOnly || void 0,
|
|
1008
|
+
optionFilters: initialOptionFilters.length > 0 ? initialOptionFilters : void 0
|
|
851
1009
|
},
|
|
852
1010
|
sortBy: initialSort !== CurrentSort.relevance ? mapSortToAction(initialSort) : void 0,
|
|
853
1011
|
pageSize: PAGE_SIZE
|
|
@@ -867,13 +1025,14 @@ async function renderFastChanging$1(props, slowCarryForward, _wixStores) {
|
|
|
867
1025
|
{
|
|
868
1026
|
rangeId: "all",
|
|
869
1027
|
label: "Show all",
|
|
870
|
-
minValue:
|
|
871
|
-
maxValue:
|
|
1028
|
+
minValue: 0,
|
|
1029
|
+
maxValue: 1e3,
|
|
872
1030
|
productCount: 0,
|
|
873
1031
|
isSelected: true
|
|
874
1032
|
}
|
|
875
1033
|
]
|
|
876
|
-
}
|
|
1034
|
+
},
|
|
1035
|
+
optionFilters: []
|
|
877
1036
|
});
|
|
878
1037
|
}).toPhaseOutput((result) => {
|
|
879
1038
|
const priceAgg = result.priceAggregation || {
|
|
@@ -883,8 +1042,8 @@ async function renderFastChanging$1(props, slowCarryForward, _wixStores) {
|
|
|
883
1042
|
{
|
|
884
1043
|
rangeId: "all",
|
|
885
1044
|
label: "Show all",
|
|
886
|
-
minValue:
|
|
887
|
-
maxValue:
|
|
1045
|
+
minValue: 0,
|
|
1046
|
+
maxValue: 1e3,
|
|
888
1047
|
productCount: result.totalCount,
|
|
889
1048
|
isSelected: true
|
|
890
1049
|
}
|
|
@@ -907,14 +1066,19 @@ async function renderFastChanging$1(props, slowCarryForward, _wixStores) {
|
|
|
907
1066
|
maxPrice: urlFilters.maxPrice ?? priceAgg.maxBound,
|
|
908
1067
|
minBound: priceAgg.minBound,
|
|
909
1068
|
maxBound: priceAgg.maxBound,
|
|
910
|
-
ranges: priceAgg.ranges
|
|
1069
|
+
ranges: priceAgg.ranges.map((r) => ({
|
|
1070
|
+
...r,
|
|
1071
|
+
minValue: r.minValue ?? 0,
|
|
1072
|
+
maxValue: r.maxValue ?? 0
|
|
1073
|
+
}))
|
|
911
1074
|
},
|
|
912
1075
|
categoryFilter: {
|
|
913
1076
|
categories: slowCarryForward.categories.map((cat) => ({
|
|
914
1077
|
categoryId: cat.categoryId,
|
|
915
1078
|
isSelected: initialCategoryIds.includes(cat.categoryId)
|
|
916
1079
|
}))
|
|
917
|
-
}
|
|
1080
|
+
},
|
|
1081
|
+
optionFilters: buildOptionFiltersViewState(slowCarryForward.baseOptionFilters, result, urlFilters.optionSelections)
|
|
918
1082
|
},
|
|
919
1083
|
sortBy: {
|
|
920
1084
|
currentSort: initialSort
|
|
@@ -927,7 +1091,8 @@ async function renderFastChanging$1(props, slowCarryForward, _wixStores) {
|
|
|
927
1091
|
searchFields: slowCarryForward.searchFields,
|
|
928
1092
|
fuzzySearch: slowCarryForward.fuzzySearch,
|
|
929
1093
|
categories: slowCarryForward.categories,
|
|
930
|
-
baseCategoryId: slowCarryForward.baseCategoryId
|
|
1094
|
+
baseCategoryId: slowCarryForward.baseCategoryId,
|
|
1095
|
+
baseOptionFilters: slowCarryForward.baseOptionFilters
|
|
931
1096
|
}
|
|
932
1097
|
};
|
|
933
1098
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/wix-stores",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Wix Stores API client for Jay Framework",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"./category-list.jay-contract": "./dist/contracts/category-list.jay-contract",
|
|
19
19
|
"./search-products.jay-action": "./dist/actions/search-products.jay-action",
|
|
20
20
|
"./get-product-by-slug.jay-action": "./dist/actions/get-product-by-slug.jay-action",
|
|
21
|
-
"./get-categories.jay-action": "./dist/actions/get-categories.jay-action"
|
|
21
|
+
"./get-categories.jay-action": "./dist/actions/get-categories.jay-action",
|
|
22
|
+
"./get-variant-stock.jay-action": "./dist/actions/get-variant-stock.jay-action"
|
|
22
23
|
},
|
|
23
24
|
"scripts": {
|
|
24
25
|
"build": "npm run clean && npm run definitions && npm run build:client && npm run build:server && npm run build:copy-contract && npm run build:types",
|
|
@@ -33,28 +34,29 @@
|
|
|
33
34
|
"test": ":"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
|
-
"@jay-framework/component": "^0.15.
|
|
37
|
-
"@jay-framework/fullstack-component": "^0.15.
|
|
38
|
-
"@jay-framework/reactive": "^0.15.
|
|
39
|
-
"@jay-framework/runtime": "^0.15.
|
|
40
|
-
"@jay-framework/secure": "^0.15.
|
|
41
|
-
"@jay-framework/stack-client-runtime": "^0.15.
|
|
42
|
-
"@jay-framework/stack-server-runtime": "^0.15.
|
|
43
|
-
"@jay-framework/wix-cart": "^0.15.
|
|
44
|
-
"@jay-framework/wix-server-client": "^0.15.
|
|
45
|
-
"@jay-framework/wix-utils": "^0.15.
|
|
37
|
+
"@jay-framework/component": "^0.15.5",
|
|
38
|
+
"@jay-framework/fullstack-component": "^0.15.5",
|
|
39
|
+
"@jay-framework/reactive": "^0.15.5",
|
|
40
|
+
"@jay-framework/runtime": "^0.15.5",
|
|
41
|
+
"@jay-framework/secure": "^0.15.5",
|
|
42
|
+
"@jay-framework/stack-client-runtime": "^0.15.5",
|
|
43
|
+
"@jay-framework/stack-server-runtime": "^0.15.5",
|
|
44
|
+
"@jay-framework/wix-cart": "^0.15.5",
|
|
45
|
+
"@jay-framework/wix-server-client": "^0.15.5",
|
|
46
|
+
"@jay-framework/wix-utils": "^0.15.5",
|
|
46
47
|
"@wix/categories": "^1.0.185",
|
|
47
48
|
"@wix/sdk": "^1.21.5",
|
|
48
|
-
"@wix/
|
|
49
|
+
"@wix/sdk-runtime": "^1.0.11",
|
|
50
|
+
"@wix/stores": "^1.0.742",
|
|
49
51
|
"js-yaml": "^4.1.0"
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
54
|
"@babel/core": "^7.23.7",
|
|
53
55
|
"@babel/preset-env": "^7.23.8",
|
|
54
56
|
"@babel/preset-typescript": "^7.23.3",
|
|
55
|
-
"@jay-framework/compiler-jay-stack": "^0.15.
|
|
56
|
-
"@jay-framework/jay-cli": "^0.15.
|
|
57
|
-
"@jay-framework/vite-plugin": "^0.15.
|
|
57
|
+
"@jay-framework/compiler-jay-stack": "^0.15.5",
|
|
58
|
+
"@jay-framework/jay-cli": "^0.15.5",
|
|
59
|
+
"@jay-framework/vite-plugin": "^0.15.5",
|
|
58
60
|
"nodemon": "^3.0.3",
|
|
59
61
|
"rimraf": "^5.0.5",
|
|
60
62
|
"tslib": "^2.6.2",
|
package/plugin.yaml
CHANGED
|
@@ -24,6 +24,8 @@ actions:
|
|
|
24
24
|
action: get-product-by-slug.jay-action
|
|
25
25
|
- name: getCategories
|
|
26
26
|
action: get-categories.jay-action
|
|
27
|
+
- name: getVariantStock
|
|
28
|
+
action: get-variant-stock.jay-action
|
|
27
29
|
|
|
28
30
|
# Plugin initialization uses makeJayInit pattern in lib/init.ts
|
|
29
31
|
# Export name defaults to 'init'
|