@wix/agent-skills 1.0.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.
Files changed (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/package.json +29 -0
  4. package/skills/wix-app/SKILL.md +344 -0
  5. package/skills/wix-app/references/APP_IDENTIFIERS.md +35 -0
  6. package/skills/wix-app/references/APP_MARKET_REVIEW.md +311 -0
  7. package/skills/wix-app/references/APP_VALIDATION.md +171 -0
  8. package/skills/wix-app/references/BACKEND_API.md +233 -0
  9. package/skills/wix-app/references/BACKEND_EVENT.md +116 -0
  10. package/skills/wix-app/references/CODE_QUALITY.md +33 -0
  11. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +587 -0
  12. package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +157 -0
  13. package/skills/wix-app/references/DASHBOARD_MODAL.md +283 -0
  14. package/skills/wix-app/references/DASHBOARD_PAGE.md +403 -0
  15. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +205 -0
  16. package/skills/wix-app/references/DATA_COLLECTION.md +451 -0
  17. package/skills/wix-app/references/DOCUMENTATION.md +42 -0
  18. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +65 -0
  19. package/skills/wix-app/references/EMBEDDED_SCRIPT.md +468 -0
  20. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +96 -0
  21. package/skills/wix-app/references/SERVICE_PLUGIN.md +235 -0
  22. package/skills/wix-app/references/SITE_PLUGIN.md +436 -0
  23. package/skills/wix-app/references/STORES_VERSIONING.md +334 -0
  24. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +84 -0
  25. package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +345 -0
  26. package/skills/wix-app/references/dashboard-menu-plugin/blog-slots.md +71 -0
  27. package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +58 -0
  28. package/skills/wix-app/references/dashboard-menu-plugin/crm-slots.md +8 -0
  29. package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +17 -0
  30. package/skills/wix-app/references/dashboard-menu-plugin/events-slots.md +29 -0
  31. package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +27 -0
  32. package/skills/wix-app/references/dashboard-menu-plugin/stores-slots.md +35 -0
  33. package/skills/wix-app/references/dashboard-page/API_SPEC.md +138 -0
  34. package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +573 -0
  35. package/skills/wix-app/references/dashboard-page/DYNAMIC_PARAMETERS.md +269 -0
  36. package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +164 -0
  37. package/skills/wix-app/references/dashboard-plugin/SLOTS.md +294 -0
  38. package/skills/wix-app/references/data-collection/WIX_DATA.md +270 -0
  39. package/skills/wix-app/references/data-collection/extension-template.ts +107 -0
  40. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +138 -0
  41. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +454 -0
  42. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +165 -0
  43. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +576 -0
  44. package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +170 -0
  45. package/skills/wix-app/references/editor-react-component/EDIT-FLOW.md +6 -0
  46. package/skills/wix-app/references/editor-react-component/PARTS.md +25 -0
  47. package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +203 -0
  48. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +167 -0
  49. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +137 -0
  50. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +112 -0
  51. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +118 -0
  52. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +87 -0
  53. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +86 -0
  54. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +135 -0
  55. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +79 -0
  56. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +68 -0
  57. package/skills/wix-app/references/site-plugin/EXAMPLES.md +311 -0
  58. package/skills/wix-app/references/site-plugin/SLOTS.md +263 -0
  59. package/skills/wix-design-system/SKILL.md +129 -0
  60. package/skills/wix-design-system/references/file-structure.md +56 -0
  61. package/skills/wix-design-system/scripts/wds.cjs +531 -0
  62. package/skills/wix-headless/SKILL.md +418 -0
  63. package/skills/wix-headless/references/DISCOVERY.md +332 -0
  64. package/skills/wix-headless/references/ORCHESTRATION.md +269 -0
  65. package/skills/wix-headless/references/SETUP.md +154 -0
  66. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +152 -0
  67. package/skills/wix-headless/references/blog/BLOG_PAGES.md +299 -0
  68. package/skills/wix-headless/references/blog/BLOG_SETUP.md +218 -0
  69. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +32 -0
  70. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +373 -0
  71. package/skills/wix-headless/references/cms/FAQ_KNOWLEDGE_BASE.md +229 -0
  72. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +40 -0
  73. package/skills/wix-headless/references/cms/PORTFOLIO.md +454 -0
  74. package/skills/wix-headless/references/cms/RESOURCE_LIBRARY.md +356 -0
  75. package/skills/wix-headless/references/cms/TEAM_DIRECTORY.md +299 -0
  76. package/skills/wix-headless/references/commands/install-app.md +42 -0
  77. package/skills/wix-headless/references/commands/known-apps.json +5 -0
  78. package/skills/wix-headless/references/commands/mcp-bootstrap.md +28 -0
  79. package/skills/wix-headless/references/designer/INSTRUCTIONS.md +828 -0
  80. package/skills/wix-headless/references/ecom/CART_PAGES.md +120 -0
  81. package/skills/wix-headless/references/ecom/CART_WIRING.md +103 -0
  82. package/skills/wix-headless/references/ecom/COMPONENTS_CSS.md +132 -0
  83. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +59 -0
  84. package/skills/wix-headless/references/forms/CONTACT_FORM.md +477 -0
  85. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +43 -0
  86. package/skills/wix-headless/references/gift-cards/COMPONENTS.md +119 -0
  87. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +51 -0
  88. package/skills/wix-headless/references/gift-cards/PAGES.md +129 -0
  89. package/skills/wix-headless/references/images/INSTRUCTIONS.md +359 -0
  90. package/skills/wix-headless/references/shared/BUILD_NOISE.md +8 -0
  91. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +185 -0
  92. package/skills/wix-headless/references/shared/IMPLEMENTER.md +155 -0
  93. package/skills/wix-headless/references/shared/MCP_PREFIX.md +105 -0
  94. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +97 -0
  95. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +423 -0
  96. package/skills/wix-headless/references/shared/STYLING.md +117 -0
  97. package/skills/wix-headless/references/stores/BACK_IN_STOCK.md +178 -0
  98. package/skills/wix-headless/references/stores/CATEGORY_PAGES.md +86 -0
  99. package/skills/wix-headless/references/stores/COMPONENTS_CSS.md +129 -0
  100. package/skills/wix-headless/references/stores/HOME_AND_NAV.md +227 -0
  101. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +96 -0
  102. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +249 -0
  103. package/skills/wix-headless/references/stores/PRODUCT_PAGES.md +213 -0
  104. package/skills/wix-headless/references/stores/SHARED_WIRING.md +164 -0
  105. package/skills/wix-headless/references/verticals/_schema.json +160 -0
  106. package/skills/wix-headless/references/verticals/_schema.md +185 -0
  107. package/skills/wix-headless/references/verticals/blog.md +125 -0
  108. package/skills/wix-headless/references/verticals/cms.md +121 -0
  109. package/skills/wix-headless/references/verticals/ecom.md +96 -0
  110. package/skills/wix-headless/references/verticals/forms.md +106 -0
  111. package/skills/wix-headless/references/verticals/gift-cards.md +121 -0
  112. package/skills/wix-headless/references/verticals/stores.md +167 -0
  113. package/skills/wix-headless/scripts/check-manifest.mjs +198 -0
  114. package/skills/wix-headless/scripts/copy-utility-templates.mjs +153 -0
  115. package/skills/wix-headless/scripts/emit-design-tokens.mjs +77 -0
  116. package/skills/wix-headless/scripts/finalize-run-json.mjs +141 -0
  117. package/skills/wix-headless/scripts/init-site-json.mjs +63 -0
  118. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +124 -0
  119. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +190 -0
  120. package/skills/wix-headless/scripts/preview.sh +45 -0
  121. package/skills/wix-headless/scripts/release.sh +73 -0
  122. package/skills/wix-headless/scripts/scaffold.sh +53 -0
  123. package/skills/wix-headless/scripts/seed-utilities.sh +55 -0
  124. package/skills/wix-headless/shared-utilities/analytics.ts +40 -0
  125. package/skills/wix-headless/shared-utilities/ricos.ts +179 -0
  126. package/skills/wix-headless/shared-utilities/wix-image.ts +72 -0
  127. package/skills/wix-headless/templates/ecom/CartBadge.tsx +84 -0
  128. package/skills/wix-headless/templates/ecom/CartView.tsx +543 -0
  129. package/skills/wix-headless/templates/ecom/cart.astro +13 -0
  130. package/skills/wix-headless/templates/ecom/components-ecom.css +215 -0
  131. package/skills/wix-headless/templates/ecom/discounts.ts +71 -0
  132. package/skills/wix-headless/templates/ecom/thank-you.astro +24 -0
  133. package/skills/wix-headless/templates/gift-cards/GiftCardPurchase.tsx +172 -0
  134. package/skills/wix-headless/templates/gift-cards/_home-teaser-snippet.astro +42 -0
  135. package/skills/wix-headless/templates/gift-cards/_nav-snippet.astro +11 -0
  136. package/skills/wix-headless/templates/gift-cards/components-gift-cards.css +219 -0
  137. package/skills/wix-headless/templates/gift-cards/gift-cards.astro +51 -0
  138. package/skills/wix-headless/templates/gift-cards/gift-cards.ts +75 -0
  139. package/skills/wix-headless/templates/stores/AddToCartButton.tsx +77 -0
  140. package/skills/wix-headless/templates/stores/BackInStockForm.tsx +176 -0
  141. package/skills/wix-headless/templates/stores/CategoryRail.astro +69 -0
  142. package/skills/wix-headless/templates/stores/ProductCard.astro +79 -0
  143. package/skills/wix-headless/templates/stores/ProductPurchase.tsx +397 -0
  144. package/skills/wix-headless/templates/stores/SeoTags.astro +21 -0
  145. package/skills/wix-headless/templates/stores/back-in-stock.ts +67 -0
  146. package/skills/wix-headless/templates/stores/categories.ts +155 -0
  147. package/skills/wix-headless/templates/stores/category/[slug].astro +130 -0
  148. package/skills/wix-headless/templates/stores/components-stores.css +295 -0
  149. package/skills/wix-headless/templates/stores/products/[slug].astro +128 -0
  150. package/skills/wix-headless/templates/stores/products/index.astro +101 -0
  151. package/skills/wix-manage/SKILL.md +280 -0
  152. package/skills/wix-manage/references/app-installation/install-wix-apps.md +155 -0
  153. package/skills/wix-manage/references/app-installation/list-installed-apps.md +124 -0
  154. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +226 -0
  155. package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +95 -0
  156. package/skills/wix-manage/references/bookings/booking-system-integration-gaps.md +416 -0
  157. package/skills/wix-manage/references/bookings/bookings-staff-setup.md +255 -0
  158. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +462 -0
  159. package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +278 -0
  160. package/skills/wix-manage/references/bookings/external-calendar-integration.md +258 -0
  161. package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +231 -0
  162. package/skills/wix-manage/references/calendar/configure-default-business-hours.md +226 -0
  163. package/skills/wix-manage/references/cms/cms-data-items-crud.md +422 -0
  164. package/skills/wix-manage/references/cms/cms-data-operations-extended.md +263 -0
  165. package/skills/wix-manage/references/cms/cms-ecommerce-catalog-integration.md +136 -0
  166. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +160 -0
  167. package/skills/wix-manage/references/cms/cms-schema-management.md +143 -0
  168. package/skills/wix-manage/references/contacts/bulk-delete-contacts.md +73 -0
  169. package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +67 -0
  170. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +434 -0
  171. package/skills/wix-manage/references/ecommerce/api-recommendation-tracking.md +420 -0
  172. package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +267 -0
  173. package/skills/wix-manage/references/ecommerce/flow-bundle-and-save.md +290 -0
  174. package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +300 -0
  175. package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +364 -0
  176. package/skills/wix-manage/references/ecommerce/flow-seasonal-promotion.md +315 -0
  177. package/skills/wix-manage/references/ecommerce/flow-stock-mover.md +308 -0
  178. package/skills/wix-manage/references/ecommerce/flow-upsell-boost.md +271 -0
  179. package/skills/wix-manage/references/ecommerce/goal-clear-inventory.md +118 -0
  180. package/skills/wix-manage/references/ecommerce/goal-drive-cross-sells.md +122 -0
  181. package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +132 -0
  182. package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +148 -0
  183. package/skills/wix-manage/references/ecommerce/goal-seasonal-revenue.md +146 -0
  184. package/skills/wix-manage/references/ecommerce/guardrail-discount-conflicts.md +118 -0
  185. package/skills/wix-manage/references/ecommerce/guardrail-margin-protection.md +88 -0
  186. package/skills/wix-manage/references/ecommerce/guardrail-rate-pricing-sanity.md +108 -0
  187. package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +124 -0
  188. package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +299 -0
  189. package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +582 -0
  190. package/skills/wix-manage/references/ecommerce/setup-coupons.md +338 -0
  191. package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +444 -0
  192. package/skills/wix-manage/references/ecommerce/setup-shipping-rates.md +86 -0
  193. package/skills/wix-manage/references/ecommerce/setup-shipping-regions.md +56 -0
  194. package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +209 -0
  195. package/skills/wix-manage/references/ecommerce/skill-graph.md +139 -0
  196. package/skills/wix-manage/references/ecommerce/troubleshoot-checkout-delivery-dropoff.md +124 -0
  197. package/skills/wix-manage/references/ecommerce/troubleshoot-discount-not-applying.md +95 -0
  198. package/skills/wix-manage/references/events/list-events.md +203 -0
  199. package/skills/wix-manage/references/forms/create-form.md +260 -0
  200. package/skills/wix-manage/references/get-paid/create-payment-links.md +212 -0
  201. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +57 -0
  202. package/skills/wix-manage/references/get-paid/payment-links-for-bookings.md +101 -0
  203. package/skills/wix-manage/references/media/upload-media-to-wix.md +216 -0
  204. package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +77 -0
  205. package/skills/wix-manage/references/pricing-plans/pricing-plans-bookings-integration.md +166 -0
  206. package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +336 -0
  207. package/skills/wix-manage/references/rich-content/ricos-converter-service.md +295 -0
  208. package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +59 -0
  209. package/skills/wix-manage/references/sites/create-site-from-template.md +183 -0
  210. package/skills/wix-manage/references/sites/query-sites.md +125 -0
  211. package/skills/wix-manage/references/stores/add-store-pages-to-site.md +57 -0
  212. package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +1041 -0
  213. package/skills/wix-manage/references/stores/create-product-catalog-v1.md +125 -0
  214. package/skills/wix-manage/references/stores/create-product-from-image.md +824 -0
  215. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +566 -0
  216. package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +194 -0
  217. package/skills/wix-manage/references/stores/query-products-catalog-v1.md +80 -0
  218. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +99 -0
  219. package/skills/wix-manage/references/stores/update-product-pre-order.md +213 -0
  220. package/skills/wix-manage/references/stores/update-product-with-options.md +318 -0
@@ -0,0 +1,477 @@
1
+ # Recipe: Contact / Lead / Signup Form
2
+
3
+ Build a complete form feature using `@wix/forms` — server-side schema fetching + client-side submission.
4
+
5
+ > **Critical Rules — Read Before Starting**
6
+ > 1. **Style migration mandatory** — when replacing `.astro` placeholder with a React island, copy the `<style is:global>` block to the page file or all form styles vanish (invisible inputs on dark sites).
7
+ > 2. **Form creation is TWO steps** — POST to create, then PATCH to add `postSubmissionTriggers`. Skipping the PATCH means submissions are silently lost (no CRM Contact created).
8
+ > 3. **Namespace propagation** — if form creation fails with `UNSUPPORTED_FORM_NAMESPACE`, wait 10s and retry up to 3 times. Do NOT fall back to hardcoded fields — a form without a `formId` cannot submit to Wix.
9
+ > 4. **Discover forms via SDK** — use `forms.listForms("wix.form_app.form")` with `auth.elevate`. Never hardcode form field IDs or form ID.
10
+
11
+ > **⚠️ CRITICAL — Style Migration Required:** When you replace the designed `.astro` form placeholder with a React island, the placeholder's `<style is:global>` block is no longer rendered and ALL form styles vanish. You MUST copy the entire `<style is:global>` block from the designed component into the page file. Without this, form inputs are invisible on dark sites. See Step 1.4 below.
12
+
13
+ ## Prerequisites
14
+
15
+ - Wix Forms app must be installed on the site (via MCP after scaffolding, or manually in Wix dashboard)
16
+ - `@wix/forms` package installed: `npm install @wix/forms`
17
+
18
+ > **Forms are auto-created during scaffolding.** When scaffolding with a forms template (e.g., the Registration template `e5d63bf1-cd06-48eb-ad77-0da9235adcf1`), a form is automatically created on the site with server-side privileges. Use `forms.listForms("wix.form_app.form")` with `auth.elevate` to discover it — no manual form ID needed. If no forms exist, use the MCP-assisted Form Setup below to create one (the Create Form API is not available to headless SDK calls, but MCP credentials can create forms).
19
+
20
+ ## Form Setup (MCP-Assisted)
21
+
22
+ Before writing any form code, ensure a contact form exists on the site via MCP:
23
+
24
+ 1. **List forms** — `CallWixSiteAPI: GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form`
25
+ 2. **If the API returns a "missing installed app" error** → install the Wix Forms app:
26
+ ```
27
+ CallWixSiteAPI: POST https://www.wixapis.com/apps-installer-service/v1/app-instance/install
28
+ body: {
29
+ "tenant": { "tenantType": "SITE", "id": "<siteId>" },
30
+ "appInstance": { "appDefId": "225dd912-7dea-4738-8688-4b8c6955ffc2", "enabled": true }
31
+ }
32
+ ```
33
+ > Translate this prose-HTTP form into the full `CallWixSiteAPI` tool-call shape — include `siteId`, `reason`, `sourceDocUrl` wrapper fields and pass `body` as a real object (NOT a stringified JSON). See `../../shared/MCP_PREFIX.md` § "CallWixSiteAPI call conventions".
34
+
35
+ Then retry listing forms.
36
+ 3. **If forms list is empty** → create a form (two-step atomic operation):
37
+ - **Step 3a:** POST to create the form (fields, layout, submit settings)
38
+ - **Step 3b:** Immediately PATCH to add `postSubmissionTriggers` (the POST silently drops this field — see step 4)
39
+ - Both steps must succeed. If 3a succeeds but 3b fails, log Status as `partial`.
40
+
41
+ > **Namespace propagation:** If form creation fails with `UNSUPPORTED_FORM_NAMESPACE` immediately after installing the Forms app, the namespace hasn't propagated yet. Wait 10 seconds and retry up to 3 times. Do NOT silently fall back to static field definitions — forms without a `formId` cannot submit to Wix.
42
+
43
+ Select only the fields needed for the form's purpose.
44
+
45
+ **Field building blocks** — assemble the `formFields` array from these individual definitions:
46
+
47
+ ```jsonc
48
+ // EMAIL (required for all form types)
49
+ {
50
+ "id": "191b2fae-7313-467a-9d4f-fd85598b1bdb",
51
+ "hidden": false, "identifier": "CONTACTS_EMAIL", "fieldType": "INPUT",
52
+ "inputOptions": {
53
+ "target": "email", "pii": true, "required": true, "inputType": "STRING", "readOnly": false,
54
+ "stringOptions": { "validation": { "format": "EMAIL", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "Email", "showLabel": true } }
55
+ }
56
+ }
57
+
58
+ // FIRST_NAME
59
+ {
60
+ "id": "c16f3def-fc74-4950-3b72-46dbdc132a90",
61
+ "hidden": false, "identifier": "CONTACTS_FIRST_NAME", "fieldType": "INPUT",
62
+ "inputOptions": {
63
+ "target": "first_name", "pii": true, "required": false, "inputType": "STRING", "readOnly": false,
64
+ "stringOptions": { "validation": { "format": "UNKNOWN_FORMAT", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "First name", "showLabel": true } }
65
+ }
66
+ }
67
+
68
+ // LAST_NAME
69
+ {
70
+ "id": "84242617-c855-486f-4ec6-87443210fb4f",
71
+ "hidden": false, "identifier": "CONTACTS_LAST_NAME", "fieldType": "INPUT",
72
+ "inputOptions": {
73
+ "target": "last_name", "pii": true, "required": false, "inputType": "STRING", "readOnly": false,
74
+ "stringOptions": { "validation": { "format": "UNKNOWN_FORMAT", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "Last name", "showLabel": true } }
75
+ }
76
+ }
77
+
78
+ // PHONE
79
+ {
80
+ "id": "5f5995cc-2951-4ce9-ae89-82ff487b1619",
81
+ "hidden": false, "identifier": "CONTACTS_PHONE", "fieldType": "INPUT",
82
+ "inputOptions": {
83
+ "target": "phone", "pii": true, "required": false, "inputType": "STRING", "readOnly": false,
84
+ "stringOptions": { "validation": { "format": "PHONE", "enum": [] }, "componentType": "PHONE_INPUT", "phoneInputOptions": { "label": "Phone", "showLabel": true } }
85
+ }
86
+ }
87
+
88
+ // MESSAGE
89
+ // ⚠️ **Naming trap:** The MESSAGE field uses `identifier: "TEXT_AREA"` but its
90
+ // `componentType` MUST be `"TEXT_INPUT"`. The API will reject `componentType: "TEXT_AREA"`
91
+ // — that enum value does not exist in the Forms API. The `identifier` controls how the
92
+ // Wix dashboard renders the field; `componentType` controls API schema validation.
93
+ // Do not confuse the two.
94
+ {
95
+ "id": "ce6fc634-bc35-48ef-6f01-c2cded75d836",
96
+ "hidden": false, "identifier": "TEXT_AREA", "fieldType": "INPUT",
97
+ "inputOptions": {
98
+ "target": "message", "pii": false, "required": false, "inputType": "STRING", "readOnly": false,
99
+ "stringOptions": { "validation": { "format": "UNKNOWN_FORMAT", "enum": [] }, "componentType": "TEXT_INPUT", "textInputOptions": { "label": "Message", "showLabel": true } }
100
+ }
101
+ }
102
+
103
+ // SUBMIT BUTTON (always include as the last field)
104
+ {
105
+ "id": "fe946b52-9719-4c57-57e8-a0791c29344f",
106
+ "hidden": false, "identifier": "SUBMIT_BUTTON", "fieldType": "DISPLAY",
107
+ "displayOptions": { "displayFieldType": "PAGE_NAVIGATION", "pageNavigationOptions": { "nextPageText": "Next", "previousPageText": "Back", "submitText": "Submit" } }
108
+ }
109
+ ```
110
+
111
+ **Assembly by purpose:**
112
+
113
+ | Purpose | Fields to include | Layout rows |
114
+ |---------|------------------|-------------|
115
+ | Waitlist / newsletter | EMAIL + SUBMIT | email row 0, submit row 1 |
116
+ | Lead capture | FIRST_NAME + EMAIL + SUBMIT | first_name row 0, email row 1, submit row 2 |
117
+ | Contact (full) | FIRST_NAME + LAST_NAME + EMAIL + PHONE + MESSAGE + SUBMIT | first+last row 0 (6+6 cols), email row 1, phone row 2, message row 3, submit row 4 |
118
+
119
+ Build the `steps[0].layout.large.items` array to match — each field gets a `{ fieldId, row, column, width: 12, height: 1 }` entry. For side-by-side fields (first + last name), use `width: 6` with `column: 0` and `column: 6`.
120
+
121
+ The `postSubmissionTriggers.upsertContact.fieldsMapping` must only include mappings for fields that exist on the form. For a waitlist form with email only:
122
+ ```json
123
+ "fieldsMapping": {
124
+ "email": { "contactField": "EMAIL", "emailInfo": { "tag": "UNTAGGED" } }
125
+ }
126
+ ```
127
+
128
+ Full MCP call structure:
129
+ ```
130
+ CallWixSiteAPI: POST https://www.wixapis.com/form-schema-service/v4/forms
131
+ body: {
132
+ "form": {
133
+ "formFields": [ <selected fields from above> ],
134
+ "steps": [{ "id": "cfa7a3ed-ef11-4ae7-3a5b-7d450c86d217", "name": "Page 1", "hidden": false,
135
+ "layout": { "large": { "items": [ <layout entries matching selected fields> ] }}
136
+ }],
137
+ "name": "<Form Name matching purpose>",
138
+ "namespace": "wix.form_app.form",
139
+ "spamFilterProtectionLevel": "ADVANCED",
140
+ "enabled": true,
141
+ "postSubmissionTriggers": {
142
+ "upsertContact": {
143
+ "fieldsMapping": { <only mappings for included contact fields> },
144
+ "labels": []
145
+ }
146
+ },
147
+ "submitSettings": {
148
+ "submitSuccessAction": "THANK_YOU_MESSAGE",
149
+ "thankYouMessageOptions": {
150
+ "durationInSeconds": 8,
151
+ "richContent": { "nodes": [{ "type": "PARAGRAPH", "id": "krhc065", "nodes": [{ "type": "TEXT", "id": "", "nodes": [], "textData": { "text": "Thanks, we received your submission.", "decorations": [] } }], "paragraphData": { "textStyle": { "textAlignment": "CENTER" } } }] }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ ```
157
+ 4. **Always PATCH `postSubmissionTriggers` after creation** — The creation API silently drops this field. Treat form creation as a two-step atomic operation:
158
+ - Step A: `POST /forms` to create the form
159
+ - Step B: `PATCH /forms/<formId>` to add `postSubmissionTriggers`
160
+ Never skip Step B, even if you included triggers in the creation payload. The cost of a redundant PATCH is zero; the cost of missing triggers is silent data loss (submissions recorded but no CRM contact created).
161
+ - GET the form: `GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form`
162
+ - PATCH using the procedure in step 5 below
163
+ 5. **If forms already exist** → verify the form has `postSubmissionTriggers.upsertContact`. This field is **critical** — without it, submissions are recorded but no Contact is created in the CRM. If missing, patch the form to add it:
164
+ ```
165
+ CallWixSiteAPI: PATCH https://www.wixapis.com/form-schema-service/v4/forms/<formId>
166
+ body: {
167
+ "form": {
168
+ "revision": "<current revision from GET>",
169
+ "postSubmissionTriggers": {
170
+ "upsertContact": {
171
+ "fieldsMapping": {
172
+ <map each contact field target in the form, e.g.:>
173
+ "email": { "contactField": "EMAIL", "emailInfo": { "tag": "UNTAGGED" } },
174
+ "first_name": { "contactField": "FIRST_NAME" },
175
+ "last_name": { "contactField": "LAST_NAME" },
176
+ "phone": { "contactField": "PHONE", "phoneInfo": { "tag": "UNTAGGED" } }
177
+ },
178
+ "labels": []
179
+ }
180
+ }
181
+ }
182
+ }
183
+ ```
184
+ Only include mappings for fields that exist on the form. The `fieldsMapping` keys must match the `inputOptions.target` values of the form's fields.
185
+
186
+ ## Files to Modify / Create
187
+
188
+ | File | Action |
189
+ |------|--------|
190
+ | Existing designed page (e.g., `src/pages/contact.astro`) | **Modify in place** — add SDK imports and replace placeholder form with React island |
191
+ | `src/components/ContactForm.tsx` | **Create** — React island for client-side submission |
192
+
193
+ ## Implementation
194
+
195
+ ### 1. Modify the Existing Designed Page
196
+
197
+ **Do NOT create a new page from scratch.** The design skill already created this page with branded styling, layout, and a `<style>` block. Modify it in place:
198
+
199
+ 1. **Add SDK imports** to the frontmatter:
200
+ ```astro
201
+ import { forms } from "@wix/forms";
202
+ import { auth } from "@wix/essentials";
203
+ import ContactForm from "../components/ContactForm.tsx";
204
+ ```
205
+
206
+ 2. **Add the form discovery query** to the frontmatter:
207
+ ```astro
208
+ const elevatedListForms = auth.elevate(forms.listForms);
209
+ const listResult = await elevatedListForms("wix.form_app.form");
210
+ const wixForm = listResult.forms?.[0];
211
+ const formId = wixForm?._id ?? "";
212
+
213
+ const formFields = wixForm?.formFields
214
+ ?.filter((field) => field.fieldType === "INPUT" && !field.hidden)
215
+ .map((field) => {
216
+ const opts = field.inputOptions;
217
+ const str = opts?.stringOptions;
218
+ const label =
219
+ str?.textInputOptions?.label ??
220
+ str?.dropdownOptions?.label ??
221
+ str?.phoneInputOptions?.label ??
222
+ "";
223
+ return {
224
+ label,
225
+ target: opts?.target ?? "",
226
+ required: opts?.required ?? false,
227
+ componentType: str?.componentType ?? "TEXT_INPUT",
228
+ identifier: field.identifier ?? "",
229
+ options: str?.dropdownOptions?.options?.map((o) => ({
230
+ value: o.value ?? o.label ?? "",
231
+ label: o.label ?? o.value ?? "",
232
+ })),
233
+ };
234
+ }) ?? [];
235
+ ```
236
+
237
+ 3. **Replace the placeholder form HTML** with the React island:
238
+ ```astro
239
+ {formId ? (
240
+ <ContactForm client:load formId={formId} fields={formFields} />
241
+ ) : (
242
+ <p>No form found. Create one in the Wix dashboard.</p>
243
+ )}
244
+ ```
245
+
246
+ 4. **Migrate the form's `<style is:global>` block into the page.**
247
+
248
+ The designed Astro placeholder component (e.g., `ContactForm.astro`) contains a `<style is:global>` block with all form CSS. Once you replace the Astro import with the React island, that component is no longer rendered and its styles vanish.
249
+
250
+ - Open the designed Astro placeholder component and copy its entire `<style is:global>...</style>` block
251
+ - Paste it into the page file, below any existing `<style>` block
252
+ - Keep both `<style>` blocks — the page's scoped styles handle layout, the `is:global` block handles form elements inside the React island
253
+ - Do NOT remove or modify the page's existing `<style>` block
254
+
255
+ **Why `is:global` is required:** Astro scoped styles use `data-astro-cid-*` attributes that don't transfer to React-rendered DOM. The form styles must be global to reach elements inside the React island.
256
+
257
+ ### 2. Contact Form Component (`src/components/ContactForm.tsx`)
258
+
259
+ The React island must match the designed component's class names and use CSS variables for all visual properties. Two approaches:
260
+
261
+ **Approach A (preferred): Page provides styles via `<style>` block.** The component reuses the class names from the designed placeholder. The page's existing `<style>` block handles all visual styling. The component only needs minimal inline styles for dynamic states.
262
+
263
+ **Approach B: Component owns its styles using CSS variables.** If the page doesn't have a `<style>` block for the form, the component includes a `<style>` JSX element using only CSS custom properties.
264
+
265
+ > **Styling note:** The React island uses the styling contract class names (`.form-container`, `.form-field`, `.form-label`, `.form-input`, `.form-textarea`, `.form-button`, `.form-success`, `.form-error`, `.form-field-error`, `.form-input-error`) from the designed ContactForm component's `<style is:global>` block. See the design skill's `COMPONENT_PATTERNS.md` → Contact Form.
266
+
267
+ ```tsx
268
+ import { useState } from "react";
269
+ import { submissions } from "@wix/forms";
270
+
271
+ interface FormField {
272
+ label: string;
273
+ target: string;
274
+ required: boolean;
275
+ componentType: string;
276
+ identifier?: string;
277
+ options?: { value: string; label: string }[];
278
+ }
279
+
280
+ interface ContactFormProps {
281
+ formId: string;
282
+ fields: FormField[];
283
+ }
284
+
285
+ export default function ContactForm({ formId, fields }: ContactFormProps) {
286
+ const [formData, setFormData] = useState<Record<string, string>>({});
287
+ const [status, setStatus] = useState<"idle" | "submitting" | "success" | "error">("idle");
288
+ const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
289
+
290
+ const handleSubmit = async (e: React.FormEvent) => {
291
+ e.preventDefault();
292
+ setStatus("submitting");
293
+ setFieldErrors({});
294
+
295
+ try {
296
+ const result = await submissions.createSubmission({
297
+ formId,
298
+ submissions: formData,
299
+ });
300
+
301
+ if (result.status === "PENDING" || result.status === "CONFIRMED") {
302
+ setStatus("success");
303
+ setFormData({});
304
+ } else {
305
+ setStatus("error");
306
+ }
307
+ } catch (err: unknown) {
308
+ const violations =
309
+ (err as any)?.details?.validationError?.fieldViolations ?? [];
310
+ const errorMap: Record<string, string> = {};
311
+
312
+ for (const v of violations) {
313
+ const fieldErrs: { errorPath?: string; errorMessage?: string }[] =
314
+ v?.data?.errors ?? [];
315
+ for (const fe of fieldErrs) {
316
+ if (fe.errorPath && !errorMap[fe.errorPath]) {
317
+ errorMap[fe.errorPath] = fe.errorMessage ?? "Invalid value";
318
+ }
319
+ }
320
+ }
321
+
322
+ if (Object.keys(errorMap).length > 0) {
323
+ setFieldErrors(errorMap);
324
+ setStatus("idle");
325
+ } else {
326
+ setStatus("error");
327
+ }
328
+ }
329
+ };
330
+
331
+ if (status === "success") {
332
+ return (
333
+ <div className="form-success">
334
+ Thank you! We'll be in touch soon.
335
+ </div>
336
+ );
337
+ }
338
+
339
+ return (
340
+ <form onSubmit={handleSubmit} className="form-container">
341
+ {/* Styles come from the designed component's <style is:global> block.
342
+ Uses styling contract classes: .form-container, .form-field, .form-label,
343
+ .form-input, .form-select, .form-textarea, .form-button, .form-error,
344
+ .form-field-error, .form-input-error.
345
+ See design skill's COMPONENT_PATTERNS.md → Contact Form. */}
346
+
347
+ {fields.map((field) => (
348
+ <div key={field.target} className="form-field">
349
+ <label className="form-label">
350
+ {field.label}
351
+ {field.required && <span className="required">*</span>}
352
+ </label>
353
+ {field.componentType === "DROPDOWN" && field.options ? (
354
+ <select
355
+ required={field.required}
356
+ value={formData[field.target] ?? ""}
357
+ onChange={(e) =>
358
+ setFormData((prev) => ({ ...prev, [field.target]: e.target.value }))
359
+ }
360
+ className={`form-select${fieldErrors[field.target] ? " form-input-error" : ""}`}
361
+ >
362
+ <option value="">Select an option</option>
363
+ {field.options.map((opt) => (
364
+ <option key={opt.value} value={opt.value}>{opt.label}</option>
365
+ ))}
366
+ </select>
367
+ ) : field.identifier === "TEXT_AREA" || field.target === "message" ? (
368
+ <textarea
369
+ required={field.required}
370
+ value={formData[field.target] ?? ""}
371
+ onChange={(e) =>
372
+ setFormData((prev) => ({ ...prev, [field.target]: e.target.value }))
373
+ }
374
+ rows={4}
375
+ className={`form-textarea${fieldErrors[field.target] ? " form-input-error" : ""}`}
376
+ />
377
+ ) : (
378
+ <input
379
+ type={
380
+ field.target === "email" ? "email" :
381
+ field.componentType === "PHONE_INPUT" ? "tel" : "text"
382
+ }
383
+ required={field.required}
384
+ value={formData[field.target] ?? ""}
385
+ onChange={(e) =>
386
+ setFormData((prev) => ({ ...prev, [field.target]: e.target.value }))
387
+ }
388
+ className={`form-input${fieldErrors[field.target] ? " form-input-error" : ""}`}
389
+ {...(field.componentType === "PHONE_INPUT" ? { placeholder: "+1234567890" } : {})}
390
+ />
391
+ )}
392
+ {fieldErrors[field.target] && (
393
+ <p className="form-field-error">{fieldErrors[field.target]}</p>
394
+ )}
395
+ </div>
396
+ ))}
397
+
398
+ <button
399
+ type="submit"
400
+ disabled={status === "submitting"}
401
+ className="form-button"
402
+ >
403
+ {status === "submitting" ? "Sending..." : "Send Message"}
404
+ </button>
405
+
406
+ {status === "error" && (
407
+ <p className="form-error">Something went wrong. Please try again.</p>
408
+ )}
409
+ </form>
410
+ );
411
+ }
412
+ ```
413
+
414
+ > **Adapting class names:** The class names above (`form-container`, `form-field`, `form-label`, `form-input`, `form-button`, `form-success`, `form-error`, `form-field-error`, `form-input-error`) are defaults. If the designed component uses different class names, use those instead — the styling contract from the design skill takes precedence.
415
+
416
+ ## Discovering Forms
417
+
418
+ Forms are auto-created when scaffolding with a forms template. Use `auth.elevate(forms.listForms)` with the `"wix.form_app.form"` namespace to discover them — no hardcoded ID needed:
419
+
420
+ ```typescript
421
+ import { forms } from "@wix/forms";
422
+ import { auth } from "@wix/essentials";
423
+
424
+ const elevatedListForms = auth.elevate(forms.listForms);
425
+ const listResult = await elevatedListForms("wix.form_app.form");
426
+ const wixForm = listResult.forms?.[0];
427
+ const formId = wixForm?._id;
428
+ ```
429
+
430
+ If no forms exist, use the MCP-assisted Form Setup section above to create one via `CallWixSiteAPI`. If MCP is not available, create one in the Wix dashboard (Forms section → Add New Form).
431
+
432
+ ## Form Field Schema (SDK Types)
433
+
434
+ Each field in `formFields` has this structure when `fieldType === "INPUT"`:
435
+
436
+ ```typescript
437
+ field.identifier // "CONTACTS_EMAIL", "TEXT_AREA", "SUBMIT_BUTTON", etc.
438
+ field.inputOptions.target // "email", "first_name", "message", custom UUIDs
439
+ field.inputOptions.required // boolean
440
+ field.inputOptions.stringOptions.componentType // "TEXT_INPUT" | "DROPDOWN" | "PHONE_INPUT"
441
+ field.inputOptions.stringOptions.textInputOptions?.label // label for text inputs
442
+ field.inputOptions.stringOptions.dropdownOptions?.label // label for dropdowns
443
+ field.inputOptions.stringOptions.dropdownOptions?.options // [{value, label}] for dropdowns
444
+ field.inputOptions.stringOptions.phoneInputOptions?.label // label for phone inputs
445
+ ```
446
+
447
+ > **⚠️ Textarea detection:** The message field's `componentType` is always `"TEXT_INPUT"` (the API rejects `"TEXT_AREA"` as a componentType value). To render a `<textarea>`, check `field.identifier === "TEXT_AREA"` or `field.target === "message"` — NOT `field.componentType`.
448
+
449
+ ## Built-In Field Targets
450
+
451
+ | Target | Type | HTML Input |
452
+ |--------|------|-----------|
453
+ | `email` | Email | `type="email"` |
454
+ | `first_name` | Text | `type="text"` |
455
+ | `last_name` | Text | `type="text"` |
456
+ | `phone` | Phone | `type="tel"` |
457
+ | `company` | Text | `type="text"` |
458
+
459
+ Custom fields use UUID-based targets from the form schema.
460
+
461
+ ## Log Results
462
+
463
+ After form verification, write a sidecar file at `.wix/logs/forms-data.md` (form schema setup) or `.wix/logs/forms-implementer.md` (Phase 2 wiring) — see `../../shared/LIFECYCLE_LOG.md` for the sidecar contract. Do **not** append to `.wix/lifecycle.log.md` directly — the orchestrator concatenates all sidecars at the end. Use a `####` heading so the entry nests under `### features-orchestrator` in the assembled log:
464
+
465
+ ```markdown
466
+ ## forms
467
+ - Status: complete
468
+ - Content: "{form name}" form created (fields: {field list})
469
+ - Images: not applicable
470
+ ```
471
+
472
+ ## Testing
473
+
474
+ 1. Run `npx @wix/cli dev`
475
+ 2. Navigate to `/contact`
476
+ 3. Fill in the form and submit
477
+ 4. Check the Wix dashboard → Forms → Submissions to verify the data arrived
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: forms-implementer
3
+ description: "Implements contact, lead, and signup forms via @wix/forms. Scopes: seed, components, pages. Extends references/shared/IMPLEMENTER.md."
4
+ ---
5
+
6
+ # Forms Implementer
7
+
8
+ Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, MCP prefix, site.json read pattern, return contract, style conventions, and common failure modes.
9
+
10
+ ## Scope routing
11
+
12
+ | Scope | Phase | Reference |
13
+ |-------|-------|-----------|
14
+ | `seed` | Seed (Forms app install + form creation + postSubmissionTriggers PATCH) | `./CONTACT_FORM.md` (§ Form Setup) |
15
+ | `components` | Components (ContactForm.tsx React island) | `./CONTACT_FORM.md` (§ React island) |
16
+ | `pages` | Pages (contact.astro wire with formId from seeded data) | `./CONTACT_FORM.md` (§ Page wiring) |
17
+
18
+ ## Files this vertical creates / contributes
19
+
20
+ See `<SKILL_ROOT>/references/verticals/forms.md` frontmatter.
21
+
22
+ ## Form purpose variants
23
+
24
+ The `seed` scope adapts field selection based on business purpose from the functional plan:
25
+
26
+ | Purpose | Fields | Layout |
27
+ |---------|--------|--------|
28
+ | Waitlist / newsletter | EMAIL + SUBMIT | Single column |
29
+ | Lead capture | FIRST_NAME + EMAIL + SUBMIT | Single column |
30
+ | Contact (full) | FIRST_NAME + LAST_NAME + EMAIL + PHONE + MESSAGE + SUBMIT | First+last side-by-side, rest stacked |
31
+ | Registration | FIRST_NAME + LAST_NAME + EMAIL + custom | Single column |
32
+
33
+ ## Forms-specific failure modes
34
+
35
+ | Wrong | Right |
36
+ |---|---|
37
+ | Skip `postSubmissionTriggers` PATCH after form creation | Always PATCH — the POST silently drops triggers; without them, submissions are silently lost (no CRM Contact created) |
38
+ | Include triggers in POST body and assume it worked | POST drops `postSubmissionTriggers`; always follow up with PATCH |
39
+ | Fall back to hardcoded fields on `UNSUPPORTED_FORM_NAMESPACE` | Wait 10s and retry up to 3 times — namespace propagation delay after Forms app install |
40
+ | Skip `auth.elevate` when listing forms | `listForms` requires elevated permissions |
41
+ | Submit forms from server-side code | Form submission must be client-side (React island with `client:load`) |
42
+ | Detect textarea by `componentType === "TEXT_AREA"` | Check `identifier === "TEXT_AREA"` or `target === "message"` |
43
+ | Hardcode form field IDs or form ID | Use `forms.listForms("wix.form_app.form")` with `auth.elevate` for runtime discovery |
@@ -0,0 +1,119 @@
1
+ # Phase 3 Components — Gift Cards
2
+
3
+ Launched in **Step 4.5** alongside other verticals' `components` scopes (after Phase 2 Design System). Writes the runtime probe utility, the buy-flow React island, and scoped CSS. Finishes before the Phase 4 Pages scopes run in Step 7.
4
+
5
+ ## Scope
6
+
7
+ Files this agent OWNS:
8
+
9
+ - `src/utils/gift-cards.ts` — runtime probe of the dashboard's eGift Card template + types
10
+ - `src/components/GiftCardPurchase.tsx` — React island; recipient form + variant pills + add-to-cart
11
+ - `src/styles/components-gift-cards.css` — scoped CSS for the form fields, amount pills, hero image frame, and home teaser
12
+
13
+ Files this agent MUST NOT touch:
14
+ - `src/utils/wix-image.ts`, `src/utils/analytics.ts` — shared utilities. Import from them; do not write copies.
15
+ - `src/components/AddToCartButton.tsx`, `src/components/CartBadge.tsx`, `src/components/CartView.tsx` — owned by stores/ecom; gift-cards uses `currentCart.addToCurrentCart` directly inside the buy-flow island, not via `AddToCartButton`.
16
+ - Any `.astro` page or layout — those are written by other scopes.
17
+ - `src/styles/global.css` — owned by designer foundation.
18
+
19
+ ## Coordination: design tokens
20
+
21
+ Your parent prompt includes the design tokens inline. Reference token CSS variables (`var(--color-cream)`, `var(--spacing-lg)`, etc.) from `components-gift-cards.css`. Do NOT read `.wix/design-tokens.css` from disk.
22
+
23
+ ## Critical rules
24
+
25
+ 1. **`httpClient.fetchWithAuth` from `@wix/essentials`** — the JS SDK's gift-vouchers package only exposes redemption methods; the buy-template query is reachable only via REST.
26
+ 2. **Module-level memoization** — hold one in-flight `Promise<GiftCardProduct | null>` at module scope so Navigation, home, and `/gift-cards` calls within the same request coalesce. The plugin's `discounts.ts` skips memoization for simplicity but the gift-card template is far more cacheable, so this pack pays the small cost.
27
+ 3. **Return `null` on every failure** — non-2xx, empty `giftCardProducts[]`, network/JSON error. The page/nav/teaser callers branch on `null` to hide gracefully. Never throw.
28
+ 4. **Hardcoded `WIX_GIFT_CARDS_APP_ID`** — `d80111c5-a0f4-47a8-b63a-65b54d774a27`. Captured from a real Wix-Editor order; not in the public registry. Export it as a named constant with a provenance comment so future maintainers can find the source.
29
+ 5. **`wixGiftCardsAppNewCatalog: true`** in the cart `options` is mandatory — without it, the cart line item type is wrong and `ORDER_PAID` won't trigger gift-card issuance.
30
+ 6. **No HTML comments in `.astro` frontmatter** — frontmatter is TypeScript; use `//` or `/* */`. (Not relevant to this scope's `.ts`/`.tsx` files but mentioned because gift-cards.ts is imported into `.astro` frontmatter elsewhere.)
31
+
32
+ ## Template files
33
+
34
+ This scope uses **template files** instead of inline code. For each file below:
35
+
36
+ 1. Read the template from `<Agent location>/templates/<filename>`
37
+ 2. Write it to the project at the target path
38
+ 3. Adapt CSS class names if the design tokens map them differently than the defaults
39
+
40
+ Do NOT modify logic, imports, or component structure.
41
+
42
+ ## Implementation
43
+
44
+ ### 1. `src/utils/gift-cards.ts`
45
+
46
+ Use template `templates/gift-cards.ts`.
47
+
48
+ Exports:
49
+ - `WIX_GIFT_CARDS_APP_ID` — string constant.
50
+ - `GiftCardImage`, `GiftCardPresetVariant`, `GiftCardProduct` — TypeScript interfaces.
51
+ - `getGiftCardProduct(): Promise<GiftCardProduct | null>` — memoized probe. Returns the first gift-card product or null on any failure.
52
+
53
+ The probe uses `httpClient.fetchWithAuth(ENDPOINT, { method: "POST", body: JSON.stringify({ query: { paging: { limit: 1 } } }) })`.
54
+
55
+ ### 2. `src/components/GiftCardPurchase.tsx`
56
+
57
+ Use template `templates/GiftCardPurchase.tsx`.
58
+
59
+ Props: `{ product: GiftCardProduct }`.
60
+
61
+ State machine: `"idle" | "adding" | "added" | "error"`. Submit handler:
62
+ 1. Validate selected `variantId`, `recipientFirstName`, `recipientEmail` (regex).
63
+ 2. Call `currentCart.addToCurrentCart` with the catalogReference shape from gift-cards.md ("Wix Gift Card app reference data" section).
64
+ 3. Dispatch `cart-updated` CustomEvent (so CartBadge updates).
65
+ 4. `trackEvent("AddToCart", { … })` — fire-and-forget; same pattern as stores.
66
+ 5. `window.location.href = "/cart"` on success.
67
+
68
+ Class names from contract:
69
+ - `.gift-card-form`, `.gift-card-fieldset`, `.gift-card-amounts`, `.gift-card-grid`, `.gift-card-field`, `.gift-card-field-wide`, `.gift-card-counter`, `.gift-card-error` — defined in `components-gift-cards.css`.
70
+ - `.option-label`, `.option-pill`, `.option-pill.selected` — shared across packs (also used by stores `ProductPurchase`).
71
+ - `.add-to-cart-btn` — shared with stores AddToCartButton; same brand styling.
72
+
73
+ ### 3. `src/styles/components-gift-cards.css`
74
+
75
+ Use template `templates/components-gift-cards.css`.
76
+
77
+ Scoped styles for:
78
+ - Buy-form layout (grid, fields, counter, error message).
79
+ - `/gift-cards` page sections (`.gift-card-page`, `.gift-card-grid-wrap`, `.gift-card-hero-image`, `.gift-card-body`, `.gift-card-lede`, `.gift-card-fineprint`).
80
+ - Home teaser (`.gift-card-teaser`, `.gift-card-teaser-link`, `.gift-card-teaser-image`, `.gift-card-teaser-body`, `.gift-card-teaser-lede`).
81
+
82
+ The page-section and teaser styles live here (not inside the .astro `<style>` blocks) so the same separation as `components-stores.css` / `components-ecom.css` holds. The pages-scope agent imports this stylesheet via `Layout.astro`'s existing CSS chain (designer foundation already wires up the `components-*.css` import).
83
+
84
+ Use brand `@theme` utilities (`bg-cream`, `text-ink`, etc.) where natural; fall back to `var(--color-*)` / `var(--spacing-*)` directly when a property isn't covered. Do not introduce new design-token names.
85
+
86
+ ## Return format
87
+
88
+ ```json
89
+ {
90
+ "status": "complete",
91
+ "phase": "gift-cards-components",
92
+ "scope": "components",
93
+ "summary": "Wrote runtime probe util, GiftCardPurchase island, and scoped gift-cards CSS",
94
+ "data": {
95
+ "utils": ["gift-cards.ts"],
96
+ "islands": ["GiftCardPurchase.tsx"],
97
+ "scopedCssFile": "src/styles/components-gift-cards.css",
98
+ "globalContractClassesReferenced": ["addToCartButton"],
99
+ "scopedContractClassesReferenced": ["optionLabel", "optionPill"]
100
+ },
101
+ "files": [
102
+ "src/utils/gift-cards.ts",
103
+ "src/components/GiftCardPurchase.tsx",
104
+ "src/styles/components-gift-cards.css"
105
+ ],
106
+ "errors": []
107
+ }
108
+ ```
109
+
110
+ ## Anti-patterns
111
+
112
+ | WRONG | CORRECT |
113
+ |-------|---------|
114
+ | Use `@wix/auto_sdk_ecom_gift-vouchers` to query the buy template | That SDK only exposes redemption — use `httpClient.fetchWithAuth` against the REST endpoint |
115
+ | Wrap `getGiftCardProduct()` in `auth.elevate(...)` | Visitor-scope is correct; elevation breaks the probe |
116
+ | Throw or log+rethrow on probe failure | Return `null`; callers gate the UI on null |
117
+ | Hardcode denomination amounts (€10, €25, etc.) | Always render from `product.presetVariants` |
118
+ | Skip `wixGiftCardsAppNewCatalog: true` in cart options | Required — without it the cart line is the wrong item type and `ORDER_PAID` does not issue a gift card |
119
+ | Write your own `wix-image.ts` / `analytics.ts` | Import from the shared utilities |