@jay-framework/wix-stores 0.19.6 → 0.20.0

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/README.md CHANGED
@@ -64,9 +64,6 @@ Run `jay-stack agent-kit` (or `yarn agent-kit`) to index the live category tree
64
64
  urls:
65
65
  product: '/products/{slug}' # default
66
66
  category: null # no category pages by default
67
-
68
- # Fallback category for pages without category context
69
- defaultCategory: 'all-products' # slug of the fallback category
70
67
  ```
71
68
 
72
69
  URL templates use three placeholders:
@@ -114,7 +111,6 @@ src/pages/products/
114
111
  urls:
115
112
  product: '/products/{prefix}/{category}/{slug}'
116
113
  category: '/products/{prefix}/{category}'
117
- defaultCategory: 'all-products'
118
114
  ```
119
115
 
120
116
  ```
@@ -154,12 +150,12 @@ src/pages/products/polgat/
154
150
 
155
151
  ### Setting Params for Static Routes
156
152
 
157
- Static route directories use `jay-params` to tell the component which prefix they represent:
153
+ Static route directories use `jay-params` to tell the component which category they represent:
158
154
 
159
155
  ```html
160
- <!-- src/pages/products/polgat/page.jay-html -->
156
+ <!-- src/pages/products/polgat/page.jay-html (root category) -->
161
157
  <script type="application/jay-params">
162
- prefix: polgat
158
+ category: polgat
163
159
  </script>
164
160
  <script
165
161
  type="application/jay-headless"
@@ -171,11 +167,10 @@ Static route directories use `jay-params` to tell the component which prefix the
171
167
 
172
168
  ## Category Header
173
169
 
174
- The category header is always loaded. The component resolves category metadata using a fallback chain:
170
+ The category header is always loaded. The component resolves category metadata:
175
171
 
176
- 1. **`category` param** → load category
177
- 2. **`prefix` param** → load root category
178
- 3. **Neither** → load `defaultCategory` from config
172
+ 1. **`category` param** → load category (always the active category slug)
173
+ 2. **No `category`** → load the "All Products" system category via `getAllProductsCategory()` (header only, no base filter)
179
174
 
180
175
  If the resolved category is missing image, description, or SEO data, the component walks up the parent chain until it finds the data. Each field inherits independently.
181
176
 
@@ -5,7 +5,11 @@ items:
5
5
  subCategory: Components
6
6
  pluginName: wix-stores
7
7
  packageName: "@jay-framework/wix-stores"
8
- thumbnail: thumbnails/wix-stores/product-search.png
8
+ thumbnail: thumbnails/wix-stores/product-search.svg
9
+ interaction:
10
+ mode: stage-place
11
+ stagePromptTemplate: |
12
+ Add a product search component at this marker location on the page.
9
13
  prompt: |
10
14
  Use headless component @jay-framework/wix-stores / contract product-search.
11
15
  Read agent-kit/materialized-contracts/wix-stores/product-search.jay-contract.
@@ -18,26 +22,52 @@ items:
18
22
  subCategory: Components
19
23
  pluginName: wix-stores
20
24
  packageName: "@jay-framework/wix-stores"
21
- thumbnail: thumbnails/wix-stores/product-page.png
25
+ thumbnail: thumbnails/wix-stores/product-page.svg
26
+ interaction:
27
+ mode: stage-place
28
+ stagePromptTemplate: |
29
+ Add a product page component at this marker location on the page.
22
30
  prompt: |
23
31
  Use headless component @jay-framework/wix-stores / contract product-page.
24
32
  Read agent-kit/materialized-contracts/wix-stores/product-page.jay-contract.
25
33
  Script key: p
26
34
  Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
27
35
 
28
- - id: wix-stores:related-products
29
- title: Related products
36
+ - id: wix-stores:category-products
37
+ title: Category products
30
38
  category: Store
31
39
  subCategory: Components
32
40
  pluginName: wix-stores
33
41
  packageName: "@jay-framework/wix-stores"
34
- thumbnail: thumbnails/wix-stores/related-products.png
42
+ thumbnail: thumbnails/wix-stores/category-products.svg
43
+ interaction:
44
+ mode: stage-place
45
+ stagePromptTemplate: |
46
+ Add a category products grid at this marker location on the page.
35
47
  prompt: |
36
- Use headless component @jay-framework/wix-stores / contract related-products.
37
- Read agent-kit/materialized-contracts/wix-stores/related-products.jay-contract.
48
+ Use headless component @jay-framework/wix-stores / contract category-products.
49
+ Read agent-kit/materialized-contracts/wix-stores/category-products.jay-contract.
38
50
  Script key: r
39
51
  Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
40
- Use alongside product-page; pass productId and categorySlug as instance props.
52
+ Pass categorySlug to show products from a category. Pass productId to exclude a product (e.g. on a product page).
53
+
54
+ - id: wix-stores:product-spotlight
55
+ title: Product spotlight
56
+ category: Store
57
+ subCategory: Components
58
+ pluginName: wix-stores
59
+ packageName: "@jay-framework/wix-stores"
60
+ thumbnail: thumbnails/wix-stores/product-spotlight.png
61
+ interaction:
62
+ mode: stage-place
63
+ stagePromptTemplate: |
64
+ Add a product spotlight at this marker location on the page.
65
+ prompt: |
66
+ Use headless component @jay-framework/wix-stores / contract product-spotlight.
67
+ Read agent-kit/materialized-contracts/wix-stores/product-spotlight.jay-contract.
68
+ Script key: spotlight
69
+ Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
70
+ Pass slug as a prop to specify which product to display.
41
71
 
42
72
  - id: wix-stores:category-list
43
73
  title: Category list
@@ -45,7 +75,11 @@ items:
45
75
  subCategory: Components
46
76
  pluginName: wix-stores
47
77
  packageName: "@jay-framework/wix-stores"
48
- thumbnail: thumbnails/wix-stores/category-list.png
78
+ thumbnail: thumbnails/wix-stores/category-list.svg
79
+ interaction:
80
+ mode: stage-place
81
+ stagePromptTemplate: |
82
+ Add a category list at this marker location on the page.
49
83
  prompt: |
50
84
  Use headless component @jay-framework/wix-stores / contract category-list.
