@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,176 @@
1
+ // REFERENCE registration surface: branches on event.registrationType — TICKETING renders
2
+ // the tier picker (quantity steppers → checkout CTA), RSVP the built-in name+email form,
3
+ // EXTERNAL a link out, NONE/closed an honest closed state — on the @theme tokens. Correct
4
+ // and complete; per the skill's model you design and build your own on useEventRegistration.
5
+ // Mount client:only — it runs visitor-session SDK calls and redirects.
6
+ import { useEventRegistration } from "../../hooks/events/useEventRegistration";
7
+ import type { EventDetail } from "../../wix/events/types";
8
+
9
+ const input =
10
+ "w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground outline-none transition-shadow focus:ring-2 focus:ring-primary";
11
+ const cta =
12
+ "mt-5 rounded-full bg-primary px-8 py-3 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-50";
13
+
14
+ export default function EventRegistrationView({ event }: { event: EventDetail }) {
15
+ const {
16
+ tiers,
17
+ quantities,
18
+ setQuantity,
19
+ ticketCount,
20
+ canCheckout,
21
+ checkout,
22
+ rsvpValues,
23
+ setRsvpValue,
24
+ canRsvp,
25
+ rsvp,
26
+ submitting,
27
+ confirmed,
28
+ error,
29
+ } = useEventRegistration(event);
30
+
31
+ if (confirmed && confirmed.kind === "rsvpConfirmed") {
32
+ return (
33
+ <div className="rounded-lg border border-border bg-secondary p-8 text-center">
34
+ <p className="text-lg font-semibold">
35
+ {confirmed.status === "WAITLIST"
36
+ ? "You're on the waitlist"
37
+ : confirmed.status === "NO"
38
+ ? "Thanks for letting us know"
39
+ : "You're in! 🎉"}
40
+ </p>
41
+ <p className="mt-2 text-sm text-muted-foreground">
42
+ {confirmed.status === "WAITLIST"
43
+ ? `${event.title} is full — we'll email you if a spot opens up.`
44
+ : confirmed.status === "NO"
45
+ ? "We've recorded that you can't make it."
46
+ : `See you at ${event.title}${event.dateLabel ? ` — ${event.dateLabel}` : ""}. A confirmation email is on its way.`}
47
+ </p>
48
+ </div>
49
+ );
50
+ }
51
+
52
+ if (!event.registrationOpen) {
53
+ return (
54
+ <div className="rounded-lg border border-border p-6 text-sm text-muted-foreground">
55
+ {event.soldOut ? "This event is sold out." : "Registration for this event is closed."}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ if (event.registrationType === "EXTERNAL") {
61
+ return (
62
+ <a href={event.externalUrl} className={`${cta} inline-block no-underline`} target="_blank" rel="noreferrer">
63
+ Register ↗
64
+ </a>
65
+ );
66
+ }
67
+
68
+ if (event.registrationType === "TICKETING") {
69
+ return (
70
+ <div>
71
+ <p className="mb-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">Tickets</p>
72
+ {tiers === null ? (
73
+ <div className="space-y-3" aria-busy="true">
74
+ {Array.from({ length: 2 }, (_, i) => (
75
+ <div key={i} className="h-16 animate-pulse rounded-lg bg-secondary" />
76
+ ))}
77
+ </div>
78
+ ) : tiers.length === 0 ? (
79
+ <p className="rounded-md border border-border p-4 text-sm text-muted-foreground">
80
+ Tickets aren't available right now.
81
+ </p>
82
+ ) : (
83
+ <div className="space-y-3">
84
+ {tiers.map((t) => {
85
+ const onSale = t.saleStatus === "SALE_STARTED";
86
+ const qty = quantities[t.id] ?? 0;
87
+ return (
88
+ <div key={t.id} className="flex items-center justify-between gap-4 rounded-lg border border-border p-4">
89
+ <div>
90
+ <p className="text-sm font-medium text-foreground">{t.name}</p>
91
+ {t.description && <p className="mt-0.5 text-xs text-muted-foreground">{t.description}</p>}
92
+ <p className="mt-1 text-sm text-foreground">{t.price}</p>
93
+ {!onSale && (
94
+ <p className="mt-1 text-xs text-muted-foreground">
95
+ {t.saleStatus === "SALE_SCHEDULED" ? "Sale hasn't started yet" : "Sale ended"}
96
+ </p>
97
+ )}
98
+ </div>
99
+ <div className="flex items-center gap-2">
100
+ <button
101
+ type="button"
102
+ aria-label={`Fewer ${t.name}`}
103
+ disabled={!onSale || qty === 0}
104
+ onClick={() => setQuantity(t.id, qty - 1)}
105
+ className="h-8 w-8 rounded-full border border-border text-foreground transition-colors hover:bg-secondary disabled:opacity-40"
106
+ >
107
+ −
108
+ </button>
109
+ <span className="w-6 text-center text-sm tabular-nums">{qty}</span>
110
+ <button
111
+ type="button"
112
+ aria-label={`More ${t.name}`}
113
+ disabled={!onSale || qty >= t.limitPerCheckout}
114
+ onClick={() => setQuantity(t.id, qty + 1)}
115
+ className="h-8 w-8 rounded-full border border-border text-foreground transition-colors hover:bg-secondary disabled:opacity-40"
116
+ >
117
+ +
118
+ </button>
119
+ </div>
120
+ </div>
121
+ );
122
+ })}
123
+ </div>
124
+ )}
125
+ {error && <p className="mt-3 text-sm text-red-600">{error}</p>}
126
+ <button type="button" disabled={!canCheckout || submitting} onClick={() => checkout().catch(() => {})} className={cta}>
127
+ {submitting ? "Reserving…" : ticketCount > 0 ? `Get ${ticketCount} ticket${ticketCount > 1 ? "s" : ""}` : "Get tickets"}
128
+ </button>
129
+ </div>
130
+ );
131
+ }
132
+
133
+ if (event.registrationType === "RSVP") {
134
+ return (
135
+ <div>
136
+ <p className="mb-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">RSVP</p>
137
+ <div className="grid max-w-md gap-3">
138
+ <label className="block">
139
+ <span className="mb-1 block text-xs font-semibold uppercase tracking-wider text-muted-foreground">First name</span>
140
+ <input type="text" value={rsvpValues.firstName} onChange={(e) => setRsvpValue("firstName", e.target.value)} className={input} />
141
+ </label>
142
+ <label className="block">
143
+ <span className="mb-1 block text-xs font-semibold uppercase tracking-wider text-muted-foreground">Last name</span>
144
+ <input type="text" value={rsvpValues.lastName} onChange={(e) => setRsvpValue("lastName", e.target.value)} className={input} />
145
+ </label>
146
+ <label className="block">
147
+ <span className="mb-1 block text-xs font-semibold uppercase tracking-wider text-muted-foreground">Email</span>
148
+ <input type="email" value={rsvpValues.email} onChange={(e) => setRsvpValue("email", e.target.value)} className={input} />
149
+ </label>
150
+ </div>
151
+ {error && <p className="mt-3 text-sm text-red-600">{error}</p>}
152
+ <div className="flex items-center gap-3">
153
+ <button type="button" disabled={!canRsvp || submitting} onClick={() => rsvp(true).catch(() => {})} className={cta}>
154
+ {submitting ? "Sending…" : "Count me in"}
155
+ </button>
156
+ {event.rsvpResponseType === "YES_AND_NO" && (
157
+ <button
158
+ type="button"
159
+ disabled={!canRsvp || submitting}
160
+ onClick={() => rsvp(false).catch(() => {})}
161
+ className="mt-5 rounded-full border border-border px-6 py-3 text-sm font-medium text-foreground transition-colors hover:bg-secondary disabled:opacity-50"
162
+ >
163
+ Can't make it
164
+ </button>
165
+ )}
166
+ </div>
167
+ </div>
168
+ );
169
+ }
170
+
171
+ return (
172
+ <div className="rounded-lg border border-border p-6 text-sm text-muted-foreground">
173
+ Registration isn't open for this event.
174
+ </div>
175
+ );
176
+ }
@@ -0,0 +1,118 @@
1
+ // REFERENCE listing surface: category filter + events grid on the @theme tokens.
2
+ // Correct and complete; per the skill's model you design and build your own on useEvents.
3
+ import type { ComponentType, ReactNode } from "react";
4
+ import { useEvents } from "../../hooks/events/useEvents";
5
+ import type { EventSummary } from "../../wix/events/types";
6
+
7
+ export interface LinkLikeProps {
8
+ href: string;
9
+ className?: string;
10
+ children?: ReactNode;
11
+ }
12
+
13
+ const PlainLink = ({ href, className, children }: LinkLikeProps) => (
14
+ <a href={href} className={className}>
15
+ {children}
16
+ </a>
17
+ );
18
+
19
+ export interface EventCardProps {
20
+ event: EventSummary;
21
+ eventHref?: (slug: string) => string;
22
+ LinkComponent?: ComponentType<LinkLikeProps>;
23
+ }
24
+
25
+ export function EventCard({
26
+ event,
27
+ eventHref = (slug) => `/events/${slug}`,
28
+ LinkComponent = PlainLink,
29
+ }: EventCardProps) {
30
+ return (
31
+ <LinkComponent href={eventHref(event.slug)} className="group block no-underline">
32
+ <div className="relative aspect-[3/2] overflow-hidden rounded-lg bg-secondary">
33
+ {event.imageUrl && (
34
+ <img
35
+ src={event.imageUrl}
36
+ alt={event.title}
37
+ loading="lazy"
38
+ className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.03]"
39
+ />
40
+ )}
41
+ {event.soldOut && (
42
+ <span className="absolute left-3 top-3 rounded-full bg-background/90 px-3 py-1 text-xs font-semibold text-foreground backdrop-blur">
43
+ Sold out
44
+ </span>
45
+ )}
46
+ </div>
47
+ {event.dateLabel && <p className="eyebrow mt-3">{event.dateLabel}</p>}
48
+ <p className="mt-1 text-sm font-medium text-foreground">{event.title}</p>
49
+ <p className="mt-0.5 text-xs text-muted-foreground">
50
+ {event.locationType === "ONLINE" ? "Online" : event.locationName}
51
+ {event.priceLabel ? ` · ${event.priceLabel}` : ""}
52
+ </p>
53
+ </LinkComponent>
54
+ );
55
+ }
56
+
57
+ export interface EventsViewProps {
58
+ initialEvents?: EventSummary[];
59
+ emptyMessage?: string;
60
+ eventHref?: EventCardProps["eventHref"];
61
+ LinkComponent?: ComponentType<LinkLikeProps>;
62
+ CardComponent?: ComponentType<EventCardProps>;
63
+ }
64
+
65
+ const pill = (active: boolean) =>
66
+ `rounded-full border px-4 py-1.5 text-sm font-medium transition-colors ${
67
+ active
68
+ ? "border-primary bg-primary text-primary-foreground"
69
+ : "border-border text-foreground hover:bg-secondary"
70
+ }`;
71
+
72
+ export default function EventsView({
73
+ initialEvents,
74
+ emptyMessage = "No upcoming events — check back soon.",
75
+ eventHref,
76
+ LinkComponent,
77
+ CardComponent = EventCard,
78
+ }: EventsViewProps) {
79
+ const { events, categories, activeCategoryId, setActiveCategoryId, error } = useEvents({
80
+ initialEvents,
81
+ });
82
+
83
+ return (
84
+ <div>
85
+ {categories.length > 1 && (
86
+ <div className="mb-8 flex flex-wrap gap-2" role="group" aria-label="Categories">
87
+ <button type="button" className={pill(activeCategoryId === null)} onClick={() => setActiveCategoryId(null)}>
88
+ All
89
+ </button>
90
+ {categories.map((c) => (
91
+ <button key={c.id} type="button" className={pill(activeCategoryId === c.id)} onClick={() => setActiveCategoryId(c.id)}>
92
+ {c.name}
93
+ </button>
94
+ ))}
95
+ </div>
96
+ )}
97
+ {error && <p className="mb-4 text-sm text-red-600">{error}</p>}
98
+ {events === null ? (
99
+ <div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3" aria-busy="true">
100
+ {Array.from({ length: 6 }, (_, i) => (
101
+ <div key={i}>
102
+ <div className="aspect-[3/2] animate-pulse rounded-lg bg-secondary" />
103
+ <div className="mt-3 h-3.5 w-2/3 animate-pulse rounded bg-secondary" />
104
+ </div>
105
+ ))}
106
+ </div>
107
+ ) : events.length === 0 ? (
108
+ <p className="py-16 text-center text-muted-foreground">{emptyMessage}</p>
109
+ ) : (
110
+ <div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
111
+ {events.map((e) => (
112
+ <CardComponent key={e.id} event={e} eventHref={eventHref} LinkComponent={LinkComponent} />
113
+ ))}
114
+ </div>
115
+ )}
116
+ </div>
117
+ );
118
+ }
@@ -0,0 +1,117 @@
1
+ // The whole registration state machine for one event, branched on event.registrationType:
2
+ // TICKETING loads the tier picker and checkout() reserves → redirects to Wix's hosted
3
+ // checkout; RSVP is the built-in name+email form submitted in place. All correctness (the
4
+ // visitor-public tier read, the reservation payload, the redirect callbacks, rsvpV2) lives
5
+ // in the data layer — this hook orchestrates; you own how it looks.
6
+ import { useEffect, useMemo, useState } from "react";
7
+ import { fetchTicketTiers, startTicketCheckout, submitRsvp } from "../../wix/events/registration";
8
+ import type { EventDetail, RegistrationResult, TicketTier } from "../../wix/events/types";
9
+
10
+ export interface UseEventRegistration {
11
+ /** TICKETING: tiers for the picker — null while loading (skeletons), [] honest empty. Other types: []. */
12
+ tiers: TicketTier[] | null;
13
+ /** Selected quantity per tier id (0 when untouched). */
14
+ quantities: Record<string, number>;
15
+ /** Clamped to 0..limitPerCheckout; ignored for tiers not on sale (saleStatus gate). */
16
+ setQuantity: (tierId: string, quantity: number) => void;
17
+ ticketCount: number;
18
+ /** True when ≥ 1 ticket is selected — gate the checkout CTA on this. */
19
+ canCheckout: boolean;
20
+ /** Reserves + redirects to the Wix-hosted checkout. On "redirect" the browser is navigating. */
21
+ checkout: () => Promise<RegistrationResult>;
22
+ /** RSVP form state — the built-in fields, exactly these. */
23
+ rsvpValues: { firstName: string; lastName: string; email: string };
24
+ setRsvpValue: (field: "firstName" | "lastName" | "email", value: string) => void;
25
+ /** True when every RSVP field is filled — gate the RSVP CTA on this. */
26
+ canRsvp: boolean;
27
+ /** attending=false only when event.rsvpResponseType is "YES_AND_NO". */
28
+ rsvp: (attending?: boolean) => Promise<RegistrationResult>;
29
+ submitting: boolean;
30
+ /** Set after an RSVP completes (kind "rsvpConfirmed"; status may be "WAITLIST"). */
31
+ confirmed: RegistrationResult | null;
32
+ error: string | null;
33
+ }
34
+
35
+ export function useEventRegistration(event: EventDetail): UseEventRegistration {
36
+ const ticketed = event.registrationType === "TICKETING";
37
+ const [tiers, setTiers] = useState<TicketTier[] | null>(ticketed ? null : []);
38
+ const [quantities, setQuantities] = useState<Record<string, number>>({});
39
+ const [rsvpValues, setRsvpValues] = useState({ firstName: "", lastName: "", email: "" });
40
+ const [submitting, setSubmitting] = useState(false);
41
+ const [confirmed, setConfirmed] = useState<RegistrationResult | null>(null);
42
+ const [error, setError] = useState<string | null>(null);
43
+
44
+ useEffect(() => {
45
+ if (!ticketed) return;
46
+ let alive = true;
47
+ setTiers(null);
48
+ setQuantities({});
49
+ fetchTicketTiers(event.id)
50
+ .then((t) => alive && setTiers(t))
51
+ .catch((e) => {
52
+ if (!alive) return;
53
+ setTiers([]);
54
+ setError(e instanceof Error ? e.message : String(e));
55
+ });
56
+ return () => {
57
+ alive = false;
58
+ };
59
+ // eslint-disable-next-line react-hooks/exhaustive-deps
60
+ }, [event.id, ticketed]);
61
+
62
+ const setQuantity = (tierId: string, quantity: number) => {
63
+ const tier = (tiers ?? []).find((t) => t.id === tierId);
64
+ if (!tier || tier.saleStatus !== "SALE_STARTED") return;
65
+ const clamped = Math.max(0, Math.min(quantity, tier.limitPerCheckout || 20));
66
+ setQuantities((q) => ({ ...q, [tierId]: clamped }));
67
+ };
68
+
69
+ const ticketCount = useMemo(
70
+ () => Object.values(quantities).reduce((sum, q) => sum + q, 0),
71
+ [quantities],
72
+ );
73
+ const canRsvp = (["firstName", "lastName", "email"] as const).every(
74
+ (f) => rsvpValues[f].trim().length > 0,
75
+ );
76
+
77
+ async function run(op: () => Promise<RegistrationResult>): Promise<RegistrationResult> {
78
+ setSubmitting(true);
79
+ setError(null);
80
+ try {
81
+ const result = await op();
82
+ if (result.kind === "redirect") {
83
+ window.location.href = result.url;
84
+ } else {
85
+ setConfirmed(result);
86
+ }
87
+ return result;
88
+ } catch (e) {
89
+ setError(e instanceof Error ? e.message : String(e));
90
+ throw e;
91
+ } finally {
92
+ setSubmitting(false);
93
+ }
94
+ }
95
+
96
+ return {
97
+ tiers,
98
+ quantities,
99
+ setQuantity,
100
+ ticketCount,
101
+ canCheckout: ticketCount > 0,
102
+ checkout: () =>
103
+ run(() =>
104
+ startTicketCheckout(
105
+ event,
106
+ Object.entries(quantities).map(([tierId, quantity]) => ({ tierId, quantity })),
107
+ ),
108
+ ),
109
+ rsvpValues,
110
+ setRsvpValue: (field, value) => setRsvpValues((v) => ({ ...v, [field]: value })),
111
+ canRsvp,
112
+ rsvp: (attending = true) => run(() => submitRsvp(event.id, rsvpValues, attending ? "YES" : "NO")),
113
+ submitting,
114
+ confirmed,
115
+ error,
116
+ };
117
+ }
@@ -0,0 +1,59 @@
1
+ // Events listing + category filter. SSR-friendly: pass server-fetched data as `initialEvents`
2
+ // (Astro frontmatter / server component) and no client fetch happens; a SPA passes nothing.
3
+ // The category menu is DERIVED from the loaded events (the categories management API is
4
+ // admin-scope — visitors can't query it); filtering is client-side.
5
+ import { useEffect, useMemo, useState } from "react";
6
+ import { fetchEvents } from "../../wix/events/events";
7
+ import type { EventSummary } from "../../wix/events/types";
8
+
9
+ export interface UseEventsOptions {
10
+ initialEvents?: EventSummary[];
11
+ }
12
+
13
+ export interface UseEvents {
14
+ /** null while the first load is in flight — render skeletons, not an empty state. */
15
+ events: EventSummary[] | null;
16
+ /** Unique assigned categories in listing order — render a filter bar only when > 1. */
17
+ categories: { id: string; name: string }[];
18
+ activeCategoryId: string | null;
19
+ setActiveCategoryId: (id: string | null) => void;
20
+ error: string | null;
21
+ }
22
+
23
+ export function useEvents({ initialEvents }: UseEventsOptions = {}): UseEvents {
24
+ const [all, setAll] = useState<EventSummary[] | null>(initialEvents ?? null);
25
+ const [activeCategoryId, setActiveCategoryId] = useState<string | null>(null);
26
+ const [error, setError] = useState<string | null>(null);
27
+
28
+ useEffect(() => {
29
+ let alive = true;
30
+ if (!initialEvents) {
31
+ fetchEvents()
32
+ .then((e) => alive && setAll(e))
33
+ .catch((e) => {
34
+ if (!alive) return;
35
+ setAll([]);
36
+ setError(e instanceof Error ? e.message : String(e));
37
+ });
38
+ }
39
+ return () => {
40
+ alive = false;
41
+ };
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps
43
+ }, []);
44
+
45
+ const categories = useMemo(() => {
46
+ const seen = new Map<string, string>();
47
+ for (const e of all ?? []) for (const c of e.categories) if (!seen.has(c.id)) seen.set(c.id, c.name);
48
+ return [...seen.entries()].map(([id, name]) => ({ id, name }));
49
+ }, [all]);
50
+
51
+ const events = useMemo(() => {
52
+ if (all === null) return null;
53
+ return activeCategoryId
54
+ ? all.filter((e) => e.categories.some((c) => c.id === activeCategoryId))
55
+ : all;
56
+ }, [all, activeCategoryId]);
57
+
58
+ return { events, categories, activeCategoryId, setActiveCategoryId, error };
59
+ }
@@ -0,0 +1,136 @@
1
+ // Event reads (Wix Events V3, the `wixEventsV2` module) — the only file that touches raw
2
+ // event entities. Everything it returns is a plain DTO from ./types. Copy as-is; extend by
3
+ // adding functions, not by editing these.
4
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/query-events.md
5
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/events/event-management/events-v3/get-event-by-slug.md
6
+ import { wixEventsV2 } from "@wix/events";
7
+ import { wixModule } from "../sdk";
8
+ import { imgSrc } from "../media";
9
+ import type { EventDetail, EventSummary, RegistrationType } from "./types";
10
+
11
+ const events = wixModule(wixEventsV2);
12
+
13
+ /** The Wix Events app id (reference only — frontend calls need no app-id constant). */
14
+ export const EVENTS_APP_ID = "140603ad-af8d-84a5-2c80-a0f60cb47351";
15
+
16
+ type Raw = Record<string, any>;
17
+
18
+ // Fieldsets are opt-in: without REGISTRATION there's no registration type to branch on;
19
+ // without CATEGORIES the category names never arrive; DETAILS carries the formatted date
20
+ // and mainImage; TEXTS carries the long description.
21
+ const LIST_FIELDS = ["DETAILS", "REGISTRATION", "CATEGORIES"];
22
+ const DETAIL_FIELDS = ["DETAILS", "TEXTS", "REGISTRATION", "URLS", "CATEGORIES"];
23
+
24
+ function lowestPriceLabel(reg: Raw): string {
25
+ const type = reg.type ?? reg.initialType;
26
+ if (type === "RSVP") return "Free";
27
+ if (type !== "TICKETING") return "";
28
+ const lowest: Raw | undefined = reg.tickets?.lowestPrice;
29
+ if (!lowest) return "";
30
+ if (Number(lowest.value ?? 0) === 0) return "Free";
31
+ if (lowest.formattedValue) return `From ${lowest.formattedValue}`;
32
+ try {
33
+ return `From ${new Intl.NumberFormat(undefined, { style: "currency", currency: lowest.currency || "USD" }).format(Number(lowest.value))}`;
34
+ } catch {
35
+ return `From ${lowest.value} ${lowest.currency ?? ""}`.trim();
36
+ }
37
+ }
38
+
39
+ // event.description is Ricos rich content ({ nodes: [...] }), NOT a string — calling string
40
+ // methods on it crashes the page. Extract plain paragraphs; detailedDescription (legacy plain
41
+ // text) is the fallback.
42
+ function toParagraphs(rich: Raw | undefined, legacy: string | undefined): string[] {
43
+ const collect = (nodes: Raw[] | undefined): string =>
44
+ (nodes ?? [])
45
+ .map((n: Raw) => (typeof n.textData?.text === "string" ? n.textData.text : collect(n.nodes)))
46
+ .join("");
47
+ const out: string[] = [];
48
+ for (const node of rich?.nodes ?? []) {
49
+ const text = collect([node]).trim();
50
+ if (text) out.push(text);
51
+ }
52
+ if (!out.length && legacy) out.push(legacy);
53
+ return out;
54
+ }
55
+
56
+ function toSummary(raw: Raw): EventSummary {
57
+ const reg: Raw = raw.registration ?? {};
58
+ const dts: Raw = raw.dateAndTimeSettings ?? {};
59
+ return {
60
+ id: raw._id ?? "", // _id, never .id — `id` is a REST-view field and is undefined here
61
+ slug: raw.slug ?? "",
62
+ title: raw.title ?? "",
63
+ shortDescription: raw.shortDescription ?? "",
64
+ dateLabel: dts.formatted?.dateAndTime ?? "",
65
+ startDateIso: dts.startDate ? new Date(dts.startDate).toISOString() : "",
66
+ locationName: raw.location?.name ?? "",
67
+ locationType: raw.location?.locationTbd
68
+ ? "TBD"
69
+ : ((raw.location?.type as "VENUE" | "ONLINE") ?? "TBD"),
70
+ imageUrl: imgSrc(raw.mainImage, 1200, 800),
71
+ // `type` is the current flavor (can become EXTERNAL later); initialType is the immutable
72
+ // seeded one — read type first.
73
+ registrationType: (reg.type ?? reg.initialType ?? "NONE") as RegistrationType,
74
+ priceLabel: lowestPriceLabel(reg),
75
+ soldOut: reg.tickets?.soldOut === true,
76
+ // `categories` is absent on the typed Event (an SDK type gap) — the Raw boundary reads
77
+ // the runtime field the CATEGORIES fieldset populates.
78
+ categories: (raw.categories?.categories ?? [])
79
+ .map((c: Raw) => ({ id: c._id ?? "", name: c.name ?? "" }))
80
+ .filter((c: { id: string }) => c.id),
81
+ };
82
+ }
83
+
84
+ function toDetail(raw: Raw): EventDetail {
85
+ const summary = toSummary(raw);
86
+ const reg: Raw = raw.registration ?? {};
87
+ return {
88
+ ...summary,
89
+ aboutParagraphs: toParagraphs(raw.description, raw.detailedDescription),
90
+ // Only OPEN_* statuses (OPEN_RSVP, OPEN_TICKETS, OPEN_EXTERNAL, …) accept registrations.
91
+ registrationOpen: typeof reg.status === "string" && reg.status.startsWith("OPEN_"),
92
+ rsvpResponseType: reg.rsvp?.responseType === "YES_AND_NO" ? "YES_AND_NO" : "YES_ONLY",
93
+ externalUrl: reg.external?.url ?? "",
94
+ addToCalendar: { google: raw.calendarUrls?.google ?? "", ics: raw.calendarUrls?.ics ?? "" },
95
+ };
96
+ }
97
+
98
+ /** List live (UPCOMING/STARTED) events, soonest first, mapped to grid-ready DTOs. */
99
+ export async function fetchEvents({ limit = 100 } = {}): Promise<EventSummary[]> {
100
+ // The query builder, never a flat/REST `{ query: {...} }` body (the SDK silently ignores
101
+ // the unknown key and returns zero events). Always set a positive limit — it defaults to 0,
102
+ // which also returns zero events with no error.
103
+ const res = await events
104
+ .queryEvents({ fields: LIST_FIELDS as any })
105
+ .in("status", ["UPCOMING", "STARTED"]) // never list or link a past event — it isn't registerable
106
+ .ascending("dateAndTimeSettings.startDate")
107
+ .limit(limit)
108
+ .find();
109
+ return (res.items ?? []).map((e: Raw) => toSummary(e));
110
+ }
111
+
112
+ /** Fetch one event by its URL slug. Null when not found. */
113
+ export async function fetchEventBySlug(slug: string): Promise<EventDetail | null> {
114
+ try {
115
+ // getEventBySlug returns a WRAPPED { event } — unlike getEvent below.
116
+ const res: Raw = await events.getEventBySlug(slug, { fields: DETAIL_FIELDS as any });
117
+ const raw = res?.event;
118
+ return raw ? toDetail(raw as Raw) : null;
119
+ } catch {
120
+ return null;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Fetch one event by id — the post-checkout confirmation read (the thank-you URL carries
126
+ * `?orderNumber=&eventId=`). getEvent returns the Event DIRECTLY (unwrapped) — the one read
127
+ * that isn't `{ event }`; assuming the wrapper crashes the page.
128
+ */
129
+ export async function fetchEventById(eventId: string): Promise<EventDetail | null> {
130
+ try {
131
+ const raw: Raw = await events.getEvent(eventId, { fields: DETAIL_FIELDS as any });
132
+ return raw?._id ? toDetail(raw) : null;
133
+ } catch {
134
+ return null;
135
+ }
136
+ }