@wix/agent-skills 1.16.3 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +115 -12
  3. package/skills/wix-app/references/APP_MARKET_REVIEW.md +1 -1
  4. package/skills/wix-app/references/BACKEND_API.md +18 -0
  5. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +6 -0
  6. package/skills/wix-app/references/DASHBOARD_MODAL.md +10 -2
  7. package/skills/wix-app/references/DASHBOARD_PAGE.md +32 -70
  8. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +145 -127
  9. package/skills/wix-app/references/SITE_PLUGIN.md +14 -2
  10. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +74 -68
  11. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +2 -2
  12. package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +79 -0
  13. package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +73 -0
  14. package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +52 -0
  15. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +184 -0
  16. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +182 -99
  17. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +94 -49
  18. package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +16 -6
  19. package/skills/wix-app/references/editor-react-component/BROWSER-SUPPORT.md +56 -0
  20. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +275 -0
  21. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +35 -40
  22. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +167 -445
  23. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +56 -29
  24. package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +53 -158
  25. package/skills/wix-app/references/editor-react-component/EDITOR-EXTENSION-CONFIGURATION.md +78 -0
  26. package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +42 -134
  27. package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +11 -3
  28. package/skills/wix-app/references/editor-react-component/PARTS.md +46 -24
  29. package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +63 -179
  30. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +79 -163
  31. package/skills/wix-app/references/editor-react-component/SITE-CONTEXT-HOOKS.md +36 -21
  32. package/skills/wix-app/references/editor-react-component/SSR.md +30 -99
  33. package/skills/wix-base44-connector/SKILL.md +156 -50
  34. package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +78 -0
  35. package/skills/wix-base44-connector/scripts/utils.js +239 -45
  36. package/skills/wix-headless/entry/bootstrap.mjs +9 -2
  37. package/skills/wix-headless/entry/skill.md +1 -1
  38. package/skills/wix-headless/references/CAPABILITIES.md +9 -1
  39. package/skills/wix-headless/references/DESIGN.md +1 -0
  40. package/skills/wix-headless/references/DISCOVERY.md +2 -1
  41. package/skills/wix-headless/references/IMAGE_GENERATION.md +1 -0
  42. package/skills/wix-headless/references/SDK_HANDOFF.md +5 -2
  43. package/skills/wix-headless/references/SEED.md +2 -1
  44. package/skills/wix-headless/references/SETUP.md +1 -0
  45. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +2 -2
  46. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +68 -31
  47. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +26 -10
  48. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +8 -8
  49. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +1 -1
  50. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +1 -1
  51. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +10 -8
  52. package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +254 -0
  53. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +27 -24
  54. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +13 -0
  55. package/skills/wix-headless/references/inline-recipes/setup-rentals.md +254 -0
  56. package/skills/wix-headless/references/managed/AUTHENTICATION.md +5 -0
  57. package/skills/wix-headless/references/non-astro.md +1 -1
  58. package/skills/wix-headless-fast/SKILL.md +166 -0
  59. package/skills/wix-headless-fast/entry/skill.md +99 -0
  60. package/skills/wix-headless-fast/install/deploy.mjs +433 -0
  61. package/skills/wix-headless-fast/install/fast-path.mjs +141 -0
  62. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +137 -0
  63. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +137 -0
  64. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +113 -0
  65. package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +42 -0
  66. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +123 -0
  67. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +70 -0
  68. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +99 -0
  69. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +76 -0
  70. package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +62 -0
  71. package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +40 -0
  72. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +99 -0
  73. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +24 -0
  74. package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +16156 -0
  75. package/skills/wix-headless-fast/references/blog/seed/SEED.md +72 -0
  76. package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +287 -0
  77. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +129 -0
  78. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +143 -0
  79. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +119 -0
  80. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +138 -0
  81. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +54 -0
  82. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +206 -0
  83. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +96 -0
  84. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +70 -0
  85. package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +40 -0
  86. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +76 -0
  87. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +22 -0
  88. package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +11962 -0
  89. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +61 -0
  90. package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +302 -0
  91. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +179 -0
  92. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +83 -0
  93. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +57 -0
  94. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +151 -0
  95. package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +75 -0
  96. package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +41 -0
  97. package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +11262 -0
  98. package/skills/wix-headless-fast/references/cms/seed/SEED.md +103 -0
  99. package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +324 -0
  100. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +149 -0
  101. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +66 -0
  102. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +176 -0
  103. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +118 -0
  104. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +117 -0
  105. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +59 -0
  106. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +136 -0
  107. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +128 -0
  108. package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +61 -0
  109. package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +40 -0
  110. package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +12 -0
  111. package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +80 -0
  112. package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +21 -0
  113. package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +11884 -0
  114. package/skills/wix-headless-fast/references/events/seed/SEED.md +86 -0
  115. package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +276 -0
  116. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +150 -0
  117. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +317 -0
  118. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +269 -0
  119. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +231 -0
  120. package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +135 -0
  121. package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +10593 -0
  122. package/skills/wix-headless-fast/references/forms/seed/SEED.md +108 -0
  123. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +320 -0
  124. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +63 -0
  125. package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +60 -0
  126. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +155 -0
  127. package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +78 -0
  128. package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +47 -0
  129. package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +66 -0
  130. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +88 -0
  131. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +53 -0
  132. package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +122 -0
  133. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +50 -0
  134. package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +21 -0
  135. package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +42 -0
  136. package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +10 -0
  137. package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +11 -0
  138. package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +10329 -0
  139. package/skills/wix-headless-fast/references/members/seed/SEED.md +39 -0
  140. package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +94 -0
  141. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +134 -0
  142. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +110 -0
  143. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +90 -0
  144. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +115 -0
  145. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +59 -0
  146. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +39 -0
  147. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +58 -0
  148. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +165 -0
  149. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +55 -0
  150. package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +40 -0
  151. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +30 -0
  152. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +21 -0
  153. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +29 -0
  154. package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +10197 -0
  155. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +78 -0
  156. package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +258 -0
  157. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +133 -0
  158. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +58 -0
  159. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +126 -0
  160. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +37 -0
  161. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +39 -0
  162. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +75 -0
  163. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +45 -0
  164. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +30 -0
  165. package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +41 -0
  166. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +40 -0
  167. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +21 -0
  168. package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +11226 -0
  169. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +71 -0
  170. package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +279 -0
  171. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +165 -0
  172. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +142 -0
  173. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +26 -0
  174. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +121 -0
  175. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +155 -0
  176. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +55 -0
  177. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +43 -0
  178. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +177 -0
  179. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +165 -0
  180. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +103 -0
  181. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +254 -0
  182. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +146 -0
  183. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +168 -0
  184. package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +47 -0
  185. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +22 -0
  186. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +12 -0
  187. package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +12560 -0
  188. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +81 -0
  189. package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +479 -0
  190. package/skills/wix-headless-fast/references/shared/CONTENT.md +22 -0
  191. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +73 -0
  192. package/skills/wix-headless-fast/references/shared/DESIGN.md +26 -0
  193. package/skills/wix-headless-fast/references/shared/app/styles/global.css +52 -0
  194. package/skills/wix-headless-fast/references/shared/app/wix/config.ts +15 -0
  195. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +27 -0
  196. package/skills/wix-headless-fast/references/shared/app/wix/money.ts +24 -0
  197. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +76 -0
  198. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +46 -0
  199. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +46 -0
  200. package/skills/wix-headless-fast/references/shared/seed/images.mjs +145 -0
  201. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +149 -0
  202. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +26 -0
  203. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +121 -0
  204. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +100 -0
  205. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +130 -0
  206. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +48 -0
  207. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +66 -0
  208. package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +103 -0
  209. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +48 -0
  210. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +140 -0
  211. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +80 -0
  212. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +101 -0
  213. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +221 -0
  214. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +215 -0
  215. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +109 -0
  216. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +46 -0
  217. package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +56 -0
  218. package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +23 -0
  219. package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +12247 -0
  220. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +85 -0
  221. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +415 -0
  222. package/skills/wix-manage/SKILL.md +28 -2
  223. package/skills/wix-manage/references/bookings/booking-system-integration-gaps.md +20 -13
  224. package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +24 -11
  225. package/skills/wix-manage/references/cms/cms-ecommerce-catalog-integration.md +5 -5
  226. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +141 -0
  227. package/skills/wix-manage/references/google-business-profile/connect-google-business-profile.md +134 -0
  228. package/skills/wix-manage/references/google-business-profile/manage-google-business-profile-locations.md +182 -0
  229. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +3 -0
  230. package/skills/wix-manage/references/seo/manage-seo-tags.md +315 -0
  231. package/skills/wix-manage/references/seo/manage-url-redirects.md +250 -0
  232. package/skills/wix-manage/references/sites/query-sites.md +72 -161
  233. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +21 -6
  234. package/skills/wix-manage/references/stores/update-product-with-options.md +83 -0
  235. package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +5 -6
  236. package/skills/wix-vibe-headless/SKILL.md +18 -4
  237. package/skills/wix-vibe-headless/install/deploy.cjs +12 -4
  238. package/skills/wix-vibe-headless/install/pin-agents-md.cjs +2 -1
  239. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +16 -10
  240. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +13 -8
  241. package/skills/wix-vibe-headless/platforms/base44.md +27 -62
  242. package/skills/wix-vibe-headless/platforms/generic.md +6 -3
  243. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +45 -0
  244. package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +67 -0
  245. package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +128 -0
  246. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +5 -3
  247. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-checkout.js +9 -8
  248. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +250 -0
  249. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +12 -5
  250. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +41 -0
  251. package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.js +267 -0
  252. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +6 -3
  253. package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +470 -0
  254. package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +425 -0
  255. package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +187 -0
  256. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +238 -0
  257. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +108 -0
  258. package/skills/wix-vibe-headless/references/forms/seed/SEED.md +1762 -0
  259. package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +13 -0
  260. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +4 -1
  261. package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +23 -11
  262. package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +1 -1
  263. package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-ordering.js +29 -31
  264. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +152 -92
  265. package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +37 -38
  266. package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +2 -2
  267. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +15 -8
  268. package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +37 -0
  269. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +35 -35
  270. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +18 -6
  271. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +30 -7
  272. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +46 -10
  273. package/skills/wix-app/references/editor-react-component/A11Y-REVIEW-REFERENCE.md +0 -172
  274. package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +0 -143
  275. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +0 -516
  276. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +0 -225
  277. package/skills/wix-app/references/editor-react-component/EDIT-FLOW.md +0 -6
  278. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +0 -95
  279. package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +0 -110
  280. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +0 -51
  281. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +0 -49
  282. package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +0 -92
  283. package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +0 -139
