@wix/agent-skills 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +1 -1
- package/skills/README.md +23 -0
- package/skills/wix-app/SKILL.md +10 -2
- package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
- package/skills/wix-app/references/CODE_QUALITY.md +1 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
- package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +23 -9
- package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
- package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
- package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
- package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
- package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
- package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
- package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
- package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
- package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
- package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +17 -105
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
- package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +15 -15
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
- package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
- package/skills/wix-auth/SKILL.md +12 -0
- package/skills/wix-auth/references/device-flow.md +140 -0
- package/skills/wix-docs/SKILL.md +188 -0
- package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
- package/skills/wix-docs/references/EXTRACTING.md +120 -0
- package/skills/wix-headless/SKILL.md +84 -130
- package/skills/wix-headless/entry/bootstrap.mjs +93 -0
- package/skills/wix-headless/entry/skill.md +78 -0
- package/skills/wix-headless/references/AI_FEATURES.md +178 -0
- package/skills/wix-headless/references/CAPABILITIES.md +84 -0
- package/skills/wix-headless/references/DISCOVERY.md +34 -56
- package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
- package/skills/wix-headless/references/FEEDBACK.md +118 -0
- package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +97 -0
- package/skills/wix-headless/references/SEED.md +44 -188
- package/skills/wix-headless/references/SETUP.md +32 -103
- package/skills/wix-headless/references/astro.md +98 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +173 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
- package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
- package/skills/wix-headless/references/inline-recipes/setup-bookings.md +228 -0
- package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
- package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +176 -0
- package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +153 -0
- package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +161 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +117 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
- package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
- package/skills/wix-headless/references/managed/CONNECT.md +74 -0
- package/skills/wix-headless/references/managed/CREATE.md +130 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +57 -0
- package/skills/wix-headless/references/non-astro.md +59 -0
- package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
- package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +7 -0
- package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
- package/skills/wix-headless/references/stripe/DEPLOYMENT.md +63 -0
- package/skills/wix-manage/SKILL.md +24 -1
- package/skills/wix-manage/references/analytics/query-site-analytics.md +241 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +265 -0
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +436 -0
- package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +209 -0
- package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +51 -0
- package/skills/wix-vibe-headless/SKILL.md +136 -0
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
- package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +153 -0
- package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
- package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +215 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
- package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
- package/skills/wix-vibe-headless/references/events/wix-events-browse.js +135 -0
- package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
- package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
- package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +194 -0
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
- package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
- package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +155 -0
- package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
- package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +142 -0
- package/skills/wix-headless/references/BUILD-astro.md +0 -244
- package/skills/wix-headless/references/BUILD-own-build.md +0 -167
- package/skills/wix-headless/references/BUILD.md +0 -105
- package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
- package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
- package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
- package/skills/wix-headless/references/PLAN-connect.md +0 -19
- package/skills/wix-headless/references/PLAN-create.md +0 -17
- package/skills/wix-headless/references/PLAN.md +0 -92
- package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
- package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
- package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
- package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
- package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
- package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
- package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
- package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
- package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
- package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
- package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
- package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
- package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
- package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
- package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
- package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
- package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
- package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
- package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
- package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
- package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
- package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
- package/skills/wix-headless/references/astro/templates/Footer.astro +0 -27
- package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
- package/skills/wix-headless/references/astro/templates/Navigation.astro +0 -38
- package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
- package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
- package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
- package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
- package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
- package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
- package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
- package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
- package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
- package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
- package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
- package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
- package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
- package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
- package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
- package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
- package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
- package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
- package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
- package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
- package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
- package/skills/wix-headless/references/astro/templates/global.css +0 -288
- package/skills/wix-headless/references/astro/templates/index.astro +0 -56
- package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
- package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
- package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
- package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
- package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
- package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
- package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
- package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
- package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
- package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
- package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
- package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
- package/skills/wix-headless/references/bookings/FLOW.md +0 -246
- package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
- package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
- package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
- package/skills/wix-headless/references/commands/known-apps.json +0 -6
- package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
- package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
- package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
- package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
- package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
- package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
- package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
- package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
- package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
- package/skills/wix-headless/references/seed-recipes.md +0 -49
- package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
- package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
- package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
- package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
- package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
- package/skills/wix-headless/references/shared/STYLING.md +0 -141
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
- package/skills/wix-headless/references/verticals/_schema.json +0 -74
- package/skills/wix-headless/references/verticals/_schema.md +0 -95
- package/skills/wix-headless/references/verticals/blog.md +0 -31
- package/skills/wix-headless/references/verticals/bookings.md +0 -41
- package/skills/wix-headless/references/verticals/cms.md +0 -30
- package/skills/wix-headless/references/verticals/ecom.md +0 -25
- package/skills/wix-headless/references/verticals/forms.md +0 -26
- package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
- package/skills/wix-headless/references/verticals/stores.md +0 -33
- package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
- package/skills/wix-headless/scripts/compose.mjs +0 -525
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
- package/skills/wix-headless/scripts/flatten.sh +0 -61
- package/skills/wix-headless/scripts/generate-images.mjs +0 -242
- package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -148
- package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
- package/skills/wix-headless/scripts/scaffold.sh +0 -139
- package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
- package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
- package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
- package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
- package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { wixApiRequest } from "./wix-client.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wix CMS (Wix Data) — REST helpers for reading and writing data items in site data collections.
|
|
5
|
+
*
|
|
6
|
+
* COLLECTIONS & PERMISSIONS: A collection (e.g. "Tutorials") is identified by its
|
|
7
|
+
* dataCollectionId — the name set in the dashboard, NOT a GUID. Field keys are also set
|
|
8
|
+
* in the dashboard. This skill runs as an ANONYMOUS VISITOR — a call only succeeds if the
|
|
9
|
+
* collection grants that action to "Anyone" (Read for listing, Insert for forms, etc.).
|
|
10
|
+
* Permission-denied throws HTTP 403 by design. Set permissions in Wix dashboard → CMS → Permissions.
|
|
11
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/cms/collection-management/data-permissions/data-permissions-object.md
|
|
12
|
+
*
|
|
13
|
+
* Data Item — every read helper returns the item's `data` payload:
|
|
14
|
+
* _id {string} — GUID (route key, itemId for get/update/remove),
|
|
15
|
+
* _createdDate, _updatedDate {string} — ISO 8601 (use { "$date": "..." } in filters),
|
|
16
|
+
* _ownerId {string}, ...fields — collection field values keyed by field key
|
|
17
|
+
* Full reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/data-item-object.md
|
|
18
|
+
*
|
|
19
|
+
* FILTERS & SORT (Wix API Query Language):
|
|
20
|
+
* filter: { field: value } for equality; { field: { $op: value } } for operators:
|
|
21
|
+
* $eq $ne $gt $gte $lt $lte $in $nin $startsWith $exists $isEmpty $hasSome $hasAll
|
|
22
|
+
* Combine with $and / $or / $not. Dates: { "$date": "2026-05-05T00:00:00.000Z" }.
|
|
23
|
+
* sort: [{ fieldName: "publishDate", order: "DESC" }]
|
|
24
|
+
* Full reference: https://dev.wix.com/docs/api-reference/articles/work-with-wix-apis/data-retrieval/about-the-wix-api-query-language.md
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Query one page of items from a collection.
|
|
29
|
+
* Reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/query-data-items.md
|
|
30
|
+
*
|
|
31
|
+
* Pass `nextCursor` back as `cursor` to load the next page. Define `filter`/`sort`/`fields`
|
|
32
|
+
* on the FIRST request only — on cursor follow-ups Wix reuses the original query and
|
|
33
|
+
* ignores them (so this helper omits them when a cursor is supplied).
|
|
34
|
+
*
|
|
35
|
+
* @param {string} dataCollectionId Collection name (e.g. "Tutorials").
|
|
36
|
+
* @param {{
|
|
37
|
+
* filter?: object,
|
|
38
|
+
* sort?: Array<{ fieldName: string, order?: "ASC"|"DESC" }>,
|
|
39
|
+
* limit?: number,
|
|
40
|
+
* cursor?: string,
|
|
41
|
+
* fields?: string[],
|
|
42
|
+
* includeReferences?: Array<{ field: string, limit?: number }>
|
|
43
|
+
* }} [options]
|
|
44
|
+
* @returns {Promise<{ items: object[], nextCursor: string|null }>} items are `data` payloads (each includes `_id`).
|
|
45
|
+
*/
|
|
46
|
+
export async function queryDataItems(dataCollectionId, { filter, sort, limit = 100, cursor, fields, includeReferences } = {}) {
|
|
47
|
+
const query = {
|
|
48
|
+
...(cursor
|
|
49
|
+
? {}
|
|
50
|
+
: {
|
|
51
|
+
...(filter ? { filter } : {}),
|
|
52
|
+
...(sort ? { sort } : {}),
|
|
53
|
+
...(fields ? { fields } : {}),
|
|
54
|
+
}),
|
|
55
|
+
cursorPaging: cursor ? { limit, cursor } : { limit },
|
|
56
|
+
};
|
|
57
|
+
const res = await wixApiRequest("/wix-data/v2/items/query", {
|
|
58
|
+
method: "POST",
|
|
59
|
+
body: {
|
|
60
|
+
dataCollectionId,
|
|
61
|
+
query,
|
|
62
|
+
...(includeReferences ? { includeReferences } : {}),
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
items: (res?.dataItems ?? []).map((d) => d.data),
|
|
67
|
+
nextCursor: res?.pagingMetadata?.cursors?.next ?? null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get a single item by its `_id`. Returns the `data` payload, or null if not found
|
|
73
|
+
* (or not readable by an anonymous visitor — see the permissions note up top).
|
|
74
|
+
* Reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/get-data-item.md
|
|
75
|
+
*
|
|
76
|
+
* @param {string} dataCollectionId
|
|
77
|
+
* @param {string} itemId The item's `_id`.
|
|
78
|
+
* @returns {Promise<object|null>}
|
|
79
|
+
*/
|
|
80
|
+
export async function getDataItem(dataCollectionId, itemId) {
|
|
81
|
+
try {
|
|
82
|
+
const res = await wixApiRequest(`/wix-data/v2/items/${encodeURIComponent(itemId)}`, {
|
|
83
|
+
method: "GET",
|
|
84
|
+
query: { dataCollectionId },
|
|
85
|
+
});
|
|
86
|
+
return res?.dataItem?.data ?? null;
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the first item whose `fieldKey` equals `value`. Use for slug-style routing —
|
|
94
|
+
* Wix Data has no native get-by-slug, so detail pages keyed off a human-readable field
|
|
95
|
+
* (e.g. a "slug" or "handle" field) resolve through this. Returns the `data` payload or null.
|
|
96
|
+
* Built on Query Data Items: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/query-data-items.md
|
|
97
|
+
*
|
|
98
|
+
* @param {string} dataCollectionId
|
|
99
|
+
* @param {string} fieldKey Field to match (e.g. "slug").
|
|
100
|
+
* @param {unknown} value Value to match.
|
|
101
|
+
* @returns {Promise<object|null>}
|
|
102
|
+
*/
|
|
103
|
+
export async function getDataItemBy(dataCollectionId, fieldKey, value) {
|
|
104
|
+
const { items } = await queryDataItems(dataCollectionId, { filter: { [fieldKey]: value }, limit: 1 });
|
|
105
|
+
return items[0] ?? null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Count items in a collection matching an optional filter. Use for empty-state logic
|
|
110
|
+
* (0 → prompt the user to add items in their Wix dashboard) and result counts.
|
|
111
|
+
* Reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/count-data-items.md
|
|
112
|
+
*
|
|
113
|
+
* @param {string} dataCollectionId
|
|
114
|
+
* @param {object} [filter] Same filter syntax as queryDataItems.
|
|
115
|
+
* @returns {Promise<number>}
|
|
116
|
+
*/
|
|
117
|
+
export async function countDataItems(dataCollectionId, filter) {
|
|
118
|
+
const res = await wixApiRequest("/wix-data/v2/items/count", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: { dataCollectionId, ...(filter ? { filter } : {}) },
|
|
121
|
+
});
|
|
122
|
+
return res?.totalCount ?? 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Insert a new item (e.g. a public form submission). The collection's Insert permission
|
|
127
|
+
* must be "Anyone" for this to succeed as a visitor. Returns the inserted `data` payload
|
|
128
|
+
* (including the assigned `_id`). Throws on failure (e.g. permission denied, validation).
|
|
129
|
+
* Reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/insert-data-item.md
|
|
130
|
+
*
|
|
131
|
+
* @param {string} dataCollectionId
|
|
132
|
+
* @param {object} data Field-keyed values, e.g. { name, email, message }. Omit `_id`
|
|
133
|
+
* to let Wix assign one; supply `_id` only for a custom GUID.
|
|
134
|
+
* @returns {Promise<object>} The inserted item's `data` payload.
|
|
135
|
+
*/
|
|
136
|
+
export async function insertDataItem(dataCollectionId, data) {
|
|
137
|
+
const res = await wixApiRequest("/wix-data/v2/items", {
|
|
138
|
+
method: "POST",
|
|
139
|
+
body: { dataCollectionId, dataItem: { data } },
|
|
140
|
+
});
|
|
141
|
+
const inserted = res?.dataItem?.data;
|
|
142
|
+
if (!inserted) throw new Error(`Insert into "${dataCollectionId}" failed (no item returned).`);
|
|
143
|
+
return inserted;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Update (REPLACE) an existing item by `_id`. Returns the updated `data` payload. Throws
|
|
148
|
+
* if the item doesn't exist or the visitor lacks Update permission (usually admin/author only).
|
|
149
|
+
* Reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/update-data-item.md
|
|
150
|
+
*
|
|
151
|
+
* ⚠ This is a FULL REPLACE: the new `data` overwrites the whole item, and any field NOT
|
|
152
|
+
* included is dropped. To change a few fields, fetch the item first (getDataItem), spread
|
|
153
|
+
* it, then pass the merged object — or use Patch Data Item for a partial change:
|
|
154
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/patch-data-item.md
|
|
155
|
+
*
|
|
156
|
+
* @param {string} dataCollectionId
|
|
157
|
+
* @param {string} itemId The item's `_id`.
|
|
158
|
+
* @param {object} data The complete new field set.
|
|
159
|
+
* @returns {Promise<object>} The updated item's `data` payload.
|
|
160
|
+
*/
|
|
161
|
+
export async function updateDataItem(dataCollectionId, itemId, data) {
|
|
162
|
+
const res = await wixApiRequest(`/wix-data/v2/items/${encodeURIComponent(itemId)}`, {
|
|
163
|
+
method: "PUT",
|
|
164
|
+
body: { dataCollectionId, dataItem: { id: itemId, data: { ...data, _id: itemId } } },
|
|
165
|
+
});
|
|
166
|
+
const updated = res?.dataItem?.data;
|
|
167
|
+
if (!updated) throw new Error(`Update of "${itemId}" in "${dataCollectionId}" failed (no item returned).`);
|
|
168
|
+
return updated;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Remove an item by `_id`. Irreversible. Returns the removed `data` payload. Throws if the
|
|
173
|
+
* visitor lacks Delete permission (usually admin/author only).
|
|
174
|
+
* Reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/remove-data-item.md
|
|
175
|
+
*
|
|
176
|
+
* @param {string} dataCollectionId
|
|
177
|
+
* @param {string} itemId The item's `_id`.
|
|
178
|
+
* @returns {Promise<object|null>} The removed item's `data` payload.
|
|
179
|
+
*/
|
|
180
|
+
export async function removeDataItem(dataCollectionId, itemId) {
|
|
181
|
+
const res = await wixApiRequest(`/wix-data/v2/items/${encodeURIComponent(itemId)}`, {
|
|
182
|
+
method: "DELETE",
|
|
183
|
+
query: { dataCollectionId },
|
|
184
|
+
});
|
|
185
|
+
return res?.dataItem?.data ?? null;
|
|
186
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
|
|
2
|
+
# Wix Events Skill
|
|
3
|
+
|
|
4
|
+
> **Source files (in this skill):** the shared transport `references/shared/wix-client.js` and the helper file(s) you need from `references/events/`. All helpers import from `"./wix-client.js"`, so copy them into the same folder (e.g. `src/rest/`).
|
|
5
|
+
>
|
|
6
|
+
> | Need | Copy |
|
|
7
|
+
> |---|---|
|
|
8
|
+
> | Event listing + detail (always) | `wix-events-browse.js` |
|
|
9
|
+
> | RSVP, ticketing, registration | `wix-events-registration.js` |
|
|
10
|
+
|
|
11
|
+
Builds a real, client-only Wix Events site. The browser talks to Wix directly over a
|
|
12
|
+
public `WIX_CLIENT_ID`. Never mock events; never hand-build registration or payment URLs —
|
|
13
|
+
register via the RSVP/ticketing APIs, and complete paid tickets on the official Wix-hosted
|
|
14
|
+
ticket form.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
- User wants an events site over Wix Events & Tickets, or asks to "connect Wix Events".
|
|
18
|
+
- Replacing placeholder/mock events with live Wix data.
|
|
19
|
+
- Adding event listings, an event detail page, RSVP, or ticket purchase over existing,
|
|
20
|
+
published Wix events.
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
1. A Wix site with **Wix Events & Tickets installed and events already published** (this skill
|
|
24
|
+
does NOT create events — it's read-only over them). Selling **paid** tickets also needs a
|
|
25
|
+
Wix premium plan + a configured payment method; **free** events and RSVP events work without.
|
|
26
|
+
2. The site's public headless **`WIX_CLIENT_ID`**, provided in the handoff prompt (the Wix
|
|
27
|
+
Business Manager surfaces a copyable prompt with the id filled in — see the router `SKILL.md`). Paste
|
|
28
|
+
it into `src/rest/wix-client.js` in place of the placeholder. It is a visitor-facing
|
|
29
|
+
credential (it only mints anonymous visitor tokens), **not** a secret, so hardcoding/
|
|
30
|
+
committing it is fine.
|
|
31
|
+
3. For paid tickets the buyer completes payment on the **Wix-hosted ticket form** (the redirect
|
|
32
|
+
target of `getTicketCheckoutUrl`). The deployed app domain may need to be allow-listed on the
|
|
33
|
+
OAuth client for that page to return cleanly — a **separate Wix setup the user completes
|
|
34
|
+
later**, out of this skill's scope. If the return fails before that's done, that's expected;
|
|
35
|
+
flag it and continue.
|
|
36
|
+
|
|
37
|
+
## The API (copy as-is; do not re-derive it)
|
|
38
|
+
This skill ships only the REST layer — no UI components. Build the events UI however the
|
|
39
|
+
project wants; wire it to these two snippets. Copy them into the app (e.g. `src/api/`) and only
|
|
40
|
+
adjust import paths:
|
|
41
|
+
- `src/rest/wix-client.js` — visitor-token mint/refresh + transport. Set `WIX_CLIENT_ID` to the
|
|
42
|
+
id from the prompt (replace the `<YOUR-CLIENT-ID>` placeholder). The visitor refresh token is
|
|
43
|
+
persisted to localStorage and IS the identity of the visitor's ticket reservation/cart — do
|
|
44
|
+
not re-mint anonymously per load.
|
|
45
|
+
- `src/rest/wix-events-browse.js` — **Browse & discovery:**
|
|
46
|
+
`queryEvents`, `getEventBySlug`, `countUpcomingEvents`, `queryEventCategories`, `listEventsByCategory`
|
|
47
|
+
- `src/rest/wix-events-registration.js` — **RSVP & ticketing:**
|
|
48
|
+
`createRsvp`, `queryTicketDefinitions`, `reserveTickets`, `getTicketCheckoutUrl`, `checkoutTickets`
|
|
49
|
+
|
|
50
|
+
The `Event`, `TicketDefinition`, and `RSVP`/`Order` shapes are documented as JSDoc comments at
|
|
51
|
+
the top of each helper file. Read the relevant file(s) before building the UI — they describe
|
|
52
|
+
the key fields and link to the full API reference for anything not shown.
|
|
53
|
+
|
|
54
|
+
## How to wire it (UI is the project's choice)
|
|
55
|
+
- **Event grid** — `queryEvents()` lists live (UPCOMING/STARTED) events, soonest first. Render
|
|
56
|
+
`title`, `mainImage.url`, `dateAndTimeSettings.formatted.dateAndTime`, `location.name`, and
|
|
57
|
+
`shortDescription`. Use `offset`/`nextOffset` from the result to page. Link each card by `slug`.
|
|
58
|
+
- **Event detail** — `getEventBySlug(slug)`; returns null on miss — show a not-found state, never
|
|
59
|
+
invent an event.
|
|
60
|
+
- **Description fields:** `event.shortDescription` is a **plain string** (safe teaser). The full
|
|
61
|
+
`event.description` is **Ricos rich content** — an object shaped `{ nodes: [...] }`, **not a string**.
|
|
62
|
+
Render it with a Ricos viewer (`@wix/ricos`) or walk `nodes` to extract text; **never** call string
|
|
63
|
+
methods (`.split`/`.slice`/`.substring`) on it — that crashes the page. When you only need text, use
|
|
64
|
+
`shortDescription`.
|
|
65
|
+
- Branch the registration UI on `event.registration.type`:
|
|
66
|
+
- `"RSVP"` → render the RSVP form (fields from `event.form.controls`).
|
|
67
|
+
- `"TICKETING"` → render the ticket picker.
|
|
68
|
+
- `"EXTERNAL"` → link out to `event.registration.external.url`.
|
|
69
|
+
- `"NONE"` → details only, no registration.
|
|
70
|
+
Only `registration.status` values starting `OPEN_` accept new registrations; otherwise show the
|
|
71
|
+
closed state.
|
|
72
|
+
- **Categories (optional)** — `queryEventCategories()` for a filter menu (`counts.assignedEventsCount`
|
|
73
|
+
per category); `listEventsByCategory(categoryId)` to list a category's events (same card fields
|
|
74
|
+
and paging as `queryEvents`).
|
|
75
|
+
- **RSVP** — `createRsvp(eventId, { firstName, lastName, email, status, additionalGuestNames?, extraFields? })`.
|
|
76
|
+
`status` defaults to `"YES"`; only offer `"NO"` when `registration.rsvp.responseType` is
|
|
77
|
+
`"YES_AND_NO"`. If the event is full with a waitlist enabled, the returned RSVP comes back with
|
|
78
|
+
status `"WAITLIST"` — tell the guest. Completes fully client-side; no redirect.
|
|
79
|
+
- **Ticketing** — show tickets, reserve, then complete on the hosted form:
|
|
80
|
+
1. `queryTicketDefinitions(eventId)` → render each ticket's `name`, price (`pricing.fixedPrice.amount`
|
|
81
|
+
+ `currency` for standard tickets; `free` boolean for free tickets; `pricing.minPrice` for
|
|
82
|
+
donation/"pay what you want"; `pricing.pricingOptions.options` for tiered), and filter on
|
|
83
|
+
`saleStatus === "SALE_STARTED"`. The endpoint already returns only non-hidden, available tickets.
|
|
84
|
+
2. `reserveTickets([{ ticketDefinitionId, quantity, guestPrice?, pricingOptionId? }])` → holds the
|
|
85
|
+
tickets; returns `{ id, expirationDate }`. Show a countdown to `expirationDate` if you like.
|
|
86
|
+
3. `window.location.href = getTicketCheckoutUrl(event, reservation.id)` → the Wix-hosted ticket
|
|
87
|
+
form collects guest details + payment and returns the buyer to the event. This is the path for
|
|
88
|
+
**all paid tickets**.
|
|
89
|
+
- **Free tickets only:** you may instead call `checkoutTickets(eventId, { reservationId, buyer, guests })`
|
|
90
|
+
to finish in-app — it returns an order with status `"FREE"`, a `ticketsPdf`, and `tickets[]`. It
|
|
91
|
+
throws for paid orders (status `INITIATED`), telling you to use the hosted form.
|
|
92
|
+
- **Empty state** — if `countUpcomingEvents()` is 0, show an empty state telling the user to publish
|
|
93
|
+
events in their Wix dashboard. Never invent events.
|
|
94
|
+
|
|
95
|
+
## Hard rules (do not violate)
|
|
96
|
+
- ✅ Complete paid ticket purchases ONLY via `reserveTickets()` → `getTicketCheckoutUrl()` redirect
|
|
97
|
+
(the official Wix-hosted ticket form).
|
|
98
|
+
- ❌ Never hand-build registration, ticket, payment, or checkout URLs — derive the hosted form URL
|
|
99
|
+
from `event.eventPageUrl` via `getTicketCheckoutUrl`.
|
|
100
|
+
- ❌ Never mock events, tickets, or guest counts — render live Wix data or the empty/closed state.
|
|
101
|
+
- ❌ Never invent reviews, ratings, attendee names, or "X spots left" numbers not returned by the API.
|
|
102
|
+
- ✅ Set `WIX_CLIENT_ID` from the prompt's value (public visitor-facing client id — safe to hardcode).
|
|
103
|
+
- ✅ Branch registration UI on `event.registration.type`; respect `registration.status` (only `OPEN_*`
|
|
104
|
+
accepts registrations) and ticket `saleStatus`/`salesDetails.soldOut`.
|
|
105
|
+
- ✅ Pass `guestPrice` for donation/"pay what you want" tickets and `pricingOptionId` for tiered tickets
|
|
106
|
+
to `reserveTickets`.
|
|
107
|
+
- The helpers fail loudly on purpose: `reserveTickets` throws when tickets aren't actually held,
|
|
108
|
+
`createRsvp` throws on closed/full registration, `checkoutTickets` throws when payment is still owed.
|
|
109
|
+
A green path means it really worked — don't swallow these.
|
|
110
|
+
|
|
111
|
+
## Beyond the snippets
|
|
112
|
+
The snippets cover the common RSVP + ticketing paths. If you hit a use case they don't cover
|
|
113
|
+
(coupons/`discount` at checkout, members/auth, schedule/agenda, seating maps, canceling a
|
|
114
|
+
reservation, a field not in the typedefs), make the call yourself with `wixApiRequest` — but look
|
|
115
|
+
up the exact endpoint, HTTP method, and request body in the **official Wix Events API reference**
|
|
116
|
+
first; never guess:
|
|
117
|
+
- Events API reference: https://dev.wix.com/docs/api-reference/business-solutions/events.md
|
|
118
|
+
- Registration (RSVP + ticketing) overview: https://dev.wix.com/docs/api-reference/business-solutions/events/registration/introduction.md
|
|
119
|
+
- Member login + a "my registrations" account view → the **members** vertical (`references/members/INSTRUCTIONS.md`).
|
|
120
|
+
- Ticketing flow (reservations → orders → tickets): https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/introduction.md
|
|
121
|
+
|
|
122
|
+
Keep the snippets as the default for everything they already do; reach for the API reference only
|
|
123
|
+
for the gap.
|
|
124
|
+
|
|
125
|
+
## Point the user to their dashboard
|
|
126
|
+
In some cases, users need to access the Wix dashboard in order to edit the events content for their site. To facilitate this, provide the user with deep links directly to the relevant dashboard pages. For events data those pages are:
|
|
127
|
+
- **Events** — `https://manage.wix.com/dashboard/{metaSiteId}/events` (`Dashboard → Events` → **+ Add Event**; create the event, then set it up as **Ticketed** or **RSVP**; only published events appear in the app)
|
|
128
|
+
|
|
129
|
+
Substitute the site's `metaSiteId` to complete the links (you have it from the handoff / `ListWixSites`). Include the in-dashboard navigation as a fallback.
|
|
130
|
+
|
|
131
|
+
## Verification checklist (before declaring done)
|
|
132
|
+
- [ ] `WIX_CLIENT_ID` set to the prompt's value (not the `<YOUR-CLIENT-ID>` placeholder)
|
|
133
|
+
- [ ] Visitor token persists across reload (same visitor identity for reservations)
|
|
134
|
+
- [ ] Event grid renders live events; clicking a card opens the detail page by `slug`
|
|
135
|
+
- [ ] Detail page branches correctly on `registration.type` (RSVP form vs. ticket picker vs. external link)
|
|
136
|
+
- [ ] RSVP submit creates a real RSVP (and surfaces a `WAITLIST` result when the event is full)
|
|
137
|
+
- [ ] Ticket purchase reserves tickets, then redirects via `getTicketCheckoutUrl` (no hand-built URL)
|
|
138
|
+
- [ ] Paid checkout lands on the Wix-hosted ticket form; buyer returns to the event afterward
|
|
139
|
+
- [ ] Closed registration / sold-out tickets show a clear state rather than a dead end
|
|
140
|
+
- [ ] Empty state shown when `countUpcomingEvents()` is 0
|
|
141
|
+
- [ ] No mock events, tickets, or attendee data anywhere
|
|
142
|
+
- [ ] Told the user at least once that they can continue setting up their events in the dashboard and provided deep links.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { wixApiRequest } from "./wix-client.js";
|
|
2
|
+
|
|
3
|
+
// Data model reference: see INSTRUCTIONS.md
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wix Events V3 Event — key fields for list cards and the detail page.
|
|
7
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/event-object.md
|
|
8
|
+
*
|
|
9
|
+
* id {string}, title {string}, slug {string},
|
|
10
|
+
* status {string} — "UPCOMING"|"STARTED"|"ENDED"|"CANCELED"|"DRAFT" (only UPCOMING/STARTED are live),
|
|
11
|
+
* shortDescription {string} — PLAIN-TEXT teaser, safe to render directly (DETAILS fieldset),
|
|
12
|
+
* description {object} — RICH CONTENT (Ricos), shape { nodes: [...] } — NOT a string (TEXTS fieldset,
|
|
13
|
+
* returned by getEventBySlug). Render it with a Ricos viewer (@wix/ricos) or walk `nodes` to extract
|
|
14
|
+
* text; NEVER call string methods (.split/.slice/.substring/.trim) on it — that crashes the page.
|
|
15
|
+
* For a plain-string teaser use shortDescription instead.
|
|
16
|
+
* detailedDescription {string} — legacy plain/HTML description (TEXTS fieldset); prefer description/shortDescription,
|
|
17
|
+
* mainImage {object} — { id, url, width, height, altText } (DETAILS fieldset),
|
|
18
|
+
* dateAndTimeSettings {object}:
|
|
19
|
+
* startDate, endDate {string} — ISO-8601; absent when dateAndTimeTbd is true,
|
|
20
|
+
* timeZoneId {string} — IANA tz,
|
|
21
|
+
* formatted.dateAndTime {string} — ready-to-render human string,
|
|
22
|
+
* location {object}: type "VENUE"|"ONLINE", name {string}, address {object}, locationTbd {boolean},
|
|
23
|
+
* registration {object} (REGISTRATION fieldset):
|
|
24
|
+
* type {string} — "RSVP"|"TICKETING"|"EXTERNAL"|"NONE",
|
|
25
|
+
* status {string} — only OPEN_* statuses accept new registrations,
|
|
26
|
+
* rsvp.responseType — "YES_AND_NO" allows a "NO" reply,
|
|
27
|
+
* tickets.currency, tickets.lowestPrice, tickets.soldOut,
|
|
28
|
+
* external.url — link out when type === "EXTERNAL",
|
|
29
|
+
* eventPageUrl {object} — { base, path } (URLS fieldset) — needed for getTicketCheckoutUrl,
|
|
30
|
+
* form {object} — registration form controls (FORM fieldset),
|
|
31
|
+
* calendarUrls {object} — { google, ics } (DETAILS fieldset)
|
|
32
|
+
*
|
|
33
|
+
* Category: { id, label, slug, counts.assignedEventsCount }
|
|
34
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/categories
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const LIST_FIELDS = ["DETAILS", "REGISTRATION", "URLS"];
|
|
38
|
+
const DETAIL_FIELDS = ["DETAILS", "TEXTS", "REGISTRATION", "URLS", "FORM"];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Query live (UPCOMING/STARTED) events for a listing grid, one page. Sorted soonest first.
|
|
42
|
+
* Uses offset paging — response carries total for empty state and "load more" math.
|
|
43
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/query-events.md
|
|
44
|
+
* @param {{ limit?: number, offset?: number, status?: string[] }} [options]
|
|
45
|
+
* @returns {Promise<{ events: object[], total: number, offset: number, nextOffset: number|null }>}
|
|
46
|
+
*/
|
|
47
|
+
export async function queryEvents({ limit = 50, offset = 0, status = ["UPCOMING", "STARTED"] } = {}) {
|
|
48
|
+
const res = await wixApiRequest("/events/v3/events/query", {
|
|
49
|
+
method: "POST",
|
|
50
|
+
body: {
|
|
51
|
+
query: {
|
|
52
|
+
filter: { status: { $in: status } },
|
|
53
|
+
sort: [{ fieldName: "dateAndTimeSettings.startDate", order: "ASC" }],
|
|
54
|
+
paging: { limit, offset },
|
|
55
|
+
},
|
|
56
|
+
fields: LIST_FIELDS,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const events = res?.events ?? [];
|
|
60
|
+
const total = res?.pagingMetadata?.total ?? events.length;
|
|
61
|
+
const nextOffset = offset + events.length < total ? offset + events.length : null;
|
|
62
|
+
return { events, total, offset, nextOffset };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Fetch a single event by its URL slug, with full detail-page fields (description, form, page URL).
|
|
67
|
+
* Returns null if not found — show a not-found state, never invent an event.
|
|
68
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/get-event-by-slug.md
|
|
69
|
+
* @param {string} slug
|
|
70
|
+
* @returns {Promise<object|null>}
|
|
71
|
+
*/
|
|
72
|
+
export async function getEventBySlug(slug) {
|
|
73
|
+
try {
|
|
74
|
+
const res = await wixApiRequest(`/events/v3/events/slug/${encodeURIComponent(slug)}`, {
|
|
75
|
+
method: "GET",
|
|
76
|
+
query: { fields: DETAIL_FIELDS },
|
|
77
|
+
});
|
|
78
|
+
return res?.event ?? null;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Total number of live events. Used for empty-state logic (0 → prompt user to publish events).
|
|
86
|
+
* @param {string[]} [status]
|
|
87
|
+
* @returns {Promise<number>}
|
|
88
|
+
*/
|
|
89
|
+
export async function countUpcomingEvents(status = ["UPCOMING", "STARTED"]) {
|
|
90
|
+
const res = await wixApiRequest("/events/v3/events/query", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
body: { query: { filter: { status: { $in: status } }, paging: { limit: 1, offset: 0 } } },
|
|
93
|
+
});
|
|
94
|
+
return res?.pagingMetadata?.total ?? 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Query event categories for a category menu/filter. counts.assignedEventsCount tells you
|
|
99
|
+
* how many events are in each category.
|
|
100
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/categories/query-categories.md
|
|
101
|
+
* @param {{ limit?: number, offset?: number }} [options]
|
|
102
|
+
* @returns {Promise<{ categories: object[], total: number }>}
|
|
103
|
+
*/
|
|
104
|
+
export async function queryEventCategories({ limit = 100, offset = 0 } = {}) {
|
|
105
|
+
const res = await wixApiRequest("/events/v1/categories/query", {
|
|
106
|
+
method: "POST",
|
|
107
|
+
body: { query: { paging: { limit, offset } }, fieldset: ["COUNTS"] },
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
categories: res?.categories ?? [],
|
|
111
|
+
total: res?.metaData?.total ?? (res?.categories?.length ?? 0),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* List live events assigned to a category, one page. Same card fields as queryEvents.
|
|
117
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/list-events-by-category.md
|
|
118
|
+
* @param {string} categoryId Category GUID from queryEventCategories.
|
|
119
|
+
* @param {{ limit?: number, offset?: number }} [options]
|
|
120
|
+
* @returns {Promise<{ events: object[], total: number, offset: number, nextOffset: number|null }>}
|
|
121
|
+
*/
|
|
122
|
+
export async function listEventsByCategory(categoryId, { limit = 50, offset = 0 } = {}) {
|
|
123
|
+
const res = await wixApiRequest(`/events/v3/events/category/${encodeURIComponent(categoryId)}`, {
|
|
124
|
+
method: "GET",
|
|
125
|
+
query: {
|
|
126
|
+
fields: LIST_FIELDS,
|
|
127
|
+
"paging.limit": String(limit),
|
|
128
|
+
"paging.offset": String(offset),
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
const events = res?.events ?? [];
|
|
132
|
+
const total = res?.pagingMetadata?.total ?? events.length;
|
|
133
|
+
const nextOffset = offset + events.length < total ? offset + events.length : null;
|
|
134
|
+
return { events, total, offset, nextOffset };
|
|
135
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { wixApiRequest } from "./wix-client.js";
|
|
2
|
+
|
|
3
|
+
// Data model reference: see INSTRUCTIONS.md
|
|
4
|
+
// Event shape (for getEventBySlug): see wix-events-browse.js
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wix Events Ticket Definition — a ticket type returned by the checkout/available-tickets endpoint.
|
|
8
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/list-available-tickets
|
|
9
|
+
*
|
|
10
|
+
* id {string}, eventId {string}, name {string}, description {string},
|
|
11
|
+
* free {boolean} — true when the ticket is free,
|
|
12
|
+
* price { amount, currency } — the display price (use pricing.* for full detail),
|
|
13
|
+
* pricing ONE-OF:
|
|
14
|
+
* fixedPrice { amount, currency } — same price for everyone,
|
|
15
|
+
* minPrice { amount, currency } — minimum for "pay what you want" (donation); pass chosen amount as ticketInfo.guestPrice,
|
|
16
|
+
* pricingOptions.options [{ id, name, price { amount, currency } }] — tiered; pass chosen option id as ticketInfo.pricingOptionId,
|
|
17
|
+
* pricingType {string} — "STANDARD" | "DONATION" | "OPTIONS",
|
|
18
|
+
* saleStatus {string} — "SALE_STARTED" is the only buyable state,
|
|
19
|
+
* limitPerCheckout {number},
|
|
20
|
+
* orderIndex {number} — display order
|
|
21
|
+
*
|
|
22
|
+
* RSVP: { id, eventId, status ("YES"|"NO"|"WAITLIST"), totalGuests, firstName, lastName, email }
|
|
23
|
+
* status "WAITLIST" when the event is full and waitlist is enabled.
|
|
24
|
+
* Order: { orderNumber, status, totalPrice, ticketsPdf, tickets[] }
|
|
25
|
+
* status "FREE" → confirmed. status "INITIATED" → redirect to getTicketCheckoutUrl.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Submit an RSVP for an RSVP-type event (registration.type === "RSVP"). Completes client-side.
|
|
30
|
+
*
|
|
31
|
+
* Pass status "NO" only when event.registration.rsvp.responseType is "YES_AND_NO".
|
|
32
|
+
* If the event is full with a waitlist enabled, the RSVP comes back with status "WAITLIST".
|
|
33
|
+
* Throws on closed registration, guest-limit exceeded, duplicate email, or invalid form.
|
|
34
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/registration/rsvp-v2/create-rsvp.md
|
|
35
|
+
*
|
|
36
|
+
* @param {string} eventId
|
|
37
|
+
* @param {{ firstName: string, lastName: string, email: string, status?: "YES"|"NO",
|
|
38
|
+
* additionalGuestNames?: string[], extraFields?: Record<string,string|string[]> }} guest
|
|
39
|
+
* @returns {Promise<object>} The created RSVP.
|
|
40
|
+
*/
|
|
41
|
+
export async function createRsvp(eventId, { firstName, lastName, email, status = "YES", additionalGuestNames = [], extraFields = {} } = {}) {
|
|
42
|
+
if (!firstName || !lastName || !email) throw new Error("createRsvp requires firstName, lastName, and email.");
|
|
43
|
+
|
|
44
|
+
// The built-in fields go TOP-LEVEL on rsvp — the API rejects them inside form.inputValues
|
|
45
|
+
// (400 "rsvp.firstName must not be empty"). form.inputValues is ONLY for the event's extra
|
|
46
|
+
// custom fields; sending a built-in-only form, or additionalGuestDetails when the event has no
|
|
47
|
+
// guest fields, throws INVALID_FORM_RESPONSE ("input additionalGuests/guestNames is unexpected").
|
|
48
|
+
const rsvp = { eventId, firstName, lastName, email, status };
|
|
49
|
+
|
|
50
|
+
const inputValues = [];
|
|
51
|
+
for (const [inputName, value] of Object.entries(extraFields)) {
|
|
52
|
+
inputValues.push(Array.isArray(value) ? { inputName, values: value } : { inputName, value });
|
|
53
|
+
}
|
|
54
|
+
if (inputValues.length) rsvp.form = { inputValues };
|
|
55
|
+
if (additionalGuestNames.length) {
|
|
56
|
+
rsvp.additionalGuestDetails = { guestCount: additionalGuestNames.length, guestNames: additionalGuestNames };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const res = await wixApiRequest("/events/v2/rsvps", { method: "POST", body: { rsvp } });
|
|
60
|
+
return res?.rsvp;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Query the buyable ticket definitions for a ticketed event (with availability).
|
|
65
|
+
*
|
|
66
|
+
* Uses GET /events/v1/checkout/available-tickets (WIX_EVENTS.READ_CHECKOUT) — the visitor-facing
|
|
67
|
+
* checkout endpoint. The V3 /ticket-definitions/query endpoint requires WIX_EVENTS.READ_TICKET_DEFINITIONS
|
|
68
|
+
* (scope SCOPE.DC-EVENTS.MANAGE-TICKET-DEF), an admin-only scope that anonymous visitor tokens do
|
|
69
|
+
* not carry, so it 403s in a headless SPA context.
|
|
70
|
+
*
|
|
71
|
+
* This endpoint already filters to non-hidden, SALE_STARTED tickets, so the result is ready to
|
|
72
|
+
* render directly. Pass includeSoldOut: false to additionally drop sold-out tiers.
|
|
73
|
+
*
|
|
74
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/list-available-tickets
|
|
75
|
+
* @param {string} eventId
|
|
76
|
+
* @param {{ includeSoldOut?: boolean }} [options]
|
|
77
|
+
* @returns {Promise<object[]>}
|
|
78
|
+
*/
|
|
79
|
+
export async function queryTicketDefinitions(eventId, { includeSoldOut = true } = {}) {
|
|
80
|
+
const res = await wixApiRequest("/events/v1/checkout/available-tickets", {
|
|
81
|
+
method: "GET",
|
|
82
|
+
query: { eventId, limit: "100" },
|
|
83
|
+
});
|
|
84
|
+
let defs = res?.definitions ?? [];
|
|
85
|
+
if (!includeSoldOut) defs = defs.filter((d) => !d.salesDetails?.soldOut);
|
|
86
|
+
return defs;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Reserve tickets for a limited time (typically 5–30 min) while the buyer completes checkout.
|
|
91
|
+
* Returns a reservation with status "PENDING" and an expirationDate.
|
|
92
|
+
* Throws if the reservation doesn't come back PENDING (e.g. tickets unavailable).
|
|
93
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/ticket-reservations/create-ticket-reservation.md
|
|
94
|
+
*
|
|
95
|
+
* @param {Array<{ ticketDefinitionId: string, quantity: number, guestPrice?: string, pricingOptionId?: string }>} tickets
|
|
96
|
+
* @returns {Promise<object>} The created reservation (id, expirationDate, tickets[], status).
|
|
97
|
+
*/
|
|
98
|
+
export async function reserveTickets(tickets) {
|
|
99
|
+
if (!Array.isArray(tickets) || !tickets.length) throw new Error("reserveTickets requires at least one ticket line.");
|
|
100
|
+
|
|
101
|
+
const lines = tickets.map(({ ticketDefinitionId, quantity, guestPrice, pricingOptionId }) => {
|
|
102
|
+
if (!ticketDefinitionId || !quantity) throw new Error("Each ticket line needs a ticketDefinitionId and quantity.");
|
|
103
|
+
const ticketInfo = {};
|
|
104
|
+
if (guestPrice !== undefined) ticketInfo.guestPrice = String(guestPrice);
|
|
105
|
+
if (pricingOptionId) ticketInfo.pricingOptionId = pricingOptionId;
|
|
106
|
+
const line = { ticketDefinitionId, quantity };
|
|
107
|
+
if (Object.keys(ticketInfo).length) line.ticketInfo = ticketInfo;
|
|
108
|
+
return line;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const res = await wixApiRequest("/events/v1/ticket-reservations", {
|
|
112
|
+
method: "POST",
|
|
113
|
+
body: { ticketReservation: { tickets: lines } },
|
|
114
|
+
});
|
|
115
|
+
const reservation = res?.ticketReservation;
|
|
116
|
+
if (!reservation?.id || reservation.status !== "PENDING") {
|
|
117
|
+
throw new Error(`Could not reserve tickets (status: ${reservation?.status ?? "unknown"}). They may be sold out.`);
|
|
118
|
+
}
|
|
119
|
+
return reservation;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Build the URL of the Wix-hosted ticket form for a reservation. Use this for ALL paid tickets
|
|
124
|
+
* (and free tickets too). The buyer fills in guest details and pays on Wix's page, then returns.
|
|
125
|
+
* Redirect with: window.location.href = getTicketCheckoutUrl(event, reservation.id)
|
|
126
|
+
*
|
|
127
|
+
* event must have been fetched with the URLS fieldset (getEventBySlug already does this).
|
|
128
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/checkout.md
|
|
129
|
+
*
|
|
130
|
+
* @param {object} event Event from getEventBySlug (needs eventPageUrl).
|
|
131
|
+
* @param {string} reservationId From reserveTickets.
|
|
132
|
+
* @returns {string} Absolute hosted ticket-form URL.
|
|
133
|
+
*/
|
|
134
|
+
export function getTicketCheckoutUrl(event, reservationId) {
|
|
135
|
+
const base = event?.eventPageUrl?.base;
|
|
136
|
+
const path = event?.eventPageUrl?.path;
|
|
137
|
+
if (!base || !path) throw new Error("Event is missing eventPageUrl — fetch it with getEventBySlug (URLS fieldset).");
|
|
138
|
+
if (!reservationId) throw new Error("getTicketCheckoutUrl requires a reservationId from reserveTickets.");
|
|
139
|
+
return `${base}${path}/ticket-form?reservationId=${encodeURIComponent(reservationId)}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check out a reservation via REST — creates the order and assigns guests.
|
|
144
|
+
* Use this ONLY for FREE ticket orders (no redirect needed): the returned order has
|
|
145
|
+
* status "FREE", a ticketsPdf, and generated tickets[].
|
|
146
|
+
*
|
|
147
|
+
* For PAID tickets, this throws and tells you to redirect to getTicketCheckoutUrl instead.
|
|
148
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/events/registration/ticketing/orders/checkout.md
|
|
149
|
+
*
|
|
150
|
+
* @param {string} eventId
|
|
151
|
+
* @param {{ reservationId: string,
|
|
152
|
+
* buyer?: { firstName: string, lastName: string, email: string },
|
|
153
|
+
* guests: Array<{ firstName: string, lastName: string, email: string, extraFields?: Record<string,string|string[]> }> }} details
|
|
154
|
+
* @returns {Promise<object>} The created order (orderNumber, status, ticketsPdf, tickets[]).
|
|
155
|
+
*/
|
|
156
|
+
export async function checkoutTickets(eventId, { reservationId, buyer, guests } = {}) {
|
|
157
|
+
if (!eventId || !reservationId) throw new Error("checkoutTickets requires eventId and reservationId.");
|
|
158
|
+
if (!Array.isArray(guests) || !guests.length) throw new Error("checkoutTickets requires at least one guest.");
|
|
159
|
+
|
|
160
|
+
const toForm = ({ firstName, lastName, email, extraFields = {} }) => {
|
|
161
|
+
const inputValues = [
|
|
162
|
+
{ inputName: "firstName", value: firstName },
|
|
163
|
+
{ inputName: "lastName", value: lastName },
|
|
164
|
+
{ inputName: "email", value: email },
|
|
165
|
+
];
|
|
166
|
+
for (const [inputName, value] of Object.entries(extraFields)) {
|
|
167
|
+
inputValues.push(Array.isArray(value) ? { inputName, values: value } : { inputName, value });
|
|
168
|
+
}
|
|
169
|
+
return { form: { inputValues } };
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const body = { reservationId, guests: guests.map(toForm) };
|
|
173
|
+
if (buyer) body.buyer = buyer;
|
|
174
|
+
|
|
175
|
+
const res = await wixApiRequest(`/events/v1/events/${encodeURIComponent(eventId)}/tickets/checkout`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
body,
|
|
178
|
+
});
|
|
179
|
+
const order = res?.order;
|
|
180
|
+
const status = order?.status;
|
|
181
|
+
if (status === "INITIATED" || status === "PENDING") {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Order ${order?.orderNumber ?? ""} needs payment (status: ${status}). Redirect the buyer to getTicketCheckoutUrl(event, reservationId) to pay on the hosted ticket form.`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return order;
|
|
187
|
+
}
|