@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.
Files changed (268) hide show
  1. package/README.md +12 -1
  2. package/package.json +1 -1
  3. package/skills/README.md +23 -0
  4. package/skills/wix-app/SKILL.md +10 -2
  5. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
  6. package/skills/wix-app/references/CODE_QUALITY.md +1 -0
  7. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
  9. package/skills/wix-app/references/DATA_COLLECTION.md +5 -3
  10. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +25 -9
  11. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  12. package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
  13. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  31. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  32. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  33. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
  34. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  35. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
  36. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  37. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  38. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
  39. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +23 -105
  40. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  41. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  42. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +18 -16
  43. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  44. package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
  45. package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
  46. package/skills/wix-app/references/stores/INVENTORY.md +37 -0
  47. package/skills/wix-app/references/stores/QUERY.md +87 -0
  48. package/skills/wix-app/references/stores/WRITE.md +55 -0
  49. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  50. package/skills/wix-auth/SKILL.md +12 -0
  51. package/skills/wix-auth/references/device-flow.md +140 -0
  52. package/skills/wix-docs/SKILL.md +188 -0
  53. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  54. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  55. package/skills/wix-headless/SKILL.md +89 -130
  56. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  57. package/skills/wix-headless/entry/skill.md +76 -0
  58. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  59. package/skills/wix-headless/references/CAPABILITIES.md +86 -0
  60. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  61. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  62. package/skills/wix-headless/references/FEEDBACK.md +129 -0
  63. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  64. package/skills/wix-headless/references/SDK_HANDOFF.md +99 -0
  65. package/skills/wix-headless/references/SEED.md +45 -187
  66. package/skills/wix-headless/references/SETUP.md +32 -103
  67. package/skills/wix-headless/references/astro.md +98 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  72. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  73. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  74. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  75. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  76. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  77. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  78. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  79. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  80. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +213 -0
  81. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  83. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +232 -0
  84. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  85. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  86. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  87. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +192 -0
  88. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +155 -0
  89. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  90. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
  91. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +197 -0
  92. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +136 -0
  93. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  94. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  95. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  96. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  97. package/skills/wix-headless/references/managed/DEPLOYMENT.md +79 -0
  98. package/skills/wix-headless/references/non-astro.md +59 -0
  99. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  100. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +15 -0
  101. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  102. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +71 -0
  103. package/skills/wix-manage/SKILL.md +29 -3
  104. package/skills/wix-manage/references/analytics/query-site-analytics.md +298 -0
  105. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
  106. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  107. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
  108. package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
  109. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +299 -0
  110. package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
  111. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
  112. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +517 -0
  113. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +218 -0
  114. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  115. package/skills/wix-manage/references/sites/site-import.md +270 -0
  116. package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
  117. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +57 -0
  118. package/skills/wix-vibe-headless/SKILL.md +142 -0
  119. package/skills/wix-vibe-headless/platforms/README.md +22 -0
  120. package/skills/wix-vibe-headless/platforms/base44.md +142 -0
  121. package/skills/wix-vibe-headless/platforms/generic.md +87 -0
  122. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  123. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  124. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +157 -0
  125. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  126. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +217 -0
  127. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  128. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  129. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  130. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +136 -0
  131. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  132. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  133. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  134. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  135. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +223 -0
  136. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  137. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  138. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  139. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  140. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  141. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  142. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  143. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +164 -0
  144. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  145. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +146 -0
  146. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  147. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  148. package/skills/wix-headless/references/BUILD.md +0 -105
  149. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  150. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  151. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  152. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  153. package/skills/wix-headless/references/PLAN-create.md +0 -17
  154. package/skills/wix-headless/references/PLAN.md +0 -92
  155. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  156. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  157. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  158. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  159. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  160. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  161. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  162. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  163. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  164. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  165. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  166. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  167. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  168. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  169. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  170. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  171. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  172. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  173. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  174. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  175. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  176. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  177. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  178. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  179. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  180. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  181. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  182. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  183. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  184. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  185. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  186. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  187. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  188. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  189. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  190. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  191. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  192. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  193. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  194. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  195. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  196. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  197. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  198. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  199. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  200. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  201. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  202. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  203. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  204. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  205. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  206. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  207. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  208. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  209. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  210. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  211. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  212. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  213. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  214. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  215. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  216. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  217. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  218. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  219. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  220. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  221. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  222. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  223. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  224. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  225. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  226. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  227. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  228. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  229. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  230. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  231. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  232. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  233. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  234. package/skills/wix-headless/references/seed-recipes.md +0 -49
  235. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  236. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  237. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  238. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  239. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  240. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  241. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  242. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  243. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  244. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  245. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  246. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  247. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  248. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  249. package/skills/wix-headless/references/verticals/blog.md +0 -31
  250. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  251. package/skills/wix-headless/references/verticals/cms.md +0 -30
  252. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  253. package/skills/wix-headless/references/verticals/forms.md +0 -26
  254. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  255. package/skills/wix-headless/references/verticals/stores.md +0 -33
  256. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  257. package/skills/wix-headless/scripts/compose.mjs +0 -520
  258. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  259. package/skills/wix-headless/scripts/flatten.sh +0 -61
  260. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  261. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  262. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  263. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  264. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  265. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  266. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  267. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  268. 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
+ }