@@ -0,0 +1,67 @@
1
+ // Rental length picker — pure UI. The options, the selection handler and the priced total all
2
+ // come from useRentalDuration; no data wiring lives here.
3
+ //
4
+ // Renders the step an ordinary service doesn't have: the customer has already picked a START
5
+ // (SlotPicker), and now picks HOW LONG. The running total sits beside the choices because a
6
+ // rental's cost depends on the length — showing only a rate per hour leaves the customer
7
+ // discovering the real number at checkout.
8
+ //
9
+ // Show this only when `rental.isRental` — an appointment or class has no length to choose.
10
+ // Styled with base44 design tokens (shadcn Tailwind classes).
11
+ const optionCls = "py-2.5 px-3 cursor-pointer text-sm font-body border rounded-sm transition-colors";
12
+ const idle = "border-border bg-card text-foreground hover:border-foreground/40";
13
+ const active = "border-primary bg-primary text-primary-foreground";
14
+
15
+ /**
16
+ * @param {{
17
+ * rental: object, // the whole return of useRentalDuration
18
+ * startLabel?: string, // e.g. "Fri 4 Sep, 10:00" — what the customer already picked
19
+ * }} props
20
+ */
21
+ export default function RentalDurationPicker({ rental, startLabel }) {
22
+ const { isRental, duration, options, selected, select, price, loading, error } = rental;
23
+ if (!isRental) return null;
24
+
25
+ const unitWord = duration?.unit === "DAY" ? "days" : "hours";
26
+
27
+ return (
28
+ <div className="mb-6">
29
+ <div className="mb-2 flex items-baseline justify-between gap-3">
30
+ <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
31
+ How long{startLabel ? ` from ${startLabel}` : ""}
32
+ </p>
33
+ {price?.total ? (
34
+ <p className="text-sm font-semibold text-foreground">{price.total}</p>
35
+ ) : selected ? (
36
+ <p className="text-sm text-muted-foreground">Pricing…</p>
37
+ ) : null}
38
+ </div>
39
+
40
+ {loading ? (
41
+ <p className="text-sm text-muted-foreground">Loading available {unitWord}…</p>
42
+ ) : error ? (
43
+ <p className="text-sm text-destructive">{error}</p>
44
+ ) : !options.length ? (
45
+ // An empty list is a real answer: this start has no bookable length. Say so, and leave
46
+ // the start picker in place so the customer can choose another one.
47
+ <p className="text-sm text-muted-foreground">
48
+ Nothing available from that start — pick another time.
49
+ </p>
50
+ ) : (
51
+ <div className="grid grid-cols-3 gap-2 sm:grid-cols-4">
52
+ {options.map((option) => (
53
+ <button
54
+ key={option.localEndDate}
55
+ type="button"
56
+ aria-pressed={selected?.localEndDate === option.localEndDate}
57
+ className={`${optionCls} ${selected?.localEndDate === option.localEndDate ? active : idle}`}
58
+ onClick={() => select(option)}
59
+ >
60
+ {option.label}
61
+ </button>
62
+ ))}
63
+ </div>
64
+ )}
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,128 @@
1
+ // useRentalDuration — the extra step a rental has and an ordinary service doesn't: once the
2
+ // customer has picked a START, they pick how LONG, and the price follows the length.
3
+ //
4
+ // Compose it with useServiceDetail rather than replacing it. That hook already loads the
5
+ // service, lists start times, holds the contact fields and runs bookAndCheckout — all of which
6
+ // a rental uses unchanged. This adds only the second choice and its price:
7
+ //
8
+ // const detail = useServiceDetail(serviceId); // service + start slots
9
+ // const rental = useRentalDuration(detail.service, detail.selectedSlot, detail.timeZone);
10
+ // // render detail.slots → customer picks a start → render rental.options → picks a length
11
+ // // book with rental.endDate as the slot's localEndDate
12
+ //
13
+ // Returns empty options for a non-rental service, so a page that renders both kinds needs no
14
+ // branch of its own — `isRental(service)` decides whether to show the length step at all.
15
+ import { useCallback, useEffect, useState } from "react";
16
+ import {
17
+ dailyEndOptions,
18
+ isRental,
19
+ listEndOptions,
20
+ previewRentalPrice,
21
+ rentalDuration,
22
+ } from "@/rest/wix-bookings-rentals";
23
+
24
+ /** "1 hr 30 min" / "2 days" — the label for one length option. */
25
+ function lengthLabel(startLocal, endLocal, unit) {
26
+ if (unit === "DAY") {
27
+ const days = Math.round((new Date(`${endLocal}Z`) - new Date(`${startLocal}Z`)) / 86400000);
28
+ return days === 1 ? "1 day" : `${days} days`;
29
+ }
30
+ const minutes = Math.round((new Date(`${endLocal}Z`) - new Date(`${startLocal}Z`)) / 60000);
31
+ const h = Math.floor(minutes / 60);
32
+ const m = minutes % 60;
33
+ return [h ? `${h} hr` : "", m ? `${m} min` : ""].filter(Boolean).join(" ") || "0 min";
34
+ }
35
+
36
+ /**
37
+ * @param {object|null} service The service from useServiceDetail — may be null while loading.
38
+ * @param {object|null} startSlot The slot the customer picked (its localStartDate is the start).
39
+ * @param {string|null} timeZone The zone the slot list came back in.
40
+ * @returns {{
41
+ * isRental: boolean,
42
+ * duration: { unit: "HOUR"|"DAY", min: number, max: number, step: number }|null,
43
+ * options: { localEndDate: string, label: string }[],
44
+ * selected: object|null,
45
+ * select: (option: object) => void,
46
+ * endDate: string|null, // pass as the booking's localEndDate
47
+ * price: { total: string, currency: string|null }|null,
48
+ * loading: boolean,
49
+ * error: string|null,
50
+ * }}
51
+ */
52
+ export function useRentalDuration(service, startSlot, timeZone) {
53
+ const [options, setOptions] = useState([]);
54
+ const [selected, setSelected] = useState(null);
55
+ const [price, setPrice] = useState(null);
56
+ const [loading, setLoading] = useState(false);
57
+ const [error, setError] = useState(null);
58
+
59
+ const rental = isRental(service);
60
+ const duration = rentalDuration(service);
61
+ const start = startSlot?.localStartDate ?? null;
62
+ // Forward the chosen slot's own location to end-options.
63
+ const location = startSlot?.location ?? null;
64
+ const serviceId = service?._id || service?.id || null;
65
+
66
+ // A new start invalidates the lengths that were valid for the previous one.
67
+ useEffect(() => {
68
+ setSelected(null);
69
+ setPrice(null);
70
+ if (!rental || !start || !serviceId) {
71
+ setOptions([]);
72
+ return;
73
+ }
74
+ let alive = true;
75
+ setLoading(true);
76
+ setError(null);
77
+ const load = duration.unit === "DAY"
78
+ ? dailyEndOptions(service, { localStartDate: start, timeZone })
79
+ .then((list) => list.map((o) => ({ localEndDate: o.localEndDate, label: lengthLabel(start, o.localEndDate, "DAY") })))
80
+ : listEndOptions(serviceId, { localStartDate: start, location, timeZone })
81
+ .then(({ endOptions }) =>
82
+ endOptions.map((o) => ({ localEndDate: o.localEndDate, label: lengthLabel(start, o.localEndDate, "HOUR") })),
83
+ );
84
+ load
85
+ .then((list) => {
86
+ if (!alive) return;
87
+ setOptions(list);
88
+ setLoading(false);
89
+ })
90
+ .catch((e) => {
91
+ if (!alive) return;
92
+ // Fail loudly — an empty length list and a failed call look identical on screen
93
+ // otherwise, and the second one is a bug the visitor should not absorb silently.
94
+ setError(e?.message || "Could not load available lengths.");
95
+ setOptions([]);
96
+ setLoading(false);
97
+ });
98
+ return () => {
99
+ alive = false;
100
+ };
101
+ // eslint-disable-next-line react-hooks/exhaustive-deps
102
+ }, [rental, serviceId, start, timeZone, duration?.unit]);
103
+
104
+ // The price comes from the server for the chosen length — never multiply the rate locally.
105
+ const select = useCallback(
106
+ (option) => {
107
+ setSelected(option);
108
+ setPrice(null);
109
+ if (!option || !serviceId || !start || !timeZone) return;
110
+ previewRentalPrice(serviceId, { localStartDate: start, localEndDate: option.localEndDate, timeZone })
111
+ .then((p) => setPrice({ total: p.total, currency: p.currency }))
112
+ .catch((e) => setError(e?.message || "Could not price that length."));
113
+ },
114
+ [serviceId, start, timeZone],
115
+ );
116
+
117
+ return {
118
+ isRental: rental,
119
+ duration,
120
+ options,
121
+ selected,
122
+ select,
123
+ endDate: selected?.localEndDate ?? null,
124
+ price,
125
+ loading,
126
+ error,
127
+ };
128
+ }
@@ -31,8 +31,9 @@ export function useServiceDetail(serviceId) {
31
31
  const [error, setError] = useState(null);
32
32
 
33
33
  useEffect(() => {
34
- // getService returns null on a miss — show not-found, never invent a service.
35
- getService(serviceId).then((s) => (s ? setService(s) : setNotFound(true)));
34
+ // getService returns null on a miss — show not-found, never invent a service. A rejected
35
+ // request (network, 4xx) must also resolve the page to not-found, never leave it loading forever.
36
+ getService(serviceId).then((s) => (s ? setService(s) : setNotFound(true))).catch(() => setNotFound(true));
36
37
  }, [serviceId]);
37
38
 
38
39
  useEffect(() => {
@@ -41,7 +42,8 @@ export function useServiceDetail(serviceId) {
41
42
  // returns { slots, nextCursor }. The LIST call takes fromLocalDate / toLocalDate — a different
42
43
  // arg naming than the single-slot re-validate call (getAvailableSlot: localStartDate/localEndDate).
43
44
  listSlotsForService(service, { fromLocalDate: localMidnight(0), toLocalDate: localMidnight(14) })
44
- .then(({ slots, nextCursor, timeZone }) => { setSlots(slots); setCursor(nextCursor); setTimeZone(timeZone); });
45
+ .then(({ slots, nextCursor, timeZone }) => { setSlots(slots); setCursor(nextCursor); setTimeZone(timeZone); })
46
+ .catch(() => setSlots([]));
45
47
  }, [service]);
46
48
 
47
49
  const loadMoreSlots = useCallback(() => {
@@ -3,7 +3,7 @@ import { wixApiRequest } from "./wix-client.js";
3
3
  // Data model reference: see INSTRUCTIONS.md
4
4
  // Service and TimeSlot shapes: see wix-bookings-services.js
5
5
 
6
- // Wix Bookings app id — required inside catalogReference for the eCommerce checkout.
6
+ // Wix Bookings app id — required inside catalogReference when adding the booking to the eCom cart.
7
7
  const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
8
8
 
9
9
  /**
@@ -101,27 +101,28 @@ export async function createBooking(slot, contactDetails, { totalParticipants =
101
101
  }
102
102
 
103
103
  /**
104
- * Create an eCommerce checkout for a created booking and return the hosted checkout URL.
104
+ * Add a created booking to the current eCom cart and return the hosted checkout URL.
105
+ * In Cart V2 the cart id IS the checkout id — add the booking line to the current cart, then create
106
+ * the redirect session straight from that cart's id (there is no separate checkout-creation call).
105
107
  * Redirect the buyer there (window.location.href = ...). On return, the booking is confirmed.
106
108
  * Throws if no redirect URL is produced.
107
- * https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/checkout.md
109
+ * https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/add-line-items-to-current-cart.md
108
110
  * @param {string} bookingId booking.id from createBooking().
109
111
  * @returns {Promise<string>} The hosted-checkout URL to redirect to.
110
112
  */
111
113
  export async function checkoutBooking(bookingId) {
112
114
  if (!bookingId) throw new Error("checkoutBooking requires a bookingId.");
113
115
 
114
- const checkoutRes = await wixApiRequest("/ecom/v1/checkouts", {
116
+ const cartRes = await wixApiRequest("/ecom/v2/carts/current/add-line-items", {
115
117
  method: "POST",
116
118
  body: {
117
- channelType: "WEB",
118
- lineItems: [
119
+ catalogItems: [
119
120
  { quantity: 1, catalogReference: { appId: BOOKINGS_APP_ID, catalogItemId: bookingId } },
120
121
  ],
121
122
  },
122
123
  });
123
- const checkoutId = checkoutRes?.checkout?.id;
124
- if (!checkoutId) throw new Error("Failed to create a checkout for the booking.");
124
+ const checkoutId = cartRes?.cart?.id;
125
+ if (!checkoutId) throw new Error("Failed to add the booking to the cart for checkout.");
125
126
 
126
127
  const redirect = await wixApiRequest("/headless/v1/redirect-session", {
127
128
  method: "POST",
@@ -0,0 +1,250 @@
1
+ import { wixApiRequest } from "./wix-client.js";
2
+ import { listAvailableSlots } from "./wix-bookings-services.js";
3
+
4
+ /**
5
+ * Wix Rentals — a DELTA on `wix-bookings-services.js`, not a replacement.
6
+ *
7
+ * Wix Rentals has no APIs of its own. A rental is a Bookings **service** with rentals-specific
8
+ * field values, so the catalog read, the booking form, and the whole
9
+ * `createBooking → cart → checkout` chain in `wix-bookings-checkout.js` are UNCHANGED. Only
10
+ * three things differ, and they are all here:
11
+ *
12
+ * 1. every catalog read filters by the rentals `appId`, or rentals and appointments interleave
13
+ * 2. the customer picks the LENGTH, so availability is two calls: start times, then the valid
14
+ * ends for the chosen start
15
+ * 3. price is a rate per unit, so the total for a chosen length comes from the server
16
+ *
17
+ * What makes a service a rental (from the docs' own mapping — nothing is inferred):
18
+ * type always "APPOINTMENT"
19
+ * appId always RENTALS_APP_ID
20
+ * schedule.availabilityConstraints.durationRange replaces `sessionDurations`
21
+ * — MUTUALLY EXCLUSIVE, one or the other
22
+ * primaryResourceType a resource type id, not staff — availability comes from its resources
23
+ * payment.rateType always "FIXED", charged per hour or per day
24
+ *
25
+ * ⚠️ Because the two constraint fields are mutually exclusive, `isRental()` below is a reliable
26
+ * test on the DATA. Never decide "this is a rental" from the brief, the service name, or a
27
+ * category — a 60-minute yoga class and a meeting room rented for 1–8 hours are told apart by
28
+ * which constraint the service carries, and nothing else.
29
+ *
30
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/rentals/wix-rentals-and-the-bookings-apis.md
31
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/rentals/about-wix-rentals-availability.md
32
+ */
33
+
34
+ /** The Wix Rentals app. Used twice: to filter the catalog, and as the cart's catalogReference.appId. */
35
+ export const RENTALS_APP_ID = "ff5d6eb1-65e4-4f9a-8b14-64d34c12cc2e";
36
+
37
+ /** The Wix Bookings app — the other half of a mixed site. */
38
+ export const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
39
+
40
+ /**
41
+ * Is this service a rental? True when it carries a duration RANGE the customer picks within,
42
+ * rather than a fixed session length. This is the only correct test.
43
+ * @param {object} service
44
+ * @returns {boolean}
45
+ */
46
+ export function isRental(service) {
47
+ return Boolean(service?.schedule?.availabilityConstraints?.durationRange);
48
+ }
49
+
50
+ /**
51
+ * The rental's unit and bounds, or null for an ordinary service.
52
+ * Hourly bounds are MINUTES (30–1440), daily bounds are DAYS (1–8).
53
+ * @param {object} service
54
+ * @returns {{ unit: "HOUR"|"DAY", min: number, max: number, step: number }|null}
55
+ */
56
+ export function rentalDuration(service) {
57
+ const range = service?.schedule?.availabilityConstraints?.durationRange;
58
+ if (!range) return null;
59
+ if (range.unitType === "DAY") {
60
+ return {
61
+ unit: "DAY",
62
+ min: range.dayOptions?.minDurationInDays ?? 1,
63
+ max: range.dayOptions?.maxDurationInDays ?? 1,
64
+ step: 1,
65
+ };
66
+ }
67
+ return {
68
+ unit: "HOUR",
69
+ min: range.hourOptions?.minDurationInMinutes ?? 30,
70
+ max: range.hourOptions?.maxDurationInMinutes ?? 1440,
71
+ step: 30,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Query rentals only. On a site that has both apps an unfiltered `queryServices` returns
77
+ * haircuts next to meeting rooms, so this is not optional — it is the same query with the
78
+ * rentals `appId` on the filter.
79
+ *
80
+ * Pass `appId: BOOKINGS_APP_ID` to get the ordinary services instead, on a mixed site.
81
+ * @param {{ limit?: number, offset?: number, appId?: string }} [options]
82
+ * @returns {Promise<{ services: object[], total: number, nextOffset: number|null }>}
83
+ */
84
+ export async function queryRentals({ limit = 100, offset = 0, appId = RENTALS_APP_ID } = {}) {
85
+ const res = await wixApiRequest("/bookings/v2/services/query", {
86
+ method: "POST",
87
+ body: { query: { filter: { hidden: false, appId }, paging: { limit, offset } } },
88
+ });
89
+ const services = res?.services ?? [];
90
+ const total = res?.pagingMetadata?.total ?? services.length;
91
+ const loaded = offset + services.length;
92
+ return { services, total, nextOffset: loaded < total ? loaded : null };
93
+ }
94
+
95
+ /**
96
+ * The resource type id(s) a rental's availability comes from — read off `serviceResources`.
97
+ * Every rental availability call must pass these as `includeResourceTypeIds`, or it returns zero slots.
98
+ * @param {object} service
99
+ * @returns {string[]}
100
+ */
101
+ export function rentalResourceTypeIds(service) {
102
+ return (service?.serviceResources ?? [])
103
+ .map((sr) => sr.resourceType?._id ?? sr.resourceType?.id)
104
+ .filter(Boolean);
105
+ }
106
+
107
+ /**
108
+ * Start times for a rental — step 1 of 2.
109
+ *
110
+ * A rental is an APPOINTMENT-typed service, so this is the same availability call an appointment
111
+ * uses; what comes back are the times a rental may START. The customer then picks how long, which is
112
+ * `listEndOptions` below. Pass the whole `service` so its resource types ride along — the
113
+ * availability engine reads them to find a resource-driven service's slots.
114
+ * @param {object} service A rental service from queryRentals.
115
+ * @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
116
+ * @returns {Promise<{ slots: object[], nextCursor: string|null, timeZone: string|null }>}
117
+ */
118
+ export function listRentalStartSlots(service, options = {}) {
119
+ const serviceId = service?._id || service?.id;
120
+ return listAvailableSlots(serviceId, { ...options, includeResourceTypeIds: rentalResourceTypeIds(service) });
121
+ }
122
+
123
+ /**
124
+ * The valid END times for a chosen start — step 2 of 2, HOURLY rentals only.
125
+ *
126
+ * Each entry is a TimeSlot sharing `localStartDate` with the request and varying only in
127
+ * `localEndDate`, sorted shortest first. The service's maximum duration always caps the list,
128
+ * so a `maxLocalEndDate` beyond it is ignored rather than rejected.
129
+ *
130
+ * ⚠️ Hourly only. A DAILY rental has no end options — its lengths are whole days, so walk
131
+ * consecutive days from the chosen start instead (`dailyEndOptions` below).
132
+ * https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slot-end-options.md
133
+ * Pass the chosen start slot's own `location`. It carries `locationType` (and an `id` on a
134
+ * multi-location site); `{ locationType: "BUSINESS" }` is enough on a single-location site.
135
+ * @param {string} serviceId
136
+ * @param {{ localStartDate: string, location: object, maxLocalEndDate?: string, timeZone?: string }} options
137
+ * @returns {Promise<{ endOptions: object[], timeZone: string|null }>}
138
+ */
139
+ export async function listEndOptions(serviceId, { localStartDate, location, maxLocalEndDate, timeZone } = {}) {
140
+ if (!localStartDate) throw new Error("listEndOptions requires localStartDate (local 'YYYY-MM-DDThh:mm:ss').");
141
+ if (!location) throw new Error("listEndOptions requires the chosen slot's location.");
142
+ const res = await wixApiRequest("/_api/service-availability/v2/time-slots/end-options", {
143
+ method: "POST",
144
+ body: {
145
+ serviceId,
146
+ localStartDate,
147
+ location,
148
+ ...(maxLocalEndDate ? { maxLocalEndDate } : {}),
149
+ ...(timeZone ? { timeZone } : {}),
150
+ },
151
+ });
152
+ return { endOptions: res?.endOptions ?? [], timeZone: res?.timeZone ?? null };
153
+ }
154
+
155
+ /**
156
+ * The lengths a DAILY rental can run for from a chosen start day — the daily counterpart to
157
+ * `listEndOptions`. A day's slot list is the availability, so this walks forward from the start
158
+ * and stops at the first day that has none: a 3-day rental needs all 3 days free, and a gap
159
+ * means the longer options are not bookable however far out the calendar goes.
160
+ *
161
+ * Returns one entry per bookable length, `{ days, localEndDate }`, shortest first.
162
+ * `localEndDate` is midnight of the day AFTER the last rented day — the end boundary is
163
+ * exclusive, which is the same convention the catalog's availability window uses.
164
+ * @param {object} service A rental service (daily).
165
+ * @param {{ localStartDate: string, timeZone?: string }} options
166
+ * @returns {Promise<{ days: number, localEndDate: string }[]>}
167
+ */
168
+ export async function dailyEndOptions(service, { localStartDate, timeZone } = {}) {
169
+ const duration = rentalDuration(service);
170
+ if (!duration || duration.unit !== "DAY") {
171
+ throw new Error("dailyEndOptions expects a DAY-unit rental — use listEndOptions for hourly.");
172
+ }
173
+ const serviceId = service._id || service.id;
174
+ const includeResourceTypeIds = rentalResourceTypeIds(service);
175
+ const startDay = localStartDate.slice(0, 10);
176
+ const dayAfter = (isoDay, n) => {
177
+ const d = new Date(`${isoDay}T00:00:00Z`);
178
+ d.setUTCDate(d.getUTCDate() + n);
179
+ return d.toISOString().slice(0, 10);
180
+ };
181
+
182
+ const out = [];
183
+ for (let days = 1; days <= duration.max; days++) {
184
+ const dayToCheck = dayAfter(startDay, days - 1);
185
+ const { slots } = await listAvailableSlots(serviceId, {
186
+ fromLocalDate: `${dayToCheck}T00:00:00`,
187
+ toLocalDate: `${dayAfter(dayToCheck, 1)}T00:00:00`,
188
+ timeZone,
189
+ limit: 1,
190
+ includeResourceTypeIds,
191
+ });
192
+ if (!slots.length) break; // a gap — every longer option is unbookable too
193
+ if (days >= duration.min) out.push({ days, localEndDate: `${dayAfter(startDay, days)}T00:00:00` });
194
+ }
195
+ return out;
196
+ }
197
+
198
+ /**
199
+ * The price for a chosen length, from the server.
200
+ *
201
+ * ⚠️ Never compute this client-side. A rental is charged as a RATE — per hour or per day — and
202
+ * hourly is calculated at per-minute granularity, so a 90-minute rental of a $10/hour room is
203
+ * $15. Multiplying a displayed base price by a rounded number of hours quietly disagrees with
204
+ * what the customer is charged at checkout.
205
+ *
206
+ * ⚠️ Omitting the local dates or the time zone does NOT error — it returns a duration-blind
207
+ * price, which is the same trap in a quieter form.
208
+ * https://dev.wix.com/docs/api-reference/business-solutions/bookings/pricing/pricing-api/preview-price.md
209
+ * @param {string} serviceId
210
+ * @param {{ localStartDate: string, localEndDate: string, timeZone: string, numberOfParticipants?: number }} options
211
+ * @returns {Promise<{ total: string, currency: string|null, raw: object }>}
212
+ */
213
+ export async function previewRentalPrice(serviceId, { localStartDate, localEndDate, timeZone, numberOfParticipants = 1 } = {}) {
214
+ if (!localStartDate || !localEndDate || !timeZone) {
215
+ throw new Error("previewRentalPrice requires localStartDate, localEndDate and timeZone — without them the price ignores the duration.");
216
+ }
217
+ const res = await wixApiRequest("/bookings/v2/pricing/v2/pricing/preview", {
218
+ method: "POST",
219
+ body: {
220
+ bookingLineItems: [{ serviceId, localStartDate, localEndDate, timeZone, numberOfParticipants }],
221
+ },
222
+ });
223
+ const info = res?.priceInfo ?? {};
224
+ const total = info.totalPrice ?? info.total ?? {};
225
+ return {
226
+ total: total.formattedValue ?? (total.value != null ? String(total.value) : ""),
227
+ currency: total.currency ?? null,
228
+ raw: info,
229
+ };
230
+ }
231
+
232
+ /**
233
+ * A rental's rate, ready to render — "$10 / hour" or "$120 / day".
234
+ * `servicePriceLabel` in `lib/serviceFacts.js` renders the same number as a flat price, which
235
+ * reads as the whole cost of the rental rather than its rate.
236
+ * @param {object} service
237
+ * @returns {string}
238
+ */
239
+ export function rentalRateLabel(service) {
240
+ const duration = rentalDuration(service);
241
+ if (!duration) return "";
242
+ const price = service?.payment?.fixed?.price;
243
+ const money =
244
+ price?.formattedValue ||
245
+ (price?.currency && price?.value != null
246
+ ? new Intl.NumberFormat(undefined, { style: "currency", currency: price.currency }).format(Number(price.value))
247
+ : "");
248
+ if (!money) return "";
249
+ return `${money} / ${duration.unit === "DAY" ? "day" : "hour"}`;
250
+ }
@@ -183,11 +183,13 @@ export async function countServices() {
183
183
  * If you render a mixed catalog, route by `service.type` via `listSlotsForService`.
184
184
  * Dates are LOCAL ("YYYY-MM-DDThh:mm:ss") in timeZone.
185
185
  * https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
186
+ * For a rental (a resource-driven service), pass its resource type id(s) as `includeResourceTypeIds`
187
+ * so the slots come back with their bookable resources; a staff appointment can omit it.
186
188
  * @param {string} serviceId
187
- * @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
189
+ * @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string, includeResourceTypeIds?: string[] }} options
188
190
  * @returns {Promise<{ slots: object[], nextCursor: string|null, timeZone: string|null }>}
189
191
  */
190
- export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
192
+ export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor, includeResourceTypeIds } = {}) {
191
193
  if (!cursor && (!fromLocalDate || !toLocalDate)) {
192
194
  throw new Error("listAvailableSlots requires fromLocalDate and toLocalDate (local 'YYYY-MM-DDThh:mm:ss').");
193
195
  }
@@ -200,6 +202,7 @@ export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate
200
202
  toLocalDate,
201
203
  timeZone: timeZone || defaultTimeZone(),
202
204
  cursorPaging: { limit },
205
+ ...(includeResourceTypeIds?.length ? { includeResourceTypeIds } : {}),
203
206
  };
204
207
  const res = await wixApiRequest("/_api/service-availability/v2/time-slots", { method: "POST", body });
205
208
  return {
@@ -287,7 +290,11 @@ export async function listEventTimeSlots(serviceIds, { fromLocalDate, toLocalDat
287
290
  export async function listSlotsForService(service, options = {}) {
288
291
  const serviceId = service?._id || service?.id;
289
292
  if (!serviceId) throw new Error("listSlotsForService requires a service with an id.");
290
- return service?.type === "CLASS" || service?.type === "COURSE"
291
- ? listEventTimeSlots(serviceId, options)
292
- : listAvailableSlots(serviceId, options);
293
+ if (service?.type === "CLASS" || service?.type === "COURSE") return listEventTimeSlots(serviceId, options);
294
+ // For a rental, name its resource type(s) so the slots come back; reading them off the service
295
+ // keeps this call type-agnostic (a staff appointment just yields an empty list here).
296
+ const includeResourceTypeIds =
297
+ options.includeResourceTypeIds ??
298
+ (service?.serviceResources ?? []).map((sr) => sr.resourceType?._id ?? sr.resourceType?.id).filter(Boolean);
299
+ return listAvailableSlots(serviceId, { ...options, includeResourceTypeIds });
293
300
  }
@@ -101,6 +101,47 @@ await seed.attachServiceImage(ctx, { serviceId: services[0].id, revision: servic
101
101
  Both bulk calls report per-item `success`/`error` — retry only the failed items **once** with the
102
102
  same body; don't loop and don't re-create the ones that already succeeded.
103
103
 
104
+ ## Rentals
105
+
106
+ `seed-rentals.js` is the rentals half — the same transport, a different create order and payload.
107
+ Wix Rentals has no APIs of its own, so a rental is a Bookings service with rentals-specific
108
+ field values.
109
+
110
+ ```js
111
+ const seed = require("…/references/bookings/seed/seed-rentals.js");
112
+ await seed.setupRentals(ctx, {
113
+ resourceTypeName: "Meeting rooms",
114
+ resources: ["Room A", "Room B"], // parallel capacity = MORE RESOURCES
115
+ rentals: [
116
+ { name: "Meeting room, hourly", description: "…", price: 25, unit: "HOUR", min: 60, max: 480 },
117
+ { name: "Meeting room, daily", description: "…", price: 180, unit: "DAY", min: 1, max: 5 },
118
+ ],
119
+ });
120
+ // → { resourceType, resources, services, ok, problems }
121
+ ```
122
+
123
+ `price` is a RATE — per hour or per day — not the cost of a rental.
124
+ Hourly `min`/`max` are MINUTES (30–1440); daily are DAYS (1–8).
125
+
126
+ **`ok` must be true.** `problems` is the read-back check: a service created without `appId` or
127
+ without `durationRange` still returns 200 and is simply a plain Bookings service from then on.
128
+
129
+ ### The four traps this seed handles
130
+
131
+ - **Install Rentals ONLY.** It provisions the Bookings infrastructure it runs on; installing
132
+ Wix Bookings as well is redundant.
133
+ - **`appId` is immutable after create.** A service created without it is a plain Bookings
134
+ service forever — there is no update that converts it.
135
+ - **Order is load-bearing: resource type → resources → services.** A service whose resource
136
+ type holds no resources has permanently empty availability, and nothing errors.
137
+ - **Rental services do not use categories.** Unlike bookings, do not create or assign one.
138
+ - **`serviceResources` is required** and the docs' Create Service parameter list omits it.
139
+ Without it the create fails `MISSING_APPOINTMENT_RESOURCES`, whose message sends you to check
140
+ the resource type's contents — a dead end, since it fails even when the type is populated.
141
+
142
+ Resources are seeded **24/7** (no working hours) so a multi-day rental stays one booking rather
143
+ than a multi-service group. Pass `workingHours` only when the brief names opening hours.
144
+
104
145
  ## Reference
105
146
  If a call returns a shape you didn't expect, or you need an operation this module doesn't cover,
106
147
  use the **`wix-docs`** skill to search + read the live Wix API reference — never guess. The