51
85
  Read agent-kit/materialized-contracts/wix-stores/category-list.jay-contract.
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="320" height="240" viewBox="0 0 320 240" fill="none">
2
+ <rect width="320" height="240" fill="#f8fafc"/>
3
+ <rect x="16" y="20" width="288" height="36" rx="8" fill="#fff" stroke="#e2e8f0"/>
4
+ <rect x="28" y="32" width="120" height="12" rx="3" fill="#cbd5e1"/>
5
+ <rect x="16" y="72" width="136" height="14" rx="4" fill="#e2e8f0"/>
6
+ <rect x="16" y="98" width="288" height="1" fill="#e2e8f0"/>
7
+ <rect x="16" y="112" width="200" height="12" rx="3" fill="#cbd5e1"/>
8
+ <rect x="16" y="136" width="160" height="10" rx="3" fill="#e2e8f0"/>
9
+ <rect x="16" y="168" width="288" height="1" fill="#e2e8f0"/>
10
+ <rect x="16" y="182" width="184" height="12" rx="3" fill="#cbd5e1"/>
11
+ <rect x="16" y="206" width="128" height="10" rx="3" fill="#e2e8f0"/>
12
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="320" height="240" viewBox="0 0 320 240" fill="none">
2
+ <rect width="320" height="240" fill="#f8fafc"/>
3
+ <rect x="16" y="24" width="120" height="12" rx="3" fill="#cbd5e1"/>
4
+ <rect x="16" y="56" width="72" height="96" rx="8" fill="#fff" stroke="#e2e8f0"/>
5
+ <rect x="24" y="64" width="56" height="48" rx="4" fill="#dbeafe"/>
6
+ <rect x="24" y="120" width="48" height="8" rx="2" fill="#cbd5e1"/>
7
+ <rect x="96" y="56" width="72" height="96" rx="8" fill="#fff" stroke="#e2e8f0"/>
8
+ <rect x="104" y="64" width="56" height="48" rx="4" fill="#fce7f3"/>
9
+ <rect x="104" y="120" width="48" height="8" rx="2" fill="#cbd5e1"/>
10
+ <rect x="176" y="56" width="72" height="96" rx="8" fill="#fff" stroke="#e2e8f0"/>
11
+ <rect x="184" y="64" width="56" height="48" rx="4" fill="#dcfce7"/>
12
+ <rect x="184" y="120" width="48" height="8" rx="2" fill="#cbd5e1"/>
13
+ <rect x="248" y="56" width="56" height="96" rx="8" fill="#fff" stroke="#e2e8f0"/>
14
+ <rect x="256" y="64" width="40" height="48" rx="4" fill="#fef3c7"/>
15
+ </svg>
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="320" height="240" viewBox="0 0 320 240" fill="none">
2
+ <rect width="320" height="240" fill="#f4f7fb"/>
3
+ <rect x="16" y="16" width="288" height="28" rx="6" fill="#e2e8f0"/>
4
+ <rect x="16" y="56" width="120" height="12" rx="3" fill="#cbd5e1"/>
5
+ <rect x="16" y="76" width="88" height="10" rx="3" fill="#e2e8f0"/>
6
+ <rect x="16" y="104" width="136" height="120" rx="8" fill="#dbeafe" stroke="#93c5fd"/>
7
+ <rect x="168" y="104" width="136" height="16" rx="4" fill="#cbd5e1"/>
8
+ <rect x="168" y="128" width="96" height="10" rx="3" fill="#e2e8f0"/>
9
+ <rect x="168" y="148" width="72" height="10" rx="3" fill="#e2e8f0"/>
10
+ <rect x="168" y="180" width="88" height="28" rx="6" fill="#2563eb"/>
11
+ <text x="188" y="198" fill="#fff" font-family="system-ui,sans-serif" font-size="11">Add to cart</text>
12
+ </svg>
@@ -0,0 +1,17 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="320" height="240" viewBox="0 0 320 240" fill="none">
2
+ <rect width="320" height="240" fill="#f8fafc"/>
3
+ <rect x="16" y="16" width="200" height="24" rx="6" fill="#fff" stroke="#cbd5e1"/>
4
+ <rect x="24" y="24" width="64" height="8" rx="2" fill="#e2e8f0"/>
5
+ <g>
6
+ <rect x="16" y="56" width="88" height="88" rx="8" fill="#fff" stroke="#e2e8f0"/>
7
+ <rect x="24" y="64" width="72" height="48" rx="4" fill="#dbeafe"/>
8
+ <rect x="24" y="120" width="56" height="8" rx="2" fill="#cbd5e1"/>
9
+ <rect x="112" y="56" width="88" height="88" rx="8" fill="#fff" stroke="#e2e8f0"/>
10
+ <rect x="120" y="64" width="72" height="48" rx="4" fill="#fce7f3"/>
11
+ <rect x="120" y="120" width="56" height="8" rx="2" fill="#cbd5e1"/>
12
+ <rect x="208" y="56" width="88" height="88" rx="8" fill="#fff" stroke="#e2e8f0"/>
13
+ <rect x="216" y="64" width="72" height="48" rx="4" fill="#dcfce7"/>
14
+ <rect x="216" y="120" width="56" height="8" rx="2" fill="#cbd5e1"/>
15
+ </g>
16
+ <rect x="16" y="160" width="288" height="64" rx="8" fill="#fff" stroke="#e2e8f0"/>
17
+ </svg>
@@ -0,0 +1,31 @@
1
+ name: category-products
2
+ description: Products from a specific category. Use for category product showcases, or as related products on a product page (pass productId to exclude the current product).
3
+ props:
4
+ - name: productId
5
+ type: string
6
+ description: Product ID to exclude from results (optional, use for related products)
7
+ - name: categorySlug
8
+ type: string
9
+ description: Category slug to filter products by
10
+ - name: limit
11
+ type: number
12
+ description: Maximum number of products to show (default 4)
13
+ tags:
14
+ - tag: products
15
+ type: sub-contract
16
+ repeated: true
17
+ trackBy: _id
18
+ phase: fast+interactive
19
+ description: Product cards from the category
20
+ link: ./product-card
21
+
22
+ - tag: hasProducts
23
+ type: variant
24
+ dataType: boolean
25
+ phase: fast+interactive
26
+ description: Whether there are products to show
27
+
28
+ - tag: categoryName
29
+ type: data
30
+ dataType: string
31
+ description: Name of the category these products belong to
@@ -0,0 +1,36 @@
1
+ import {JayContract} from "@jay-framework/runtime";
2
+ import {ProductCardViewState, ProductCardRefs, ProductCardRepeatedRefs} from "./product-card.jay-contract";
3
+
4
+ export interface CategoryProductsViewState {
5
+ products: Array<ProductCardViewState>,
6
+ hasProducts: boolean,
7
+ categoryName: string
8
+ }
9
+
10
+ export type CategoryProductsSlowViewState = Pick<CategoryProductsViewState, 'categoryName'>;
11
+
12
+ export type CategoryProductsFastViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
13
+ products: Array<CategoryProductsViewState['products'][number]>;
14
+ };
15
+
16
+ export type CategoryProductsInteractiveViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
17
+ products: Array<CategoryProductsViewState['products'][number]>;
18
+ };
19
+
20
+
21
+ export interface CategoryProductsRefs {
22
+ products: ProductCardRepeatedRefs
23
+ }
24
+
25
+
26
+ export interface CategoryProductsRepeatedRefs {
27
+ products: ProductCardRepeatedRefs
28
+ }
29
+
30
+ export interface CategoryProductsProps {
31
+ productId?: string;
32
+ categorySlug?: string;
33
+ limit?: number;
34
+ }
35
+
36
+ export type CategoryProductsContract = JayContract<CategoryProductsViewState, CategoryProductsRefs, CategoryProductsSlowViewState, CategoryProductsFastViewState, CategoryProductsInteractiveViewState, CategoryProductsProps>
@@ -0,0 +1,16 @@
1
+ name: product-spotlight
2
+ description: A single product card by slug. Use to feature a specific product on any page (homepage, content pages, etc.).
3
+ props:
4
+ - name: slug
5
+ type: string
6
+ description: Product slug to display
7
+ tags:
8
+ - tag: product
9
+ type: sub-contract
10
+ description: The product card
11
+ link: ./product-card
12
+
13
+ - tag: hasProduct
14
+ type: variant
15
+ dataType: boolean
16
+ description: Whether the product was found
@@ -0,0 +1,61 @@
1
+ import {JayContract} from "@jay-framework/runtime";
2
+ import {ProductCardViewState, ProductCardRefs, ProductCardRepeatedRefs} from "./product-card.jay-contract";
3
+
4
+ export interface ProductSpotlightViewState {
5
+ product: ProductCardViewState,
6
+ hasProduct: boolean
7
+ }
8
+
9
+ export type ProductSpotlightSlowViewState = Pick<ProductSpotlightViewState, 'hasProduct'> & {
10
+ product: Pick<ProductSpotlightViewState['product'], '_id' | 'name' | 'slug' | 'productUrl' | 'categoryPrefix' | 'hasDiscount' | 'hasRibbon' | 'productType' | 'quickAddType'> & {
11
+ mainMedia: ProductSpotlightViewState['product']['mainMedia'];
12
+ thumbnail: ProductSpotlightViewState['product']['thumbnail'];
13
+ inventory: ProductSpotlightViewState['product']['inventory'];
14
+ ribbon: ProductSpotlightViewState['product']['ribbon'];
15
+ brand: ProductSpotlightViewState['product']['brand'];
16
+ quickOption: Pick<ProductSpotlightViewState['product']['quickOption'], '_id' | 'name' | 'optionRenderType'> & {
17
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
18
+ };
19
+ secondQuickOption: Pick<ProductSpotlightViewState['product']['secondQuickOption'], '_id' | 'name' | 'optionRenderType'> & {
20
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
21
+ };
22
+ };
23
+ };
24
+
25
+ export type ProductSpotlightFastViewState = {
26
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
27
+ quickOption: {
28
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
29
+ };
30
+ secondQuickOption: {
31
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
32
+ };
33
+ };
34
+ };
35
+
36
+ export type ProductSpotlightInteractiveViewState = {
37
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
38
+ quickOption: {
39
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
40
+ };
41
+ secondQuickOption: {
42
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
43
+ };
44
+ };
45
+ };
46
+
47
+
48
+ export interface ProductSpotlightRefs {
49
+ product: ProductCardRefs
50
+ }
51
+
52
+
53
+ export interface ProductSpotlightRepeatedRefs {
54
+ product: ProductCardRepeatedRefs
55
+ }
56
+
57
+ export interface ProductSpotlightProps {
58
+ slug?: string;
59
+ }
60
+
61
+ export type ProductSpotlightContract = JayContract<ProductSpotlightViewState, ProductSpotlightRefs, ProductSpotlightSlowViewState, ProductSpotlightFastViewState, ProductSpotlightInteractiveViewState, ProductSpotlightProps>
@@ -886,7 +886,7 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
886
886
  };
