@wix/agent-skills 1.17.0 → 1.18.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 (253) 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 +74 -83
  9. package/skills/wix-app/references/SITE_PLUGIN.md +12 -0
  10. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +74 -68
  11. package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +79 -0
  12. package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +73 -0
  13. package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +52 -0
  14. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +184 -0
  15. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +12 -11
  16. package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +1 -0
  17. package/skills/wix-app/references/editor-react-component/BROWSER-SUPPORT.md +56 -0
  18. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +16 -8
  19. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +47 -14
  20. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +3 -0
  21. package/skills/wix-base44-connector/SKILL.md +133 -44
  22. package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +78 -0
  23. package/skills/wix-base44-connector/scripts/utils.js +216 -30
  24. package/skills/wix-headless/entry/skill.md +1 -1
  25. package/skills/wix-headless/references/CAPABILITIES.md +9 -1
  26. package/skills/wix-headless/references/DESIGN.md +1 -0
  27. package/skills/wix-headless/references/DISCOVERY.md +2 -1
  28. package/skills/wix-headless/references/IMAGE_GENERATION.md +1 -0
  29. package/skills/wix-headless/references/SDK_HANDOFF.md +4 -1
  30. package/skills/wix-headless/references/SEED.md +2 -1
  31. package/skills/wix-headless/references/SETUP.md +1 -0
  32. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +2 -2
  33. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +26 -8
  34. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +26 -10
  35. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +8 -8
  36. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +1 -1
  37. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +1 -1
  38. package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +254 -0
  39. package/skills/wix-headless/references/inline-recipes/setup-forms.md +57 -10
  40. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +13 -0
  41. package/skills/wix-headless/references/inline-recipes/setup-rentals.md +254 -0
  42. package/skills/wix-headless/references/non-astro.md +1 -1
  43. package/skills/wix-headless-fast/SKILL.md +166 -0
  44. package/skills/wix-headless-fast/entry/skill.md +99 -0
  45. package/skills/wix-headless-fast/install/deploy.mjs +433 -0
  46. package/skills/wix-headless-fast/install/fast-path.mjs +141 -0
  47. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +137 -0
  48. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +137 -0
  49. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +113 -0
  50. package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +42 -0
  51. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +123 -0
  52. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +70 -0
  53. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +99 -0
  54. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +76 -0
  55. package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +62 -0
  56. package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +40 -0
  57. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +99 -0
  58. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +24 -0
  59. package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +16156 -0
  60. package/skills/wix-headless-fast/references/blog/seed/SEED.md +72 -0
  61. package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +287 -0
  62. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +129 -0
  63. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +143 -0
  64. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +119 -0
  65. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +138 -0
  66. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +54 -0
  67. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +206 -0
  68. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +96 -0
  69. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +70 -0
  70. package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +40 -0
  71. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +76 -0
  72. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +22 -0
  73. package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +11962 -0
  74. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +61 -0
  75. package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +302 -0
  76. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +179 -0
  77. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +83 -0
  78. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +57 -0
  79. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +151 -0
  80. package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +75 -0
  81. package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +41 -0
  82. package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +11262 -0
  83. package/skills/wix-headless-fast/references/cms/seed/SEED.md +103 -0
  84. package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +324 -0
  85. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +149 -0
  86. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +66 -0
  87. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +176 -0
  88. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +118 -0
  89. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +117 -0
  90. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +59 -0
  91. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +136 -0
  92. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +128 -0
  93. package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +61 -0
  94. package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +40 -0
  95. package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +12 -0
  96. package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +80 -0
  97. package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +21 -0
  98. package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +11884 -0
  99. package/skills/wix-headless-fast/references/events/seed/SEED.md +86 -0
  100. package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +276 -0
  101. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +150 -0
  102. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +317 -0
  103. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +269 -0
  104. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +231 -0
  105. package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +135 -0
  106. package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +10593 -0
  107. package/skills/wix-headless-fast/references/forms/seed/SEED.md +108 -0
  108. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +320 -0
  109. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +63 -0
  110. package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +60 -0
  111. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +155 -0
  112. package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +78 -0
  113. package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +47 -0
  114. package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +66 -0
  115. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +88 -0
  116. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +53 -0
  117. package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +122 -0
  118. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +50 -0
  119. package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +21 -0
  120. package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +42 -0
  121. package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +10 -0
  122. package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +11 -0
  123. package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +10329 -0
  124. package/skills/wix-headless-fast/references/members/seed/SEED.md +39 -0
  125. package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +94 -0
  126. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +134 -0
  127. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +110 -0
  128. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +90 -0
  129. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +115 -0
  130. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +59 -0
  131. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +39 -0
  132. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +58 -0
  133. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +165 -0
  134. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +55 -0
  135. package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +40 -0
  136. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +30 -0
  137. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +21 -0
  138. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +29 -0
  139. package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +10197 -0
  140. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +78 -0
  141. package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +258 -0
  142. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +133 -0
  143. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +58 -0
  144. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +126 -0
  145. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +37 -0
  146. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +39 -0
  147. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +75 -0
  148. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +45 -0
  149. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +30 -0
  150. package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +41 -0
  151. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +40 -0
  152. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +21 -0
  153. package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +11226 -0
  154. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +71 -0
  155. package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +279 -0
  156. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +165 -0
  157. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +142 -0
  158. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +26 -0
  159. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +121 -0
  160. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +155 -0
  161. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +55 -0
  162. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +43 -0
  163. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +177 -0
  164. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +165 -0
  165. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +103 -0
  166. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +254 -0
  167. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +146 -0
  168. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +168 -0
  169. package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +47 -0
  170. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +22 -0
  171. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +12 -0
  172. package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +12560 -0
  173. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +81 -0
  174. package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +479 -0
  175. package/skills/wix-headless-fast/references/shared/CONTENT.md +22 -0
  176. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +73 -0
  177. package/skills/wix-headless-fast/references/shared/DESIGN.md +26 -0
  178. package/skills/wix-headless-fast/references/shared/app/styles/global.css +52 -0
  179. package/skills/wix-headless-fast/references/shared/app/wix/config.ts +15 -0
  180. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +27 -0
  181. package/skills/wix-headless-fast/references/shared/app/wix/money.ts +24 -0
  182. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +76 -0
  183. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +46 -0
  184. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +46 -0
  185. package/skills/wix-headless-fast/references/shared/seed/images.mjs +145 -0
  186. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +149 -0
  187. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +26 -0
  188. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +121 -0
  189. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +100 -0
  190. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +130 -0
  191. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +48 -0
  192. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +66 -0
  193. package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +103 -0
  194. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +48 -0
  195. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +140 -0
  196. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +80 -0
  197. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +101 -0
  198. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +221 -0
  199. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +215 -0
  200. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +109 -0
  201. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +46 -0
  202. package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +56 -0
  203. package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +23 -0
  204. package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +12247 -0
  205. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +85 -0
  206. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +415 -0
  207. package/skills/wix-manage/SKILL.md +113 -89
  208. package/skills/wix-manage/references/dashboard-navigation/routes.json +12 -52
  209. package/skills/wix-manage/references/ecommerce/shipping/ecom-shipping-api.md +0 -2
  210. package/skills/wix-manage/references/forms/create-form.md +272 -184
  211. package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +13 -10
  212. package/skills/wix-manage/references/forms/update-form.md +122 -0
  213. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +141 -0
  214. package/skills/wix-manage/references/google-business-profile/connect-google-business-profile.md +134 -0
  215. package/skills/wix-manage/references/google-business-profile/manage-google-business-profile-locations.md +182 -0
  216. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +3 -0
  217. package/skills/wix-manage/references/media/generate-image-with-ai.md +86 -0
  218. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +37 -7
  219. package/skills/wix-manage/references/stores/update-product-with-options.md +83 -0
  220. package/skills/wix-vibe-headless/SKILL.md +18 -4
  221. package/skills/wix-vibe-headless/install/deploy.cjs +12 -4
  222. package/skills/wix-vibe-headless/install/pin-agents-md.cjs +2 -1
  223. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
  224. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +3 -1
  225. package/skills/wix-vibe-headless/platforms/base44.md +27 -61
  226. package/skills/wix-vibe-headless/platforms/generic.md +6 -3
  227. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +43 -0
  228. package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +67 -0
  229. package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +128 -0
  230. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +5 -3
  231. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +250 -0
  232. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +12 -5
  233. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +41 -0
  234. package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.js +267 -0
  235. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +6 -3
  236. package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +486 -0
  237. package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +425 -0
  238. package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +187 -0
  239. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +238 -0
  240. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +108 -0
  241. package/skills/wix-vibe-headless/references/forms/seed/SEED.md +1836 -0
  242. package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +13 -0
  243. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +137 -84
  244. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +15 -8
  245. package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +37 -0
  246. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +18 -6
  247. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +30 -7
  248. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +46 -10
  249. package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +0 -110
  250. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +0 -51
  251. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +0 -49
  252. package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +0 -92
  253. package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +0 -139
