@wix/agent-skills 1.16.1 → 1.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +72 -8
  3. package/skills/wix-app/references/CODE_QUALITY.md +0 -1
  4. package/skills/wix-app/references/DASHBOARD_MODAL.md +21 -14
  5. package/skills/wix-app/references/DASHBOARD_PAGE.md +30 -184
  6. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +14 -6
  7. package/skills/wix-app/references/EMBEDDED_SCRIPT.md +6 -0
  8. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +172 -0
  9. package/skills/wix-app/references/dashboard-page/WDS_LAYOUT.md +187 -0
  10. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +32 -2
  11. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +38 -1
  12. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +27 -4
  13. package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +167 -0
  14. package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +19 -0
  15. package/skills/wix-app/references/editor-react-component/PARTS.md +1 -2
  16. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +2 -2
  17. package/skills/wix-base44-connector/SKILL.md +185 -0
  18. package/skills/wix-base44-connector/scripts/utils.js +212 -0
  19. package/skills/wix-design-system/SKILL.md +19 -15
  20. package/skills/wix-docs/SKILL.md +75 -1
  21. package/skills/wix-manage/SKILL.md +16 -18
  22. package/skills/wix-manage/references/bookings/create-class-service.md +1 -1
  23. package/skills/wix-manage/references/domains/domain-search-purchase-and-connect.md +2603 -0
  24. package/skills/wix-manage/references/events/create-wix-event.md +112 -90
  25. package/skills/wix-manage/references/events/manage-wix-events.md +33 -94
  26. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +182 -5
  27. package/skills/wix-manage/references/sites/create-headless-site.md +1 -0
  28. package/skills/wix-manage/references/sites/manage-oauth-apps.md +154 -0
  29. package/skills/wix-manage/references/sites/read-site-context.md +125 -0
  30. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +273 -0
  31. package/skills/wix-vibe-headless/install/deploy.cjs +1 -2
  32. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +104 -0
  33. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +74 -0
  34. package/skills/wix-vibe-headless/platforms/base44.md +3 -3
  35. package/skills/wix-vibe-headless/references/blog/seed/SEED.md +1 -1
  36. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +1 -1
  37. package/skills/wix-vibe-headless/references/events/seed/SEED.md +1 -1
  38. package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +1 -1
  39. package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +1 -1
  40. package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +1 -1
  41. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +68 -2
  42. package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +29 -86
  43. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +3 -8
  44. package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +78 -105
  45. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +92 -0
  46. package/skills/wix-vibe-headless/references/storefront/app/hooks/useVariantOptions.js +76 -0
  47. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +1 -1
  48. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +0 -326
  49. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +0 -115
  50. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +0 -264
  51. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +0 -37
  52. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +0 -86
  53. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +0 -119
  54. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +0 -103
  55. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +0 -76
  56. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +0 -82
  57. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +0 -89
  58. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +0 -124
  59. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +0 -91
  60. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +0 -71
  61. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +0 -70
  62. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +0 -73
  63. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +0 -98
  64. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +0 -278
  65. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +0 -94
  66. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +0 -43
  67. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +0 -66
  68. package/skills/wix-app/references/auto-patterns-dashboard/views.md +0 -76
  69. package/skills/wix-app/scripts/generate-auto-patterns.js +0 -510
  70. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +0 -437
  71. package/skills/wix-manage/references/stores/create-product-from-image.md +0 -1031
  72. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +0 -623
  73. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +0 -106
