@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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +29 -0
- package/skills/wix-app/SKILL.md +344 -0
- package/skills/wix-app/references/APP_IDENTIFIERS.md +35 -0
- package/skills/wix-app/references/APP_MARKET_REVIEW.md +311 -0
- package/skills/wix-app/references/APP_VALIDATION.md +171 -0
- package/skills/wix-app/references/BACKEND_API.md +233 -0
- package/skills/wix-app/references/BACKEND_EVENT.md +116 -0
- package/skills/wix-app/references/CODE_QUALITY.md +33 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +587 -0
- package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +157 -0
- package/skills/wix-app/references/DASHBOARD_MODAL.md +283 -0
- package/skills/wix-app/references/DASHBOARD_PAGE.md +403 -0
- package/skills/wix-app/references/DASHBOARD_PLUGIN.md +205 -0
- package/skills/wix-app/references/DATA_COLLECTION.md +451 -0
- package/skills/wix-app/references/DOCUMENTATION.md +42 -0
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +65 -0
- package/skills/wix-app/references/EMBEDDED_SCRIPT.md +468 -0
- package/skills/wix-app/references/EXTENSION_REGISTRATION.md +96 -0
- package/skills/wix-app/references/SERVICE_PLUGIN.md +235 -0
- package/skills/wix-app/references/SITE_PLUGIN.md +436 -0
- package/skills/wix-app/references/STORES_VERSIONING.md +334 -0
- package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +84 -0
- package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +345 -0
- package/skills/wix-app/references/dashboard-menu-plugin/blog-slots.md +71 -0
- package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +58 -0
- package/skills/wix-app/references/dashboard-menu-plugin/crm-slots.md +8 -0
- package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +17 -0
- package/skills/wix-app/references/dashboard-menu-plugin/events-slots.md +29 -0
- package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +27 -0
- package/skills/wix-app/references/dashboard-menu-plugin/stores-slots.md +35 -0
- package/skills/wix-app/references/dashboard-page/API_SPEC.md +138 -0
- package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +573 -0
- package/skills/wix-app/references/dashboard-page/DYNAMIC_PARAMETERS.md +269 -0
- package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +164 -0
- package/skills/wix-app/references/dashboard-plugin/SLOTS.md +294 -0
- package/skills/wix-app/references/data-collection/WIX_DATA.md +270 -0
- package/skills/wix-app/references/data-collection/extension-template.ts +107 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +138 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +454 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +165 -0
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +576 -0
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +170 -0
- package/skills/wix-app/references/editor-react-component/EDIT-FLOW.md +6 -0
- package/skills/wix-app/references/editor-react-component/PARTS.md +25 -0
- package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +203 -0
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +167 -0
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +137 -0
- package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +112 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +118 -0
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +87 -0
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +86 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +135 -0
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +79 -0
- package/skills/wix-app/references/service-plugin/VALIDATIONS.md +68 -0
- package/skills/wix-app/references/site-plugin/EXAMPLES.md +311 -0
- package/skills/wix-app/references/site-plugin/SLOTS.md +263 -0
- package/skills/wix-design-system/SKILL.md +129 -0
- package/skills/wix-design-system/references/file-structure.md +56 -0
- package/skills/wix-design-system/scripts/wds.cjs +531 -0
- package/skills/wix-headless/SKILL.md +418 -0
- package/skills/wix-headless/references/DISCOVERY.md +332 -0
- package/skills/wix-headless/references/ORCHESTRATION.md +269 -0
- package/skills/wix-headless/references/SETUP.md +154 -0
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +152 -0
- package/skills/wix-headless/references/blog/BLOG_PAGES.md +299 -0
- package/skills/wix-headless/references/blog/BLOG_SETUP.md +218 -0
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +32 -0
- package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +373 -0
- package/skills/wix-headless/references/cms/FAQ_KNOWLEDGE_BASE.md +229 -0
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +40 -0
- package/skills/wix-headless/references/cms/PORTFOLIO.md +454 -0
- package/skills/wix-headless/references/cms/RESOURCE_LIBRARY.md +356 -0
- package/skills/wix-headless/references/cms/TEAM_DIRECTORY.md +299 -0
- package/skills/wix-headless/references/commands/install-app.md +42 -0
- package/skills/wix-headless/references/commands/known-apps.json +5 -0
- package/skills/wix-headless/references/commands/mcp-bootstrap.md +28 -0
- package/skills/wix-headless/references/designer/INSTRUCTIONS.md +828 -0
- package/skills/wix-headless/references/ecom/CART_PAGES.md +120 -0
- package/skills/wix-headless/references/ecom/CART_WIRING.md +103 -0
- package/skills/wix-headless/references/ecom/COMPONENTS_CSS.md +132 -0
- package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +59 -0
- package/skills/wix-headless/references/forms/CONTACT_FORM.md +477 -0
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +43 -0
- package/skills/wix-headless/references/gift-cards/COMPONENTS.md +119 -0
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +51 -0
- package/skills/wix-headless/references/gift-cards/PAGES.md +129 -0
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +359 -0
- package/skills/wix-headless/references/shared/BUILD_NOISE.md +8 -0
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +185 -0
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +155 -0
- package/skills/wix-headless/references/shared/MCP_PREFIX.md +105 -0
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +97 -0
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +423 -0
- package/skills/wix-headless/references/shared/STYLING.md +117 -0
- package/skills/wix-headless/references/stores/BACK_IN_STOCK.md +178 -0
- package/skills/wix-headless/references/stores/CATEGORY_PAGES.md +86 -0
- package/skills/wix-headless/references/stores/COMPONENTS_CSS.md +129 -0
- package/skills/wix-headless/references/stores/HOME_AND_NAV.md +227 -0
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +96 -0
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +249 -0
- package/skills/wix-headless/references/stores/PRODUCT_PAGES.md +213 -0
- package/skills/wix-headless/references/stores/SHARED_WIRING.md +164 -0
- package/skills/wix-headless/references/verticals/_schema.json +160 -0
- package/skills/wix-headless/references/verticals/_schema.md +185 -0
- package/skills/wix-headless/references/verticals/blog.md +125 -0
- package/skills/wix-headless/references/verticals/cms.md +121 -0
- package/skills/wix-headless/references/verticals/ecom.md +96 -0
- package/skills/wix-headless/references/verticals/forms.md +106 -0
- package/skills/wix-headless/references/verticals/gift-cards.md +121 -0
- package/skills/wix-headless/references/verticals/stores.md +167 -0
- package/skills/wix-headless/scripts/check-manifest.mjs +198 -0
- package/skills/wix-headless/scripts/copy-utility-templates.mjs +153 -0
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +77 -0
- package/skills/wix-headless/scripts/finalize-run-json.mjs +141 -0
- package/skills/wix-headless/scripts/init-site-json.mjs +63 -0
- package/skills/wix-headless/scripts/patch-decorative-slots.mjs +124 -0
- package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +190 -0
- package/skills/wix-headless/scripts/preview.sh +45 -0
- package/skills/wix-headless/scripts/release.sh +73 -0
- package/skills/wix-headless/scripts/scaffold.sh +53 -0
- package/skills/wix-headless/scripts/seed-utilities.sh +55 -0
- package/skills/wix-headless/shared-utilities/analytics.ts +40 -0
- package/skills/wix-headless/shared-utilities/ricos.ts +179 -0
- package/skills/wix-headless/shared-utilities/wix-image.ts +72 -0
- package/skills/wix-headless/templates/ecom/CartBadge.tsx +84 -0
- package/skills/wix-headless/templates/ecom/CartView.tsx +543 -0
- package/skills/wix-headless/templates/ecom/cart.astro +13 -0
- package/skills/wix-headless/templates/ecom/components-ecom.css +215 -0
- package/skills/wix-headless/templates/ecom/discounts.ts +71 -0
- package/skills/wix-headless/templates/ecom/thank-you.astro +24 -0
- package/skills/wix-headless/templates/gift-cards/GiftCardPurchase.tsx +172 -0
- package/skills/wix-headless/templates/gift-cards/_home-teaser-snippet.astro +42 -0
- package/skills/wix-headless/templates/gift-cards/_nav-snippet.astro +11 -0
- package/skills/wix-headless/templates/gift-cards/components-gift-cards.css +219 -0
- package/skills/wix-headless/templates/gift-cards/gift-cards.astro +51 -0
- package/skills/wix-headless/templates/gift-cards/gift-cards.ts +75 -0
- package/skills/wix-headless/templates/stores/AddToCartButton.tsx +77 -0
- package/skills/wix-headless/templates/stores/BackInStockForm.tsx +176 -0
- package/skills/wix-headless/templates/stores/CategoryRail.astro +69 -0
- package/skills/wix-headless/templates/stores/ProductCard.astro +79 -0
- package/skills/wix-headless/templates/stores/ProductPurchase.tsx +397 -0
- package/skills/wix-headless/templates/stores/SeoTags.astro +21 -0
- package/skills/wix-headless/templates/stores/back-in-stock.ts +67 -0
- package/skills/wix-headless/templates/stores/categories.ts +155 -0
- package/skills/wix-headless/templates/stores/category/[slug].astro +130 -0
- package/skills/wix-headless/templates/stores/components-stores.css +295 -0
- package/skills/wix-headless/templates/stores/products/[slug].astro +128 -0
- package/skills/wix-headless/templates/stores/products/index.astro +101 -0
- package/skills/wix-manage/SKILL.md +280 -0
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +155 -0
- package/skills/wix-manage/references/app-installation/list-installed-apps.md +124 -0
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +226 -0
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +95 -0
- package/skills/wix-manage/references/bookings/booking-system-integration-gaps.md +416 -0
- package/skills/wix-manage/references/bookings/bookings-staff-setup.md +255 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +462 -0
- package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +278 -0
- package/skills/wix-manage/references/bookings/external-calendar-integration.md +258 -0
- package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +231 -0
- package/skills/wix-manage/references/calendar/configure-default-business-hours.md +226 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +422 -0
- package/skills/wix-manage/references/cms/cms-data-operations-extended.md +263 -0
- package/skills/wix-manage/references/cms/cms-ecommerce-catalog-integration.md +136 -0
- package/skills/wix-manage/references/cms/cms-references-and-relationships.md +160 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +143 -0
- package/skills/wix-manage/references/contacts/bulk-delete-contacts.md +73 -0
- package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +67 -0
- package/skills/wix-manage/references/domains/domain-search-and-purchase.md +434 -0
- package/skills/wix-manage/references/ecommerce/api-recommendation-tracking.md +420 -0
- package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +267 -0
- package/skills/wix-manage/references/ecommerce/flow-bundle-and-save.md +290 -0
- package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +300 -0
- package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +364 -0
- package/skills/wix-manage/references/ecommerce/flow-seasonal-promotion.md +315 -0
- package/skills/wix-manage/references/ecommerce/flow-stock-mover.md +308 -0
- package/skills/wix-manage/references/ecommerce/flow-upsell-boost.md +271 -0
- package/skills/wix-manage/references/ecommerce/goal-clear-inventory.md +118 -0
- package/skills/wix-manage/references/ecommerce/goal-drive-cross-sells.md +122 -0
- package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +132 -0
- package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +148 -0
- package/skills/wix-manage/references/ecommerce/goal-seasonal-revenue.md +146 -0
- package/skills/wix-manage/references/ecommerce/guardrail-discount-conflicts.md +118 -0
- package/skills/wix-manage/references/ecommerce/guardrail-margin-protection.md +88 -0
- package/skills/wix-manage/references/ecommerce/guardrail-rate-pricing-sanity.md +108 -0
- package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +124 -0
- package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +299 -0
- package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +582 -0
- package/skills/wix-manage/references/ecommerce/setup-coupons.md +338 -0
- package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +444 -0
- package/skills/wix-manage/references/ecommerce/setup-shipping-rates.md +86 -0
- package/skills/wix-manage/references/ecommerce/setup-shipping-regions.md +56 -0
- package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +209 -0
- package/skills/wix-manage/references/ecommerce/skill-graph.md +139 -0
- package/skills/wix-manage/references/ecommerce/troubleshoot-checkout-delivery-dropoff.md +124 -0
- package/skills/wix-manage/references/ecommerce/troubleshoot-discount-not-applying.md +95 -0
- package/skills/wix-manage/references/events/list-events.md +203 -0
- package/skills/wix-manage/references/forms/create-form.md +260 -0
- package/skills/wix-manage/references/get-paid/create-payment-links.md +212 -0
- package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +57 -0
- package/skills/wix-manage/references/get-paid/payment-links-for-bookings.md +101 -0
- package/skills/wix-manage/references/media/upload-media-to-wix.md +216 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +77 -0
- package/skills/wix-manage/references/pricing-plans/pricing-plans-bookings-integration.md +166 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +336 -0
- package/skills/wix-manage/references/rich-content/ricos-converter-service.md +295 -0
- package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +59 -0
- package/skills/wix-manage/references/sites/create-site-from-template.md +183 -0
- package/skills/wix-manage/references/sites/query-sites.md +125 -0
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +57 -0
- package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +1041 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v1.md +125 -0
- package/skills/wix-manage/references/stores/create-product-from-image.md +824 -0
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +566 -0
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +194 -0
- package/skills/wix-manage/references/stores/query-products-catalog-v1.md +80 -0
- package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +99 -0
- package/skills/wix-manage/references/stores/update-product-pre-order.md +213 -0
- 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 |
|