@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,147 @@
|
|
|
1
|
+
import { wixApiRequest } from "./wix-client.js";
|
|
2
|
+
|
|
3
|
+
// Data model reference: see INSTRUCTIONS.md
|
|
4
|
+
// Service and TimeSlot shapes: see wix-bookings-services.js
|
|
5
|
+
|
|
6
|
+
// Wix Bookings app id — required inside catalogReference for the eCommerce checkout.
|
|
7
|
+
const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Wix Bookings Booking (Bookings Writer V2) — what createBooking returns.
|
|
11
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-writer-v2/create-booking.md
|
|
12
|
+
*
|
|
13
|
+
* id {string}, status "CREATED" (not yet on calendar; confirmed after hosted checkout),
|
|
14
|
+
* paymentStatus {string} — "NOT_PAID" until checkout completes,
|
|
15
|
+
* bookedEntity.slot {object} — { serviceId, scheduleId, startDate, endDate, timezone, location },
|
|
16
|
+
* contactDetails {object}, totalParticipants {number}
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Visitor's local IANA time zone — used as default for the booking timezone. */
|
|
20
|
+
function defaultTimeZone() {
|
|
21
|
+
try {
|
|
22
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
23
|
+
} catch {
|
|
24
|
+
return "UTC";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a booking for a slot. The booking starts as status "CREATED" and is NOT yet on the
|
|
30
|
+
* calendar — it is confirmed automatically after the buyer completes the hosted checkout (call
|
|
31
|
+
* checkoutBooking next). selectedPaymentOption is "ONLINE".
|
|
32
|
+
*
|
|
33
|
+
* Works for **both** service types — it reads the discriminator off the slot:
|
|
34
|
+
* - APPOINTMENT slot (from `listAvailableSlots`/`getAvailableSlot`) carries `scheduleId`.
|
|
35
|
+
* - CLASS/COURSE slot (from `listEventTimeSlots`) carries `eventInfo.eventId` and no `scheduleId`;
|
|
36
|
+
* Wix derives the session's date/resource/location from that event.
|
|
37
|
+
* For appointments, call `getAvailableSlot` first to re-validate and get staff (`availableResources`).
|
|
38
|
+
* Throws on an unbookable slot or missing booking id.
|
|
39
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-writer-v2/create-booking.md
|
|
40
|
+
*
|
|
41
|
+
* @param {object} slot A TimeSlot from listAvailableSlots/getAvailableSlot/listEventTimeSlots.
|
|
42
|
+
* @param {{ firstName?: string, lastName?: string, email: string, phone?: string }} contactDetails
|
|
43
|
+
* @param {{ totalParticipants?: number, timeZone?: string, title?: string }} [options]
|
|
44
|
+
* @returns {Promise<object>} The created booking (status "CREATED").
|
|
45
|
+
*/
|
|
46
|
+
export async function createBooking(slot, contactDetails, { totalParticipants = 1, timeZone, title } = {}) {
|
|
47
|
+
if (!slot || slot.bookable === false) {
|
|
48
|
+
throw new Error("Cannot book: the selected slot is not bookable. Re-check availability and pick another time.");
|
|
49
|
+
}
|
|
50
|
+
// A slot is bound either by scheduleId (appointment) or by eventInfo.eventId (class/course).
|
|
51
|
+
const eventId = slot.eventInfo?.eventId;
|
|
52
|
+
if (!slot.serviceId || (!slot.scheduleId && !eventId) || !slot.localStartDate || !slot.localEndDate) {
|
|
53
|
+
throw new Error("Cannot book: slot is missing serviceId, localStartDate/localEndDate, and a scheduleId (appointment) or eventInfo.eventId (class/course).");
|
|
54
|
+
}
|
|
55
|
+
if (!contactDetails?.email) {
|
|
56
|
+
throw new Error("Cannot book: contactDetails.email is required.");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const resource = slot.availableResources?.[0]?.resources?.[0];
|
|
60
|
+
|
|
61
|
+
// The availability slot's location uses the SERVICE location enum (e.g. "BUSINESS"), but the
|
|
62
|
+
// createBooking endpoint expects the BOOKING location enum [UNDEFINED, OWNER_BUSINESS,
|
|
63
|
+
// OWNER_CUSTOM, CUSTOM]. Passing slot.location straight through 400s
|
|
64
|
+
// ("slot.location.locationType enum must be in [...]"). Remap before sending.
|
|
65
|
+
const bookingLocation = (() => {
|
|
66
|
+
const loc = slot.location;
|
|
67
|
+
if (!loc) return null;
|
|
68
|
+
const valid = ["UNDEFINED", "OWNER_BUSINESS", "OWNER_CUSTOM", "CUSTOM"];
|
|
69
|
+
const map = { BUSINESS: "OWNER_BUSINESS", CUSTOM: "CUSTOM", CUSTOMER: "CUSTOM" };
|
|
70
|
+
const locationType = map[loc.locationType] || (valid.includes(loc.locationType) ? loc.locationType : "OWNER_BUSINESS");
|
|
71
|
+
return { ...loc, locationType };
|
|
72
|
+
})();
|
|
73
|
+
|
|
74
|
+
const bookedSlot = {
|
|
75
|
+
serviceId: slot.serviceId,
|
|
76
|
+
// Appointment → scheduleId; class/course → eventId. Send exactly one.
|
|
77
|
+
...(eventId ? { eventId } : { scheduleId: slot.scheduleId }),
|
|
78
|
+
startDate: slot.localStartDate,
|
|
79
|
+
endDate: slot.localEndDate,
|
|
80
|
+
timezone: timeZone || defaultTimeZone(),
|
|
81
|
+
...(bookingLocation ? { location: bookingLocation } : {}),
|
|
82
|
+
...(resource ? { resource: { id: resource.id, name: resource.name } } : {}),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const res = await wixApiRequest("/bookings/v2/bookings", {
|
|
86
|
+
method: "POST",
|
|
87
|
+
body: {
|
|
88
|
+
booking: {
|
|
89
|
+
bookedEntity: { slot: bookedSlot, title: title || undefined, tags: ["INDIVIDUAL"] },
|
|
90
|
+
contactDetails,
|
|
91
|
+
additionalFields: [],
|
|
92
|
+
totalParticipants,
|
|
93
|
+
selectedPaymentOption: "ONLINE",
|
|
94
|
+
},
|
|
95
|
+
participantNotification: { notifyParticipants: true },
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
const booking = res?.booking;
|
|
99
|
+
if (!booking?.id) throw new Error("Failed to create the booking (no booking id returned).");
|
|
100
|
+
return booking;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create an eCommerce checkout for a created booking and return the hosted checkout URL.
|
|
105
|
+
* Redirect the buyer there (window.location.href = ...). On return, the booking is confirmed.
|
|
106
|
+
* Throws if no redirect URL is produced.
|
|
107
|
+
* https://dev.wix.com/docs/rest/business-solutions/e-commerce/checkout/create-checkout.md
|
|
108
|
+
* @param {string} bookingId booking.id from createBooking().
|
|
109
|
+
* @returns {Promise<string>} The hosted-checkout URL to redirect to.
|
|
110
|
+
*/
|
|
111
|
+
export async function checkoutBooking(bookingId) {
|
|
112
|
+
if (!bookingId) throw new Error("checkoutBooking requires a bookingId.");
|
|
113
|
+
|
|
114
|
+
const checkoutRes = await wixApiRequest("/ecom/v1/checkouts", {
|
|
115
|
+
method: "POST",
|
|
116
|
+
body: {
|
|
117
|
+
channelType: "WEB",
|
|
118
|
+
lineItems: [
|
|
119
|
+
{ quantity: 1, catalogReference: { appId: BOOKINGS_APP_ID, catalogItemId: bookingId } },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const checkoutId = checkoutRes?.checkout?.id;
|
|
124
|
+
if (!checkoutId) throw new Error("Failed to create a checkout for the booking.");
|
|
125
|
+
|
|
126
|
+
const redirect = await wixApiRequest("/headless/v1/redirect-session", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
body: { ecomCheckout: { checkoutId }, callbacks: { postFlowUrl: window.location.href } },
|
|
129
|
+
});
|
|
130
|
+
const url = redirect?.redirectSession?.fullUrl;
|
|
131
|
+
if (!url) throw new Error("Failed to create the checkout redirect session.");
|
|
132
|
+
return url;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Convenience: create the booking and return the hosted-checkout URL in one call.
|
|
137
|
+
* Equivalent to createBooking() then checkoutBooking(booking.id). Throws loudly on any failure.
|
|
138
|
+
* @param {object} slot
|
|
139
|
+
* @param {{ firstName?: string, lastName?: string, email: string, phone?: string }} contactDetails
|
|
140
|
+
* @param {{ totalParticipants?: number, timeZone?: string, title?: string }} [options]
|
|
141
|
+
* @returns {Promise<{ booking: object, checkoutUrl: string }>}
|
|
142
|
+
*/
|
|
143
|
+
export async function bookAndCheckout(slot, contactDetails, options = {}) {
|
|
144
|
+
const booking = await createBooking(slot, contactDetails, options);
|
|
145
|
+
const checkoutUrl = await checkoutBooking(booking.id);
|
|
146
|
+
return { booking, checkoutUrl };
|
|
147
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { wixApiRequest } from "./wix-client.js";
|
|
2
|
+
|
|
3
|
+
// Data model reference: see INSTRUCTIONS.md
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wix Bookings Service (Services V2) — key fields for the services list and booking UI.
|
|
7
|
+
* This skill ships the APPOINTMENT flow; CLASS/COURSE are "Beyond the snippets".
|
|
8
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/query-services.md
|
|
9
|
+
*
|
|
10
|
+
* id {string}, type "APPOINTMENT"|"CLASS"|"COURSE", name {string}, tagLine {string},
|
|
11
|
+
* description {string}, hidden {boolean},
|
|
12
|
+
* media.items {array} — [{ image: { id, url, width, height, altText } }]
|
|
13
|
+
* (url may be a bare Wix media handle — pass through mediaUrl() before rendering),
|
|
14
|
+
* payment.rateType "FIXED"|"CUSTOM"|"VARIED"|"NO_FEE"|"SUBSCRIPTION",
|
|
15
|
+
* payment.fixed.price { value, currency, formattedValue },
|
|
16
|
+
* payment.options { online, inPerson, deposit, pricingPlan },
|
|
17
|
+
* schedule.id {string}, onlineBooking.enabled {boolean},
|
|
18
|
+
* category { id, name }, staffMemberIds {string[]}, locations {array},
|
|
19
|
+
* bookingPolicy.participantsPolicy { enabled {boolean}, maxParticipantsPerBooking {number} }
|
|
20
|
+
* — the MOST participants a single booking may reserve. Cap the participant selector at this
|
|
21
|
+
* value (for a CLASS, also bound by the slot's remainingCapacity). It is commonly 1, in which
|
|
22
|
+
* case there is no participant choice at all — book exactly 1. Sending totalParticipants above
|
|
23
|
+
* this makes createBooking fail.
|
|
24
|
+
*
|
|
25
|
+
* TimeSlot (Time Slots V2, appointments):
|
|
26
|
+
* serviceId {string}, localStartDate {string}, localEndDate {string} — "YYYY-MM-DDThh:mm:ss" (no zone),
|
|
27
|
+
* bookable {boolean}, scheduleId {string},
|
|
28
|
+
* location { id, name, formattedAddress, locationType "BUSINESS"|"CUSTOM"|"CUSTOMER" },
|
|
29
|
+
* totalCapacity {number}, remainingCapacity {number},
|
|
30
|
+
* availableResources {array} — staff (populated by getAvailableSlot, not listAvailableSlots)
|
|
31
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** The visitor's local IANA time zone — used as the default for availability queries. */
|
|
35
|
+
function defaultTimeZone() {
|
|
36
|
+
try {
|
|
37
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
38
|
+
} catch {
|
|
39
|
+
return "UTC";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a service image to an absolute URL. Wix media fields are sometimes a bare
|
|
45
|
+
* handle ("abc.jpg") rather than a full URL; this prefixes the Wix static host.
|
|
46
|
+
* @param {{ url?: string, id?: string }|string} image
|
|
47
|
+
* @returns {string|null}
|
|
48
|
+
*/
|
|
49
|
+
export function mediaUrl(image) {
|
|
50
|
+
const raw = typeof image === "string" ? image : image?.url || image?.id;
|
|
51
|
+
if (!raw) return null;
|
|
52
|
+
if (raw.startsWith("http")) return raw;
|
|
53
|
+
if (raw.startsWith("wix:image://")) {
|
|
54
|
+
const stripped = raw.slice("wix:image://".length).replace(/^v1\//, "");
|
|
55
|
+
return `https://static.wixstatic.com/media/${stripped.split("#")[0].split("/")[0]}`;
|
|
56
|
+
}
|
|
57
|
+
return `https://static.wixstatic.com/media/${raw}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Query bookable services (one page). Returns only services visible to visitors (hidden:false).
|
|
62
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/query-services.md
|
|
63
|
+
* @param {{ limit?: number, offset?: number }} [options]
|
|
64
|
+
* @returns {Promise<{ services: object[], total: number, nextOffset: number|null }>}
|
|
65
|
+
*/
|
|
66
|
+
export async function queryServices({ limit = 100, offset = 0 } = {}) {
|
|
67
|
+
const res = await wixApiRequest("/bookings/v2/services/query", {
|
|
68
|
+
method: "POST",
|
|
69
|
+
body: {
|
|
70
|
+
query: {
|
|
71
|
+
filter: { hidden: false },
|
|
72
|
+
paging: { limit, offset },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const services = res?.services ?? [];
|
|
77
|
+
const total = res?.pagingMetadata?.total ?? services.length;
|
|
78
|
+
const loaded = offset + services.length;
|
|
79
|
+
return { services, total, nextOffset: loaded < total ? loaded : null };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fetch a single service by its GUID. Returns null if not found.
|
|
84
|
+
* @param {string} serviceId
|
|
85
|
+
* @returns {Promise<object|null>}
|
|
86
|
+
*/
|
|
87
|
+
export async function getService(serviceId) {
|
|
88
|
+
const res = await wixApiRequest("/bookings/v2/services/query", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: { query: { filter: { id: serviceId }, paging: { limit: 1 } } },
|
|
91
|
+
});
|
|
92
|
+
return res?.services?.[0] ?? null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Total number of visitor-visible services. Used for empty-state logic.
|
|
97
|
+
* @returns {Promise<number>}
|
|
98
|
+
*/
|
|
99
|
+
export async function countServices() {
|
|
100
|
+
const res = await wixApiRequest("/bookings/v2/services/query", {
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: { query: { filter: { hidden: false }, paging: { limit: 1 } } },
|
|
103
|
+
});
|
|
104
|
+
return res?.pagingMetadata?.total ?? (res?.services?.length ?? 0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* List bookable time slots for an **APPOINTMENT** service within a local date range (availability
|
|
109
|
+
* comes from staff working hours). For **CLASS / COURSE** services (booked against scheduled
|
|
110
|
+
* sessions) use `listEventTimeSlots` — this endpoint returns an empty list for them, silently.
|
|
111
|
+
* If you render a mixed catalog, route by `service.type` via `listSlotsForService`.
|
|
112
|
+
* Dates are LOCAL ("YYYY-MM-DDThh:mm:ss") in timeZone.
|
|
113
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
|
|
114
|
+
* @param {string} serviceId
|
|
115
|
+
* @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
|
|
116
|
+
* @returns {Promise<{ slots: object[], nextCursor: string|null }>}
|
|
117
|
+
*/
|
|
118
|
+
export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
|
|
119
|
+
if (!cursor && (!fromLocalDate || !toLocalDate)) {
|
|
120
|
+
throw new Error("listAvailableSlots requires fromLocalDate and toLocalDate (local 'YYYY-MM-DDThh:mm:ss').");
|
|
121
|
+
}
|
|
122
|
+
const body = cursor
|
|
123
|
+
? { cursorPaging: { limit, cursor } }
|
|
124
|
+
: {
|
|
125
|
+
serviceId,
|
|
126
|
+
bookable: true,
|
|
127
|
+
fromLocalDate,
|
|
128
|
+
toLocalDate,
|
|
129
|
+
timeZone: timeZone || defaultTimeZone(),
|
|
130
|
+
cursorPaging: { limit },
|
|
131
|
+
};
|
|
132
|
+
const res = await wixApiRequest("/_api/service-availability/v2/time-slots", { method: "POST", body });
|
|
133
|
+
return {
|
|
134
|
+
slots: res?.timeSlots ?? [],
|
|
135
|
+
nextCursor: res?.cursorPagingMetadata?.cursors?.next ?? null,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Re-validate a specific appointment slot right before booking, and fetch its available
|
|
141
|
+
* resources (staff). Always re-check before createBooking — slots can be taken between listing
|
|
142
|
+
* and booking. Returns the slot or null if it's gone.
|
|
143
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/get-availability-time-slot.md
|
|
144
|
+
* @param {string} serviceId
|
|
145
|
+
* @param {{ localStartDate: string, localEndDate: string, timeZone?: string, location?: object }} options
|
|
146
|
+
* @returns {Promise<object|null>}
|
|
147
|
+
*/
|
|
148
|
+
export async function getAvailableSlot(serviceId, { localStartDate, localEndDate, timeZone, location } = {}) {
|
|
149
|
+
if (!localStartDate || !localEndDate) {
|
|
150
|
+
throw new Error("getAvailableSlot requires localStartDate and localEndDate.");
|
|
151
|
+
}
|
|
152
|
+
const res = await wixApiRequest("/_api/service-availability/v2/time-slots/get", {
|
|
153
|
+
method: "POST",
|
|
154
|
+
body: {
|
|
155
|
+
serviceId,
|
|
156
|
+
localStartDate,
|
|
157
|
+
localEndDate,
|
|
158
|
+
timeZone: timeZone || defaultTimeZone(),
|
|
159
|
+
...(location ? { location } : {}),
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
return res?.timeSlot ?? null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* List bookable session slots for **CLASS / COURSE** services within a local date range. These
|
|
167
|
+
* services are booked against scheduled sessions (calendar events), not staff working hours, so
|
|
168
|
+
* they use a different endpoint than appointments. Each returned slot carries `eventInfo.eventId`
|
|
169
|
+
* (the session) and has **no** `scheduleId` — `createBooking` reads that `eventId`.
|
|
170
|
+
* Dates are LOCAL ("YYYY-MM-DDThh:mm:ss") in timeZone.
|
|
171
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-event-time-slots.md
|
|
172
|
+
* @param {string|string[]} serviceIds One service id or several (the endpoint takes a plural list).
|
|
173
|
+
* @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
|
|
174
|
+
* @returns {Promise<{ slots: object[], nextCursor: string|null }>}
|
|
175
|
+
*/
|
|
176
|
+
export async function listEventTimeSlots(serviceIds, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
|
|
177
|
+
if (!cursor && (!fromLocalDate || !toLocalDate)) {
|
|
178
|
+
throw new Error("listEventTimeSlots requires fromLocalDate and toLocalDate (local 'YYYY-MM-DDThh:mm:ss').");
|
|
179
|
+
}
|
|
180
|
+
const ids = Array.isArray(serviceIds) ? serviceIds : [serviceIds];
|
|
181
|
+
const body = cursor
|
|
182
|
+
? { cursorPaging: { limit, cursor } }
|
|
183
|
+
: {
|
|
184
|
+
serviceIds: ids,
|
|
185
|
+
bookable: true,
|
|
186
|
+
fromLocalDate,
|
|
187
|
+
toLocalDate,
|
|
188
|
+
timeZone: timeZone || defaultTimeZone(),
|
|
189
|
+
cursorPaging: { limit },
|
|
190
|
+
};
|
|
191
|
+
const res = await wixApiRequest("/_api/service-availability/v2/time-slots/event", { method: "POST", body });
|
|
192
|
+
return {
|
|
193
|
+
slots: res?.timeSlots ?? [],
|
|
194
|
+
nextCursor: res?.cursorPagingMetadata?.cursors?.next ?? null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Type-agnostic convenience: list bookable slots for a service, routing to the right endpoint by
|
|
200
|
+
* `service.type` — APPOINTMENT → `listAvailableSlots`, CLASS/COURSE → `listEventTimeSlots`. The
|
|
201
|
+
* returned slots are shaped the same either way (`localStartDate`/`localEndDate`/`location`/
|
|
202
|
+
* `availableResources`), and each is bookable via `createBooking`.
|
|
203
|
+
* NOTE: a **COURSE** is enrolled as a whole (not per session), so it typically returns **no** slots
|
|
204
|
+
* here — the per-slot booking flow applies to APPOINTMENT and CLASS.
|
|
205
|
+
* @param {{ _id?: string, id?: string, type?: string }} service A service from queryServices.
|
|
206
|
+
* @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
|
|
207
|
+
* @returns {Promise<{ slots: object[], nextCursor: string|null }>}
|
|
208
|
+
*/
|
|
209
|
+
export async function listSlotsForService(service, options = {}) {
|
|
210
|
+
const serviceId = service?._id || service?.id;
|
|
211
|
+
if (!serviceId) throw new Error("listSlotsForService requires a service with an id.");
|
|
212
|
+
return service?.type === "CLASS" || service?.type === "COURSE"
|
|
213
|
+
? listEventTimeSlots(serviceId, options)
|
|
214
|
+
: listAvailableSlots(serviceId, options);
|
|
215
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
|
|
2
|
+
# Wix CMS Skill
|
|
3
|
+
|
|
4
|
+
> **Source files (in this skill):** the shared transport `references/shared/wix-client.js` and this vertical's `references/cms/wix-cms.js`. Copy **both** into your app's `src/rest/` side by side — the helper does `import { wixApiRequest } from "./wix-client.js"`, so they must land in the same folder.
|
|
5
|
+
|
|
6
|
+
Builds a real, client-only Wix CMS-backed app. The browser talks to Wix Data directly
|
|
7
|
+
over a public `WIX_CLIENT_ID` to read and write items in the site's data collections.
|
|
8
|
+
Never mock data; never hand-build API URLs — always go through the helpers, which call
|
|
9
|
+
the official Wix Data endpoints.
|
|
10
|
+
|
|
11
|
+
## When to use
|
|
12
|
+
- User wants to display Wix CMS / Content Manager content (a collection of posts,
|
|
13
|
+
tutorials, recipes, team members, listings, FAQs, etc.) on a site.
|
|
14
|
+
- Replacing placeholder/mock content with live Wix Data items.
|
|
15
|
+
- Adding list pages, detail pages, category/tag filtering, or free-text search over an
|
|
16
|
+
existing Wix data collection.
|
|
17
|
+
- Wiring a public form (contact, RSVP, review, signup) that writes a row into a collection.
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
1. A Wix site with **a data collection already created and populated** (this skill does
|
|
21
|
+
NOT provision collections — it reads/writes existing ones). The merchant creates
|
|
22
|
+
collections, fields, and items in the Wix dashboard (CMS / Content Manager).
|
|
23
|
+
2. The site's public headless **`WIX_CLIENT_ID`**, provided in the handoff prompt (the
|
|
24
|
+
Wix Business Manager surfaces a copyable prompt with the id filled in — see
|
|
25
|
+
the router `SKILL.md`). Paste it into `src/rest/wix-client.js` in place of the placeholder. It is
|
|
26
|
+
a buyer-facing credential (it only mints anonymous visitor tokens), **not** a secret,
|
|
27
|
+
so hardcoding/committing it is fine.
|
|
28
|
+
3. **Collection permissions** must match what you're doing. This skill runs as an
|
|
29
|
+
anonymous visitor, so a call only works if the collection grants that action to
|
|
30
|
+
"Anyone": Read for listing content, Insert for a public form. Update/Delete are
|
|
31
|
+
almost always admin- or author-only and will fail for a visitor. The site owner sets
|
|
32
|
+
these in the Wix dashboard (CMS → collection → Permissions). This is a **separate Wix
|
|
33
|
+
setup step the user completes** — out of this skill's scope. If a read/insert fails
|
|
34
|
+
with a permission error (HTTP 403) before that's set, that's expected; flag it and
|
|
35
|
+
continue.
|
|
36
|
+
4. You need each collection's **collection ID** (its name, e.g. `Tutorials`) and its
|
|
37
|
+
**field keys** (e.g. `title`, `publishDate`). Read field keys off a fetched item, or
|
|
38
|
+
from the collection schema (see "Beyond the snippets").
|
|
39
|
+
|
|
40
|
+
## The API (copy as-is; do not re-derive it)
|
|
41
|
+
This skill ships only the REST layer — no UI components. Build the UI however the project
|
|
42
|
+
wants; wire it to these two snippets. Copy them into the app (e.g. `src/api/`) and only
|
|
43
|
+
adjust import paths:
|
|
44
|
+
- `src/rest/wix-client.js` — visitor-token mint/refresh + transport. Set `WIX_CLIENT_ID`
|
|
45
|
+
to the id from the prompt (replace the `<YOUR-CLIENT-ID>` placeholder). The visitor
|
|
46
|
+
refresh token is persisted to localStorage; do not re-mint anonymously per load.
|
|
47
|
+
- `src/rest/wix-cms.js` — exports:
|
|
48
|
+
- **Read:** `queryDataItems`, `getDataItem`, `getDataItemBy`, `countDataItems`
|
|
49
|
+
- **Write:** `insertDataItem`, `updateDataItem`, `removeDataItem`
|
|
50
|
+
|
|
51
|
+
The Data Item shape, the per-collection **permissions model**, and the **filter/sort
|
|
52
|
+
syntax** are documented as JSDoc comments at the top of `wix-cms.js`. Read them before
|
|
53
|
+
building the UI — read helpers return the item's flat `data` payload (which always
|
|
54
|
+
includes `_id`), and writes are bound by collection permissions.
|
|
55
|
+
|
|
56
|
+
## How to wire it (UI is the project's choice)
|
|
57
|
+
- **Content list** — `queryDataItems(collectionId, { filter?, sort?, limit?, cursor? })`
|
|
58
|
+
for the listing; render fields directly off each returned item (`item.title`, etc.).
|
|
59
|
+
Pass the returned `nextCursor` back as `cursor` to load the next page. Define
|
|
60
|
+
`filter`/`sort` on the first request only — cursor follow-ups reuse the original query.
|
|
61
|
+
- **Detail page** — route by the item's `_id` and call `getDataItem(collectionId, itemId)`;
|
|
62
|
+
returns null on miss → show a not-found state, never invent an item. For human-readable
|
|
63
|
+
URLs, add a slug-like field to the collection and route via
|
|
64
|
+
`getDataItemBy(collectionId, "slug", slugFromUrl)`.
|
|
65
|
+
- **Filter & search** — pass a `filter` to `queryDataItems` using the operators documented
|
|
66
|
+
in `wix-cms.js` (`$eq`, `$in`, `$gte`, `$startsWith`, `$hasSome`, `$and`/`$or`, …). For a
|
|
67
|
+
simple text search, `{ title: { $startsWith: term } }`; for richer free-text/fuzzy search
|
|
68
|
+
across fields, see "Beyond the snippets" (Search Data Items).
|
|
69
|
+
- **Public form** — on submit, `insertDataItem(collectionId, { name, email, message })`
|
|
70
|
+
(field keys must match the collection). Requires the collection's Insert permission to be
|
|
71
|
+
"Anyone". Show success/failure from the resolved/thrown result; never fake a success.
|
|
72
|
+
- **Edit / delete (admin/author flows)** — `updateDataItem(collectionId, itemId, data)`
|
|
73
|
+
(⚠ full replace — fetch + merge first to preserve other fields) and
|
|
74
|
+
`removeDataItem(collectionId, itemId)`. These need Update/Delete granted to the caller,
|
|
75
|
+
which visitors normally don't have — expect them to fail unless the collection is opened up.
|
|
76
|
+
- **Empty state** — if `countDataItems(collectionId)` is 0, show an empty state telling
|
|
77
|
+
the user to add items in their Wix dashboard. Never invent items.
|
|
78
|
+
|
|
79
|
+
## Hard rules (do not violate)
|
|
80
|
+
- ✅ Read/write ONLY through the helpers in `wix-cms.js` (which call the official Wix Data
|
|
81
|
+
`/wix-data/v2/items` endpoints).
|
|
82
|
+
- ❌ Never hand-build Wix Data URLs or invent endpoint paths.
|
|
83
|
+
- ❌ Never mock data — render live Wix Data items or the empty state.
|
|
84
|
+
- ❌ Never invent fields, reviews, ratings, or content not present in the collection.
|
|
85
|
+
- ✅ Set `WIX_CLIENT_ID` from the prompt's value (public client id — safe to hardcode).
|
|
86
|
+
- ✅ Use the item's `_id` as the route key and as `itemId` for get/update/remove.
|
|
87
|
+
- ✅ `updateDataItem` REPLACES the whole item — fetch with `getDataItem`, merge your
|
|
88
|
+
changes, then pass the full object, or use Patch Data Item (see below) for partial edits.
|
|
89
|
+
- ✅ Treat permission errors (HTTP 403) as a configuration step, not a code bug: tell the
|
|
90
|
+
user which permission to grant in the dashboard. Writes throw on failure — don't swallow
|
|
91
|
+
the error and show a fake success.
|
|
92
|
+
|
|
93
|
+
## Beyond the snippets
|
|
94
|
+
The snippets cover the common CMS paths (list, detail, filter, count, insert, update,
|
|
95
|
+
remove). If you hit a use case they don't cover, make the call yourself with
|
|
96
|
+
`wixApiRequest` — but look up the exact endpoint, HTTP method, and request body in the
|
|
97
|
+
**official Wix API reference** first; never guess:
|
|
98
|
+
- CMS / Data Items API reference: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items.md
|
|
99
|
+
- Data Items sample flows: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/sample-flows.md
|
|
100
|
+
- **Partial update** (change some fields, keep the rest): Patch Data Item —
|
|
101
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/patch-data-item.md
|
|
102
|
+
- **Upsert** (insert or update by id): Save Data Item —
|
|
103
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/save-data-item.md
|
|
104
|
+
- **Bulk** insert/update/save/remove (many items in one call):
|
|
105
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items.md
|
|
106
|
+
- **Free-text / fuzzy search** across fields: Search Data Items —
|
|
107
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/search-data-items.md
|
|
108
|
+
- **Aggregations** (counts/averages grouped by a field): Aggregate Data Items —
|
|
109
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/aggregate-data-items.md
|
|
110
|
+
- **Distinct values** (e.g. all categories for a filter menu): Query Distinct Values —
|
|
111
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/query-distinct-values.md
|
|
112
|
+
- **Referenced items** (expand a reference field beyond the inline limit): Query Referenced
|
|
113
|
+
Data Items — https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/query-referenced-data-items.md
|
|
114
|
+
- **Collection schema / field keys & types**: Get Data Collection —
|
|
115
|
+
https://dev.wix.com/docs/api-reference/business-solutions/cms/collection-management/data-collections/get-data-collection.md
|
|
116
|
+
- **Member-gated & user-generated content** → the **members** vertical
|
|
117
|
+
(`references/members/INSTRUCTIONS.md`). A collection whose permissions are `read: Anyone`,
|
|
118
|
+
`insert: Site Member`, `update/remove: Site Member Author` gives you the classic pattern: anyone
|
|
119
|
+
reads, only logged-in members write, and each member edits only their own. Sign the member in with
|
|
120
|
+
custom login (on your own UI), then `insertDataItem` runs as the member and Wix stamps the item's
|
|
121
|
+
`_owner` automatically — so a **"my items"** view is just
|
|
122
|
+
`queryDataItems(collectionId, { filter: { _owner: <memberId> } })`, and author-only
|
|
123
|
+
`updateDataItem`/`removeDataItem` are enforced server-side. (This skill never provisions the
|
|
124
|
+
collection — the owner creates it with those permissions in the dashboard.)
|
|
125
|
+
**Prefer Wix for member-generated content, and keep one feature's data and identity together.**
|
|
126
|
+
Content that belongs to the Wix site (likes, reviews, submissions) is best kept in a Wix collection
|
|
127
|
+
via these helpers, keyed on the Wix member's server-stamped `_owner`. What breaks is a *split*
|
|
128
|
+
feature — the row stored in one place while the member is identified from the Wix session elsewhere
|
|
129
|
+
(or filtered by some other `created_by`/user id): the two ids won't match, so ownership filters
|
|
130
|
+
miss. Keep them on one side; for Wix-backed rows that's `_owner`.
|
|
131
|
+
|
|
132
|
+
Keep the snippets as the default for everything they already do; reach for the API
|
|
133
|
+
reference only for the gap.
|
|
134
|
+
|
|
135
|
+
## Point the user to their dashboard
|
|
136
|
+
In some cases, users need to access the Wix dashboard in order to edit the CMS content for their site. To facilitate this, provide the user with deep links directly to the relevant dashboard pages. For CMS data those pages are:
|
|
137
|
+
- **Collections & items** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-cms` (`Dashboard → CMS`) → **Create Collection**, then open a collection to add items.
|
|
138
|
+
- **Permissions** — no separate deep link; in the same CMS area, open the collection → **More Actions → Permissions & Privacy**. For the headless app to work anonymously, set **Show content** to *Everyone* (visitor reads) and, for a public form, **Collect content** to *Everyone* (visitor inserts). Update/Delete stay admin-only.
|
|
139
|
+
|
|
140
|
+
Substitute the site's `metaSiteId` to complete the links (you have it from the handoff / `ListWixSites`). Include the in-dashboard navigation as a fallback.
|
|
141
|
+
|
|
142
|
+
## Verification checklist (before declaring done)
|
|
143
|
+
- [ ] `WIX_CLIENT_ID` set to the prompt's value (not the `<YOUR-CLIENT-ID>` placeholder)
|
|
144
|
+
- [ ] Visitor token persists across reload (same anonymous visitor, no re-mint per load)
|
|
145
|
+
- [ ] `queryDataItems` returns live items; pagination via `nextCursor` loads more
|
|
146
|
+
- [ ] Detail page uses the item's `_id` (or a slug field via `getDataItemBy`) and shows a
|
|
147
|
+
not-found state on miss — no invented item
|
|
148
|
+
- [ ] Filter/sort produce the expected subset (operators match the field types)
|
|
149
|
+
- [ ] Public form `insertDataItem` succeeds only when Insert is "Anyone"; on a 403 the user
|
|
150
|
+
is told to grant the permission — no fake success
|
|
151
|
+
- [ ] Update is treated as a full replace (fetch + merge), so no fields are silently dropped
|
|
152
|
+
- [ ] Empty state shown when `countDataItems` is 0
|
|
153
|
+
- [ ] No mock data anywhere; no hand-built Wix Data URLs
|
|
154
|
+
- [ ] Told the user at least once that they can continue setting up their content in the dashboard and provided deep links.
|