@jay-framework/wix-stores 0.20.0 → 0.21.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 +8 -1
- package/agent-kit/aiditor/add-menu.template.yaml +109 -0
- package/agent-kit/aiditor/thumbnails/wix-stores/category-list.svg +12 -0
- package/agent-kit/aiditor/thumbnails/wix-stores/category-products.svg +15 -0
- package/agent-kit/aiditor/thumbnails/wix-stores/product-page.svg +12 -0
- package/agent-kit/aiditor/thumbnails/wix-stores/product-search.svg +17 -0
- package/agent-kit/aiditor/thumbnails/wix-stores/related-products.svg +15 -0
- package/agent-kit/designer/related-products.md +123 -0
- package/dist/agent-kit/aiditor/add-menu.template.yaml +30 -8
- package/dist/agent-kit/aiditor/thumbnails/wix-stores/related-products.svg +15 -0
- package/dist/index.client.js +130 -14
- package/dist/index.d.ts +36 -10
- package/dist/index.js +394 -137
- package/package.json +17 -20
package/README.md
CHANGED
|
@@ -40,6 +40,12 @@ Supports three roles depending on route params:
|
|
|
40
40
|
|
|
41
41
|
Lists all store categories for navigation.
|
|
42
42
|
|
|
43
|
+
### Category Products (`category-products`)
|
|
44
|
+
|
|
45
|
+
Product grid from a specific category. Props: `categorySlug`, optional `productId` (exclude one product), optional `limit` (default 4).
|
|
46
|
+
|
|
47
|
+
**Related products on a product page:** bind `productId="{p._id}"` and `categorySlug="{p.categorySlug}"` from an existing `product-page` key. Same-category siblings — not machine-learning recommendations. See `agent-kit/designer/related-products.md` (copied to the project on `jay-stack agent-kit`) and Add Menu item `wix-stores:related-products`.
|
|
48
|
+
|
|
43
49
|
## Configuration
|
|
44
50
|
|
|
45
51
|
### Setup
|
|
@@ -50,12 +56,13 @@ The plugin requires `@jay-framework/wix-server-client` to be configured with Wix
|
|
|
50
56
|
jay-stack setup wix-stores
|
|
51
57
|
```
|
|
52
58
|
|
|
53
|
-
This creates `config/.wix-stores.yaml` and writes static AIditor Add Menu component items to `agent-kit/aiditor/add-menu/wix-stores.yaml
|
|
59
|
+
This creates `config/.wix-stores.yaml` and writes static AIditor Add Menu component items to `agent-kit/aiditor/add-menu/wix-stores.yaml` (includes **Related products** — `wix-stores:related-products`).
|
|
54
60
|
|
|
55
61
|
Run `jay-stack agent-kit` (or `yarn agent-kit`) to index the live category tree and refresh:
|
|
56
62
|
|
|
57
63
|
- `agent-kit/references/wix-stores/categories.yaml` — full hierarchy for agent discovery
|
|
58
64
|
- `agent-kit/aiditor/add-menu/wix-stores.generated.yaml` — one Add Menu item per category (Store → Categories)
|
|
65
|
+
- `agent-kit/designer/related-products.md` — recipe for related products on product pages (from this package)
|
|
59
66
|
|
|
60
67
|
### Config File (`config/.wix-stores.yaml`)
|
|
61
68
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
items:
|
|
2
|
+
- id: wix-stores:product-search
|
|
3
|
+
title: Product search
|
|
4
|
+
category: Store
|
|
5
|
+
subCategory: Components
|
|
6
|
+
pluginName: wix-stores
|
|
7
|
+
packageName: "@jay-framework/wix-stores"
|
|
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.
|
|
13
|
+
prompt: |
|
|
14
|
+
Use headless component @jay-framework/wix-stores / contract product-search.
|
|
15
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/product-search.jay-contract.
|
|
16
|
+
Script key: search
|
|
17
|
+
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
18
|
+
|
|
19
|
+
- id: wix-stores:product-page
|
|
20
|
+
title: Product page
|
|
21
|
+
category: Store
|
|
22
|
+
subCategory: Components
|
|
23
|
+
pluginName: wix-stores
|
|
24
|
+
packageName: "@jay-framework/wix-stores"
|
|
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.
|
|
30
|
+
prompt: |
|
|
31
|
+
Use headless component @jay-framework/wix-stores / contract product-page.
|
|
32
|
+
Read agent-kit/materialized-contracts/wix-stores/product-page.jay-contract.
|
|
33
|
+
Script key: p
|
|
34
|
+
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
35
|
+
|
|
36
|
+
- id: wix-stores:related-products
|
|
37
|
+
title: Related products
|
|
38
|
+
category: Store
|
|
39
|
+
subCategory: Components
|
|
40
|
+
pluginName: wix-stores
|
|
41
|
+
packageName: "@jay-framework/wix-stores"
|
|
42
|
+
thumbnail: thumbnails/wix-stores/related-products.svg
|
|
43
|
+
interaction:
|
|
44
|
+
mode: stage-place
|
|
45
|
+
stagePromptTemplate: |
|
|
46
|
+
Add a related products section at this marker location on the page.
|
|
47
|
+
prompt: |
|
|
48
|
+
"Related products" uses contract category-products (no separate related-products contract).
|
|
49
|
+
Read agent-kit/designer/related-products.md for the full recipe.
|
|
50
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/category-products.jay-contract.
|
|
51
|
+
On a product page that already has product-page (key "p"):
|
|
52
|
+
1. Add headless import for category-products in <head> (no key attribute).
|
|
53
|
+
2. Place <jay:category-products productId="{p._id}" categorySlug="{p.categorySlug}" limit="4"> at the marker.
|
|
54
|
+
3. Inside the wrapper: forEach="products" with product-card fields; refs use products.* prefix.
|
|
55
|
+
Same-category siblings excluding the current product — not ML recommendations.
|
|
56
|
+
|
|
57
|
+
- id: wix-stores:category-products
|
|
58
|
+
title: Category products
|
|
59
|
+
category: Store
|
|
60
|
+
subCategory: Components
|
|
61
|
+
pluginName: wix-stores
|
|
62
|
+
packageName: "@jay-framework/wix-stores"
|
|
63
|
+
thumbnail: thumbnails/wix-stores/category-products.svg
|
|
64
|
+
interaction:
|
|
65
|
+
mode: stage-place
|
|
66
|
+
stagePromptTemplate: |
|
|
67
|
+
Add a category products grid at this marker location on the page.
|
|
68
|
+
prompt: |
|
|
69
|
+
Use headless component @jay-framework/wix-stores / contract category-products.
|
|
70
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/category-products.jay-contract.
|
|
71
|
+
Instance pattern: declare import in <head> without key, then <jay:category-products categorySlug="...">.
|
|
72
|
+
Pass categorySlug to show products from a category. Pass productId to exclude a product.
|
|
73
|
+
On product pages for "related products", see agent-kit/designer/related-products.md instead.
|
|
74
|
+
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
75
|
+
|
|
76
|
+
- id: wix-stores:product-spotlight
|
|
77
|
+
title: Product spotlight
|
|
78
|
+
category: Store
|
|
79
|
+
subCategory: Components
|
|
80
|
+
pluginName: wix-stores
|
|
81
|
+
packageName: "@jay-framework/wix-stores"
|
|
82
|
+
thumbnail: thumbnails/wix-stores/product-spotlight.png
|
|
83
|
+
interaction:
|
|
84
|
+
mode: stage-place
|
|
85
|
+
stagePromptTemplate: |
|
|
86
|
+
Add a product spotlight at this marker location on the page.
|
|
87
|
+
prompt: |
|
|
88
|
+
Use headless component @jay-framework/wix-stores / contract product-spotlight.
|
|
89
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/product-spotlight.jay-contract.
|
|
90
|
+
Instance pattern: declare import in <head> without key, then <jay:product-spotlight slug="...">.
|
|
91
|
+
Pass slug as a prop to specify which product to display.
|
|
92
|
+
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
93
|
+
|
|
94
|
+
- id: wix-stores:category-list
|
|
95
|
+
title: Category list
|
|
96
|
+
category: Store
|
|
97
|
+
subCategory: Components
|
|
98
|
+
pluginName: wix-stores
|
|
99
|
+
packageName: "@jay-framework/wix-stores"
|
|
100
|
+
thumbnail: thumbnails/wix-stores/category-list.svg
|
|
101
|
+
interaction:
|
|
102
|
+
mode: stage-place
|
|
103
|
+
stagePromptTemplate: |
|
|
104
|
+
Add a category list at this marker location on the page.
|
|
105
|
+
prompt: |
|
|
106
|
+
Use headless component @jay-framework/wix-stores / contract category-list.
|
|
107
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/category-list.jay-contract.
|
|
108
|
+
Script key: categories
|
|
109
|
+
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
@@ -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,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,123 @@
|
|
|
1
|
+
# Related Products (Product Page)
|
|
2
|
+
|
|
3
|
+
Show a row of product cards at the bottom of a product detail page — same-category siblings, excluding the product being viewed.
|
|
4
|
+
|
|
5
|
+
**Synonym:** user requests for "Related Products", "You may also like", or "More from this category" on a product page all map to the **`category-products`** contract. There is no separate `related-products` contract.
|
|
6
|
+
|
|
7
|
+
**Data model:** products from the displayed product's category (`product-page.categorySlug`), excluding the current product (`product-page._id`). This is **not** machine-learning recommendations — it is same-category catalog browsing.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
The page already imports **`product-page`** with a script key (convention: `p`):
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<script
|
|
15
|
+
type="application/jay-headless"
|
|
16
|
+
plugin="@jay-framework/wix-stores"
|
|
17
|
+
contract="product-page"
|
|
18
|
+
key="p"
|
|
19
|
+
></script>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`p.categorySlug` and `p._id` must be available from the product-page contract.
|
|
23
|
+
|
|
24
|
+
## Import
|
|
25
|
+
|
|
26
|
+
Add a **second** headless import — **no `key`** (instance-based pattern):
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<script
|
|
30
|
+
type="application/jay-headless"
|
|
31
|
+
plugin="@jay-framework/wix-stores"
|
|
32
|
+
contract="category-products"
|
|
33
|
+
></script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Template
|
|
37
|
+
|
|
38
|
+
Place after the main product layout, typically before the footer:
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<jay:category-products productId="{p._id}" categorySlug="{p.categorySlug}" limit="4">
|
|
42
|
+
<section class="related-section" if="hasProducts">
|
|
43
|
+
<h2>Related products</h2>
|
|
44
|
+
<div class="related-grid">
|
|
45
|
+
<article class="related-card" forEach="products" trackBy="_id" ref="products.cardContainer">
|
|
46
|
+
<a href="{productUrl}" class="related-card-image">
|
|
47
|
+
<img src="{thumbnail.url}" alt="{thumbnail.altText}" loading="lazy" />
|
|
48
|
+
<span class="related-ribbon" if="hasRibbon">{ribbon.name}</span>
|
|
49
|
+
</a>
|
|
50
|
+
<div class="related-card-body">
|
|
51
|
+
<h3 class="related-card-name">
|
|
52
|
+
<a href="{productUrl}" ref="products.productLink">{name}</a>
|
|
53
|
+
</h3>
|
|
54
|
+
<div class="related-card-price">
|
|
55
|
+
<span class="original" if="hasDiscount">{strikethroughPrice}</span>
|
|
56
|
+
<span>{price}</span>
|
|
57
|
+
</div>
|
|
58
|
+
<button
|
|
59
|
+
class="btn-add"
|
|
60
|
+
ref="products.addToCartButton"
|
|
61
|
+
if="quickAddType===SIMPLE && inventory.availabilityStatus===IN_STOCK"
|
|
62
|
+
disabled="isAddingToCart"
|
|
63
|
+
>
|
|
64
|
+
<span if="!isAddingToCart">Add to Cart</span>
|
|
65
|
+
<span if="isAddingToCart">Adding...</span>
|
|
66
|
+
</button>
|
|
67
|
+
<a
|
|
68
|
+
href="{productUrl}"
|
|
69
|
+
class="btn-add btn-options"
|
|
70
|
+
ref="products.viewOptionsButton"
|
|
71
|
+
if="quickAddType!==SIMPLE && inventory.availabilityStatus!==OUT_OF_STOCK"
|
|
72
|
+
>View Options</a>
|
|
73
|
+
</div>
|
|
74
|
+
</article>
|
|
75
|
+
</div>
|
|
76
|
+
</section>
|
|
77
|
+
</jay:category-products>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Inside `<jay:category-products>`, bindings resolve to **that instance's** contract tags (`products`, `hasProducts`, `categoryName`) — not the parent `p` key.
|
|
81
|
+
|
|
82
|
+
## Props
|
|
83
|
+
|
|
84
|
+
| Prop | Value on product page | Purpose |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `productId` | `{p._id}` | Exclude the current product from results |
|
|
87
|
+
| `categorySlug` | `{p.categorySlug}` | Load siblings from the same category |
|
|
88
|
+
| `limit` | `4` (optional) | Max cards to show (default 4) |
|
|
89
|
+
|
|
90
|
+
## Refs
|
|
91
|
+
|
|
92
|
+
Product cards use the `product-card` sub-contract. Prefix refs with `products.`:
|
|
93
|
+
|
|
94
|
+
| Ref | Element |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `products.productLink` | Product name link |
|
|
97
|
+
| `products.addToCartButton` | Quick add (SIMPLE products) |
|
|
98
|
+
| `products.viewOptionsButton` | Link to product page (configured products) |
|
|
99
|
+
| `products.cardContainer` | Card root (variant stock preload on hover) |
|
|
100
|
+
|
|
101
|
+
See [product-card example](../contracts/examples/product-card.md) for all available fields.
|
|
102
|
+
|
|
103
|
+
## Contract paths
|
|
104
|
+
|
|
105
|
+
| Contract | Path |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `category-products` | `node_modules/@jay-framework/wix-stores/dist/contracts/category-products.jay-contract` |
|
|
108
|
+
| `product-page` | `agent-kit/materialized-contracts/wix-stores/product-page.jay-contract` (dynamic — materialized on setup) |
|
|
109
|
+
| `product-card` | Linked sub-contract inside `category-products` |
|
|
110
|
+
|
|
111
|
+
Or read paths from `agent-kit/plugins-index.yaml`.
|
|
112
|
+
|
|
113
|
+
## Category showcases (not product page)
|
|
114
|
+
|
|
115
|
+
To show products from a **specific** category on any page (homepage, landing page), use the same `category-products` contract with a static or param-driven `categorySlug`. Omit `productId` when there is no product to exclude. See Add Menu item **Category products** (`wix-stores:category-products`).
|
|
116
|
+
|
|
117
|
+
## Reference implementation
|
|
118
|
+
|
|
119
|
+
`examples/store-light/src/pages/products/[slug]/page.jay-html` in the wix-stores package repo — Related Products section at page bottom.
|
|
120
|
+
|
|
121
|
+
## AIditor Add Menu
|
|
122
|
+
|
|
123
|
+
Attach **Related products** (`wix-stores:related-products`) when marking an area on a product page. The item prompt includes this recipe; `jay-stack setup wix-stores` writes the catalog to `agent-kit/aiditor/add-menu/wix-stores.yaml`.
|
|
@@ -12,7 +12,7 @@ items:
|
|
|
12
12
|
Add a product search component at this marker location on the page.
|
|
13
13
|
prompt: |
|
|
14
14
|
Use headless component @jay-framework/wix-stores / contract product-search.
|
|
15
|
-
Read
|
|
15
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/product-search.jay-contract.
|
|
16
16
|
Script key: search
|
|
17
17
|
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
18
18
|
|
|
@@ -33,6 +33,27 @@ items:
|
|
|
33
33
|
Script key: p
|
|
34
34
|
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
35
35
|
|
|
36
|
+
- id: wix-stores:related-products
|
|
37
|
+
title: Related products
|
|
38
|
+
category: Store
|
|
39
|
+
subCategory: Components
|
|
40
|
+
pluginName: wix-stores
|
|
41
|
+
packageName: "@jay-framework/wix-stores"
|
|
42
|
+
thumbnail: thumbnails/wix-stores/related-products.svg
|
|
43
|
+
interaction:
|
|
44
|
+
mode: stage-place
|
|
45
|
+
stagePromptTemplate: |
|
|
46
|
+
Add a related products section at this marker location on the page.
|
|
47
|
+
prompt: |
|
|
48
|
+
"Related products" uses contract category-products (no separate related-products contract).
|
|
49
|
+
Read agent-kit/designer/related-products.md for the full recipe.
|
|
50
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/category-products.jay-contract.
|
|
51
|
+
On a product page that already has product-page (key "p"):
|
|
52
|
+
1. Add headless import for category-products in <head> (no key attribute).
|
|
53
|
+
2. Place <jay:category-products productId="{p._id}" categorySlug="{p.categorySlug}" limit="4"> at the marker.
|
|
54
|
+
3. Inside the wrapper: forEach="products" with product-card fields; refs use products.* prefix.
|
|
55
|
+
Same-category siblings excluding the current product — not ML recommendations.
|
|
56
|
+
|
|
36
57
|
- id: wix-stores:category-products
|
|
37
58
|
title: Category products
|
|
38
59
|
category: Store
|
|
@@ -46,10 +67,11 @@ items:
|
|
|
46
67
|
Add a category products grid at this marker location on the page.
|
|
47
68
|
prompt: |
|
|
48
69
|
Use headless component @jay-framework/wix-stores / contract category-products.
|
|
49
|
-
Read
|
|
50
|
-
|
|
70
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/category-products.jay-contract.
|
|
71
|
+
Instance pattern: declare import in <head> without key, then <jay:category-products categorySlug="...">.
|
|
72
|
+
Pass categorySlug to show products from a category. Pass productId to exclude a product.
|
|
73
|
+
On product pages for "related products", see agent-kit/designer/related-products.md instead.
|
|
51
74
|
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
52
|
-
Pass categorySlug to show products from a category. Pass productId to exclude a product (e.g. on a product page).
|
|
53
75
|
|
|
54
76
|
- id: wix-stores:product-spotlight
|
|
55
77
|
title: Product spotlight
|
|
@@ -64,10 +86,10 @@ items:
|
|
|
64
86
|
Add a product spotlight at this marker location on the page.
|
|
65
87
|
prompt: |
|
|
66
88
|
Use headless component @jay-framework/wix-stores / contract product-spotlight.
|
|
67
|
-
Read
|
|
68
|
-
|
|
69
|
-
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
89
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/product-spotlight.jay-contract.
|
|
90
|
+
Instance pattern: declare import in <head> without key, then <jay:product-spotlight slug="...">.
|
|
70
91
|
Pass slug as a prop to specify which product to display.
|
|
92
|
+
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
71
93
|
|
|
72
94
|
- id: wix-stores:category-list
|
|
73
95
|
title: Category list
|
|
@@ -82,6 +104,6 @@ items:
|
|
|
82
104
|
Add a category list at this marker location on the page.
|
|
83
105
|
prompt: |
|
|
84
106
|
Use headless component @jay-framework/wix-stores / contract category-list.
|
|
85
|
-
Read
|
|
107
|
+
Read node_modules/@jay-framework/wix-stores/dist/contracts/category-list.jay-contract.
|
|
86
108
|
Script key: categories
|
|
87
109
|
Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
|
|
@@ -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>
|
package/dist/index.client.js
CHANGED
|
@@ -4,9 +4,7 @@ import { makeJayStackComponent, makeJayInit } from "@jay-framework/fullstack-com
|
|
|
4
4
|
import { registerReactiveGlobalContext, createSignal, createMemo, createEffect } from "@jay-framework/component";
|
|
5
5
|
import { patch, REPLACE } from "@jay-framework/json-patch";
|
|
6
6
|
import { createJayContext, useGlobalContext } from "@jay-framework/runtime";
|
|
7
|
-
import { WIX_CLIENT_CONTEXT } from "@jay-framework/wix-server-client/client";
|
|
8
|
-
import { productsV3 } from "@wix/stores";
|
|
9
|
-
import "@wix/categories";
|
|
7
|
+
import { wixFetch, WIX_CLIENT_CONTEXT } from "@jay-framework/wix-server-client/client";
|
|
10
8
|
import { createActionCaller } from "@jay-framework/stack-client-runtime";
|
|
11
9
|
var StockStatus = /* @__PURE__ */ ((StockStatus2) => {
|
|
12
10
|
StockStatus2[StockStatus2["OUT_OF_STOCK"] = 0] = "OUT_OF_STOCK";
|
|
@@ -18,27 +16,145 @@ var Selected = /* @__PURE__ */ ((Selected2) => {
|
|
|
18
16
|
Selected2[Selected2["notSelected"] = 1] = "notSelected";
|
|
19
17
|
return Selected2;
|
|
20
18
|
})(Selected || {});
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
function raw(value) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function rawArray(value) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function str(value) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function normalizeProduct(product) {
|
|
29
|
+
if (!product) return product;
|
|
30
|
+
const normalized = { ...product };
|
|
31
|
+
if (normalized.id && !normalized._id) {
|
|
32
|
+
normalized._id = normalized.id;
|
|
33
|
+
}
|
|
34
|
+
const media = raw(normalized.media);
|
|
35
|
+
if (media?.main) {
|
|
36
|
+
const main = raw(media.main);
|
|
37
|
+
const mainImage = raw(main.image);
|
|
38
|
+
normalized.media = {
|
|
39
|
+
...media,
|
|
40
|
+
main: {
|
|
41
|
+
_id: main.id || main._id,
|
|
42
|
+
url: mainImage?.url || main.url,
|
|
43
|
+
altText: mainImage?.altText || main.altText,
|
|
44
|
+
mediaType: main.mediaType,
|
|
45
|
+
width: mainImage?.width,
|
|
46
|
+
height: mainImage?.height
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const updatedMedia = raw(normalized.media);
|
|
51
|
+
const itemsInfo = raw(updatedMedia?.itemsInfo);
|
|
52
|
+
if (itemsInfo?.items) {
|
|
53
|
+
itemsInfo.items = rawArray(itemsInfo.items).map((item) => {
|
|
54
|
+
const image = raw(item.image);
|
|
55
|
+
return {
|
|
56
|
+
_id: item.id || item._id,
|
|
57
|
+
url: image?.url || item.url,
|
|
58
|
+
altText: image?.altText || item.altText,
|
|
59
|
+
mediaType: item.mediaType,
|
|
60
|
+
width: image?.width,
|
|
61
|
+
height: image?.height
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (normalized.options) {
|
|
66
|
+
normalized.options = rawArray(normalized.options).map((opt) => {
|
|
67
|
+
const choicesSettings = raw(opt.choicesSettings);
|
|
68
|
+
return {
|
|
69
|
+
...opt,
|
|
70
|
+
_id: opt.id || opt._id,
|
|
71
|
+
choices: choicesSettings?.choices ? rawArray(choicesSettings.choices).map((c) => {
|
|
72
|
+
const linkedMedia = c.linkedMedia;
|
|
73
|
+
const firstMedia = linkedMedia?.[0];
|
|
74
|
+
const firstMediaImage = firstMedia ? raw(firstMedia.image) : void 0;
|
|
75
|
+
return {
|
|
76
|
+
...c,
|
|
77
|
+
_id: c.choiceId || c._id,
|
|
78
|
+
value: c.name || c.value,
|
|
79
|
+
media: firstMedia ? {
|
|
80
|
+
_id: firstMedia.id,
|
|
81
|
+
url: str(firstMediaImage?.url) || firstMedia.url,
|
|
82
|
+
mediaType: firstMedia.mediaType
|
|
83
|
+
} : void 0
|
|
84
|
+
};
|
|
85
|
+
}) : void 0
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (normalized.modifiers) {
|
|
90
|
+
normalized.modifiers = rawArray(normalized.modifiers).map((mod) => {
|
|
91
|
+
const choicesSettings = raw(mod.choicesSettings);
|
|
92
|
+
return {
|
|
93
|
+
...mod,
|
|
94
|
+
_id: mod.id || mod._id,
|
|
95
|
+
choices: choicesSettings?.choices ? rawArray(choicesSettings.choices).map((c) => ({
|
|
96
|
+
...c,
|
|
97
|
+
_id: c.choiceId || c._id,
|
|
98
|
+
value: c.name || c.value
|
|
99
|
+
})) : void 0
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (normalized.infoSections) {
|
|
104
|
+
normalized.infoSections = rawArray(normalized.infoSections).map((s) => ({
|
|
105
|
+
...s,
|
|
106
|
+
_id: s.id || s._id
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
const variantsInfo = raw(normalized.variantsInfo);
|
|
110
|
+
if (variantsInfo?.variants) {
|
|
111
|
+
variantsInfo.variants = rawArray(variantsInfo.variants).map((v) => ({
|
|
112
|
+
...v,
|
|
113
|
+
_id: v.id || v._id
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
if (normalized.ribbon) {
|
|
117
|
+
const ribbon = raw(normalized.ribbon);
|
|
118
|
+
normalized.ribbon = {
|
|
119
|
+
...ribbon,
|
|
120
|
+
_id: ribbon.id || ribbon._id
|
|
121
|
+
};
|
|
27
122
|
}
|
|
28
|
-
return
|
|
123
|
+
return normalized;
|
|
124
|
+
}
|
|
125
|
+
async function getProduct(client, productId, options) {
|
|
126
|
+
const params = new URLSearchParams();
|
|
127
|
+
{
|
|
128
|
+
params.set("includeMerchantSpecificData", "true");
|
|
129
|
+
}
|
|
130
|
+
if (options?.fields) {
|
|
131
|
+
for (const f of options.fields) {
|
|
132
|
+
params.append("fields", f);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const qs = params.toString();
|
|
136
|
+
const result = await wixFetch(
|
|
137
|
+
client,
|
|
138
|
+
`/stores/v3/products/${productId}${qs ? "?" + qs : ""}`,
|
|
139
|
+
{
|
|
140
|
+
method: "GET"
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
if (result.product) {
|
|
144
|
+
result.product = normalizeProduct(result.product);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
29
147
|
}
|
|
30
148
|
const WIX_STORES_CONTEXT = createJayContext("wix:stores");
|
|
31
149
|
function provideWixStoresContext() {
|
|
32
150
|
const wixClientContext = useGlobalContext(WIX_CLIENT_CONTEXT);
|
|
33
151
|
const wixClient = wixClientContext.client;
|
|
34
152
|
const cartContext = useGlobalContext(WIX_CART_CONTEXT);
|
|
35
|
-
const catalogClient = getProductsV3Client(wixClient);
|
|
36
153
|
const storesContext = registerReactiveGlobalContext(WIX_STORES_CONTEXT, () => {
|
|
37
154
|
async function addToCart(productId, quantity = 1, selections) {
|
|
38
155
|
console.log(`[WixStores] Adding to cart: ${productId} x ${quantity}`, selections);
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
});
|
|
156
|
+
const result = await getProduct(wixClient, productId, {});
|
|
157
|
+
const product = result.product;
|
|
42
158
|
let variant = product.variantsInfo?.variants?.find(
|
|
43
159
|
(v) => v.choices?.every(
|
|
44
160
|
(choice) => selections?.options?.[choice.optionChoiceIds?.optionId] === choice.optionChoiceIds?.choiceId
|