887
887
  }
888
888
  const productSearch = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(ProductSearchInteractive);
889
- function RelatedProductsInteractive(_props, refs, viewStateSignals, _fastCarryForward, storesContext) {
889
+ function CategoryProductsInteractive(_props, refs, viewStateSignals, _fastCarryForward, storesContext) {
890
890
  const { products: [products, setProducts], hasProducts: [hasProducts] } = viewStateSignals;
891
891
  setupCardInteractions(refs.products, { get: products, set: setProducts }, storesContext);
892
892
  return {
@@ -896,7 +896,34 @@ function RelatedProductsInteractive(_props, refs, viewStateSignals, _fastCarryFo
896
896
  })
897
897
  };
898
898
  }
899
- const relatedProducts = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(RelatedProductsInteractive);
899
+ const categoryProducts = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(CategoryProductsInteractive);
900
+ function ProductSpotlightInteractive(_props, refs, viewStateSignals, fastCarryForward, storesContext) {
901
+ const { product: [product, setProduct] } = viewStateSignals;
902
+ const fullProduct = fastCarryForward.product;
903
+ refs.product.addToCartButton.onclick(async () => {
904
+ if (!fullProduct._id || fullProduct.quickAddType !== QuickAddType.SIMPLE)
905
+ return;
906
+ setProduct({ ...product(), isAddingToCart: true });
907
+ try {
908
+ await storesContext.addToCart(fullProduct._id, 1);
909
+ } catch (error) {
910
+ console.error("Failed to add to cart:", error);
911
+ } finally {
912
+ setProduct({ ...product(), isAddingToCart: false });
913
+ }
914
+ });
915
+ refs.product.viewOptionsButton.onclick(() => {
916
+ if (fullProduct.productUrl) {
917
+ window.location.href = fullProduct.productUrl;
918
+ }
919
+ });
920
+ return {
921
+ render: () => ({
922
+ product: product()
923
+ })
924
+ };
925
+ }
926
+ const productSpotlight = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(ProductSpotlightInteractive);
900
927
  const categoryList = makeJayStackComponent().withProps();