@@ -8,45 +8,35 @@ only), safe in the frontend; use it directly for the Wix client setup.
8
8
 
9
9
  > **The Wix skills installed below are the complete build and seed path for this app — the Wix connector supplies the token for admin API calls.** **Do NOT use the Base44 commerce kit (or any Base44 solution kit).**
10
10
 
11
- Follow STEPs 1–5 below exactly (run STEP 4 in parallel with STEP 3).
11
+ Follow STEPs 1–5 below exactly (run STEP 4 in parallel with STEP 3 — **except `forms`**, see STEP 3).
12
12
 
13
13
  ## STEP 1 — Install the Wix skills locally
14
14
 
15
- Install three skills — they land under `.agents/skills/`:
16
- - **`wix-vibe-headless`** — the client build **and** seed guide (STEPs 3–4): your main source of
17
- truth. Seeding lives here too, per-vertical under `references/<vertical>/seed/`.
18
- - **`wix-manage`** — REST recipes for managing and configuring the site (install apps, seed
19
- catalogs, business setup).
20
- - **`wix-base44-connector`** — building on Wix from the Base44 sandbox: site context, API
21
- contracts, and the docs **fallback** — find + read the Wix API docs for anything
22
- `wix-vibe-headless` doesn't cover.
15
+ Install three skills under `.agents/skills/`: **`wix-vibe-headless`** (the client build + seed guide — your main source of truth), **`wix-manage`** (REST recipes to manage/configure the site), and **`wix-base44-connector`** (site context + API-doc discovery, the fallback for anything the others don't cover).
23
16
 
24
17
  Run this through exec_tool, exactly as written — installs all three skills, deploys REST scaffolds + UI into `src/`, writes `wix-config.js`, and pins the AGENTS.md note.
25
18
 
26
- **Set `VERTICALS`** to what the prompt asks for (too vague to tell? do STEP 2 first, then set it):
19
+ **Set `VERTICALS`** to what the prompt asks for — **list every vertical the app uses**, since several often join the main one (too vague to tell? do STEP 2 first, then set it). Adding one later: re-run with the extra name.
27
20
 
28
21
  | vertical | pick it when the app needs to |
29
22
  |---|---|
30
23
  | `storefront` | sell products |
31
24
  | `bookings` | take appointments or service bookings |
25
+ | `rentals` | rent out an item for a length the customer picks (by the hour or by the day) |
32
26
  | `blog` | publish articles |
33
27
  | `events` | publish events with RSVPs or ticket sales |
34
28
  | `portfolio` | showcase creative work |
35
- | `pricing-plans` | sell memberships or subscriptions |
29
+ | `pricing-plans` | sell memberships or subscriptions, incl. paid enrollment/access to an online course or program — but an "online store selling courses" is still `storefront` |
36
30
  | `restaurants` | show a menu, take orders, book tables |
37
31
  | `members` | let visitors sign in — this is **auth** |
38
- | `cms` | keep its own structured content — user submissions, galleries, listings, anything the rows above don't already cover |
39
-
40
- **List every vertical the app uses.** `cms` for app-stored content, `members` for visitor sign-in — these often join the main vertical. Adding one later: re-run with the extra name.
32
+ | `forms` | any visitor-fillable form: contact, signup, waitlist, application, survey, quote request (an event RSVP is `events`; a per-service booking form is `bookings`) |
33
+ | `cms` | structured content the app reads back — galleries, listings, "my submissions" (a visitor-fillable form is `forms`) |
41
34
 
42
35
  ```js
43
36
  const { execSync } = require('child_process');
44
37
  const { existsSync, readdirSync } = require('fs');
45
- const VERTICALS = ['storefront']; // ← set from the prompt; list every vertical the app uses
46
- // const VERTICALS = ['members', 'cms']; // ← e.g. visitors sign in AND the app stores what they submit
47
- // COPY these two straight from the prompt — deploy writes them into src/rest/wix-config.js, so never
48
- // retype either one into a file by hand afterwards.
49
- const WIX_CLIENT_ID = '<client id from the prompt>';
38
+ const VERTICALS = ['storefront']; // ← set from the prompt; list every vertical the app uses (e.g. ['members','cms'])
39
+ const WIX_CLIENT_ID = '<client id from the prompt>'; // copy both from the prompt — deploy writes them into src/rest/wix-config.js
50
40
  const WIX_METASITE_ID = '<site id from the prompt>';
51
41
  const results = {};
52
42
  for (const skill of ['wix-vibe-headless', 'wix-manage', 'wix-base44-connector']) {
@@ -93,63 +83,39 @@ truth for how the client is built.
93
83
 
94
84
  **REST scaffolds + `wix-config.js` are already in `src/rest/`** (STEP 1 wrote them). Some verticals also ship a ready UI client in `src/` — theme + wire it per `INSTRUCTIONS.md`, don't rebuild. **Don't `read_file` deployed files** — every field shape is in `INSTRUCTIONS.md`; read one only on a real error or gap.
95
85
 
86
+ **⚠️ `forms` is the ONE vertical that does NOT run in parallel with STEP 4** — its UI is gated on a
87
+ file the seed writes. Read its `INSTRUCTIONS.md` **Prerequisites** before building any form UI; seed
88
+ it first (STEP 4) and build the rest of the app meanwhile.
89
+
96
90
  **`src/App.jsx`: edit surgically, never rewrite.** It carries required platform auth scaffolding
97
91
  (`AuthProvider`/`useAuth` from `@/lib/AuthContext`); a full rewrite drops them → the validator
98
92
  rejects the write. Wire routes/imports in with `find_replace`, leave the rest as-is.
99
93
 
100
94
  ## STEP 4 — Manage and seed the business
101
95
 
102
- **Never delete or clean up anything on the user's site — seeding is additive only.** It's a live
103
- user-owned business, so never delete or overwrite existing content, even apparent sample data. If a
104
- cleanup truly seems needed, ask the user first.
105
-
106
96
  Seed by calling your vertical's ready-made seed module — read
107
- `.agents/skills/wix-vibe-headless/references/<vertical>/seed/SEED.md` and load its `seed-*.js` via
108
- its loader snippet (build-time exec_tool); call its functions with your data. Gaps or an unexpected
109
- shape → the **`wix-base44-connector`** skill's doc discovery.
110
-
111
- **Auth for these admin calls is the already-configured Wix connector — nothing else.** Get its
112
- access token and send it as a bearer token:
97
+ `.agents/skills/wix-vibe-headless/references/<vertical>/seed/SEED.md` and follow it (the loader
98
+ snippet, admin connector token, entity images with the final `media.base44.com` url, and every field
99
+ shape are there). Gaps or an unexpected shape → the **`wix-base44-connector`** skill's doc discovery.
100
+ Seeding is **admin-only** — not part of the client, which is built solely per the `wix-vibe-headless` skill.
113
101
 
114
- ```js
115
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
116
- // then: fetch(url, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" }, ... })
117
- ```
118
-
119
- Inline via exec_tool, `base44` is already declared — use it directly; do **not** import
120
- `@base44/sdk`, re-declare it, or call `createClient()` (that's for standalone `.js` files only;
121
- inline it throws *"Identifier 'base44' has already been declared."*).
122
-
123
- **Entity images.** For image-bearing entities (store products, blog covers, bookings services,
124
- restaurant items, portfolio projects, event heroes, CMS items), generate with **Base44's built-in
125
- image generation**, then attach via the vertical seed module's image-attach step —
126
- `wix-base44-connector` doc discovery if the module doesn't cover that entity.
127
-
128
- **Seed images with the FINAL url, in one call.** Seeding writes to Wix, so use the real
129
- `https://media.base44.com/...` url from the **completed** `generate_image` result and pass it straight
130
- into your single `setupStore`/seed call (images included). A still-generating `/__generating__/<id>.png`
131
- placeholder is not a real url — Wix can't fetch it. `generate_image` runs in the background while you
132
- build the client, so the urls are ready by the time you seed.
133
-
134
- The connector + seeding are **admin-only** (STEP 4) — **not** part of the client, which is built
135
- solely per the `wix-vibe-headless` skill.
102
+ - **Additive only:** never delete or overwrite the user's content, even apparent sample data; ask first if a cleanup truly seems needed.
103
+ - Inline in exec_tool, `base44` is already declared — use it directly; do **not** `import @base44/sdk`, re-declare it, or call `createClient()` (throws *"Identifier 'base44' has already been declared."*).
136
104
 
137
105
  ## STEP 5 — Wrap up
138
106
 
139
- Once the site is built and seeded:
140
-
141
- 1. **Mount the preview-only manage banner** (required; links the app to its Wix back office; shipped,
142
- self-gates to the preview, never on the published site): render the `<WixManageBanner/>` component
143
- in your Layout's fixed top region, above the header, per your vertical's INSTRUCTIONS STEP 4.
144
- 2. **Ask the user to open** `https://manage.wix.com/dashboard/{metaSiteId}` (substitute your
145
- metasite id) to complete setup in Wix (required), and mention that the preview shows a dismissible
146
- top banner linking to this same dashboard.
107
+ 1. **Mount `<WixManageBanner/>`** (required) in your Layout's fixed top region, above the header, per your vertical's INSTRUCTIONS — it links the app to its Wix back office, self-gates to the preview, and never shows on the published site.
108
+ 2. **Ask the user to open** `https://manage.wix.com/dashboard/{metaSiteId}` (your metasite id) to complete setup in Wix.
147
109
 
148
- **Preview briefly, don't chase images.** Broken images are expected — `generate_image` returns a `/__generating__/…` placeholder that the platform swaps for the final url automatically at turn end (failures get a stock fallback). **Do NOT edit, re-seed, or debug image urls.** Leave them and finish.
110
+ **Don't chase images.** A `/__generating__/…` placeholder is swapped for the final url automatically at turn end — do **not** edit, re-seed, or debug image urls.
149
111
 
150
112
  ## Later admin requests
151
113
 
152
114
  For any later admin/management request, work as in STEP 4: your vertical's seed module first, else
153
115
  `wix-base44-connector` doc discovery — all over the connector.
154
116
 
155
- version: v1338
117
+ **A change to what the business data COLLECTS is one of these, however UI it sounds** — a new form
118
+ field, product option or service setting. Change it on Wix first per the vertical's `seed/SEED.md`,
119
+ then edit the client against the verified result.
120
+
121
+ version: v1341
@@ -27,7 +27,7 @@ Everything is under `.agents/skills/wix-vibe-headless/references/`.
27
27
  - `INSTRUCTIONS.md` — the vertical's build guide + field-shape snippets; `seed/SEED.md` — how the seed
28
28
  module is run.
29
29
 
30
- **The nine verticals — data layer (`app/rest/`) + seed module (`seed/`):**
30
+ **The verticals — data layer (`app/rest/`) + seed module (`seed/`):**
31
31
 
32
32
  | vertical | `app/rest/` data layer | seed module |
33
33
  |---|---|---|
@@ -37,7 +37,8 @@ Everything is under `.agents/skills/wix-vibe-headless/references/`.
37
37
  | events | `wix-events-browse.js`, `wix-events-registration.js` — events & categories; RSVP / ticketing + checkout | `seed-events.js` |
38
38
  | portfolio | `wix-portfolio.js` — collections, projects, galleries | `seed-portfolio.js` |
39
39
  | restaurants | `wix-restaurants-menu.js`, `-ordering.js`, `-reservations.js` — menu; online ordering; table reservations | `seed-restaurants.js` |
40
- | cms | `wix-cms.js` — Wix Data collections: list / detail / filter + form CRUD | `seed-cms.js` |
40
+ | forms | `wix-forms.js`, `wix-forms-submissions.js` — any visitor-fillable form: read the schema; upload attachments, create a submission (schema accessors ship alongside in `lib/wix-form-schema-utils.js`) | *(none — REST shapes in `seed/SEED.md`)* |
41
+ | cms | `wix-cms.js` — Wix Data collections: list / detail / filter | `seed-cms.js` |
41
42
  | pricing-plans | `wix-pricing-plans.js` — plans list + subscribe/checkout | `seed-pricing-plans.js` |
42
43
  | members | `wix-members-auth.js` — custom login/signup (email+password, social, SSO), session, account | *(none — members sign up at runtime; nothing to seed)* |
43
44
 
@@ -49,7 +50,9 @@ the token set) is **universal — follow it as-is**.
49
50
 