@@ -0,0 +1,273 @@
1
+ ---
2
+ name: "Create Product (Catalog V3)"
3
+ description: Mandatory first recipe for every Wix Stores Catalog V3 create-product request. Before any mutation, require an explicit name and price for every product; an absent price is never 0. If no product is identified, offer both image-upload and text-description paths and stop. If name or price is missing, ask or offer a suggestion and stop. When both are present, create from the supplied details without requiring optional enrichment. Covers single/bulk creation, inventory, physical/digital products, images, options, variants, SKUs, and validation.
4
+ ---
5
+
6
+ # Create Product (Catalog V3)
7
+
8
+ Use this recipe for one or multiple Catalog V3 products. Catalog V1 remains supported through [Create Product (Catalog V1)](create-product-catalog-v1.md).
9
+
10
+ > [!IMPORTANT]
11
+ > **Apply the no-product gate first.** A vague request such as “I’d like to add a new product” does not identify a product. Respond only:
12
+ >
13
+ > **“What product would you like to create? You can upload up to 3 images and I’ll generate the product information from them, or describe the product in text.”**
14
+ >
15
+ > Then end the turn. Do not ask for name, price, product type, options, SEO, inventory, or any other structured field yet. Do not replace the image-or-text choice with a checklist, form, or questionnaire. Reading this recipe must be the final tool call in that turn.
16
+
17
+ Canonical example:
18
+
19
+ - User: “For my Wix store, I’d like to add a new product.”
20
+ - Assistant: “What product would you like to create? You can upload up to 3 images and I’ll generate the product information from them, or describe the product in text.”
21
+ - Result: **Stop. Do not request structured product fields yet.**
22
+
23
+ ## Preflight: respond and return before mutation
24
+
25
+ Name and price are mandatory **workflow inputs for every product**, even when the API schema accepts an omitted price or initializes it to zero. Evaluate the cases below in order. When a case says **RETURN**, end the turn immediately: do not call a create endpoint, do not continue to examples, and do not create first and ask afterward.
26
+
27
+ 1. **No product identified:** Respond only: “What product would you like to create? You can upload up to 3 images and I’ll generate the product information from them, or describe the product in text.” **RETURN.** Do not replace the two paths with a form, checklist, questionnaire, text-only prompt, or requests for name, price, type, options, SEO, or inventory.
28
+ 2. **Any product lacks an explicit name or price:** Ask for all missing mandatory values in one question and offer to suggest them. When only price is missing, respond only: “What price should I set for [product name]? I can suggest one if you’d like.” **RETURN.**
29
+ 3. **Every name and price is known:** Creation may continue. Options, variations, description, SEO, info sections, type, SKU, inventory, and images are optional. Use them when supplied or requested; do not ask the user to resolve them before creation.
30
+
31
+ “Create it,” “create it now,” “for my store, create,” or similar mutation language does not satisfy a missing price. A usable price must be either explicitly supplied by the user or a suggestion the user accepted. Treat `0` as supplied only when the user explicitly requests a free or zero-priced product.
32
+
33
+ Before every create call, assert both conditions:
34
+
35
+ - Every product has a non-empty name from the user or an accepted suggestion.
36
+ - Every variant has a price from the user or an accepted suggestion; no price came from an API default, placeholder, example, or assumption.
37
+
38
+ If any product is missing a price, **do not call the API**. Ask for the missing price, using the product's supplied name when available, and offer to suggest one. Never put `"amount": "0"` in the request unless the user explicitly chose a zero price.
39
+
40
+ ## Core flow
41
+
42
+ 1. Apply the preflight. When it says **RETURN**, make no API call in that turn.
43
+ 2. Send site-scoped calls with `Authorization`, `wix-site-id`, and `Content-Type: application/json`.
44
+ 3. If the catalog version is unknown, run:
45
+
46
+ ```bash
47
+ curl --request GET \
48
+ --url 'https://www.wixapis.com/stores/v3/provision/version' \
49
+ --header 'Authorization: <AUTH>' \
50
+ --header 'wix-site-id: <SITE_ID>' \
51
+ --header 'Content-Type: application/json'
52
+ ```
53
+
54
+ Continue here only for `V3_CATALOG`; use the V1 recipe for `V1_CATALOG`.
55
+ 4. Resolve each product's supplied type, variants, SKUs, inventory intent, and images after its mandatory name and variant price are known. Do not invent optional attributes the user did not supply or request.
56
+ 5. Choose the endpoint, build the products, upload any images, create, and validate every result. Continue until all requested products succeed or report the exact failed items.
57
+
58
+ ## Choose the endpoint
59
+
60
+ | Request | No inventory | Quantity or in-stock state |
61
+ |---|---|---|
62
+ | One product | `POST /stores/v3/products` | `POST /stores/v3/products-with-inventory` |
63
+ | Multiple products | `POST /stores/v3/bulk/products/create` | `POST /stores/v3/bulk/products-with-inventory/create` |
64
+
65
+ - Bulk endpoints accept 1–100 products, so a compatible bulk endpoint is also valid for one product.
66
+ - For multiple products requested as one batch, send one bulk call. Use the with-inventory endpoint when any product needs inventory; include `inventoryItem` only on tracked variants.
67
+ - Never send `inventoryItem` to a no-inventory endpoint. Do not invent inventory.
68
+ - Digital products normally use a no-inventory endpoint.
69
+ - Per bulk request, allow at most 100 products, 100 total options, 100 modifiers, 100 info sections, and 1000 variants. Split larger input into compliant batches.
70
+
71
+ ## Build each product
72
+
73
+ | Concern | Rule |
74
+ |---|---|
75
+ | Type | `PHYSICAL` uses product- and variant-level `physicalProperties`; `DIGITAL` omits physical properties and inventory. |
76
+ | Variants | Include at least one. Price, SKU, barcode, inventory, and digital file are variant-level fields. |
77
+ | Price | Use a string in `price.actualPrice.amount`; add `compareAtPrice` only when supplied. |
78
+ | SKU | Preserve supplied strings exactly, including `#`, punctuation, and leading zeroes. Keep variant SKUs unique unless explicitly requested otherwise. |
79
+ | Options | Define non-empty choices and create every requested Cartesian combination. Each variant references one choice from every option through `optionChoiceNames`. |
80
+ | Text | Prefer `plainDescription` for unformatted copy. Use `description` only for a valid Ricos document. |
81
+ | Media | Up to 3 images are supported per product. Preserve their supplied order in `media.itemsInfo.items`; `media.main` is read-only. |
82
+
83
+ This inventory-aware physical product is the reusable shape. Remove `inventoryItem` when using a no-inventory endpoint; remove `media` when no image was supplied.
84
+
85
+ ```json
86
+ {
87
+ "name": "Canvas Tee",
88
+ "productType": "PHYSICAL",
89
+ "physicalProperties": {},
90
+ "media": {"itemsInfo": {"items": [{
91
+ "url": "https://static.wixstatic.com/media/example.jpg",
92
+ "altText": "Canvas Tee"
93
+ }]}},
94
+ "options": [{
95
+ "name": "Size",
96
+ "optionRenderType": "TEXT_CHOICES",
97
+ "choicesSettings": {"choices": [
98
+ {"choiceType": "CHOICE_TEXT", "name": "Small"},
99
+ {"choiceType": "CHOICE_TEXT", "name": "Large"}
100
+ ]}
101
+ }],
102
+ "variantsInfo": {"variants": [
103
+ {
104
+ "choices": [{"optionChoiceNames": {
105
+ "optionName": "Size", "choiceName": "Small", "renderType": "TEXT_CHOICES"
106
+ }}],
107
+ "sku": "TEE-001-S",
108
+ "price": {
109
+ "actualPrice": {"amount": "18.50"},
110
+ "compareAtPrice": {"amount": "22.00"}
111
+ },
112
+ "inventoryItem": {"quantity": 13},
113
+ "physicalProperties": {},
114
+ "visible": true
115
+ },
116
+ {
117
+ "choices": [{"optionChoiceNames": {
118
+ "optionName": "Size", "choiceName": "Large", "renderType": "TEXT_CHOICES"
119
+ }}],
120
+ "sku": "TEE-001-L",
121
+ "price": {"actualPrice": {"amount": "18.50"}},
122
+ "inventoryItem": {"inStock": true},
123
+ "physicalProperties": {},
124
+ "visible": true
125
+ }
126
+ ]}
127
+ }
128
+ ```
129
+
130
+ For a color option, use `SWATCH_CHOICES`; each choice uses `choiceType: ONE_COLOR` and `colorCode`, and the variant reference uses `renderType: SWATCH_CHOICES`.
131
+
132
+ A digital product uses the smaller shape below. Add the supplied variant-level digital file only when one was provided.
133
+
134
+ ```json
135
+ {
136
+ "name": "Focus Planner PDF",
137
+ "productType": "DIGITAL",
138
+ "variantsInfo": {"variants": [{
139
+ "sku": "PDF-FOCUS-01",
140
+ "price": {"actualPrice": {"amount": "9.99"}},
141
+ "visible": true
142
+ }]}
143
+ }
144
+ ```
145
+
146
+ ### Generated descriptions and SEO
147
+
148
+ - When copy is generated from an image, every factual claim must be user-supplied or directly visible. Preserve user-supplied facts such as a stated material; do not reject them merely because the image alone cannot prove them. Color, shape, and visible decoration may come from the image. Dimensions, capacity, care instructions, durability, compatibility, performance, and any material the user did not identify must be omitted. For example, never write “dishwasher safe” or “microwave safe” unless the user supplied that fact. Omit uncertain claims instead of turning them into marketing language.
149
+ - Use `plainDescription` unless formatting is required. SEO tags use `seoData.tags`.
150
+ - For formatted bullets, the Ricos node is `BULLETED_LIST`, not `BULLET_LIST`, and its data field is `bulletedListData`:
151
+
152
+ ```json
153
+ {
154
+ "description": {"nodes": [{
155
+ "type": "BULLETED_LIST",
156
+ "nodes": [{"type": "LIST_ITEM", "nodes": [{
157
+ "type": "PARAGRAPH",
158
+ "nodes": [{"type": "TEXT", "textData": {"text": "Visible blue glaze"}}],
159
+ "paragraphData": {}
160
+ }]}],
161
+ "bulletedListData": {}
162
+ }]}
163
+ }
164
+ ```
165
+
166
+ ## Add images
167
+
168
+ Skip this step when no image was supplied. Prefer the available dedicated Wix image-upload capability and upload all attachments and public image URLs together. Do not reconstruct that capability inside a general API-execution call. When no dedicated uploader is available, use `POST /site-media/v1/files/import` with the external URL in the body field named exactly `url`—not `importUrl`:
169
+
170
+ ```json
171
+ {"url":"https://example.com/product.jpg","mimeType":"image/jpeg","displayName":"Product.jpg","mediaType":"IMAGE"}
172
+ ```
173
+
174
+ A successful upload that returns the matching `wixstatic.com` URL may be used in product media while asynchronous processing is `PENDING`; do not treat `PENDING` alone as a failure. Stop if the upload reports `FAILED`. Poll `GET /site-media/v1/files/get-file-by-id?fileId=<FILE_ID>` until `file.operationStatus` is `READY` only when the workflow needs guaranteed processing, image metadata, transformations, or independent proof that the asset is ready. A pending URL proves submission, not completed processing, so do not describe it as ready or fully verified unless a later response proves that. Do not use a thumbnail URL or reuse the original external URL after upload.
175
+
176
+ Track every upload as its own product-to-file record. A poll or timeout fallback may return only that file's URL—never another product's URL. If an upload fails, retry the same asset through another supported transfer method when possible; never silently replace it with a different image. If the exact asset cannot be preserved, create only products whose requested images uploaded successfully and report every blocked product and failed source exactly; for a single required-image product, make no product-create call.
177
+
178
+ ## Wrap the request
179
+
180
+ Use the chosen product object directly in either envelope:
181
+
182
+ ```json
183
+ {"product": PRODUCT}
184
+ ```
185
+
186
+ ```json
187
+ {"products": [PRODUCT_1, PRODUCT_2], "returnEntity": true}
188
+ ```
189
+
190
+ The first is for single endpoints; the second is for bulk endpoints. If the user supplies only a SKU pattern for variants, generate a stable unique SKU per combination and preserve the mapping in the result.
191
+
192
+ For one product when the user supplied no quantity, stock state, or inventory-tracking request, the complete direct path is `POST /stores/v3/products` with `{"product": PRODUCT}`. Do not infer `inStock: true`, add `inventoryItem`, or choose `/products-with-inventory`; physical product type does not imply inventory. If it has ready images, add `"fields": ["MEDIA_ITEMS_INFO"]` to the same no-inventory request.
193
+
194
+ For one physical product with inventory, the complete direct path is one `POST /stores/v3/products-with-inventory` call with `{"product": PRODUCT}`. Put `inventoryItem` on its priced variant. If it has ready images, add `"fields": ["MEDIA_ITEMS_INFO"]` to that same request. The product shape, envelope, endpoint, and validation paths are complete here: do not search another recipe or schema, probe an alternate endpoint, or make a post-create read unless the projected create response does not prove requested media.
195
+
196
+ ### Request projected media when validating images
197
+
198
+ `media.itemsInfo.items` is a projected response field. Creating a product with media does not guarantee that the create response—or an ordinary Get Product call—returns the gallery. To receive it, pass `"MEDIA_ITEMS_INFO"` in the request-level `fields` array.
199
+
200
+ For a single create with images:
201
+
202
+ ```json
203
+ {"product": PRODUCT_WITH_MEDIA, "fields": ["MEDIA_ITEMS_INFO"]}
204
+ ```
205
+
206
+ For a bulk create with images:
207
+
208
+ ```json
209
+ {
210
+ "products": [PRODUCT_1, PRODUCT_2],
211
+ "returnEntity": true,
212
+ "fields": ["MEDIA_ITEMS_INFO"]
213
+ }
214
+ ```
215
+
216
+ If the create result still does not prove the stored gallery, make at most one read-only verification call:
217
+
218
+ ```http
219
+ GET /stores/v3/products/<PRODUCT_ID>?fields=MEDIA_ITEMS_INFO
220
+ ```
221
+
222
+ Inspect `product.media.itemsInfo.items` from that projected response. Do not interpret an omitted `itemsInfo` as lost media, search the Update Product schema, or PATCH the same media merely to retrieve it. A PATCH is appropriate only when a projected response proves the stored media is actually wrong.
223
+
224
+ ## Validate before reporting success
225
+
226
+ | Endpoint result | Required checks |
227
+ |---|---|
228
+ | Single, no inventory | Require `product.id`; report only response-backed fields as verified. For images, request `MEDIA_ITEMS_INFO` on create or use one projected Get Product verification call. |
229
+ | Single, with inventory | Require `product.id`; inspect `inventoryResults.results`, item metadata, and top-level error. |
230
+ | Bulk, no inventory | Inspect every `results[].itemMetadata`; with `returnEntity: true`, the created entity is `results[].item`, not `results[].product`. Reconcile `bulkActionMetadata.totalSuccesses` and `totalFailures` with the requested count. |
231
+ | Bulk, with inventory | Use the exact two-sibling response paths below. Inspect every product and inventory result, both bulk metadata objects, item metadata, and the inventory error before reporting success. |
232
+
233
+ `POST /stores/v3/bulk/products-with-inventory/create` does **not** return the same flat envelope as `/stores/v3/bulk/products/create`. Its response has two top-level sibling objects:
234
+
235
+ ```json
236
+ {
237
+ "productResults": {
238
+ "results": [{"itemMetadata": {}, "item": {}}],
239
+ "bulkActionMetadata": {}
240
+ },
241
+ "inventoryResults": {
242
+ "results": [{"itemMetadata": {}, "item": {}}],
243
+ "bulkActionMetadata": {},
244
+ "error": null
245
+ }
246
+ }
247
+ ```
248
+
249
+ Before claiming that an inventory-aware bulk create succeeded:
250
+
251
+ 1. Inspect every `productResults.results[]` entry and require `itemMetadata.success`; with `returnEntity: true`, read the product from `item`.
252
+ 2. Reconcile `productResults.bulkActionMetadata.totalSuccesses`, `totalFailures`, and `undetailedFailures` with the requested product count.
253
+ 3. Separately inspect every top-level `inventoryResults.results[]` entry and require `itemMetadata.success`; with `returnEntity: true`, read the inventory entity from `item`.
254
+ 4. Reconcile `inventoryResults.bulkActionMetadata.totalSuccesses`, `totalFailures`, and `undetailedFailures` with the expected inventory-item count, and inspect `inventoryResults.error`.
255
+
256
+ The inventory error check is mandatory even when every item and metadata count succeeded. Explicitly read the sibling field—for example, normalize `inventoryResults.error ?? null`—and report success only when it is absent or null. Do not infer that value from HTTP 200, item metadata, or bulk metadata.
257
+
258
+ For `POST /stores/v3/products-with-inventory`, inspect the returned `product.id`, every `inventoryResults.results[].itemMetadata`, and top-level `inventoryResults.error` directly from the create response. Do not issue inventory queries, product searches, Get Product calls, or PATCH calls merely to reconfirm values already proven by that response. If item success proves the inventory write but the response omits the requested quantity, report the quantity as submitted; if the user needs it independently verified, make one targeted inventory read using the returned product ID without first fetching the product again. The only other allowed extra read is the single projected media verification described above when requested images are not present in the projected create result. GET and HEAD requests must not include a request body.
259
+
260
+ Never read `productResults.inventoryResults`: that path does not exist. A successful product result or HTTP 200 does not prove inventory creation succeeded.
261
+
262
+ Retry only failed transient items, not successful products. If a requested field is absent from the response, call it submitted rather than API-verified; never echo the request as proof that it was stored.
263
+
264
+ `product.visible: true` proves catalog visibility, not that an unpublished site is live. Say “created” or “visible in the catalog”; say “live” only when site publication is independently confirmed.
265
+
266
+ ## Failure guide
267
+
268
+ - Inventory missing: use the with-inventory endpoint and include `inventoryItem` on each tracked variant.
269
+ - Multiple requested products sent separately: use one compatible bulk call unless they cannot form a compliant batch or specific failed items are being retried.
270
+ - `variantsInfo must not be empty`: include at least one priced variant per product.
271
+ - Option/variant mismatch: create every requested combination and reference every option once per variant.
272
+ - Media not visible: use the uploaded `wixstatic.com` URL in `media.itemsInfo.items`; do not set `media.main`.
273
+ - Digital product rejected: omit physical properties and inventory; keep price, SKU, and any supplied digital file on the variant.
@@ -144,8 +144,7 @@ const finalClientId = onDisk.clientId || clientId;
144
144
  const finalMetaSiteId = onDisk.metaSiteId || metaSiteId;
