@wix/agent-skills 1.14.0 → 1.15.1
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 +12 -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/DATA_COLLECTION.md +5 -3
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +25 -9
- package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
- package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
- 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/backend-event/COMMON-EVENTS.md +5 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
- 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/COMPONENT-PREVIEW.md +57 -0
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +23 -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 +18 -16
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
- package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
- package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
- package/skills/wix-app/references/stores/INVENTORY.md +37 -0
- package/skills/wix-app/references/stores/QUERY.md +87 -0
- package/skills/wix-app/references/stores/WRITE.md +55 -0
- 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 +89 -130
- package/skills/wix-headless/entry/bootstrap.mjs +93 -0
- package/skills/wix-headless/entry/skill.md +76 -0
- package/skills/wix-headless/references/AI_FEATURES.md +178 -0
- package/skills/wix-headless/references/CAPABILITIES.md +86 -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 +129 -0
- package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +99 -0
- package/skills/wix-headless/references/SEED.md +45 -187
- 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 +213 -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 +232 -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 +192 -0
- package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +155 -0
- package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +197 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +136 -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 +79 -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 +15 -0
- package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
- package/skills/wix-headless/references/stripe/DEPLOYMENT.md +71 -0
- package/skills/wix-manage/SKILL.md +29 -3
- package/skills/wix-manage/references/analytics/query-site-analytics.md +298 -0
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
- package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
- package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +299 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
- package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +517 -0
- package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +218 -0
- package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
- package/skills/wix-manage/references/sites/site-import.md +270 -0
- package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +57 -0
- package/skills/wix-vibe-headless/SKILL.md +142 -0
- package/skills/wix-vibe-headless/platforms/README.md +22 -0
- package/skills/wix-vibe-headless/platforms/base44.md +142 -0
- package/skills/wix-vibe-headless/platforms/generic.md +87 -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 +157 -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 +217 -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 +136 -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 +223 -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 +164 -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 +146 -0
- package/skills/wix-headless/references/BUILD-astro.md +0 -253
- 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/Layout.astro +0 -132
- 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/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 -140
- 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 -520
- 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 -154
- 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,157 @@
|
|
|
1
|
+
|
|
2
|
+
# Wix Bookings Skill
|
|
3
|
+
|
|
4
|
+
> **Source files (in this skill):** the shared transport `references/shared/wix-client.js` and both bookings helpers from `references/bookings/`. All helpers import from `"./wix-client.js"`, so copy them into the same folder (e.g. `src/rest/`). Copy **both** for the full booking flow:
|
|
5
|
+
>
|
|
6
|
+
> | File | What it covers |
|
|
7
|
+
> |---|---|
|
|
8
|
+
> | `wix-bookings-services.js` | Service listing, slot availability, media URL helper |
|
|
9
|
+
> | `wix-bookings-checkout.js` | Create booking, hosted checkout, bookAndCheckout convenience |
|
|
10
|
+
|
|
11
|
+
Builds a real, client-only Wix Bookings front end. The browser talks to Wix directly over a
|
|
12
|
+
public `WIX_CLIENT_ID`. Never mock services or slots; never hand-build a `/checkout` URL —
|
|
13
|
+
always create the booking through the API and complete it via the eCom checkout + redirect-session.
|
|
14
|
+
|
|
15
|
+
This skill ships the single-service booking flow for **APPOINTMENT and CLASS** services: browse
|
|
16
|
+
services → pick a service → pick an available slot → enter details → book → hosted checkout.
|
|
17
|
+
Appointments and classes differ only in the availability call (`listAvailableSlots` vs
|
|
18
|
+
`listEventTimeSlots`); `listSlotsForService` routes by `service.type`, and `createBooking` handles
|
|
19
|
+
either slot. **COURSE (whole-course enrollment) is not covered** — see "Beyond the snippets".
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
- User wants a Wix Bookings appointment site or asks to "connect Wix Bookings".
|
|
23
|
+
- Replacing placeholder/mock services or fake calendars with live Wix data.
|
|
24
|
+
- Adding service listings, a slot picker, booking creation, or checkout over an existing
|
|
25
|
+
Wix Bookings setup.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
1. A Wix site with **Wix Bookings installed and at least one appointment service created**
|
|
29
|
+
(this skill does NOT provision — it's read-only over services). Staff/resources and a
|
|
30
|
+
booking policy should be configured so slots are bookable.
|
|
31
|
+
2. The site's public headless **`WIX_CLIENT_ID`**, provided in the handoff prompt (the Wix
|
|
32
|
+
Business Manager surfaces a copyable prompt with the id filled in — see the router `SKILL.md`). Paste
|
|
33
|
+
it into `src/rest/wix-client.js` in place of the placeholder. It is a buyer-facing credential
|
|
34
|
+
(it only mints anonymous visitor tokens), **not** a secret, so hardcoding/committing it is fine.
|
|
35
|
+
3. The site must **accept payments** for paid services, and the deployed app domain must be
|
|
36
|
+
allow-listed on the OAuth client for Wix-hosted checkout to return. These are **separate Wix
|
|
37
|
+
setup flows the user completes later** — out of this skill's scope. If checkout return fails
|
|
38
|
+
before that setup is done, that's expected; flag it and continue.
|
|
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 booking UI however the
|
|
42
|
+
project 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` to the
|
|
45
|
+
id from the prompt (replace the `<YOUR-CLIENT-ID>` placeholder). The visitor refresh token IS
|
|
46
|
+
the booking visitor identity; it is persisted to localStorage. Do not re-mint anonymously per
|
|
47
|
+
load.
|
|
48
|
+
- `src/rest/wix-bookings-services.js` — **Services & availability:**
|
|
49
|
+
`queryServices`, `getService`, `countServices`, `listAvailableSlots`, `getAvailableSlot`,
|
|
50
|
+
`mediaUrl` (resolve a service image to an absolute URL)
|
|
51
|
+
- `src/rest/wix-bookings-checkout.js` — **Booking & checkout:**
|
|
52
|
+
`createBooking`, `checkoutBooking`, `bookAndCheckout`
|
|
53
|
+
|
|
54
|
+
The `Service`, `TimeSlot`, and `Booking` shapes are documented as JSDoc comments at the top of
|
|
55
|
+
each helper file. Read them before building the UI — they describe the key fields and link to
|
|
56
|
+
the full API reference for anything not shown.
|
|
57
|
+
|
|
58
|
+
## How to wire it (UI is the project's choice)
|
|
59
|
+
- **Service list** — `queryServices()` for the listing (visitor-visible services only). Render
|
|
60
|
+
`name`, `tagLine`, the image via `mediaUrl(service.media?.items?.[0]?.image)`, and the price
|
|
61
|
+
from `payment.fixed.price`. `value` + `currency` are always present; `formattedValue` is
|
|
62
|
+
**optional** (it may be missing), so don't depend on it — build the price from `value`+`currency`
|
|
63
|
+
and use `formattedValue` only when present. e.g. `new Intl.NumberFormat(undefined, { style:
|
|
64
|
+
'currency', currency }).format(Number(value))`. (Rendering `formattedValue` alone leaves the price
|
|
65
|
+
blank — or your "Varies" fallback — whenever it's absent.) Pass the returned
|
|
66
|
+
`nextOffset` back as `offset` to load the next page. Books **APPOINTMENT and CLASS** services
|
|
67
|
+
(see the slot picker below); COURSE is whole-course enrollment and out of scope.
|
|
68
|
+
- **Service detail** — `getService(serviceId)` keyed off the URL/route; returns null on miss —
|
|
69
|
+
show a not-found state, never invent a service. Render `description`, price, and `locations`.
|
|
70
|
+
- **Slot picker** — use **`listSlotsForService(service, { fromLocalDate, toLocalDate, timeZone? })`**:
|
|
71
|
+
it routes by `service.type` — APPOINTMENT → `listAvailableSlots` (staff working hours), CLASS/COURSE
|
|
72
|
+
→ `listEventTimeSlots` (scheduled sessions). Both return the same slot shape. (Call the specific
|
|
73
|
+
function directly if you already know the type.) Dates are **local** wall-clock strings
|
|
74
|
+
`"YYYY-MM-DDThh:mm:ss"` (no zone), interpreted in `timeZone` (defaults to the visitor's IANA zone).
|
|
75
|
+
Only `bookable: true` slots come back. Render each `slot.localStartDate`/`localEndDate`; group by
|
|
76
|
+
day for a calendar. Pass `nextCursor` back
|
|
77
|
+
as `cursor` to page.
|
|
78
|
+
- **Booking form** — collect the buyer's `firstName`, `lastName`, `email`, `phone`. Keep it
|
|
79
|
+
minimal; richer per-service form fields live in the service's `form.id` (see "Beyond the snippets").
|
|
80
|
+
- **Participant count** — cap it by the service policy, not just slot capacity. The most a single
|
|
81
|
+
booking may reserve is `service.bookingPolicy.participantsPolicy.maxParticipantsPerBooking`. Only
|
|
82
|
+
render a participant selector when that value is `> 1`, and bound its max at
|
|
83
|
+
`min(maxParticipantsPerBooking, slot.remainingCapacity)` for a class; when it is `1` (the common
|
|
84
|
+
case) show no selector and book exactly one. Never offer a fixed range like 1–4 — the slot's
|
|
85
|
+
`remainingCapacity` tells you the class's open spots, not how many one buyer may take, so relying
|
|
86
|
+
on it alone lets the buyer pick a count that `createBooking` then rejects. Pass the chosen count
|
|
87
|
+
as `createBooking`'s `totalParticipants`.
|
|
88
|
+
- **Re-validate + book** — right before submitting, call
|
|
89
|
+
`getAvailableSlot(serviceId, { localStartDate, localEndDate, timeZone? })` to confirm the slot
|
|
90
|
+
is still open (and to pick up the staff resource). Then create + check out in one step:
|
|
91
|
+
`window.location.href = (await bookAndCheckout(slot, { email, firstName, lastName, phone })).checkoutUrl;`
|
|
92
|
+
Or split it: `const booking = await createBooking(slot, contact); const url = await checkoutBooking(booking.id);`
|
|
93
|
+
- **Confirmation / return** — after the buyer returns from hosted checkout, the order is placed
|
|
94
|
+
and Wix Bookings confirms the booking automatically (status becomes `CONFIRMED`, or `PENDING`
|
|
95
|
+
if the service needs manual approval). Show a confirmation screen on return.
|
|
96
|
+
- **Empty state** — if `countServices()` is 0, show an empty state telling the user to add a
|
|
97
|
+
service in their Wix dashboard. Never invent services.
|
|
98
|
+
|
|
99
|
+
## Hard rules (do not violate)
|
|
100
|
+
- ✅ Book ONLY via `createBooking()` → `checkoutBooking()` (or `bookAndCheckout()`), then redirect
|
|
101
|
+
to the returned `fullUrl`.
|
|
102
|
+
- ❌ Never hand-build a `/checkout`, booking, or calendar URL.
|
|
103
|
+
- ❌ Never mock services, time slots, or availability — render live Wix data or the empty state.
|
|
104
|
+
- ❌ Never invent reviews, ratings, staff, or testimonials. Empty review UI only.
|
|
105
|
+
- ✅ Set `WIX_CLIENT_ID` from the prompt's value (public client id — safe to hardcode).
|
|
106
|
+
- ✅ Send availability/booking dates as **local** `"YYYY-MM-DDThh:mm:ss"` strings plus a
|
|
107
|
+
`timeZone` — do not send UTC `Z` timestamps to the slot APIs.
|
|
108
|
+
- ✅ Re-validate the slot with `getAvailableSlot()` before `createBooking()` — slots get taken.
|
|
109
|
+
- ✅ Cap participant count at `service.bookingPolicy.participantsPolicy.maxParticipantsPerBooking`
|
|
110
|
+
(render no selector when it is 1); never offer a fixed range and never use slot capacity as the
|
|
111
|
+
per-buyer limit — a count above the policy makes `createBooking` fail.
|
|
112
|
+
- The client fails loudly on purpose: `createBooking`/`checkoutBooking` throw on an unbookable
|
|
113
|
+
slot, a missing booking id, or a missing redirect URL. A green path means it's really bookable —
|
|
114
|
+
don't swallow these.
|
|
115
|
+
|
|
116
|
+
## Beyond the snippets
|
|
117
|
+
The snippets cover **APPOINTMENT and CLASS** bookings (the slot picker routes by `service.type`).
|
|
118
|
+
For anything beyond that, extend the client: add a new helper on `wixApiRequest`, looking up the
|
|
119
|
+
exact endpoint, method, and body in the **official Wix API reference** first (never guess):
|
|
120
|
+
- Official Wix API reference: https://dev.wix.com/docs/api-reference.md
|
|
121
|
+
- Single-service booking flow (the full picture): https://dev.wix.com/docs/api-reference/business-solutions/bookings/flow-single-service-booking.md
|
|
122
|
+
- **Courses are NOT covered — a course is enrolled as a *whole*, not booked per session.**
|
|
123
|
+
`listEventTimeSlots` returns **no** per-session slots for a COURSE (verified — empty even for admin),
|
|
124
|
+
so the slot-picker flow doesn't apply. Enrolling in a course uses a course-specific flow (whole-course
|
|
125
|
+
capacity computed from bookings) that these snippets don't implement —
|
|
126
|
+
https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-reader-v2/query-extended-bookings.md
|
|
127
|
+
- **Service variants / participants** (duration- or person-based pricing):
|
|
128
|
+
https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/service-options-and-variants/get-service-options-and-variants-by-service-id.md
|
|
129
|
+
- **Add-ons:** https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/list-add-on-groups-by-service-id.md
|
|
130
|
+
- **Custom booking form fields** (render `service.form.id`): Get Form Summary —
|
|
131
|
+
https://dev.wix.com/docs/rest/crm/forms/form-schemas/get-form-summary.md
|
|
132
|
+
- **Member login + a "my bookings" account view** → the **members** vertical
|
|
133
|
+
(`references/members/INSTRUCTIONS.md`): booking itself works anonymously, but signing a member in
|
|
134
|
+
(custom login on your own UI) lets them see their own appointments/history.
|
|
135
|
+
|
|
136
|
+
Keep the snippets as the default for everything they already do; reach for the API reference
|
|
137
|
+
only for the gap.
|
|
138
|
+
|
|
139
|
+
## Point the user to their dashboard
|
|
140
|
+
In some cases, users need to access the Wix dashboard in order to edit the bookings content for their site. To facilitate this, provide the user with deep links directly to the relevant dashboard pages. For bookings data those pages are:
|
|
141
|
+
- **Booking Services** — `https://manage.wix.com/dashboard/{metaSiteId}/bookings/services` (`Dashboard → Bookings → Booking Services`; add services and service categories)
|
|
142
|
+
- **Staff** — `https://manage.wix.com/dashboard/{metaSiteId}/bookings/staff` (`Dashboard → Bookings → Staff`; add staff and set working hours, so slots are actually bookable)
|
|
143
|
+
|
|
144
|
+
Substitute the site's `metaSiteId` to complete the links (you have it from the handoff / `ListWixSites`). Include the in-dashboard navigation as a fallback.
|
|
145
|
+
|
|
146
|
+
## Verification checklist (before declaring done)
|
|
147
|
+
- [ ] `WIX_CLIENT_ID` set to the prompt's value (not the `<YOUR-CLIENT-ID>` placeholder)
|
|
148
|
+
- [ ] Visitor token persists across reload (same visitor identity across reloads)
|
|
149
|
+
- [ ] `queryServices()` renders live services; `countServices()` 0 → empty state (no mock services)
|
|
150
|
+
- [ ] `listAvailableSlots()` returns real bookable slots for a chosen service and date range
|
|
151
|
+
- [ ] Slot is re-validated with `getAvailableSlot()` immediately before booking
|
|
152
|
+
- [ ] Participant selector capped by `maxParticipantsPerBooking` (hidden when 1) — a count above the policy is not offerable
|
|
153
|
+
- [ ] `createBooking()` returns a booking with `status: "CREATED"` and a real id
|
|
154
|
+
- [ ] Checkout redirects via redirect-session `fullUrl` (no hand-built URL)
|
|
155
|
+
- [ ] On return from checkout the booking is confirmed (status `CONFIRMED`/`PENDING`)
|
|
156
|
+
- [ ] No mock services, slots, or availability anywhere
|
|
157
|
+
- [ ] Told the user at least once that they can continue setting up their bookings in the dashboard and provided deep links.
|
|
@@ -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,217 @@
|
|
|
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 } — value + currency are always
|
|
16
|
+
* present; formattedValue is optional (may be missing), so format the price from
|
|
17
|
+
* value+currency and use formattedValue only when present,
|
|
18
|
+
* payment.options { online, inPerson, deposit, pricingPlan },
|
|
19
|
+
* schedule.id {string}, onlineBooking.enabled {boolean},
|
|
20
|
+
* category { id, name }, staffMemberIds {string[]}, locations {array},
|
|
21
|
+
* bookingPolicy.participantsPolicy { enabled {boolean}, maxParticipantsPerBooking {number} }
|
|
22
|
+
* — the MOST participants a single booking may reserve. Cap the participant selector at this
|
|
23
|
+
* value (for a CLASS, also bound by the slot's remainingCapacity). It is commonly 1, in which
|
|
24
|
+
* case there is no participant choice at all — book exactly 1. Sending totalParticipants above
|
|
25
|
+
* this makes createBooking fail.
|
|
26
|
+
*
|
|
27
|
+
* TimeSlot (Time Slots V2, appointments):
|
|
28
|
+
* serviceId {string}, localStartDate {string}, localEndDate {string} — "YYYY-MM-DDThh:mm:ss" (no zone),
|
|
29
|
+
* bookable {boolean}, scheduleId {string},
|
|
30
|
+
* location { id, name, formattedAddress, locationType "BUSINESS"|"CUSTOM"|"CUSTOMER" },
|
|
31
|
+
* totalCapacity {number}, remainingCapacity {number},
|
|
32
|
+
* availableResources {array} — staff (populated by getAvailableSlot, not listAvailableSlots)
|
|
33
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** The visitor's local IANA time zone — used as the default for availability queries. */
|
|
37
|
+
function defaultTimeZone() {
|
|
38
|
+
try {
|
|
39
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
40
|
+
} catch {
|
|
41
|
+
return "UTC";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a service image to an absolute URL. Wix media fields are sometimes a bare
|
|
47
|
+
* handle ("abc.jpg") rather than a full URL; this prefixes the Wix static host.
|
|
48
|
+
* @param {{ url?: string, id?: string }|string} image
|
|
49
|
+
* @returns {string|null}
|
|
50
|
+
*/
|
|
51
|
+
export function mediaUrl(image) {
|
|
52
|
+
const raw = typeof image === "string" ? image : image?.url || image?.id;
|
|
53
|
+
if (!raw) return null;
|
|
54
|
+
if (raw.startsWith("http")) return raw;
|
|
55
|
+
if (raw.startsWith("wix:image://")) {
|
|
56
|
+
const stripped = raw.slice("wix:image://".length).replace(/^v1\//, "");
|
|
57
|
+
return `https://static.wixstatic.com/media/${stripped.split("#")[0].split("/")[0]}`;
|
|
58
|
+
}
|
|
59
|
+
return `https://static.wixstatic.com/media/${raw}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Query bookable services (one page). Returns only services visible to visitors (hidden:false).
|
|
64
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/query-services.md
|
|
65
|
+
* @param {{ limit?: number, offset?: number }} [options]
|
|
66
|
+
* @returns {Promise<{ services: object[], total: number, nextOffset: number|null }>}
|
|
67
|
+
*/
|
|
68
|
+
export async function queryServices({ limit = 100, offset = 0 } = {}) {
|
|
69
|
+
const res = await wixApiRequest("/bookings/v2/services/query", {
|
|
70
|
+
method: "POST",
|
|
71
|
+
body: {
|
|
72
|
+
query: {
|
|
73
|
+
filter: { hidden: false },
|
|
74
|
+
paging: { limit, offset },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
const services = res?.services ?? [];
|
|
79
|
+
const total = res?.pagingMetadata?.total ?? services.length;
|
|
80
|
+
const loaded = offset + services.length;
|
|
81
|
+
return { services, total, nextOffset: loaded < total ? loaded : null };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fetch a single service by its GUID. Returns null if not found.
|
|
86
|
+
* @param {string} serviceId
|
|
87
|
+
* @returns {Promise<object|null>}
|
|
88
|
+
*/
|
|
89
|
+
export async function getService(serviceId) {
|
|
90
|
+
const res = await wixApiRequest("/bookings/v2/services/query", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
body: { query: { filter: { id: serviceId }, paging: { limit: 1 } } },
|
|
93
|
+
});
|
|
94
|
+
return res?.services?.[0] ?? null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Total number of visitor-visible services. Used for empty-state logic.
|
|
99
|
+
* @returns {Promise<number>}
|
|
100
|
+
*/
|
|
101
|
+
export async function countServices() {
|
|
102
|
+
const res = await wixApiRequest("/bookings/v2/services/query", {
|
|
103
|
+
method: "POST",
|
|
104
|
+
body: { query: { filter: { hidden: false }, paging: { limit: 1 } } },
|
|
105
|
+
});
|
|
106
|
+
return res?.pagingMetadata?.total ?? (res?.services?.length ?? 0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* List bookable time slots for an **APPOINTMENT** service within a local date range (availability
|
|
111
|
+
* comes from staff working hours). For **CLASS / COURSE** services (booked against scheduled
|
|
112
|
+
* sessions) use `listEventTimeSlots` — this endpoint returns an empty list for them, silently.
|
|
113
|
+
* If you render a mixed catalog, route by `service.type` via `listSlotsForService`.
|
|
114
|
+
* Dates are LOCAL ("YYYY-MM-DDThh:mm:ss") in timeZone.
|
|
115
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
|
|
116
|
+
* @param {string} serviceId
|
|
117
|
+
* @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
|
|
118
|
+
* @returns {Promise<{ slots: object[], nextCursor: string|null }>}
|
|
119
|
+
*/
|
|
120
|
+
export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
|
|
121
|
+
if (!cursor && (!fromLocalDate || !toLocalDate)) {
|
|
122
|
+
throw new Error("listAvailableSlots requires fromLocalDate and toLocalDate (local 'YYYY-MM-DDThh:mm:ss').");
|
|
123
|
+
}
|
|
124
|
+
const body = cursor
|
|
125
|
+
? { cursorPaging: { limit, cursor } }
|
|
126
|
+
: {
|
|
127
|
+
serviceId,
|
|
128
|
+
bookable: true,
|
|
129
|
+
fromLocalDate,
|
|
130
|
+
toLocalDate,
|
|
131
|
+
timeZone: timeZone || defaultTimeZone(),
|
|
132
|
+
cursorPaging: { limit },
|
|
133
|
+
};
|
|
134
|
+
const res = await wixApiRequest("/_api/service-availability/v2/time-slots", { method: "POST", body });
|
|
135
|
+
return {
|
|
136
|
+
slots: res?.timeSlots ?? [],
|
|
137
|
+
nextCursor: res?.cursorPagingMetadata?.cursors?.next ?? null,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Re-validate a specific appointment slot right before booking, and fetch its available
|
|
143
|
+
* resources (staff). Always re-check before createBooking — slots can be taken between listing
|
|
144
|
+
* and booking. Returns the slot or null if it's gone.
|
|
145
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/get-availability-time-slot.md
|
|
146
|
+
* @param {string} serviceId
|
|
147
|
+
* @param {{ localStartDate: string, localEndDate: string, timeZone?: string, location?: object }} options
|
|
148
|
+
* @returns {Promise<object|null>}
|
|
149
|
+
*/
|
|
150
|
+
export async function getAvailableSlot(serviceId, { localStartDate, localEndDate, timeZone, location } = {}) {
|
|
151
|
+
if (!localStartDate || !localEndDate) {
|
|
152
|
+
throw new Error("getAvailableSlot requires localStartDate and localEndDate.");
|
|
153
|
+
}
|
|
154
|
+
const res = await wixApiRequest("/_api/service-availability/v2/time-slots/get", {
|
|
155
|
+
method: "POST",
|
|
156
|
+
body: {
|
|
157
|
+
serviceId,
|
|
158
|
+
localStartDate,
|
|
159
|
+
localEndDate,
|
|
160
|
+
timeZone: timeZone || defaultTimeZone(),
|
|
161
|
+
...(location ? { location } : {}),
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
return res?.timeSlot ?? null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* List bookable session slots for **CLASS / COURSE** services within a local date range. These
|
|
169
|
+
* services are booked against scheduled sessions (calendar events), not staff working hours, so
|
|
170
|
+
* they use a different endpoint than appointments. Each returned slot carries `eventInfo.eventId`
|
|
171
|
+
* (the session) and has **no** `scheduleId` — `createBooking` reads that `eventId`.
|
|
172
|
+
* Dates are LOCAL ("YYYY-MM-DDThh:mm:ss") in timeZone.
|
|
173
|
+
* https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-event-time-slots.md
|
|
174
|
+
* @param {string|string[]} serviceIds One service id or several (the endpoint takes a plural list).
|
|
175
|
+
* @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
|
|
176
|
+
* @returns {Promise<{ slots: object[], nextCursor: string|null }>}
|
|
177
|
+
*/
|
|
178
|
+
export async function listEventTimeSlots(serviceIds, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
|
|
179
|
+
if (!cursor && (!fromLocalDate || !toLocalDate)) {
|
|
180
|
+
throw new Error("listEventTimeSlots requires fromLocalDate and toLocalDate (local 'YYYY-MM-DDThh:mm:ss').");
|
|
181
|
+
}
|
|
182
|
+
const ids = Array.isArray(serviceIds) ? serviceIds : [serviceIds];
|
|
183
|
+
const body = cursor
|
|
184
|
+
? { cursorPaging: { limit, cursor } }
|
|
185
|
+
: {
|
|
186
|
+
serviceIds: ids,
|
|
187
|
+
bookable: true,
|
|
188
|
+
fromLocalDate,
|
|
189
|
+
toLocalDate,
|
|
190
|
+
timeZone: timeZone || defaultTimeZone(),
|
|
191
|
+
cursorPaging: { limit },
|
|
192
|
+
};
|
|
193
|
+
const res = await wixApiRequest("/_api/service-availability/v2/time-slots/event", { method: "POST", body });
|
|
194
|
+
return {
|
|
195
|
+
slots: res?.timeSlots ?? [],
|
|
196
|
+
nextCursor: res?.cursorPagingMetadata?.cursors?.next ?? null,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Type-agnostic convenience: list bookable slots for a service, routing to the right endpoint by
|
|
202
|
+
* `service.type` — APPOINTMENT → `listAvailableSlots`, CLASS/COURSE → `listEventTimeSlots`. The
|
|
203
|
+
* returned slots are shaped the same either way (`localStartDate`/`localEndDate`/`location`/
|
|
204
|
+
* `availableResources`), and each is bookable via `createBooking`.
|
|
205
|
+
* NOTE: a **COURSE** is enrolled as a whole (not per session), so it typically returns **no** slots
|
|
206
|
+
* here — the per-slot booking flow applies to APPOINTMENT and CLASS.
|
|
207
|
+
* @param {{ _id?: string, id?: string, type?: string }} service A service from queryServices.
|
|
208
|
+
* @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
|
|
209
|
+
* @returns {Promise<{ slots: object[], nextCursor: string|null }>}
|
|
210
|
+
*/
|
|
211
|
+
export async function listSlotsForService(service, options = {}) {
|
|
212
|
+
const serviceId = service?._id || service?.id;
|
|
213
|
+
if (!serviceId) throw new Error("listSlotsForService requires a service with an id.");
|
|
214
|
+
return service?.type === "CLASS" || service?.type === "COURSE"
|
|
215
|
+
? listEventTimeSlots(serviceId, options)
|
|
216
|
+
: listAvailableSlots(serviceId, options);
|
|
217
|
+
}
|