50
51
  ## The flow — install → build client → seed → done
51
52
 
52
- Run **build the client** (step 2) and **seed** (step 3) in parallel; parallelize independent work within each.
53
+ Run **build the client** (step 2) and **seed** (step 3) in parallel; parallelize independent work
54
+ within each. **One exception — `forms` must be seeded before its UI is built; read that gate in
55
+ `references/forms/INSTRUCTIONS.md` (Prerequisites) first.** Everything else still parallelizes.
53
56
 
54
57
  ### 1 · Install the skills
55
58
 
@@ -225,6 +225,49 @@ the areas they sit in:
225
225
  - eCommerce checkout (paid bookings): https://dev.wix.com/docs/api-reference/business-solutions/e-commerce.md
226
226
  - Headless redirect session (hosted checkout): https://dev.wix.com/docs/api-reference/business-management/headless/redirects.md
227
227
 
228
+ ## Rentals — the same client, one extra step
229
+
230
+ **Wix Rentals has no APIs of its own.** A rental is a Bookings *service* with rentals-specific
231
+ field values, so this whole client works on it: the catalog read, the booking form, and the
232
+ `createBooking → cart → checkout` chain are all unchanged. `rest/wix-bookings-rentals.js` and
233
+ `hooks/useRentalDuration.js` carry the only differences.
234
+
235
+ **What tells a rental apart — the DATA, never the brief.** A service carries either a fixed
236
+ session length or a duration RANGE the customer picks within; the two are mutually exclusive:
237
+
238
+ ```js
239
+ import { isRental, rentalDuration, rentalRateLabel } from "@/rest/wix-bookings-rentals";
240
+ isRental(service) // true only when schedule.availabilityConstraints.durationRange is present
241
+ ```
242
+
243
+ A 60-minute yoga class and a 60-minute massage are **not** rentals even though a massage is
244
+ `type: "APPOINTMENT"` exactly like a rental is. Never branch on the service's name, its
245
+ category, or what the brief called it.
246
+
247
+ **The extra step.** An ordinary service is one choice (a slot). A rental is two: pick a START,
248
+ then pick HOW LONG — and the price follows the length.
249
+
250
+ ```jsx
251
+ const detail = useServiceDetail(serviceId); // service + start times
252
+ const rental = useRentalDuration(detail.service, detail.selectedSlot, detail.timeZone);
253
+
254
+ <SlotPicker … /> {/* pick a start, as ever */}
255
+ {rental.isRental && <RentalDurationPicker rental={rental} />} {/* then pick a length */}
256
+ ```
257
+
258
+ Book with `rental.endDate` as the slot's `localEndDate`; everything else in the booking call is
259
+ identical.
260
+
261
+ **Hard rules on top of the ones below:**
262
+
263
+ - **Filter every catalog read by `appId`.** `queryRentals()` does it. On a site with both apps
264
+ an unfiltered `queryServices` returns haircuts next to meeting rooms.
265
+ - **Never compute the total yourself.** A rental is charged as a rate, hourly at per-minute
266
+ granularity — 90 minutes of a $10/hour room is $15. `useRentalDuration` prices the chosen
267
+ length on the server; multiplying the displayed rate disagrees with checkout.
268
+ - **Show the total before the customer commits.** A rate alone is not a price.
269
+ - **One unit type per service.** A room rented by the hour *and* by the day is two services.
270
+
228
271
  ## Hard rules
229
272
  - Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
230
273
  - Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped
@@ -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(() => {