145
145
 
146
146
  if (!finalClientId || !finalMetaSiteId) {
147
- deployed.wixConfig = 'missing_ids — src/rest/wix-config.js is not configured; re-run with '
148
- + '--client-id and --metasite-id rather than editing the file by hand';
147
+ deployed.wixConfig = 'skipped — no ids provided';
149
148
  } else if (finalClientId === onDisk.clientId && finalMetaSiteId === onDisk.metaSiteId) {
150
149
  deployed.wixConfig = 'already_set';
151
150
  } else {
@@ -0,0 +1,104 @@
1
+ # Wix Managed Headless — Base44 storefront client + seed
2
+
3
+ You are building a **Wix Managed** headless storefront inside Base44 — the business is described in your
4
+ initial prompt, and the Wix connector is already configured.
5
+
6
+ Your Wix client id is in your prompt — a public, buyer-facing credential (anonymous visitor tokens
7
+ only), safe in the frontend; use it directly for the Wix client setup.
8
+
9
+ > **`wix-vibe-headless` and `wix-docs` are the complete build and seed path for this app.** **Do NOT use the Base44 commerce kit (or any Base44 solution kit).**
10
+
11
+ Follow STEPs 1–4 below exactly (run STEP 3 in parallel with STEP 2).
12
+
13
+ ## STEP 1 — Install the Wix skills and deploy the storefront vertical to the app
14
+
15
+ Install two skills — they land under `.agents/skills/`:
16
+ - **`wix-vibe-headless`** — the storefront build + seed guide (STEPs 2–3): your main source of truth.
17
+ - **`wix-docs`** — a **fallback**: search + read the Wix API docs for anything `wix-vibe-headless`
18
+ doesn't cover.
19
+
20
+ Run this through exec_tool, exactly as written — installs both skills, deploys the `storefront` REST scaffolds + UI into `src/`, and pins the AGENTS.md note.
21
+
22
+ ```js
23
+ const { execSync } = require('child_process');
24
+ const { existsSync, readdirSync } = require('fs');
25
+ const results = {};
26
+ for (const skill of ['wix-vibe-headless', 'wix-docs']) {
27
+ if (existsSync(`/app/.agents/skills/${skill}/SKILL.md`)) { results[skill] = 'already_installed'; continue; }
28
+ try {
29
+ const out = execSync(`CI=1 npx -y skills add wix/skills/skills/${skill} --yes 2>&1`,
30
+ { cwd: '/app', timeout: 60000, shell: '/bin/bash' }).toString().replace(/\x1b\[[0-9;]*m/g, '');
31
+ results[skill] = /installed 1 skill|found 1 skill/i.test(out) ? 'success'
32
+ : out.includes('No valid skills') ? 'not_found' : 'unknown';
33
+ } catch (e) { results[skill] = 'error: ' + e.message; }
34
+ }
35
+ const deploy = execSync(`node /app/.agents/skills/wix-vibe-headless/install/deploy.cjs storefront`, { cwd: '/app' }).toString();
36
+ const agentsMd = execSync(`node /app/.agents/skills/wix-vibe-headless/install/pin-agents-md.cjs`, { cwd: '/app' }).toString();
37
+ return { results, installed: readdirSync('/app/.agents/skills'), deploy: JSON.parse(deploy), agentsMd: JSON.parse(agentsMd) };
38
+ ```
39
+
40
+ Read skills with **`read_file`** using workspace-relative paths (e.g. `.agents/skills/wix-vibe-headless/SKILL.md`) — absolute `/app/...` fails. Always read from `.agents/skills/` exactly on every turn; ignore stray copies like `agent/skills/`.
41
+
42
+ ## STEP 2 — Build the client
43
+
44
+ Read `.agents/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md` and follow it **EXACTLY** — the single source of truth for how the storefront client is built.
45
+
46
+ **REST scaffolds are already in `src/rest/`** (STEP 1 deployed them). The storefront also ships a ready UI client in `src/` — theme + wire it per `INSTRUCTIONS.md`, don't rebuild. **Don't `read_file` deployed files** — every field shape is in `INSTRUCTIONS.md`; read one only on a real error or gap.
47
+
48
+ **`src/App.jsx`: edit surgically, never rewrite.** It carries required platform auth scaffolding
49
+ (`AuthProvider`/`useAuth` from `@/lib/AuthContext`); a full rewrite drops them → the validator
50
+ rejects the write. Wire routes/imports in with `find_replace`, leave the rest as-is.
51
+
52
+ ## STEP 3 — Seed the storefront
53
+
54
+ **Never delete or clean up anything on the user's site — seeding is additive only.** It's a live
55
+ user-owned business, so never delete or overwrite existing content, even apparent sample data. If a
56
+ cleanup truly seems needed, ask the user first.
57
+
58
+ Seed by calling the storefront's ready-made seed module — read
59
+ `.agents/skills/wix-vibe-headless/references/storefront/seed/SEED.md` and load its `seed-*.js` via
60
+ its loader snippet (build-time exec_tool); call its functions with your data. Gaps or an unexpected
61
+ shape → the **`wix-docs`** skill.
62
+
63
+ **Auth for these admin calls is the already-configured Wix headless connector — nothing else.** Get its
64
+ access token and send it as a bearer token:
65
+
66
+ ```js
67
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
68
+ // then: fetch(url, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" }, ... })
69
+ ```
70
+
71
+ Inline via exec_tool, `base44` is already declared — use it directly; do **not** import
72
+ `@base44/sdk`, re-declare it, or call `createClient()` (that's for standalone `.js` files only;
73
+ inline it throws *"Identifier 'base44' has already been declared."*).
74
+
75
+ **Product images.** Generate with **Base44's built-in image generation**, then attach via the
76
+ storefront seed module's image-attach step — `wix-docs` if the module doesn't cover it.
77
+
78
+ **Seed images with the FINAL url, in one call.** Use the real `https://media.base44.com/...` url
79
+ from the **completed** `generate_image` result and pass it straight into your single `setupStore`
80
+ call (images included). A still-generating `/__generating__/<id>.png` placeholder is not a real url
81
+ — Wix can't fetch it. `generate_image` runs in the background while you build the client, so the
82
+ urls are ready by the time you seed.
83
+
84
+ ## STEP 4 — Wrap up
85
+
86
+ **Do NOT add the `<WixManageBanner/>` component** and do not send the user Wix dashboard links — this flow does not include the manage banner or back-office handoff.
87
+
88
+ **Preview briefly, don't chase images.** Broken images are expected — `generate_image` returns a `/__generating__/…` placeholder that the platform swaps for the final url automatically at turn end (failures get a stock fallback). **Do NOT edit or debug image urls.** Leave them and finish.
89
+
90
+ ## Later admin requests
91
+
92
+ When the user asks for follow-up changes (next iterations), use the already-connected Wix headless connector — get an access token and call the Wix API directly:
93
+
94
+ ```js
95
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
96
+ const res = await fetch("https://www.wixapis.com/<api-path>", {
97
+ method: "POST",
98
+ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
99
+ body: JSON.stringify({ /* request body */ }),
100
+ });
101
+ return await res.json();
102
+ ```
103
+
104
+ `base44` is already declared inline in exec_tool — use it directly; do **not** import `@base44/sdk` or call `createClient()`. For what to call, check the storefront seed module (`.agents/skills/wix-vibe-headless/references/storefront/seed/SEED.md`) or look up the endpoint via `wix-docs`.
@@ -0,0 +1,74 @@
1
+ # Wix Managed Headless — Base44 storefront client
2
+
3
+ You are building a **Wix Managed** headless storefront inside Base44 — the business is described in your
4
+ initial prompt, and the Wix connector is already configured.
5
+
6
+ Your Wix client id is in your prompt — a public, buyer-facing credential (anonymous visitor tokens
7
+ only), safe in the frontend; use it directly for the Wix client setup.
8
+
9
+ > **`wix-vibe-headless` and `wix-docs` are the complete build path for this app.** **Do NOT use the Base44 commerce kit (or any Base44 solution kit).** **This flow builds the client only — there is no seeding step.**
10
+
11
+ Follow STEPs 1–3 below exactly.
12
+
13
+ ## STEP 1 — Install the Wix skills and deploy the storefront vertical to the app
14
+
15
+ Install two skills — they land under `.agents/skills/`:
16
+ - **`wix-vibe-headless`** — the storefront build guide (STEP 2): your main source of truth.
17
+ - **`wix-docs`** — a **fallback**: search + read the Wix API docs for anything `wix-vibe-headless`
18
+ doesn't cover.
19
+
20
+ Run this through exec_tool, exactly as written — installs both skills, deploys the `storefront` REST scaffolds + UI into `src/`, and pins the AGENTS.md note.
21
+
22
+ ```js
23
+ const { execSync } = require('child_process');
24
+ const { existsSync, readdirSync } = require('fs');
25
+ const results = {};
26
+ for (const skill of ['wix-vibe-headless', 'wix-docs']) {
27
+ if (existsSync(`/app/.agents/skills/${skill}/SKILL.md`)) { results[skill] = 'already_installed'; continue; }
28
+ try {
29
+ const out = execSync(`CI=1 npx -y skills add wix/skills/skills/${skill} --yes 2>&1`,
30
+ { cwd: '/app', timeout: 60000, shell: '/bin/bash' }).toString().replace(/\x1b\[[0-9;]*m/g, '');
31
+ results[skill] = /installed 1 skill|found 1 skill/i.test(out) ? 'success'
32
+ : out.includes('No valid skills') ? 'not_found' : 'unknown';
33
+ } catch (e) { results[skill] = 'error: ' + e.message; }
34
+ }
35
+ const deploy = execSync(`node /app/.agents/skills/wix-vibe-headless/install/deploy.cjs storefront`, { cwd: '/app' }).toString();
36
+ const agentsMd = execSync(`node /app/.agents/skills/wix-vibe-headless/install/pin-agents-md.cjs`, { cwd: '/app' }).toString();
37
+ return { results, installed: readdirSync('/app/.agents/skills'), deploy: JSON.parse(deploy), agentsMd: JSON.parse(agentsMd) };
38
+ ```
39
+
40
+ Read skills with **`read_file`** using workspace-relative paths (e.g. `.agents/skills/wix-vibe-headless/SKILL.md`) — absolute `/app/...` fails. Always read from `.agents/skills/` exactly on every turn; ignore stray copies like `agent/skills/`.
41
+
42
+ ## STEP 2 — Build the client
43
+
44
+ Read `.agents/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md` and follow it **EXACTLY** — the single source of truth for how the storefront client is built.
45
+
46
+ **REST scaffolds are already in `src/rest/`** (STEP 1 deployed them). The storefront also ships a ready UI client in `src/` — theme + wire it per `INSTRUCTIONS.md`, don't rebuild. **Don't `read_file` deployed files** — every field shape is in `INSTRUCTIONS.md`; read one only on a real error or gap.
47
+
48
+ **`src/App.jsx`: edit surgically, never rewrite.** It carries required platform auth scaffolding
49
+ (`AuthProvider`/`useAuth` from `@/lib/AuthContext`); a full rewrite drops them → the validator
50
+ rejects the write. Wire routes/imports in with `find_replace`, leave the rest as-is.
51
+
52
+ ## STEP 3 — Wrap up
53
+
54
+ **No seeding in this flow** — the client is the only deliverable. Do not seed, populate, or write data to Wix.
55
+
56
+ **Do NOT add the `<WixManageBanner/>` component** and do not send the user Wix dashboard links — this flow does not include the manage banner or back-office handoff.
57
+
58
+ **Preview briefly, don't chase images.** Broken images are expected — `generate_image` returns a `/__generating__/…` placeholder that the platform swaps for the final url automatically at turn end (failures get a stock fallback). **Do NOT edit or debug image urls.** Leave them and finish.
59
+
60
+ ## Later admin requests
61
+
62
+ When the user asks for follow-up changes (next iterations), use the already-connected Wix headless connector — get an access token and call the Wix API directly:
63
+
64
+ ```js
65
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
66
+ const res = await fetch("https://www.wixapis.com/<api-path>", {
67
+ method: "POST",
68
+ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
69
+ body: JSON.stringify({ /* request body */ }),
70
+ });
71
+ return await res.json();
72
+ ```
73
+
74
+ `base44` is already declared inline in exec_tool — use it directly; do **not** import `@base44/sdk` or call `createClient()`. For what to call, check the storefront seed module (`.agents/skills/wix-vibe-headless/references/storefront/seed/SEED.md`) or look up the endpoint via `wix-docs`.
@@ -106,9 +106,7 @@ its loader snippet (build-time exec_tool); call its functions with your data. Ga
106
106
  shape → the **`wix-docs`** skill.
107
107
 
108
108
  **Auth for these admin calls is the already-configured Wix connector — nothing else.** Get its
109
- access token and send it as a bearer token; do **not** hand-roll a token getter (e.g.
110
- `getAdminToken()`), install/run the Wix CLI (`@wix/cli`), or device-login (no managed-project auth
111
- flow applies to Base44):
109
+ access token and send it as a bearer token:
112
110
 
113
111
  ```js
114
112
  const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
@@ -150,3 +148,5 @@ Once the site is built and seeded:
150
148
 
151
149
  For any later admin/management request, work as in STEP 4: your vertical's seed module first, else
152
150
  `wix-docs` — all over the connector.
151
+
152
+ version: v1338
@@ -14,7 +14,7 @@ for you (Blog binds the cover by the Wix Media file id, not a url).
14
14
 
15
15
  ```js
16
16
  // build-time exec_tool
17
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
17
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
18
18
  const fs = require("fs");
19
19
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
20
20
  const seed = (() => { const m = { exports: {} };
@@ -8,7 +8,7 @@ Bookings seed operation. Load it and call **`setupBookings` — the one-call pat
8
8
 
9
9
  ```js
10
10
  // build-time exec_tool
11
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
11
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
12
12
  const fs = require("fs");
13
13
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
14
14
  const seed = (() => { const m = { exports: {} };
@@ -13,7 +13,7 @@ after create, and **publishing is one-way**. So per event: create DRAFT → (tic
13
13
 
14
14
  ```js
15
15
  // build-time exec_tool
16
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
16
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
17
17
  const fs = require("fs");
18
18
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
19
19
  const seed = (() => { const m = { exports: {} };
@@ -16,7 +16,7 @@ first, then thread their real ids into each project.
16
16
 
17
17
  ```js
18
18
  // build-time exec_tool
19
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
19
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
20
20
  const fs = require("fs");
21
21
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
22
22
  const seed = (() => { const m = { exports: {} };
@@ -9,7 +9,7 @@ plain data.
9
9
 
10
10
  ```js
11
11
  // build-time exec_tool
12
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
12
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
13
13
  const fs = require("fs");
14
14
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
15
15
  const seed = (() => { const m = { exports: {} };
@@ -17,7 +17,7 @@ across exec calls. Online ordering and table reservations run only when the plan
17
17
 
18
18
  ```js
19
19
  // build-time exec_tool
20
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
20
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
21
21
  const fs = require("fs");
22
22
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
23
23
  const seed = (() => { const m = { exports: {} };
@@ -24,12 +24,15 @@ so you don't need to open them:**
24
24
  | `context/CartContext.jsx` | `useCart()` provider: server cart, add/update/remove, checkout |
25
25
  | `hooks/useProductDetail.js` | PDP data — product + variant resolution for a slug, plus load/add state |
26
26
  | `hooks/useShop.js` | catalog listing — category menu, cursor paging, sort, failure state |
27
- | `components/ProductCard.jsx`, `ProductGrid.jsx` | product listing UI (grid + card, skeletons, empty state). The tile carries quick add for single-variant products, colour dots + an option summary, and pre-order / sold-out / limited-stock / percent-off / merchant-ribbon badges — all from the list query, no extra request |
27
+ | `hooks/useProductCard.js` | headless data layer for a grid tile — returns `leftBadges`, `promoBadge`, `priceDisplay`, `compareAtDisplay`, `colors`, `optionLabel`, `isQuickAddable`, `image`, `hoverImage`; **always use this to build your own product card UI on the grid** |
28
+ | `components/ProductCard.jsx` | reference implementation of a grid tile built on `useProductCard` — read it for inspiration, build your own component rather than using it directly |
29
+ | `components/ProductGrid.jsx` | reference grid layout (2-col mobile → auto-fill desktop) with skeleton + empty state — read it for the skeleton/empty-state patterns, build your own layout rather than using it directly |
28
30
  | `components/ProductGallery.jsx` | PDP main image + thumbnails |
29
31
  | `lib/storeImage.js` | `productImage()` / `productGallery()` / `storeImage()` — normalise Wix image urls |
30
32
  | `components/CartButton.jsx` | header cart **icon** button with a live-count badge |
31
33
  | `components/CartDrawer.jsx` | slide-over cart (mount once; opens from `useCart`) |
32
- | `components/VariantPicker.jsx` | option/variant selector used on the PDP — colour options render as real swatches, and picking one moves the gallery to that colour's photo |
34
+ | `hooks/useVariantOptions.js` | headless data layer for options/modifiers — returns `optionGroups` + `modifierGroups` (normalised, render-agnostic); **always use this to build your own variant UI on the PDP** |
35
+ | `components/VariantPicker.jsx` | reference implementation of a variant selector built on `useVariantOptions` (pills + colour swatches) — read it for inspiration, but build your own component rather than using it directly |
33
36
  | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
34
37
  | `pages/Shop.jsx`, `pages/ProductDetail.jsx` | the two shipped routes (`/shop`, `/product/:slug`) |
35
38
  | `rest/wix-config.js` | the two ids, written by the install step |
@@ -70,6 +73,69 @@ replace it.
70
73
  banner is dismissed.
71
74
  - Routes under the Layout: `/shop` → `Shop`, `/product/:slug` → `ProductDetail` (both shipped, as-is).
72
75
  **You add `/` → your own Home** page.
76
+ - **Build your own variant selector on the PDP — this is required, not optional, and it's your chance to be creative.** `ProductDetail.jsx` ships with a `VariantPicker` import — remove it and replace with your own component built on `useVariantOptions`. Design the controls to fit the brief: the business type, the tone, the audience. A fashion brand might want large colour swatches and a size chart link; a tech store might want a compact dropdown. `VariantPicker.jsx` is in `src/` for reference — read it, don't use it:
77
+ - **Build your own product card for the grid — this is required, not optional, and it's your chance to be creative.** `ProductGrid.jsx` ships with a `ProductCard` import — replace it with your own component built on `useProductCard`. The hook hands you everything the tile needs (badges, price display, colour dots, quick-add flag, images) — you decide the layout, shape, hover behaviour, and CTA style. A lifestyle brand might want full-bleed images with an overlay gradient; a tech store might want a compact horizontal list item. `ProductCard.jsx` is in `src/` for reference — read it, don't use it:
78
+ - **Build your own grid layout — this is required, not optional, and it's your chance to be creative.** `ProductGrid.jsx` ships as a reference (2-col mobile → auto-fill desktop, 220px min) — replace it with an arrangement that fits the brief. A curated boutique might want a 3-col asymmetric editorial layout; a high-volume store might want a dense 4-col grid; a featured strip on the home page might want horizontal scroll. Keep the skeleton and empty-state patterns from `ProductGrid.jsx` (copy them into your own component) — the states themselves are correct, just the layout is yours to choose.
79
+
80
+ ```jsx
81
+ import { Link } from "react-router-dom";
82
+ import { useCart } from "@/context/CartContext";
83
+ import { useProductCard } from "@/hooks/useProductCard";
84
+
85
+ export default function MyProductCard({ product }) {
86
+ const { addToCart } = useCart();
87
+ const {
88
+ isSoldOut, isPreorder,
89
+ leftBadges, // [{ type: 'pre-order'|'sold-out'|'limited-stock', label }] — render left side of image
90
+ promoBadge, // { type: 'discount'|'ribbon', label } | null — render right side
91
+ priceDisplay, // "€10" or "€10 – €20" (range when variants differ)
92
+ compareAtDisplay, // original price string | null
93
+ colors, // hex strings → render as dots (slice to how many you want)
94
+ optionLabel, // "3 sizes · 2 materials" or empty string
95
+ isQuickAddable, // true for single-variant, in-stock products
96
+ image, // primary image URL | null
97
+ hoverImage, // second image URL | null
98
+ } = useProductCard(product);
99
+
100
+ // Then render however you want:
101
+ return (
102
+ <div>
103
+ {/* image, badges, price, colour dots, quick-add or "Choose options" CTA */}
104
+ {isQuickAddable && <button onClick={() => addToCart(product.id)}>Quick add</button>}
105
+ {!isQuickAddable && !isSoldOut && <Link to={`/product/${product.slug}`}>Choose options</Link>}
106
+ {isSoldOut && isPreorder && <Link to={`/product/${product.slug}`}>Pre-order</Link>}
107
+ </div>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ```jsx
113
+ import { useVariantOptions } from "@/hooks/useVariantOptions";
114
+
115
+ // options/modifiers/selectedOptions/modifierValues come from useProductDetail:
116
+ const { optionGroups, modifierGroups } = useVariantOptions(options, modifiers, selectedOptions, modifierValues);
117
+
118
+ // optionGroups: [{ id, name, isColor, choices: [{ choiceId, name, colorCode, isColorSwatch, inStock, selected }] }]
119
+ // modifierGroups: [{ key, name, mandatory, type: 'choices'|'text', choices?: [{ key, name, selected }], value?: string }]
120
+
121
+ // Then render however you want:
122
+ optionGroups.map((group) =>
123
+ group.choices.map((c) =>
124
+ c.isColorSwatch
125
+ ? <MySwatch key={c.choiceId} color={c.colorCode} active={c.selected} disabled={!c.inStock}
126
+ onClick={() => selectOption(group.id, c.choiceId)} />
127
+ : <MyPill key={c.choiceId} active={c.selected} disabled={!c.inStock}
128
+ onClick={() => selectOption(group.id, c.choiceId)}>{c.name}</MyPill>
129
+ )
130
+ );
131
+ modifierGroups.map((m) =>
132
+ m.type === "text"
133
+ ? <MyInput key={m.key} label={m.name} value={m.value} onChange={(v) => setModifier(m.key, v)} />
134
+ : m.choices.map((c) =>
135
+ <MyPill key={c.key} active={c.selected} onClick={() => setModifier(m.key, c.key)}>{c.name}</MyPill>
136
+ )
137
+ );
138
+ ```
73
139
 
74
140
  ```jsx
75
141
  import { useRef, useState, useEffect } from "react";