901
928
  const init = makeJayInit().withClient(async (data) => {
902
929
  console.log("[wix-stores] Initializing client-side stores context...");
@@ -909,9 +936,10 @@ export {
909
936
  cartIndicator,
910
937
  cartPage,
911
938
  categoryList,
939
+ categoryProducts,
912
940
  init,
913
941
  productPage,
914
942
  productSearch,
915
- provideWixStoresContext,
916
- relatedProducts
943
+ productSpotlight,
944
+ provideWixStoresContext
917
945
  };
package/dist/index.d.ts CHANGED
@@ -41,6 +41,13 @@ interface ProductOptionsViewState {
41
41
  }
42
42
 
43
43
 
44
+ interface ProductOptionsRefs {
45
+ choices: {
46
+ choiceButton: HTMLElementCollectionProxy<ChoiceOfProductOptionsViewState, HTMLButtonElement>
47
+ }
48
+ }
49
+
50
+
44
51
  interface ProductOptionsRepeatedRefs {
45
52
  choices: {
46
53
  choiceButton: HTMLElementCollectionProxy<ChoiceOfProductOptionsViewState, HTMLButtonElement>
@@ -127,6 +134,16 @@ interface ProductCardViewState {
127
134
  }
128
135
 
129
136
 
137
+ interface ProductCardRefs {
138
+ productLink: HTMLElementProxy<ProductCardViewState, HTMLAnchorElement>,
139
+ addToCartButton: HTMLElementProxy<ProductCardViewState, HTMLButtonElement>,
140
+ cardContainer: HTMLElementProxy<ProductCardViewState, HTMLElement>,
141
+ viewOptionsButton: HTMLElementProxy<ProductCardViewState, HTMLButtonElement>,
142
+ quickOption: ProductOptionsRefs,
143
+ secondQuickOption: ProductOptionsRefs
144
+ }
145
+
146
+
130
147
  interface ProductCardRepeatedRefs {
131
148
  productLink: HTMLElementCollectionProxy<ProductCardViewState, HTMLAnchorElement>,
132
149
  addToCartButton: HTMLElementCollectionProxy<ProductCardViewState, HTMLButtonElement>,
@@ -470,6 +487,8 @@ interface WixStoresService {
470
487
  getCustomizations(): Promise<Customization[]>;
471
488
  /** Get cached data extension schemas for products. Lazily loaded. */
472
489
  getDataExtensionSchemas(): Promise<DataExtensionSchema[]>;
490
+ /** Get the "All Products" system category ID. Lazily fetched and cached. */
491
+ getAllProductsCategoryId(): Promise<string | null>;
473
492
  }
474
493
  /**
475
494
  * Server service marker for Wix Stores.
@@ -865,36 +884,92 @@ interface ProductFastCarryForward {
865
884
  */
866
885
  declare const productPage: _jay_framework_fullstack_component.JayStackComponentDefinition<ProductPageRefs, ProductPageSlowViewState, ProductPageFastViewState, ProductPageInteractiveViewState, [ProductSlowCarryForward, WixStoresService], [Signals<ProductPageFastViewState>, ProductFastCarryForward, WixStoresContext], PageProps & ProductPageParams, ProductPageParams, _jay_framework_component.JayComponentCore<PageProps & ProductPageParams, ProductPageInteractiveViewState>>;
867
886
 
868
- interface RelatedProductsViewState {
887
+ interface CategoryProductsViewState {
869
888
  products: Array<ProductCardViewState>,
870
889
  hasProducts: boolean,
871
890
  categoryName: string
872
891
  }
873
892
 
874
- type RelatedProductsSlowViewState = Pick<RelatedProductsViewState, 'categoryName'>;
893
+ type CategoryProductsSlowViewState = Pick<CategoryProductsViewState, 'categoryName'>;
875
894
 
876
- type RelatedProductsFastViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
877
- products: Array<RelatedProductsViewState['products'][number]>;
895
+ type CategoryProductsFastViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
896
+ products: Array<CategoryProductsViewState['products'][number]>;
878
897
  };
879
898
 
880
- type RelatedProductsInteractiveViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
881
- products: Array<RelatedProductsViewState['products'][number]>;
899
+ type CategoryProductsInteractiveViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
900
+ products: Array<CategoryProductsViewState['products'][number]>;
882
901
  };
883
902
 
884
903
 
885
- interface RelatedProductsRefs {
904
+ interface CategoryProductsRefs {
886
905
  products: ProductCardRepeatedRefs
887
906
  }
888
907
 
889
- interface RelatedProductsProps {
890
- productId: string;
908
+ interface CategoryProductsProps {
909
+ productId?: string;
891
910
  categorySlug?: string;
892
911
  limit?: number;
893
912
  }
894
- interface RelatedSlowCarryForward {
913
+ interface SlowCarryForward {
895
914
  products: ProductCardViewState[];
896
915
  }
897
- declare const relatedProducts: _jay_framework_fullstack_component.JayStackComponentDefinition<RelatedProductsRefs, RelatedProductsSlowViewState, RelatedProductsFastViewState, RelatedProductsInteractiveViewState, [RelatedSlowCarryForward, WixStoresService], [Signals<RelatedProductsFastViewState>, Record<string, never>, WixStoresContext], PageProps & RelatedProductsProps, {}, _jay_framework_component.JayComponentCore<PageProps & RelatedProductsProps, RelatedProductsInteractiveViewState>>;
916
+ declare const categoryProducts: _jay_framework_fullstack_component.JayStackComponentDefinition<CategoryProductsRefs, CategoryProductsSlowViewState, CategoryProductsFastViewState, CategoryProductsInteractiveViewState, [SlowCarryForward, WixStoresService], [Signals<CategoryProductsFastViewState>, Record<string, never>, WixStoresContext], PageProps & CategoryProductsProps, {}, _jay_framework_component.JayComponentCore<PageProps & CategoryProductsProps, CategoryProductsInteractiveViewState>>;
917
+
918
+ interface ProductSpotlightViewState {
919
+ product: ProductCardViewState,
920
+ hasProduct: boolean
921
+ }
922
+
923
+ type ProductSpotlightSlowViewState = Pick<ProductSpotlightViewState, 'hasProduct'> & {
924
+ product: Pick<ProductSpotlightViewState['product'], '_id' | 'name' | 'slug' | 'productUrl' | 'categoryPrefix' | 'hasDiscount' | 'hasRibbon' | 'productType' | 'quickAddType'> & {
925
+ mainMedia: ProductSpotlightViewState['product']['mainMedia'];
926
+ thumbnail: ProductSpotlightViewState['product']['thumbnail'];
927
+ inventory: ProductSpotlightViewState['product']['inventory'];
928
+ ribbon: ProductSpotlightViewState['product']['ribbon'];
929
+ brand: ProductSpotlightViewState['product']['brand'];
930
+ quickOption: Pick<ProductSpotlightViewState['product']['quickOption'], '_id' | 'name' | 'optionRenderType'> & {
931
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
932
+ };
933
+ secondQuickOption: Pick<ProductSpotlightViewState['product']['secondQuickOption'], '_id' | 'name' | 'optionRenderType'> & {
934
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
935
+ };
936
+ };
937
+ };
938
+
939
+ type ProductSpotlightFastViewState = {
940
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
941
+ quickOption: {
942
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
943
+ };
944
+ secondQuickOption: {
945
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
946
+ };
947
+ };
948
+ };
949
+
950
+ type ProductSpotlightInteractiveViewState = {
951
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
952
+ quickOption: {
953
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
954
+ };
955
+ secondQuickOption: {
956
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
957
+ };
958
+ };
959
+ };
960
+
961
+
962
+ interface ProductSpotlightRefs {
963
+ product: ProductCardRefs
964
+ }
965
+
966
+ interface ProductSpotlightProps {
967
+ slug: string;
968
+ }
969
+ interface SpotlightCarryForward {
970
+ product: ProductCardViewState;
971
+ }
972
+ declare const productSpotlight: _jay_framework_fullstack_component.JayStackComponentDefinition<ProductSpotlightRefs, ProductSpotlightSlowViewState, ProductSpotlightFastViewState, ProductSpotlightInteractiveViewState, [SpotlightCarryForward, WixStoresService], [Signals<ProductSpotlightFastViewState>, SpotlightCarryForward, WixStoresContext], PageProps & ProductSpotlightProps, {}, _jay_framework_component.JayComponentCore<PageProps & ProductSpotlightProps, ProductSpotlightInteractiveViewState>>;
898
973
 
899
974
  interface CategoryOfCategoryListViewState {
900
975
  _id: string,
@@ -1065,4 +1140,4 @@ declare function generateWixStoresReferences(ctx: PluginReferencesContext): Prom
1065
1140
  */
1066
1141
  declare const generator: _jay_framework_fullstack_component.DynamicContractGenerator<[WixStoresService]>;
1067
1142
 
1068
- export { type CategoryListParams, type CategoryTree, type GetProductBySlugInput, type ProductPageParams, type ProductSearchParams, type RelatedProductsProps, 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, generator as productPageContractGenerator, productSearch, provideWixStoresService, relatedProducts, searchProducts, setupWixStores };
1143
+ export { type CategoryListParams, type CategoryProductsProps, type CategoryTree, type GetProductBySlugInput, type ProductPageParams, type ProductSearchParams, type ProductSpotlightProps, WIX_STORES_CONTEXT, WIX_STORES_SERVICE_MARKER, type WixStoresContext, type WixStoresInitData, type WixStoresService, type WixStoresServiceOptions, buildCategoryUrl, buildProductUrl, categoryList, categoryProducts, findCategoryImage, findRootCategoryId, findRootCategorySlug, generateWixStoresReferences, getCategories, getProductBySlug, getVariantStock, init, productPage, generator as productPageContractGenerator, productSearch, productSpotlight, provideWixStoresService, searchProducts, setupWixStores };
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ function provideWixStoresService(wixClient, options) {
60
60
  let cachedTree = null;
61
61
  let cachedCustomizations = null;
62
62
  let cachedExtensionSchemas = null;
63
+ let cachedAllProductsCategoryId;
63
64
  const categoriesClient = getCategoriesClient(wixClient);
64
65
  const customizationsClient = getCustomizationsV3Client(wixClient);
65
66
  const service = {
@@ -134,6 +135,17 @@ function provideWixStoresService(wixClient, options) {
134
135
  cachedExtensionSchemas = [];
135
136
  }
136
137
  return cachedExtensionSchemas;
138
+ },
139
+ async getAllProductsCategoryId() {
140
+ if (cachedAllProductsCategoryId !== void 0) return cachedAllProductsCategoryId;
141
+ try {
142
+ const result = await service.products.getAllProductsCategory();
143
+ cachedAllProductsCategoryId = result.categoryId ?? null;
144
+ } catch (error) {
145
+ console.error("[wix-stores] Failed to get All Products category:", error);
146
+ cachedAllProductsCategoryId = null;
147
+ }
148
+ return cachedAllProductsCategoryId;
137
149
  }
138
150
  };
139
151
  registerService(WIX_STORES_SERVICE_MARKER, service);
@@ -716,6 +728,7 @@ const getProductBySlug = makeJayQuery("wixStores.getProductBySlug").withServices
716
728
  const fields = [
717
729
  "MEDIA_ITEMS_INFO",
718
730
  "VARIANT_OPTION_CHOICE_NAMES",
731
+ "CURRENCY",
719
732
  ...needsCategoryInfo(wixStores) ? ["ALL_CATEGORIES_INFO"] : []
720
733
  ];
721
734
  const result = await wixStores.products.getProductBySlug(slug, {
@@ -878,7 +891,10 @@ const EMPTY_CATEGORY_HEADER = {
878
891
  };
879
892
  async function findCategoryBySlug$1(categoriesClient, slug) {
880
893
  const result = await categoriesClient.queryCategories({ treeReference: { appNamespace: "@wix/stores" } }).eq("slug", slug).eq("visible", true).limit(1).find();
881
- return result.items?.[0] ?? null;
894
+ const cat = result.items?.[0];
895
+ if (!cat?._id)
896
+ return null;
897
+ return loadCategoryDetails(categoriesClient, cat._id);
882
898
  }
883
899
  async function loadCategoryDetails(categoriesClient, categoryId) {
884
900
  try {
@@ -888,8 +904,7 @@ async function loadCategoryDetails(categoriesClient, categoryId) {
888
904
  }
889
905
  }
890
906
  async function buildCategoryHeader(wixStoreService, category, categoryUrlTemplate) {
891
- const details = await loadCategoryDetails(wixStoreService.categories, category._id);
892
- const cat = details || category;
907
+ const cat = category;
893
908
  const imageUrl = cat.image ? formatWixMediaUrl("", cat.image) : "";
894
909
  const description = cat.description || "";
895
910
  const categoryTree = await wixStoreService.getCategoryTree();
@@ -955,21 +970,19 @@ async function buildCategoryHeader(wixStoreService, category, categoryUrlTemplat
955
970
  }
956
971
  return header;
957
972
  }
958
- async function renderSlowlyChanging$3(props, wixStores) {
973
+ async function renderSlowlyChanging$4(props, wixStores) {
959
974
  const Pipeline = RenderPipeline.for();
960
975
  const categorySlug = props.category ?? null;
961
- const prefixSlug = props.prefix ?? null;
962
- const defaultCategorySlug = wixStores.defaultCategory;
963
976
  let activeCategory = null;
964
977
  let baseCategoryId = null;
965
978
  if (categorySlug) {
966
979
  activeCategory = await findCategoryBySlug$1(wixStores.categories, categorySlug);
967
980
  baseCategoryId = activeCategory?._id ?? null;
968
- } else if (prefixSlug) {
969
- activeCategory = await findCategoryBySlug$1(wixStores.categories, prefixSlug);
970
- baseCategoryId = activeCategory?._id ?? null;
971
- } else if (defaultCategorySlug) {
972
- activeCategory = await findCategoryBySlug$1(wixStores.categories, defaultCategorySlug);
981
+ } else {
982
+ const allProductsCategoryId = await wixStores.getAllProductsCategoryId();
983
+ if (allProductsCategoryId) {
984
+ activeCategory = await loadCategoryDetails(wixStores.categories, allProductsCategoryId);
985
+ }
973
986
  }
974
987
  const tree = await wixStores.getCategoryTree();
975
988
  const categoryHeader = activeCategory ? await buildCategoryHeader(wixStores, activeCategory, wixStores.urls.category) : EMPTY_CATEGORY_HEADER;
@@ -1028,7 +1041,7 @@ async function renderSlowlyChanging$3(props, wixStores) {
1028
1041
  };
1029
1042
  });
1030
1043
  }
1031
- async function renderFastChanging$2(props, slowCarryForward, _wixStores) {
1044
+ async function renderFastChanging$3(props, slowCarryForward, _wixStores) {
1032
1045
  const Pipeline = RenderPipeline.for();
1033
1046
  const urlFilters = parseUrlFilters(props.url);
1034
1047
  const initialSort = parseSortParam(urlFilters.sort);
@@ -1155,12 +1168,13 @@ async function* loadSearchParams([wixStores]) {
1155
1168
  if (rootSlug) {
1156
1169
  params.push({ prefix: rootSlug, category: node.slug });
1157
1170
  } else {
1158
- params.push({ prefix: node.slug });
1171
+ params.push({ category: node.slug });
1159
1172
  }
1160
1173
  for (const child of node.children) {
1161
1174
  collectParams(child, rootSlug ?? node.slug);
1162
1175
  }
1163
1176
  };
1177
+ const allProductsCategoryId = await wixStores.getAllProductsCategoryId();
1164
1178
  const allCategories = [];
1165
1179
  let result = await wixStores.categories.queryCategories({
1166
1180
  treeReference: { appNamespace: "@wix/stores" }
@@ -1176,6 +1190,8 @@ async function* loadSearchParams([wixStores]) {
1176
1190
  for (const cat of allCategories) {
1177
1191
  if (!cat._id)
1178
1192
  continue;
1193
+ if (cat._id === allProductsCategoryId)
1194
+ continue;
1179
1195
  const node = {
1180
1196
  slug: cat.slug || "",
1181
1197
  itemCount: cat.itemCounter ?? 0,
@@ -1205,7 +1221,7 @@ async function* loadSearchParams([wixStores]) {
1205
1221
  for (const root of roots) {
1206
1222
  sumItems(root);
1207
1223
  }
1208
- const params = [];
1224
+ const params = [{}];
1209
1225
  for (const root of roots) {
1210
1226
  collectParams(root, null);
1211
1227
  }
@@ -1215,7 +1231,7 @@ async function* loadSearchParams([wixStores]) {
1215
1231
  yield [];
1216
1232
  }
1217
1233
  }
1218
- const productSearch = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadSearchParams).withSlowlyRender(renderSlowlyChanging$3).withFastRender(renderFastChanging$2);
1234
+ const productSearch = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadSearchParams).withSlowlyRender(renderSlowlyChanging$4).withFastRender(renderFastChanging$3);
1219
1235
  var ProductType = /* @__PURE__ */ ((ProductType2) => {
1220
1236
  ProductType2[ProductType2["PHYSICAL"] = 0] = "PHYSICAL";
1221
1237
  ProductType2[ProductType2["DIGITAL"] = 1] = "DIGITAL";
@@ -1449,7 +1465,7 @@ function mapVariants(variantsInfo) {
1449
1465
  strikethroughPrice: variant.price.compareAtPrice?.formattedAmount || ""
1450
1466
  })) || [];
1451
1467
  }
1452
- async function renderSlowlyChanging$2(props, wixStores) {
1468
+ async function renderSlowlyChanging$3(props, wixStores) {
1453
1469
  const Pipeline = RenderPipeline.for();
1454
1470
  const template = wixStores.urls.product;
1455
1471
  const needsCategories = template.includes("{category}") || template.includes("{prefix}");
@@ -1505,7 +1521,7 @@ async function renderSlowlyChanging$2(props, wixStores) {
1505
1521
  };
1506
1522
  });
1507
1523
  }
1508
- async function renderFastChanging$1(props, slowCarryForward, wixStores) {
1524
+ async function renderFastChanging$2(props, slowCarryForward, wixStores) {
1509
1525
  const Pipeline = RenderPipeline.for();
1510
1526
  const { variants } = slowCarryForward;
1511
1527
  const defaultVariant = variants.find((v) => v.inventoryStatus === StockStatus.IN_STOCK) || variants[0];
@@ -1555,9 +1571,9 @@ async function renderFastChanging$1(props, slowCarryForward, wixStores) {
1555
1571
  }
1556
1572
  }));
1557
1573
  }
1558
- const productPage = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadProductParams).withSlowlyRender(renderSlowlyChanging$2).withFastRender(renderFastChanging$1);
1574
+ const productPage = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadProductParams).withSlowlyRender(renderSlowlyChanging$3).withFastRender(renderFastChanging$2);
1559
1575
  const DEFAULT_LIMIT = 4;
1560
- async function renderSlowlyChanging$1(props, wixStores) {
1576
+ async function renderSlowlyChanging$2(props, wixStores) {
1561
1577
  const Pipeline = RenderPipeline.for();
1562
1578
  const limit = props.limit ?? DEFAULT_LIMIT;
1563
1579
  const categorySlug = props.categorySlug;
@@ -1601,7 +1617,7 @@ async function renderSlowlyChanging$1(props, wixStores) {
1601
1617
  carryForward: { products }
1602
1618
  }));
1603
1619
  }
1604
- async function renderFastChanging(_props, slowCarryForward, _wixStores) {
1620
+ async function renderFastChanging$1(_props, slowCarryForward, _wixStores) {
1605
1621
  const Pipeline = RenderPipeline.for();
1606
1622
  const { products } = slowCarryForward;
1607
1623
  return Pipeline.ok({
@@ -1609,7 +1625,95 @@ async function renderFastChanging(_props, slowCarryForward, _wixStores) {
1609
1625
  hasProducts: products.length > 0
1610
1626
  }).toPhaseOutput((viewState) => ({ viewState, carryForward: {} }));
1611
1627
  }
1612
- const relatedProducts = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withSlowlyRender(renderSlowlyChanging$1).withFastRender(renderFastChanging);
1628
+ const categoryProducts = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withSlowlyRender(renderSlowlyChanging$2).withFastRender(renderFastChanging$1);
1629
+ async function renderSlowlyChanging$1(props, _wixStores) {
1630
+ const Pipeline = RenderPipeline.for();
1631
+ return Pipeline.try(async () => {
1632
+ if (!props.slug) {
1633
+ throw new Error("No product slug provided");
1634
+ }
1635
+ const card = await getProductBySlug({ slug: props.slug });
1636
+ if (!card) {
1637
+ throw new Error(`Product not found: ${props.slug}`);
1638
+ }
1639
+ return card;
1640
+ }).recover((error) => {
1641
+ return handleError(error);
1642
+ }).toPhaseOutput((product) => {
1643
+ return {
1644
+ viewState: {
1645
+ hasProduct: true,
1646
+ product: {
1647
+ _id: product._id,
1648
+ name: product.name,
1649
+ slug: product.slug,
1650
+ productUrl: product.productUrl,
1651
+ categoryPrefix: product.categoryPrefix,
1652
+ mainMedia: product.mainMedia,
1653
+ thumbnail: product.thumbnail,
1654
+ hasDiscount: product.hasDiscount,
1655
+ inventory: product.inventory,
1656
+ ribbon: product.ribbon,
1657
+ hasRibbon: product.hasRibbon,
1658
+ brand: product.brand,
1659
+ productType: product.productType,
1660
+ quickAddType: product.quickAddType,
1661
+ quickOption: {
1662
+ _id: product.quickOption?._id,
1663
+ name: product.quickOption?.name,
1664
+ optionRenderType: product.quickOption?.optionRenderType,
1665
+ choices: (product.quickOption?.choices ?? []).map((c) => ({
1666
+ choiceId: c.choiceId,
1667
+ name: c.name,
1668
+ choiceType: c.choiceType,
1669
+ colorCode: c.colorCode
1670
+ }))
1671
+ },
1672
+ secondQuickOption: {
1673
+ _id: product.secondQuickOption?._id,
1674
+ name: product.secondQuickOption?.name,
1675
+ optionRenderType: product.secondQuickOption?.optionRenderType,
1676
+ choices: (product.secondQuickOption?.choices ?? []).map((c) => ({
1677
+ choiceId: c.choiceId,
1678
+ name: c.name,
1679
+ choiceType: c.choiceType,
1680
+ colorCode: c.colorCode
1681
+ }))
1682
+ }
1683
+ }
1684
+ },
1685
+ carryForward: { product }
1686
+ };
1687
+ });
1688
+ }
1689
+ async function renderFastChanging(_props, carryForward, _wixStores) {
1690
+ const Pipeline = RenderPipeline.for();
1691
+ const { product } = carryForward;
1692
+ return Pipeline.ok({
1693
+ product: {
1694
+ price: product.price,
1695
+ strikethroughPrice: product.strikethroughPrice,
1696
+ isAddingToCart: false,
1697
+ quickOption: {
1698
+ choices: (product.quickOption?.choices ?? []).map((c) => ({
1699
+ choiceId: c.choiceId,
1700
+ inStock: c.inStock,
1701
+ isSelected: c.isSelected
1702
+ }))
1703
+ },
1704
+ secondQuickOption: {
1705
+ choices: (product.secondQuickOption?.choices ?? []).map((c) => ({
1706
+ choiceId: c.choiceId,
1707
+ inStock: c.inStock,
1708
+ isSelected: c.isSelected
1709
+ }))
1710
+ }
1711
+ }
1712
+ }).toPhaseOutput((viewState) => {
1713
+ return { viewState, carryForward: { product } };
1714
+ });
1715
+ }
1716
+ const productSpotlight = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withSlowlyRender(renderSlowlyChanging$1).withFastRender(renderFastChanging);
1613
1717
  async function findCategoryBySlug(categoriesClient, slug) {
1614
1718
  const result = await categoriesClient.queryCategories({ treeReference: { appNamespace: "@wix/stores" } }).eq("slug", slug).eq("visible", true).limit(1).find();
1615
1719
  return result.items?.[0] ?? null;
@@ -1807,12 +1911,12 @@ function buildCategoryPrompt(config, entry) {
1807
1911
  );
1808
1912
  }
1809
1913
  lines.push(
1810
- ` related-products — pass categorySlug="${node.slug}" alongside a productId when showing related items in this category`,
1914
+ ` category-products — pass categorySlug="${node.slug}" to show products from this category; optionally pass productId to exclude a product`,
1811
1915
  "",
1812
1916
  "Contracts:",
1813
1917
  " agent-kit/materialized-contracts/wix-stores/product-search.jay-contract",
1814
1918
  " agent-kit/materialized-contracts/wix-stores/category-list.jay-contract",
1815
- " agent-kit/materialized-contracts/wix-stores/related-products.jay-contract",
1919
+ " agent-kit/materialized-contracts/wix-stores/category-products.jay-contract",
1816
1920
  "",
1817
1921
  "Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md."
1818
1922
  );
@@ -1843,16 +1947,39 @@ function buildCategoryAddMenuItems(roots, config) {
1843
1947
  prompt: buildCategoryPrompt(config, entry)
1844
1948
  }));
1845
1949
  }
1950
+ const PUBLIC_THUMBNAIL_ROOT = path.join("public", "aiditor-add-menu-thumbnails");
1951
+ function copyAiditorAddMenuThumbnails(ctx, resolvePackagePath, pluginName) {
1952
+ const sourceDir = resolvePackagePath(
1953
+ path.join("agent-kit", "aiditor", "thumbnails", pluginName)
1954
+ );
1955
+ if (!fs.existsSync(sourceDir)) return [];
1956
+ const destDir = path.join(ctx.projectRoot, PUBLIC_THUMBNAIL_ROOT, pluginName);
1957
+ const created = [];
1958
+ fs.mkdirSync(destDir, { recursive: true });
1959
+ for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
1960
+ if (!entry.isFile()) continue;
1961
+ const src = path.join(sourceDir, entry.name);
1962
+ const dest = path.join(destDir, entry.name);
1963
+ if (!fs.existsSync(dest) || ctx.force) {
1964
+ fs.copyFileSync(src, dest);
1965
+ created.push(path.posix.join(PUBLIC_THUMBNAIL_ROOT, pluginName, entry.name));
1966
+ }
1967
+ }
1968
+ return created;
1969
+ }
1846
1970
  const CONFIG_FILE_NAME = ".wix-stores.yaml";
1847
1971
  const ADD_MENU_OUTPUT_REL = "agent-kit/aiditor/add-menu/wix-stores.yaml";
1848
1972
  const ADD_MENU_GENERATED_REL = "agent-kit/aiditor/add-menu/wix-stores.generated.yaml";
1849
- function resolveAddMenuTemplatePath() {
1973
+ function resolvePackageAgentKitPath(relativePath) {
1850
1974
  const thisDir = path.dirname(fileURLToPath(import.meta.url));
1851
- const fromDist = path.join(thisDir, "agent-kit/aiditor/add-menu.template.yaml");
1975
+ const fromDist = path.join(thisDir, relativePath);
1852
1976
  if (fs.existsSync(fromDist)) {
1853
1977
  return fromDist;
1854
1978
  }
1855
- return path.join(thisDir, "..", "agent-kit/aiditor/add-menu.template.yaml");
1979
+ return path.join(thisDir, "..", relativePath);
1980
+ }
1981
+ function resolveAddMenuTemplatePath() {
1982
+ return resolvePackageAgentKitPath("agent-kit/aiditor/add-menu.template.yaml");
1856
1983
  }
1857
1984
  function writeAddMenuCatalog(ctx) {
1858
1985
  const outputPath = path.join(ctx.projectRoot, ADD_MENU_OUTPUT_REL);
@@ -1933,6 +2060,9 @@ async function setupWixStores(ctx) {
1933
2060
  if (addMenuCreated) {
1934
2061
  configCreated.push(addMenuCreated);
1935
2062
  }
2063
+ configCreated.push(
2064
+ ...copyAiditorAddMenuThumbnails(ctx, resolvePackageAgentKitPath, "wix-stores")
2065
+ );
1936
2066
  const message = `Wix Stores configured (product URL: ${service.urls.product})`;
1937
2067
  return {
1938
2068
  status: "configured",
@@ -2276,6 +2406,7 @@ export {
2276
2406
  cartIndicator,
2277
2407
  cartPage,
2278
2408
  categoryList,
2409
+ categoryProducts,
2279
2410
  findCategoryImage,
2280
2411
  findRootCategoryId,
2281
2412
  findRootCategorySlug,
@@ -2287,10 +2418,10 @@ export {
2287
2418
  productPage,
2288
2419
  generator as productPageContractGenerator,
2289
2420
  productSearch,
2421
+ productSpotlight,
2290
2422
  provideWixCartContext,
2291
2423
  provideWixCartService,
2292
2424
  provideWixStoresService,
2293
- relatedProducts,
2294
2425
  searchProducts,
2295
2426
  setupWixStores
2296
2427
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/wix-stores",
3
- "version": "0.19.6",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "Wix Stores API client for Jay Framework",
6
6
  "license": "Apache-2.0",
@@ -17,7 +17,8 @@
17
17
  "./media.jay-contract": "./dist/contracts/media.jay-contract",
18
18
  "./media-gallery.jay-contract": "./dist/contracts/media-gallery.jay-contract",
19
19
  "./product-search.jay-contract": "./dist/contracts/product-search.jay-contract",
20
- "./related-products.jay-contract": "./dist/contracts/related-products.jay-contract",
20
+ "./category-products.jay-contract": "./dist/contracts/category-products.jay-contract",
21
+ "./product-spotlight.jay-contract": "./dist/contracts/product-spotlight.jay-contract",
21
22
  "./category-list.jay-contract": "./dist/contracts/category-list.jay-contract",
22
23
  "./search-products.jay-action": "./dist/actions/search-products.jay-action",
23
24
  "./get-product-by-slug.jay-action": "./dist/actions/get-product-by-slug.jay-action",
@@ -30,7 +31,7 @@
30
31
  "validate": "jay-stack-cli validate-plugin",
31
32
  "build:client": "vite build",
32
33
  "build:server": "vite build --ssr",
33
- "build:copy-contract": "mkdir -p dist/contracts && cp lib/contracts/*.jay-contract* dist/contracts/ && mkdir -p dist/actions && cp lib/actions/*.jay-action dist/actions/ && mkdir -p dist/agent-kit/aiditor && cp agent-kit/aiditor/add-menu.template.yaml dist/agent-kit/aiditor/",
34
+ "build:copy-contract": "mkdir -p dist/contracts && cp lib/contracts/*.jay-contract* dist/contracts/ && mkdir -p dist/actions && cp lib/actions/*.jay-action dist/actions/ && mkdir -p dist/agent-kit/aiditor && cp agent-kit/aiditor/add-menu.template.yaml dist/agent-kit/aiditor/ && cp -R agent-kit/aiditor/thumbnails dist/agent-kit/aiditor/",
34
35
  "build:types": "tsup lib/index.ts --dts-only --format esm",
35
36
  "build:check-types": "tsc",
36
37
  "clean": "rimraf dist",
@@ -38,16 +39,16 @@
38
39
  "test": "vitest run"
39
40
  },
40
41
  "dependencies": {
41
- "@jay-framework/component": "^0.19.6",
42
- "@jay-framework/fullstack-component": "^0.19.6",
43
- "@jay-framework/reactive": "^0.19.6",
44
- "@jay-framework/runtime": "^0.19.6",
45
- "@jay-framework/secure": "^0.19.6",
46
- "@jay-framework/stack-client-runtime": "^0.19.6",
47
- "@jay-framework/stack-server-runtime": "^0.19.6",
48
- "@jay-framework/wix-cart": "^0.19.6",
49
- "@jay-framework/wix-server-client": "^0.19.6",
50
- "@jay-framework/wix-utils": "^0.19.6",
42
+ "@jay-framework/component": "^0.20.0",
43
+ "@jay-framework/fullstack-component": "^0.20.0",
44
+ "@jay-framework/reactive": "^0.20.0",
45
+ "@jay-framework/runtime": "^0.20.0",
46
+ "@jay-framework/secure": "^0.20.0",
47
+ "@jay-framework/stack-client-runtime": "^0.20.0",
48
+ "@jay-framework/stack-server-runtime": "^0.20.0",
49
+ "@jay-framework/wix-cart": "^0.20.0",
50
+ "@jay-framework/wix-server-client": "^0.20.0",
51
+ "@jay-framework/wix-utils": "^0.20.0",
51
52
  "@wix/categories": "^1.0.185",
52
53
  "@wix/data-extension-schema": "^1.0.221",
53
54
  "@wix/sdk": "^1.21.5",
@@ -59,10 +60,10 @@
59
60
  "@babel/core": "^7.23.7",
60
61
  "@babel/preset-env": "^7.23.8",
61
62
  "@babel/preset-typescript": "^7.23.3",
62
- "@jay-framework/compiler-jay-stack": "^0.19.6",
63
- "@jay-framework/jay-cli": "^0.19.6",
64
- "@jay-framework/jay-stack-cli": "^0.19.6",
65
- "@jay-framework/vite-plugin": "^0.19.6",
63
+ "@jay-framework/compiler-jay-stack": "^0.20.0",
64
+ "@jay-framework/jay-cli": "^0.20.0",
65
+ "@jay-framework/jay-stack-cli": "^0.20.0",
66
+ "@jay-framework/vite-plugin": "^0.20.0",
66
67
  "nodemon": "^3.0.3",
67
68
  "rimraf": "^5.0.5",
68
69
  "tslib": "^2.6.2",
package/plugin.yaml CHANGED
@@ -7,10 +7,14 @@ contracts:
7
7
  component: productSearch
8
8
  description: Headless product search page
9
9
  # category-page removed — use product-search with prefix/category params instead
10
- - name: related-products
11
- contract: related-products.jay-contract
12
- component: relatedProducts
13
- description: Related products from the same category
10
+ - name: category-products
11
+ contract: category-products.jay-contract
12
+ component: categoryProducts
13
+ description: Products from a category — use for showcases or as related products (exclude current product via productId)
14
+ - name: product-spotlight
15
+ contract: product-spotlight.jay-contract
16
+ component: productSpotlight
17
+ description: A single product card by product ID — use to feature a product on any page
14
18
  - name: category-list
15
19
  contract: category-list.jay-contract
16
20
  component: categoryList
@@ -1,31 +0,0 @@
1
- name: related-products
2
- description: Related products grid showing products from the same category. Use on product pages alongside the product-page component.
3
- props:
4
- - name: productId
5
- type: string
6
- description: Product ID to exclude from results
7
- - name: categorySlug
8
- type: string
9
- description: Category slug to filter related products by
10
- - name: limit
11
- type: number
12
- description: Maximum number of related products to show (default 4)
13
- tags:
14
- - tag: products
15
- type: sub-contract
16
- repeated: true
17
- trackBy: _id
18
- phase: fast+interactive
19
- description: Related product cards
20
- link: ./product-card
21
-
22
- - tag: hasProducts
23
- type: variant
24
- dataType: boolean
25
- phase: fast+interactive
26
- description: Whether there are related products to show
27
-
28
- - tag: categoryName
29
- type: data
30
- dataType: string
31
- description: Name of the category these products belong to
@@ -1,36 +0,0 @@
1
- import {JayContract} from "@jay-framework/runtime";
2
- import {ProductCardViewState, ProductCardRefs, ProductCardRepeatedRefs} from "./product-card.jay-contract";
3
-
4
- export interface RelatedProductsViewState {
5
- products: Array<ProductCardViewState>,
6
- hasProducts: boolean,
7
- categoryName: string
8
- }
9
-
10
- export type RelatedProductsSlowViewState = Pick<RelatedProductsViewState, 'categoryName'>;
11
-
12
- export type RelatedProductsFastViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
13
- products: Array<RelatedProductsViewState['products'][number]>;
14
- };
15
-
16
- export type RelatedProductsInteractiveViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
17
- products: Array<RelatedProductsViewState['products'][number]>;
18
- };
19
-
20
-
21
- export interface RelatedProductsRefs {
22
- products: ProductCardRepeatedRefs
23
- }
24
-
25
-
26
- export interface RelatedProductsRepeatedRefs {
27
- products: ProductCardRepeatedRefs
28
- }
29
-
30
- export interface RelatedProductsProps {
31
- productId?: string;
32
- categorySlug?: string;
33
- limit?: number;
34
- }
35
-
36
- export type RelatedProductsContract = JayContract<RelatedProductsViewState, RelatedProductsRefs, RelatedProductsSlowViewState, RelatedProductsFastViewState, RelatedProductsInteractiveViewState, RelatedProductsProps>