@wix/agent-skills 1.15.1 → 1.16.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 (332) hide show
  1. package/README.md +3 -7
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +4 -1
  4. package/skills/wix-app/references/APP_TOOLS.md +134 -0
  5. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +1 -1
  6. package/skills/wix-app/references/DASHBOARD_PAGE.md +0 -10
  7. package/skills/wix-app/references/DATA_COLLECTION.md +1 -1
  8. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +42 -8
  9. package/skills/wix-app/references/SERVICE_PLUGIN.md +12 -10
  10. package/skills/wix-app/references/app-tools/TOOLS.md +75 -0
  11. package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +2 -2
  12. package/skills/wix-app/references/editor-react-component/A11Y-REVIEW-REFERENCE.md +172 -0
  13. package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +143 -0
  14. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +23 -2
  15. package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +29 -0
  16. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +1 -1
  17. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +60 -0
  18. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +27 -2
  19. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +5 -3
  20. package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +24 -7
  21. package/skills/wix-app/references/editor-react-component/PARTS.md +15 -2
  22. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +2 -3
  23. package/skills/wix-app/references/editor-react-component/SITE-CONTEXT-HOOKS.md +187 -0
  24. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +13 -9
  25. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +4 -0
  26. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
  27. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +3 -1
  28. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +4 -0
  29. package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +54 -0
  30. package/skills/wix-app/references/stores/QUERY.md +32 -0
  31. package/skills/wix-app/scripts/scan-a11y-code.cjs +777 -0
  32. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +201 -0
  33. package/skills/wix-docs/SKILL.md +35 -3
  34. package/skills/wix-headless/SKILL.md +3 -1
  35. package/skills/wix-headless/references/CAPABILITIES.md +16 -3
  36. package/skills/wix-headless/references/CONTENT.md +36 -0
  37. package/skills/wix-headless/references/DESIGN.md +54 -0
  38. package/skills/wix-headless/references/DISCOVERY.md +24 -0
  39. package/skills/wix-headless/references/IMAGE_GENERATION.md +10 -0
  40. package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
  41. package/skills/wix-headless/references/SEED.md +1 -1
  42. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +3 -2
  43. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +560 -102
  44. package/skills/wix-headless/references/inline-recipes/setup-forms.md +210 -143
  45. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +32 -23
  46. package/skills/wix-headless/references/managed/CONNECT.md +1 -1
  47. package/skills/wix-headless/references/managed/CREATE.md +14 -0
  48. package/skills/wix-headless/references/managed/DEPLOYMENT.md +25 -0
  49. package/skills/wix-manage/SKILL.md +130 -7
  50. package/skills/wix-manage/references/accessibility/scan-site-accessibility.md +98 -0
  51. package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +40 -0
  52. package/skills/wix-manage/references/app-installation/app-installation-dashboard-navigation.md +28 -0
  53. package/skills/wix-manage/references/blog/blog-dashboard-navigation.md +56 -0
  54. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +31 -6
  55. package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +3 -1
  56. package/skills/wix-manage/references/bookings/bookings-dashboard-navigation.md +63 -0
  57. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +10 -1
  58. package/skills/wix-manage/references/cms/cms-dashboard-navigation.md +44 -0
  59. package/skills/wix-manage/references/cms/cms-data-items-crud.md +31 -3
  60. package/skills/wix-manage/references/cms/cms-publishing-flow.md +287 -0
  61. package/skills/wix-manage/references/cms/cms-schema-management.md +27 -1
  62. package/skills/wix-manage/references/contacts/contacts-dashboard-navigation.md +46 -0
  63. package/skills/wix-manage/references/contacts/create-a-contact.md +82 -0
  64. package/skills/wix-manage/references/contacts/update-a-contact.md +119 -0
  65. package/skills/wix-manage/references/dashboard-navigation/dashboard-navigation.md +82 -0
  66. package/skills/wix-manage/references/dashboard-navigation/routes.json +1733 -0
  67. package/skills/wix-manage/references/domains/domains-dashboard-navigation.md +32 -0
  68. package/skills/wix-manage/references/ecommerce/ecom-pricing.md +4 -4
  69. package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
  70. package/skills/wix-manage/references/ecommerce/gift-cards/ecom-gift-cards-goal-sell-gift-cards.md +338 -0
  71. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-discount-rule.md +42 -78
  72. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-bundle-and-save.md +1 -1
  73. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-stock-mover.md +1 -1
  74. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-upsell-boost.md +1 -1
  75. package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +90 -92
  76. package/skills/wix-manage/references/events/create-wix-event.md +176 -0
  77. package/skills/wix-manage/references/events/manage-wix-events.md +174 -0
  78. package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +44 -0
  79. package/skills/wix-manage/references/get-paid/get-paid-dashboard-navigation.md +52 -0
  80. package/skills/wix-manage/references/google-ads/billing-and-payment.md +80 -0
  81. package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +212 -0
  82. package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +110 -0
  83. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +25 -0
  84. package/skills/wix-manage/references/google-ads/install-and-create-account.md +155 -0
  85. package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +63 -0
  86. package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +155 -0
  87. package/skills/wix-manage/references/marketing/marketing-dashboard-navigation.md +52 -0
  88. package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +99 -1
  89. package/skills/wix-manage/references/pricing-plans/pricing-plans-dashboard-navigation.md +45 -0
  90. package/skills/wix-manage/references/restaurants/restaurants-dashboard-navigation.md +68 -0
  91. package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +94 -125
  92. package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +48 -9
  93. package/skills/wix-manage/references/site-properties/site-properties-dashboard-navigation.md +31 -0
  94. package/skills/wix-manage/references/sites/create-headless-site.md +52 -0
  95. package/skills/wix-manage/references/sites/create-site-from-template.md +2 -25
  96. package/skills/wix-manage/references/sites/site-import.md +217 -192
  97. package/skills/wix-manage/references/sites/sites-dashboard-navigation.md +29 -0
  98. package/skills/wix-manage/references/stores/add-store-pages-to-site.md +17 -1
  99. package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +26 -8
  100. package/skills/wix-manage/references/stores/stores-dashboard-navigation.md +65 -0
  101. package/skills/wix-manage/references/stores/update-product-with-options.md +39 -7
  102. package/skills/wix-replatform/.publish-manifest.json +8 -0
  103. package/skills/wix-replatform/SKILL.md +693 -0
  104. package/skills/wix-replatform/lib/artifact-freshness.js +215 -0
  105. package/skills/wix-replatform/lib/completion-report.js +200 -0
  106. package/skills/wix-replatform/lib/config-env.js +59 -0
  107. package/skills/wix-replatform/lib/execution-manifest.js +185 -0
  108. package/skills/wix-replatform/lib/execution-state.js +173 -0
  109. package/skills/wix-replatform/lib/import-recovery.js +313 -0
  110. package/skills/wix-replatform/lib/local-state.js +642 -0
  111. package/skills/wix-replatform/lib/orchestration-preflight.js +143 -0
  112. package/skills/wix-replatform/lib/orchestration-router.js +122 -0
  113. package/skills/wix-replatform/lib/orchestration-state.js +455 -0
  114. package/skills/wix-replatform/lib/progress-log.js +224 -0
  115. package/skills/wix-replatform/lib/progress-log.sh +140 -0
  116. package/skills/wix-replatform/lib/url-preservation-state.js +391 -0
  117. package/skills/wix-replatform/resources/rp-discovery/SKILL.md +184 -0
  118. package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +529 -0
  119. package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +316 -0
  120. package/skills/wix-replatform/resources/rp-execute-setup/scripts/mint-token.sh +64 -0
  121. package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +1114 -0
  122. package/skills/wix-replatform/resources/rp-mapper/SKILL.md +430 -0
  123. package/skills/wix-replatform/resources/rp-mapper/lib/mapping-resolve.js +217 -0
  124. package/skills/wix-replatform/resources/rp-mapper/scripts/resolve-mapping.js +148 -0
  125. package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +378 -0
  126. package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +183 -0
  127. package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +478 -0
  128. package/skills/wix-replatform/resources/rp-source-csv/lib/canonical-record.js +136 -0
  129. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
  130. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fingerprint.js +310 -0
  131. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-layout.js +1081 -0
  132. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-parse.js +618 -0
  133. package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +949 -0
  134. package/skills/wix-replatform/resources/rp-source-csv/source-schema.example.json +196 -0
  135. package/skills/wix-replatform/resources/rp-source-csv/vendors/bigcommerce.json +66 -0
  136. package/skills/wix-replatform/resources/rp-source-csv/vendors/magento.json +60 -0
  137. package/skills/wix-replatform/resources/rp-source-csv/vendors/shopify.json +95 -0
  138. package/skills/wix-replatform/resources/rp-source-csv/vendors/woocommerce.json +82 -0
  139. package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +256 -0
  140. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +249 -0
  141. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +444 -0
  142. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +774 -0
  143. package/skills/wix-replatform/resources/rp-source-wordpress/source-schema.example.json +71 -0
  144. package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +632 -0
  145. package/skills/wix-replatform/resources/rp-target-wix/SPEC-product-reviews-and-blog-authorship.md +330 -0
  146. package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +21 -0
  147. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +19 -0
  148. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +37 -0
  149. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +37 -0
  150. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +39 -0
  151. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +35 -0
  152. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +14 -0
  153. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +35 -0
  154. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +36 -0
  155. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +35 -0
  156. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +14 -0
  157. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +131 -0
  158. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +37 -0
  159. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +37 -0
  160. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +21 -0
  161. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +37 -0
  162. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +37 -0
  163. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +97 -0
  164. package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +528 -0
  165. package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +14 -0
  166. package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +38 -0
  167. package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +37 -0
  168. package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +13 -0
  169. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +37 -0
  170. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +53 -0
  171. package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +84 -0
  172. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +25 -0
  173. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +37 -0
  174. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +37 -0
  175. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +38 -0
  176. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +37 -0
  177. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +90 -0
  178. package/skills/wix-replatform/resources/rp-target-wix/lib/audit-summary.js +64 -0
  179. package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +141 -0
  180. package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +384 -0
  181. package/skills/wix-replatform/resources/rp-target-wix/lib/ndjson.js +260 -0
  182. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +238 -0
  183. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +237 -0
  184. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +238 -0
  185. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +2219 -0
  186. package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +31 -0
  187. package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge.js +100 -0
  188. package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +107 -0
  189. package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +344 -0
  190. package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +250 -0
  191. package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +1927 -0
  192. package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +83 -0
  193. package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +141 -0
  194. package/skills/wix-replatform/scripts/artifact-freshness.js +76 -0
  195. package/skills/wix-replatform/scripts/execution-manifest-validate.js +43 -0
  196. package/skills/wix-replatform/scripts/execution-state-prepare.js +128 -0
  197. package/skills/wix-replatform/scripts/orchestration-preflight.js +48 -0
  198. package/skills/wix-replatform/scripts/orchestration-route.js +46 -0
  199. package/skills/wix-replatform/scripts/orchestration-state.js +245 -0
  200. package/skills/wix-vibe-headless/SKILL.md +49 -25
  201. package/skills/wix-vibe-headless/install/deploy.cjs +156 -0
  202. package/skills/wix-vibe-headless/install/pin-agents-md.cjs +34 -0
  203. package/skills/wix-vibe-headless/platforms/README.md +8 -4
  204. package/skills/wix-vibe-headless/platforms/base44.md +111 -101
  205. package/skills/wix-vibe-headless/platforms/generic.md +99 -64
  206. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +226 -119
  207. package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +43 -0
  208. package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +25 -0
  209. package/skills/wix-vibe-headless/references/blog/app/components/PostGrid.jsx +15 -0
  210. package/skills/wix-vibe-headless/references/blog/app/context/TaxonomyContext.jsx +35 -0
  211. package/skills/wix-vibe-headless/references/blog/app/hooks/usePostDetail.js +38 -0
  212. package/skills/wix-vibe-headless/references/blog/app/pages/Blog.jsx +40 -0
  213. package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +54 -0
  214. package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +65 -0
  215. package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +50 -0
  216. package/skills/wix-vibe-headless/references/blog/seed/SEED.md +103 -0
  217. package/skills/wix-vibe-headless/references/blog/seed/seed-blog.js +270 -0
  218. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +256 -149
  219. package/skills/wix-vibe-headless/references/bookings/app/components/BookingForm.jsx +65 -0
  220. package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +47 -0
  221. package/skills/wix-vibe-headless/references/bookings/app/components/ServiceGrid.jsx +13 -0
  222. package/skills/wix-vibe-headless/references/bookings/app/components/SlotPicker.jsx +126 -0
  223. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +108 -0
  224. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServices.js +58 -0
  225. package/skills/wix-vibe-headless/references/bookings/app/lib/serviceFacts.js +95 -0
  226. package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +94 -0
  227. package/skills/wix-vibe-headless/references/bookings/app/pages/Services.jsx +89 -0
  228. package/skills/wix-vibe-headless/references/bookings/{wix-bookings-checkout.js → app/rest/wix-bookings-checkout.js} +1 -1
  229. package/skills/wix-vibe-headless/references/bookings/{wix-bookings-services.js → app/rest/wix-bookings-services.js} +87 -11
  230. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +117 -0
  231. package/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.js +333 -0
  232. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +168 -147
  233. package/skills/wix-vibe-headless/references/cms/app/collection.config.js +31 -0
  234. package/skills/wix-vibe-headless/references/cms/app/lib/wixImage.js +18 -0
  235. package/skills/wix-vibe-headless/references/cms/{wix-cms.js → app/rest/wix-cms.js} +47 -2
  236. package/skills/wix-vibe-headless/references/cms/seed/SEED.md +145 -0
  237. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +223 -131
  238. package/skills/wix-vibe-headless/references/events/app/components/CategoryFilter.jsx +23 -0
  239. package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +43 -0
  240. package/skills/wix-vibe-headless/references/events/app/components/EventGrid.jsx +13 -0
  241. package/skills/wix-vibe-headless/references/events/app/components/EventRegistration.jsx +29 -0
  242. package/skills/wix-vibe-headless/references/events/app/components/RsvpForm.jsx +57 -0
  243. package/skills/wix-vibe-headless/references/events/app/components/TicketPicker.jsx +78 -0
  244. package/skills/wix-vibe-headless/references/events/app/hooks/useEventDetail.js +27 -0
  245. package/skills/wix-vibe-headless/references/events/app/hooks/useEventsList.js +54 -0
  246. package/skills/wix-vibe-headless/references/events/app/hooks/useRsvpForm.js +40 -0
  247. package/skills/wix-vibe-headless/references/events/app/hooks/useTicketing.js +81 -0
  248. package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +48 -0
  249. package/skills/wix-vibe-headless/references/events/app/pages/Events.jsx +36 -0
  250. package/skills/wix-vibe-headless/references/events/{wix-events-browse.js → app/rest/wix-events-browse.js} +1 -1
  251. package/skills/wix-vibe-headless/references/events/seed/SEED.md +107 -0
  252. package/skills/wix-vibe-headless/references/events/seed/seed-events.js +251 -0
  253. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +272 -188
  254. package/skills/wix-vibe-headless/references/members/app/components/LoginForm.jsx +101 -0
  255. package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +33 -0
  256. package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +21 -0
  257. package/skills/wix-vibe-headless/references/members/app/components/SocialButtons.jsx +29 -0
  258. package/skills/wix-vibe-headless/references/members/app/context/MemberContext.jsx +54 -0
  259. package/skills/wix-vibe-headless/references/members/app/hooks/useLoginForm.js +77 -0
  260. package/skills/wix-vibe-headless/references/members/app/pages/Account.jsx +36 -0
  261. package/skills/wix-vibe-headless/references/members/app/pages/Callback.jsx +32 -0
  262. package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +35 -0
  263. package/skills/wix-vibe-headless/references/members/{wix-members-auth.js → app/rest/wix-members-auth.js} +6 -3
  264. package/skills/wix-vibe-headless/references/members/install/Register.jsx +22 -0
  265. package/skills/wix-vibe-headless/references/members/seed/SEED.md +22 -0
  266. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +229 -104
  267. package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +31 -0
  268. package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionGrid.jsx +15 -0
  269. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +39 -0
  270. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectGrid.jsx +15 -0
  271. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectMedia.jsx +29 -0
  272. package/skills/wix-vibe-headless/references/portfolio/app/hooks/useCollectionProjects.js +35 -0
  273. package/skills/wix-vibe-headless/references/portfolio/app/hooks/usePortfolioGallery.js +27 -0
  274. package/skills/wix-vibe-headless/references/portfolio/app/hooks/useProjectDetail.js +25 -0
  275. package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +39 -0
  276. package/skills/wix-vibe-headless/references/portfolio/app/pages/Portfolio.jsx +27 -0
  277. package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +60 -0
  278. package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +94 -0
  279. package/skills/wix-vibe-headless/references/portfolio/seed/seed-portfolio.js +236 -0
  280. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +236 -117
  281. package/skills/wix-vibe-headless/references/pricing-plans/app/components/OrderCard.jsx +35 -0
  282. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +48 -0
  283. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanGrid.jsx +16 -0
  284. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanPrice.jsx +35 -0
  285. package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/useMyPlans.js +25 -0
  286. package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlanDetail.js +28 -0
  287. package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlans.js +30 -0
  288. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/MyPlans.jsx +30 -0
  289. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +53 -0
  290. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/Plans.jsx +28 -0
  291. package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +86 -0
  292. package/skills/wix-vibe-headless/references/pricing-plans/seed/seed-pricing-plans.js +251 -0
  293. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +244 -124
  294. package/skills/wix-vibe-headless/references/restaurants/app/components/ItemDialog.jsx +81 -0
  295. package/skills/wix-vibe-headless/references/restaurants/app/components/MenuItemCard.jsx +71 -0
  296. package/skills/wix-vibe-headless/references/restaurants/app/components/MenuList.jsx +43 -0
  297. package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartButton.jsx +21 -0
  298. package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +60 -0
  299. package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +63 -0
  300. package/skills/wix-vibe-headless/references/restaurants/app/hooks/useItemOrder.js +32 -0
  301. package/skills/wix-vibe-headless/references/restaurants/app/hooks/useReservation.js +73 -0
  302. package/skills/wix-vibe-headless/references/restaurants/app/pages/Menu.jsx +28 -0
  303. package/skills/wix-vibe-headless/references/restaurants/app/pages/Reservations.jsx +102 -0
  304. package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +180 -0
  305. package/skills/wix-vibe-headless/references/restaurants/seed/seed-restaurants.js +452 -0
  306. package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +56 -0
  307. package/skills/wix-vibe-headless/references/shared/{wix-client.js → app/rest/wix-client.js} +6 -8
  308. package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +8 -0
  309. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +259 -157
  310. package/skills/wix-vibe-headless/references/storefront/app/components/CartButton.jsx +20 -0
  311. package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +150 -0
  312. package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +167 -0
  313. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +51 -0
  314. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +54 -0
  315. package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +119 -0
  316. package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +71 -0
  317. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +109 -0
  318. package/skills/wix-vibe-headless/references/storefront/app/hooks/useShop.js +98 -0
  319. package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +27 -0
  320. package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +139 -0
  321. package/skills/wix-vibe-headless/references/storefront/app/pages/Shop.jsx +75 -0
  322. package/skills/wix-vibe-headless/references/storefront/{wix-store-cart.js → app/rest/wix-store-cart.js} +1 -1
  323. package/skills/wix-vibe-headless/references/storefront/{wix-store-catalog.js → app/rest/wix-store-catalog.js} +20 -1
  324. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +111 -0
  325. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +338 -0
  326. /package/skills/wix-vibe-headless/references/blog/{wix-blog.js → app/rest/wix-blog.js} +0 -0
  327. /package/skills/wix-vibe-headless/references/events/{wix-events-registration.js → app/rest/wix-events-registration.js} +0 -0
  328. /package/skills/wix-vibe-headless/references/portfolio/{wix-portfolio.js → app/rest/wix-portfolio.js} +0 -0
  329. /package/skills/wix-vibe-headless/references/pricing-plans/{wix-pricing-plans.js → app/rest/wix-pricing-plans.js} +0 -0
  330. /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-menu.js → app/rest/wix-restaurants-menu.js} +0 -0
  331. /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-ordering.js → app/rest/wix-restaurants-ordering.js} +0 -0
  332. /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-reservations.js → app/rest/wix-restaurants-reservations.js} +0 -0
@@ -9,27 +9,55 @@ import { wixApiRequest } from "./wix-client.js";
9
9
  *
10
10
  * id {string}, type "APPOINTMENT"|"CLASS"|"COURSE", name {string}, tagLine {string},
11
11
  * description {string}, hidden {boolean},
12
- * media.items {array} — [{ image: { id, url, width, height, altText } }]
13
- * (url may be a bare Wix media handle — pass through mediaUrl() before rendering),
14
- * payment.rateType "FIXED"|"CUSTOM"|"VARIED"|"NO_FEE"|"SUBSCRIPTION",
12
+ * defaultCapacity {number} — bookable seats per session; 1 for a typical appointment,
13
+ * media.mainMedia.image { id, url, width, height, altText } — the PRIMARY service image (what the
14
+ * seed sets and the card shows); media.items {array} is the gallery, media.coverMedia the cover.
15
+ * Read mainMedia first, fall back to items[0]/coverMedia. url may be a bare Wix media handle —
16
+ * pass through mediaUrl() before rendering,
17
+ * mainSlug.name {string} — merchant-facing slug ("swedish-massage"). This client routes on the
18
+ * service id; switching to slugs means changing the shipped route too,
19
+ * payment.rateType "FIXED"|"CUSTOM"|"VARIED"|"NO_FEE"|"SUBSCRIPTION" — branch on this before
20
+ * reading a price. Use servicePriceLabel() from lib/serviceFacts.js instead of payment.fixed
21
+ * directly, or VARIED and NO_FEE services render with no price,
15
22
  * payment.fixed.price { value, currency, formattedValue } — value + currency are always
16
23
  * present; formattedValue is optional (may be missing), so format the price from
17
24
  * value+currency and use formattedValue only when present,
18
- * payment.options { online, inPerson, deposit, pricingPlan },
19
- * schedule.id {string}, onlineBooking.enabled {boolean},
20
- * category { id, name }, staffMemberIds {string[]}, locations {array},
25
+ * payment.varied { defaultPrice, minPrice, maxPrice } — Money each, same shape as fixed.price,
26
+ * payment.options { online, inPerson, deposit, pricingPlan } — how a customer may PAY. `online`
27
+ * here means "can pay online", NOT that the service happens online; don't build a location
28
+ * label from it,
29
+ * conferencing.enabled {boolean} — a video service; this is the real "online" signal,
30
+ * schedule.id {string},
31
+ * schedule.availabilityConstraints.sessionDurations {number[]} — session minutes for an
32
+ * APPOINTMENT, empty for CLASS/COURSE; see serviceDuration(),
33
+ * onlineBooking { enabled, requireManualApproval } — requireManualApproval means the business
34
+ * confirms the booking after the fact, so the CTA is a request, not a confirmation,
35
+ * category { id, name } — a service with no category is not visible on the live site,
36
+ * staffMemberIds {string[]},
37
+ * locations {array} — [{ id, type "BUSINESS"|"CUSTOMER"|"CUSTOM", calculatedAddress }]. The type is
38
+ * where the service happens; calculatedAddress is empty for CUSTOMER and often absent until the
39
+ * business configures a location. See serviceLocationLabel(),
21
40
  * bookingPolicy.participantsPolicy { enabled {boolean}, maxParticipantsPerBooking {number} }
22
41
  * — the MOST participants a single booking may reserve. Cap the participant selector at this
23
42
  * value (for a CLASS, also bound by the slot's remainingCapacity). It is commonly 1, in which
24
43
  * case there is no participant choice at all — book exactly 1. Sending totalParticipants above
25
44
  * this makes createBooking fail.
45
+ * bookingPolicy.reschedulePolicy / .cancellationPolicy { enabled, limitLatestReschedule /
46
+ * limitLatestCancellation, latestRescheduleInMinutes / latestCancellationInMinutes } — see
47
+ * serviceRescheduleNote(),
26
48
  *
27
49
  * TimeSlot (Time Slots V2, appointments):
28
50
  * serviceId {string}, localStartDate {string}, localEndDate {string} — "YYYY-MM-DDThh:mm:ss" (no zone),
29
51
  * bookable {boolean}, scheduleId {string},
30
52
  * location { id, name, formattedAddress, locationType "BUSINESS"|"CUSTOM"|"CUSTOMER" },
31
53
  * totalCapacity {number}, remainingCapacity {number},
32
- * availableResources {array} — staff (populated by getAvailableSlot, not listAvailableSlots)
54
+ * bookableCapacity {number} — CLASS slots: seats still bookable after policy limits, which can be
55
+ * lower than remainingCapacity,
56
+ * bookingPolicyViolations { tooEarlyToBook, tooLateToBook, bookOnlineDisabled } — why a listed slot
57
+ * still isn't bookable; only reachable if you drop the `bookable: true` filter,
58
+ * availableResources {array} — staff. getAvailableSlot populates it for an APPOINTMENT; a
59
+ * CLASS/COURSE slot already carries it from the list call,
60
+ * eventInfo { eventId, eventTitle } — CLASS/COURSE only; those slots have no scheduleId
33
61
  * Full model: https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
34
62
  */
35
63
 
@@ -81,6 +109,48 @@ export async function queryServices({ limit = 100, offset = 0 } = {}) {
81
109
  return { services, total, nextOffset: loaded < total ? loaded : null };
82
110
  }
83
111
 
112
+ /**
113
+ * Query visitor-visible services in one category, for a category-filtered listing.
114
+ * The filter key is the dotted path `category.id` — a bare `categoryId` is rejected as an unknown
115
+ * field.
116
+ * @param {string} categoryId
117
+ * @param {{ limit?: number, offset?: number }} [options]
118
+ * @returns {Promise<{ services: object[], total: number, nextOffset: number|null }>}
119
+ */
120
+ export async function queryServicesByCategory(categoryId, { limit = 100, offset = 0 } = {}) {
121
+ const res = await wixApiRequest("/bookings/v2/services/query", {
122
+ method: "POST",
123
+ body: {
124
+ query: {
125
+ filter: { hidden: false, "category.id": categoryId },
126
+ paging: { limit, offset },
127
+ },
128
+ },
129
+ });
130
+ const services = res?.services ?? [];
131
+ const total = res?.pagingMetadata?.total ?? services.length;
132
+ const loaded = offset + services.length;
133
+ return { services, total, nextOffset: loaded < total ? loaded : null };
134
+ }
135
+
136
+ /**
137
+ * The categories that visitor-visible services actually belong to, in the order Wix sorts them.
138
+ * Derived from a services page rather than the Categories API, so the menu can never list a category
139
+ * with nothing bookable in it.
140
+ * @param {object[]} services
141
+ * @returns {{ id: string, name: string }[]}
142
+ */
143
+ export function categoriesOf(services) {
144
+ const byId = new Map();
145
+ for (const s of services || []) {
146
+ const c = s?.category;
147
+ if (c?.id && !byId.has(c.id)) byId.set(c.id, { id: c.id, name: c.name, sortOrder: c.sortOrder ?? 0 });
148
+ }
149
+ return [...byId.values()]
150
+ .sort((a, b) => a.sortOrder - b.sortOrder)
151
+ .map(({ id, name }) => ({ id, name }));
152
+ }
153
+
84
154
  /**
85
155
  * Fetch a single service by its GUID. Returns null if not found.
86
156
  * @param {string} serviceId
@@ -115,7 +185,7 @@ export async function countServices() {
115
185
  * https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
116
186
  * @param {string} serviceId
117
187
  * @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
118
- * @returns {Promise<{ slots: object[], nextCursor: string|null }>}
188
+ * @returns {Promise<{ slots: object[], nextCursor: string|null, timeZone: string|null }>}
119
189
  */
120
190
  export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
121
191
  if (!cursor && (!fromLocalDate || !toLocalDate)) {
@@ -135,6 +205,9 @@ export async function listAvailableSlots(serviceId, { fromLocalDate, toLocalDate
135
205
  return {
136
206
  slots: res?.timeSlots ?? [],
137
207
  nextCursor: res?.cursorPagingMetadata?.cursors?.next ?? null,
208
+ // The zone the returned localStartDate strings are in — label the picker with this, so the times
209
+ // on screen and the zone named beside them can't disagree.
210
+ timeZone: res?.timeZone ?? null,
138
211
  };
139
212
  }
140
213
 
@@ -173,7 +246,7 @@ export async function getAvailableSlot(serviceId, { localStartDate, localEndDate
173
246
  * https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-event-time-slots.md
174
247
  * @param {string|string[]} serviceIds One service id or several (the endpoint takes a plural list).
175
248
  * @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
176
- * @returns {Promise<{ slots: object[], nextCursor: string|null }>}
249
+ * @returns {Promise<{ slots: object[], nextCursor: string|null, timeZone: string|null }>}
177
250
  */
178
251
  export async function listEventTimeSlots(serviceIds, { fromLocalDate, toLocalDate, timeZone, limit = 1000, cursor } = {}) {
179
252
  if (!cursor && (!fromLocalDate || !toLocalDate)) {
@@ -193,7 +266,10 @@ export async function listEventTimeSlots(serviceIds, { fromLocalDate, toLocalDat
193
266
  const res = await wixApiRequest("/_api/service-availability/v2/time-slots/event", { method: "POST", body });
194
267
  return {
195
268
  slots: res?.timeSlots ?? [],
196
- nextCursor: res?.cursorPagingMetadata?.cursors?.next ?? null,
269
+ // This endpoint nests the cursor under `pagingMetadata`, where the appointment one uses
270
+ // `cursorPagingMetadata` — reading only the latter silently disables paging for classes.
271
+ nextCursor: res?.pagingMetadata?.cursors?.next ?? res?.cursorPagingMetadata?.cursors?.next ?? null,
272
+ timeZone: res?.timeZone ?? null,
197
273
  };
198
274
  }
199
275
 
@@ -206,7 +282,7 @@ export async function listEventTimeSlots(serviceIds, { fromLocalDate, toLocalDat
206
282
  * here — the per-slot booking flow applies to APPOINTMENT and CLASS.
207
283
  * @param {{ _id?: string, id?: string, type?: string }} service A service from queryServices.
208
284
  * @param {{ fromLocalDate: string, toLocalDate: string, timeZone?: string, limit?: number, cursor?: string }} options
209
- * @returns {Promise<{ slots: object[], nextCursor: string|null }>}
285
+ * @returns {Promise<{ slots: object[], nextCursor: string|null, timeZone: string|null }>}
210
286
  */
211
287
  export async function listSlotsForService(service, options = {}) {
212
288
  const serviceId = service?._id || service?.id;
@@ -0,0 +1,117 @@
1
+ # Bookings — seeding
2
+
3
+ Seed a Wix Bookings catalog by **calling `seed-bookings.js`** — don't hand-write the REST calls.
4
+ It's a build-time module (run via `exec_tool`, not shipped in the app) that abstracts every Wix
5
+ Bookings seed operation. Load it and call **`setupBookings` — the one-call path** — with plain data.
6
+
7
+ > **Transcribed from `wix-headless/references/inline-recipes/setup-bookings.md` — NOT yet live-verified.**
8
+
9
+ ```js
10
+ // build-time exec_tool
11
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
12
+ const fs = require("fs");
13
+ // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
14
+ const seed = (() => { const m = { exports: {} };
15
+ new Function("module", "exports", "require", fs.readFileSync("/app/.agents/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.js", "utf8"))(m, m.exports, require);
16
+ return m.exports; })();
17
+ const ctx = { token: accessToken, siteId: WIX_METASITE_ID };
18
+
19
+ // ONE call: install → resolve staff → categories → services → CLASS sessions → images, all in the
20
+ // correct order with ids kept in memory (no hand-threading of scheduleId/resourceId). `category` is
21
+ // a NAME (resolved to an id internally). Free service: set free:true, omit price. CLASS `sessions`
22
+ // are local "YYYY-MM-DDThh:mm:ss".
23
+ const result = await seed.setupBookings(ctx, {
24
+ services: [
25
+ // `imageUrl` per service is optional and attached IN this one call. Pass a plain url — the module
26
+ // imports it into Wix Media for you (bookings binds a service image by the Wix Media file id, not a
27
+ // url). Use the FINAL https://media.base44.com/... url from the COMPLETED generate_image (it runs in
28
+ // the background while you build — wait for it), never a still-generating /__generating__/<id>.png.
29
+ { type: "APPOINTMENT", name: "Consultation", description: "…", tagLine: "…", price: 75, duration: 60, category: "Our Services", imageUrl: "https://media.base44.com/…" },
30
+ { type: "CLASS", name: "Morning Yoga", description: "…", price: 20, capacity: 20, category: "Our Services",
31
+ sessions: [{ start: "2026-08-10T09:00:00", end: "2026-08-10T10:00:00" }] },
32
+ ],
33
+ // staffResourceId defaults to the fresh install's owner (queryStaff()[0].resourceId).
34
+ });
35
+ // result: { services:[...], categories:[{id,name}], resourceId, sessionsScheduled, imagesAttached }
36
+
37
+ // fallback (finer control / re-attach): import the url to Wix Media, then patch one service after the fact.
38
+ // const file = await seed.importImage(ctx, imageUrl); // → { id, url } (Wix Media file id + wixstatic url)
39
+ // await seed.attachServiceImage(ctx, { serviceId: s.id, revision: s.revision, image: { id: file.id, url: file.url, width: 1024, height: 1024 } });
40
+ ```
41
+
42
+ **Seeding is additive — never delete or overwrite existing content.** Don't clean up, don't remove
43
+ "sample" data, don't reset. Just add.
44
+
45
+ ## What the shipped UI renders from this
46
+
47
+ Pass these and the catalog reads like a real one; omit them and the pages have visible holes:
48
+
49
+ - **`sessions` on every CLASS.** A class's bookable times ARE its sessions, so a class seeded without
50
+ them shows "No available times in this range" — which reads as broken, not empty. Schedule a few
51
+ weeks out. An APPOINTMENT needs none: its slots come from staff working hours, which a fresh
52
+ install's owner already has.
53
+ - **`duration`** (APPOINTMENT, minutes) → the card's `60 min · 1-to-1` line and the DURATION cell on
54
+ the service page. A class has no service-level duration and shows CAPACITY instead, so pass
55
+ `capacity` there.
56
+ - **`tagLine`** → the card's second line. One line about the service, not a copy of `description`.
57
+ - **`price`**, or **`free: true`** for a no-fee service, which the UI labels "Free".
58
+
59
+ Two things this module can't set, so don't try: **varied pricing** (a "From €85" service needs
60
+ `payment.varied`) and **`conferencing`** (the UI's "Online" location label reads that flag — seeded
61
+ services all sit at the business location, which shows as "In person").
62
+
63
+ ## Escape hatch — individual functions
64
+ Reach for the functions below only when the one-call `setupBookings` doesn't fit (partial re-seed,
65
+ custom staff/ordering). `setupBookings` is built from them, in this order:
66
+
67
+ ```js
68
+ await seed.installBookingsApp(ctx);
69
+ const staff = await seed.queryStaffWithRetry(ctx); // → [{resourceId,id,name}]; polls (fresh install provisions the owner async); use resourceId, NOT id
70
+ const cats = await seed.createCategories(ctx, ["Our Services"]); // → [{id,name}]; every service needs a categoryId
71
+ const services = await seed.createServices(ctx, [ // → [{id,slug,revision,type,scheduleId}]
72
+ { type: "APPOINTMENT", name: "Consultation", price: 75, duration: 60, categoryId: cats[0].id, staffMemberIds: [staff[0].resourceId] },
73
+ { type: "CLASS", name: "Morning Yoga", price: 20, capacity: 20, categoryId: cats[0].id },
74
+ ]);
75
+ // CLASS only: schedule sessions with each class's returned scheduleId (local wall-clock times)
76
+ await seed.scheduleClassSessions(ctx, [
77
+ { scheduleId: services[1].scheduleId, resourceId: staff[0].resourceId, start: "2026-08-10T09:00:00", end: "2026-08-10T10:00:00", capacity: 20 },
78
+ ]);
79
+ // images (optional): import the url to Wix Media first (bookings binds by file id), then attach.
80
+ // Use the FINAL https://media.base44.com/... url only (never a /__generating__/ placeholder).
81
+ const file = await seed.importImage(ctx, imageUrl); // → { id, url } (Wix Media file id + wixstatic url)
82
+ await seed.attachServiceImage(ctx, { serviceId: services[0].id, revision: services[0].revision, image: { id: file.id, url: file.url, width: 1024, height: 1024 } });
83
+ ```
84
+
85
+ ## Functions
86
+ | fn | does |
87
+ |---|---|
88
+ | `setupBookings(ctx, {services, staffResourceId?})` | **one-call**: install → staff → categories → services → CLASS sessions → images |
89
+ | `installBookingsApp(ctx)` | install the Wix Bookings app on the site |
90
+ | `queryStaff(ctx)` | `[{resourceId,id,name}]` — one shot; use `resourceId`, not `id` |
91
+ | `queryStaffWithRetry(ctx)` | STEP 1 — same, but polls until the default owner provisions after a fresh install (avoids `MISSING_APPOINTMENT_RESOURCES`) |
92
+ | `createStaff(ctx, [{name,description}])` | create named staff → `[{resourceId,id,name}]` (omit email/phone) |
93
+ | `queryCategories(ctx)` | `[{id,name}]` — reuse a category created earlier this run |
94
+ | `createCategories(ctx, names)` | STEP 2 — every service needs a `category.id` or it's invisible → `[{id,name}]`; reuses an existing same-named category (idempotent, no dupes on re-run) |
95
+ | `createServices(ctx, services)` | STEP 3 — one bulk create (APPOINTMENT/CLASS mixed) → `[{id,slug,revision,type,scheduleId,success,error}]` |
96
+ | `scheduleClassSessions(ctx, sessions)` | STEP 4 (CLASS only) — one bulk Calendar-Events-V3 create → `[{id,success,error}]` |
97
+ | `getService(ctx, serviceId)` | fetch a service (current `revision`; confirm an image landed) |
98
+ | `importImage(ctx, url)` | import an external url into Wix Media → `{id,url}` (file id + wixstatic url); bookings binds by this file id |
99
+ | `attachServiceImage(ctx, {serviceId,revision,image})` | optional — patch `media.mainMedia`/`coverMedia` (revision-checked); `image.id` MUST be a Wix Media file id from `importImage` |
100
+
101
+ Both bulk calls report per-item `success`/`error` — retry only the failed items **once** with the
102
+ same body; don't loop and don't re-create the ones that already succeeded.
103
+
104
+ ## Reference
105
+ If a call returns a shape you didn't expect, or you need an operation this module doesn't cover,
106
+ use the **`wix-docs`** skill to search + read the live Wix API reference — never guess. The
107
+ authoritative source recipe is `wix-headless/references/inline-recipes/setup-bookings.md`.
108
+
109
+ Read a method's page before writing its call: it carries the exact body shape, the required
110
+ permission scope, and the response envelope.
111
+ - Install a Wix app onto the site: https://dev.wix.com/docs/api-reference/business-management/app-installation/app-installation/install-app.md
112
+ - Import an image into Wix Media: https://dev.wix.com/docs/api-reference/assets/media/media-manager/files/import-file.md
113
+ - Bulk Create Services: https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/bulk-create-services.md
114
+ - Create Category: https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/categories-v2/create-category.md
115
+ - Create Staff Member: https://dev.wix.com/docs/api-reference/business-solutions/bookings/staff-members/staff-members/create-staff-member.md
116
+ - Query Staff Members: https://dev.wix.com/docs/api-reference/business-solutions/bookings/staff-members/staff-members/query-staff-members.md
117
+ - Bulk Create Event (calendar sessions): https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/bulk-create-event.md
@@ -0,0 +1,333 @@
1
+ // Bookings seed helpers — run at BUILD TIME via exec_tool (NOT shipped in the app).
2
+ // The agent requires this and calls the functions with plain data; all Wix Bookings
3
+ // request/response mechanics (the flat Services-V2 payload, the resourceId-vs-staff-id trap,
4
+ // the category-visibility invariant, the { event: {…} } session wrapper, the silent media.image
5
+ // drop) live here, once.
6
+ //
7
+ // **NOT yet live-verified — transcribed from setup-bookings.md.**
8
+ //
9
+ // Usage (build-time exec_tool):
10
+ // const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
11
+ // const seed = require("/app/.agents/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.js");
12
+ // const ctx = { token: accessToken, siteId: WIX_METASITE_ID };
13
+ //
14
+ // await seed.installBookingsApp(ctx); // if the site doesn't have Wix Bookings yet
15
+ //
16
+ // // ORDER MATTERS: resolve a staff resource (STEP 1) and a category (STEP 2) BEFORE services (STEP 3).
17
+ // const staff = await seed.queryStaffWithRetry(ctx); // polls: fresh install provisions the owner async
18
+ // const resourceId = staff[0].resourceId; // NB: resourceId, NOT staff id
19
+ // const cats = await seed.createCategories(ctx, ["Our Services"]);
20
+ // const services = await seed.createServices(ctx, [
21
+ // { type: "APPOINTMENT", name: "Consultation", description: "…", tagLine: "…",
22
+ // price: 75, duration: 60, categoryId: cats[0].id, staffMemberIds: [resourceId] },
23
+ // { type: "CLASS", name: "Morning Yoga", description: "…", capacity: 20,
24
+ // price: 20, categoryId: cats[0].id },
25
+ // ]);
26
+ // // STEP 4 (CLASS only): needs each class's returned scheduleId.
27
+ // await seed.scheduleClassSessions(ctx, services.filter(s => s.type === "CLASS").map(s => ({
28
+ // scheduleId: s.scheduleId, resourceId, start: "2026-08-10T09:00:00", end: "2026-08-10T10:00:00", capacity: 20,
29
+ // })));
30
+ // // optional — import the image url to Wix Media first (bookings binds by file id), then patch (revision-checked).
31
+ // // const file = await seed.importImage(ctx, imageUrl); // → { id, url } (Wix Media file id + wixstatic url)
32
+ // // await seed.attachServiceImage(ctx, { serviceId: s.id, revision: s.revision, image: { id: file.id, url: file.url, width: 1024, height: 1024 } });
33
+ //
34
+ // If any call fails with a shape the caller didn't expect, or you need an operation this module
35
+ // doesn't cover, fall back to the wix-docs skill (search + read the live Wix API reference) —
36
+ // never guess. Source recipe (authoritative): wix-headless/references/inline-recipes/setup-bookings.md.
37
+
38
+ const API = "https://www.wixapis.com";
39
+ // Wix Bookings app id (from the recipe's "API surfaces" note).
40
+ const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
41
+
42
+ async function req(ctx, path, { method = "POST", body } = {}) {
43
+ const res = await fetch(API + path, {
44
+ method,
45
+ headers: {
46
+ Authorization: `Bearer ${ctx.token}`,
47
+ "wix-site-id": ctx.siteId,
48
+ "Content-Type": "application/json",
49
+ },
50
+ body: body ? JSON.stringify(body) : undefined,
51
+ });
52
+ const json = await res.json().catch(() => ({}));
53
+ if (!res.ok) throw new Error(`${method} ${path} -> ${res.status}: ${JSON.stringify(json).slice(0, 400)}`);
54
+ return json;
55
+ }
56
+
57
+ // name -> url slug fallback when item.mainSlug.name is absent (lowercase, non-alphanumerics -> hyphens, dedupe)
58
+ function slugify(name) {
59
+ return String(name || "")
60
+ .toLowerCase()
61
+ .replace(/[^a-z0-9]+/g, "-")
62
+ .replace(/^-+|-+$/g, "");
63
+ }
64
+
65
+ // plain service -> flat Services V2 create object. price omitted / free:true -> NO_FEE.
66
+ // sessionDurations is APPOINTMENT-only; staffMemberIds is required-non-empty for APPOINTMENT, omitted for CLASS.
67
+ function buildService(s) {
68
+ const isAppointment = s.type === "APPOINTMENT";
69
+ const free = s.free === true || s.price == null;
70
+ const out = {
71
+ type: s.type,
72
+ name: s.name,
73
+ description: s.description,
74
+ tagLine: s.tagLine,
75
+ defaultCapacity: s.capacity ?? (isAppointment ? 1 : undefined), // required for ALL types
76
+ onlineBooking: { enabled: true, requireManualApproval: false, allowMultipleRequests: false },
77
+ payment: free
78
+ ? { rateType: "NO_FEE", options: { online: false, inPerson: true } }
79
+ : {
80
+ rateType: "FIXED",
81
+ fixed: { price: { value: String(s.price), currency: s.currency ?? "USD" } }, // value is a STRING; site currency wins
82
+ options: { online: true, inPerson: false },
83
+ },
84
+ category: { id: s.categoryId }, // mandatory for live-site visibility
85
+ locations: [{ type: "BUSINESS" }], // never OWNER_BUSINESS on the services endpoint
86
+ };
87
+ if (isAppointment) {
88
+ out.schedule = { availabilityConstraints: { sessionDurations: [s.duration] } }; // APPOINTMENT only, minutes
89
+ out.staffMemberIds = s.staffMemberIds; // resourceId(s) from STEP 1 — non-empty or MISSING_APPOINTMENT_RESOURCES
90
+ }
91
+ return out;
92
+ }
93
+
94
+ // ---- exported operations ----
95
+
96
+ async function installBookingsApp(ctx) {
97
+ return req(ctx, "/apps-installer-service/v1/app-instance/install", { body: {
98
+ tenant: { tenantType: "SITE", id: ctx.siteId },
99
+ appInstance: { appDefId: BOOKINGS_APP_ID, enabled: true },
100
+ } });
101
+ }
102
+
103
+ // The default "Business Owner" is a RESOURCE, not a service — it won't appear here.
104
+ async function listServices(ctx) {
105
+ const r = await req(ctx, "/bookings/v2/services/query", { body: { query: { paging: { limit: 100 } } } });
106
+ return (r.services ?? []).map((s) => ({ id: s.id, name: s.name }));
107
+ }
108
+
109
+ // STEP 1: resolve staff resources. Returns [{ resourceId, id, name }].
110
+ // ⚠️ staffMemberIds takes resourceId, NOT the staff id — read staffMember.resourceId.
111
+ async function queryStaff(ctx) {
112
+ const r = await req(ctx, "/bookings/v1/staff-members/query", {
113
+ body: { query: {}, fields: ["RESOURCE_DETAILS"] },
114
+ });
115
+ return (r.staffMembers ?? []).map((m) => ({ resourceId: m.resourceId, id: m.id, name: m.name }));
116
+ }
117
+
118
+ // installBookingsApp → provisioning is async (Wix signals completion via the App-Instance-Installed
119
+ // webhook, which an exec script can't receive). The default "Business Owner" resource isn't queryable
120
+ // in the same tick, so queryStaff returns [] (or throws "Business schedule not found") until it lands.
121
+ // Poll so the one-call path resolves the owner instead of failing APPOINTMENT services with
122
+ // MISSING_APPOINTMENT_RESOURCES. Already-provisioned sites return on the first try (no added latency).
123
+ async function queryStaffWithRetry(ctx, { tries = 10, delayMs = 2000 } = {}) {
124
+ let lastErr;
125
+ for (let i = 0; i < tries; i++) {
126
+ try { const s = await queryStaff(ctx); if (s.length) return s; }
127
+ catch (e) { lastErr = e; }
128
+ if (i < tries - 1) await new Promise((r) => setTimeout(r, delayMs));
129
+ }
130
+ if (lastErr) throw lastErr; // never provisioned → surface the real error rather than a silent []
131
+ return [];
132
+ }
133
+
134
+ // STEP 1 (optional): create named staff when the request names stylists/providers. staff: [{ name, description }].
135
+ // ⚠️ Omit email/phone unless you have a real value — V1 rejects empty strings. Returns [{ resourceId, id, name }].
136
+ async function createStaff(ctx, staff) {
137
+ const out = [];
138
+ for (const m of staff) {
139
+ const body = { staffMember: { name: m.name, description: m.description } };
140
+ const r = await req(ctx, "/bookings/v1/staff-members", { body });
141
+ out.push({ resourceId: r.staffMember?.resourceId, id: r.staffMember?.id, name: r.staffMember?.name });
142
+ }
143
+ return out;
144
+ }
145
+
146
+ // STEP 2: query existing categories (to reuse one created earlier this run). Returns [{ id, name }].
147
+ async function queryCategories(ctx) {
148
+ const r = await req(ctx, "/bookings/v2/categories/query", { body: { query: {} } });
149
+ return (r.categories ?? []).map((c) => ({ id: c.id, name: c.name }));
150
+ }
151
+
152
+ // STEP 2: create categories — every service needs a category.id or it's invisible on the live site.
153
+ // Independent (no shared revision, unlike Stores categories); looped here one call per category.
154
+ async function createCategories(ctx, names) {
155
+ // Reuse an existing same-named category instead of creating a duplicate. Bookings categories aren't
156
+ // unique-by-name, so a re-run (or a partial-failure retry — categories are created before services)
157
+ // would otherwise pile up dupes. Keeps the seed additive-but-idempotent.
158
+ const byName = new Map((await queryCategories(ctx)).map((c) => [c.name, c]));
159
+ const out = [];
160
+ for (const name of names) {
161
+ let cat = byName.get(name);
162
+ if (!cat) {
163
+ const r = await req(ctx, "/bookings/v2/categories", { body: { category: { name } } });
164
+ cat = { id: r.category?.id, name };
165
+ byName.set(name, cat);
166
+ }
167
+ out.push(cat);
168
+ }
169
+ return out;
170
+ }
171
+
172
+ /**
173
+ * STEP 3: bulk-create services (up to 100), one call. APPOINTMENT and CLASS may be mixed.
174
+ * MUST run AFTER staff (STEP 1) and category (STEP 2) are resolved.
175
+ * @param services [{ type:"APPOINTMENT"|"CLASS", name, description, tagLine?, categoryId,
176
+ * price?, currency?, free?, duration?(APPOINTMENT minutes), capacity?, staffMemberIds?(resourceIds, APPOINTMENT) }]
177
+ * @returns [{ id, slug, revision, type, scheduleId, index, success, error }]
178
+ * scheduleId feeds scheduleClassSessions (CLASS); revision feeds attachServiceImage.
179
+ * Retry only the items where success===false, ONCE, with the same body — don't loop, don't re-create successes.
180
+ */
181
+ async function createServices(ctx, services) {
182
+ const body = { services: services.map(buildService), returnEntity: true };
183
+ const r = await req(ctx, "/bookings/v2/bulk/services/create", { body });
184
+ return (r.results ?? []).map((res, i) => {
185
+ const item = res.item ?? {};
186
+ return {
187
+ id: item.id ?? res.itemMetadata?.id,
188
+ slug: item.mainSlug?.name ?? slugify(item.name ?? services[i]?.name),
189
+ revision: item.revision,
190
+ type: item.type ?? services[i]?.type,
191
+ scheduleId: item.schedule?.id,
192
+ index: res.itemMetadata?.originalIndex ?? i,
193
+ success: res.itemMetadata?.success ?? false,
194
+ error: res.itemMetadata?.error,
195
+ };
196
+ });
197
+ }
198
+
199
+ /**
200
+ * STEP 4 (CLASS only): schedule sessions in one bulk Calendar-Events-V3 call. Skip for APPOINTMENT.
201
+ * @param sessions [{ scheduleId(CLASS item.schedule.id), resourceId(from STEP 1), start, end, capacity? }]
202
+ * start/end are LOCAL wall-clock "YYYY-MM-DDThh:mm:ss" (no Z), today-or-future.
203
+ * @returns [{ id, index, success, error }] — events send no returnEntity, so ids come from itemMetadata only.
204
+ * Retry only failed events once.
205
+ */
206
+ async function scheduleClassSessions(ctx, sessions) {
207
+ const body = {
208
+ events: sessions.map((s) => ({
209
+ event: {
210
+ scheduleId: s.scheduleId,
211
+ type: "CLASS",
212
+ start: { localDate: s.start },
213
+ end: { localDate: s.end },
214
+ resources: [{ id: s.resourceId, permissionRole: "WRITER" }], // non-empty + WRITER, else UNKNOWN_ROLE 400
215
+ ...(s.capacity != null ? { totalCapacity: s.capacity } : {}),
216
+ },
217
+ })),
218
+ };
219
+ const r = await req(ctx, "/calendar/v3/bulk/events/create", { body });
220
+ return (r.results ?? []).map((res, i) => ({
221
+ id: res.itemMetadata?.id,
222
+ index: res.itemMetadata?.originalIndex ?? i,
223
+ success: res.itemMetadata?.success ?? false,
224
+ error: res.itemMetadata?.error,
225
+ }));
226
+ }
227
+
228
+ // Fetch a service (for its current revision before an image patch, or to confirm an image landed).
229
+ async function getService(ctx, serviceId) {
230
+ return req(ctx, `/bookings/v2/services/${serviceId}`, { method: "GET" });
231
+ }
232
+
233
+ /**
234
+ * Import an external image URL into Wix Media → { id, url }. Bookings binds a service image by the
235
+ * Wix Media file **id** (`mainMedia.image.id`), NOT a url — so an external url (e.g. a base44
236
+ * generate_image result) MUST be imported first; the raw url as the id stores (200) but renders
237
+ * nothing. `id` is the wixstatic file id (`<hash>~mv2.png`), `url` the permanent wixstatic url.
238
+ */
239
+ async function importImage(ctx, url, displayName = "image.png") {
240
+ const r = await req(ctx, "/site-media/v1/files/import", { body: { url, mimeType: "image/png", displayName } });
241
+ const f = r.file || r;
242
+ if (!f?.id) throw new Error(`import-file returned no file id: ${JSON.stringify(r).slice(0, 200)}`);
243
+ return { id: f.id, url: f.url };
244
+ }
245
+
246
+ /**
247
+ * Attach an image (optional). Writes under media.mainMedia + media.coverMedia; revision-checked.
248
+ * @param it { serviceId, revision, image: { id, url, width, height } } — `image.id` MUST be a Wix
249
+ * Media file id (from importImage), never a raw external url.
250
+ * ⚠️ Writing under media.image (not mainMedia/coverMedia) returns 200 but SILENTLY drops the image — a 200
251
+ * is not proof; confirm with getService and check media.mainMedia is populated. Never block on failure.
252
+ */
253
+ async function attachServiceImage(ctx, it) {
254
+ return req(ctx, `/bookings/v2/services/${it.serviceId}`, {
255
+ method: "PATCH",
256
+ body: {
257
+ service: {
258
+ id: it.serviceId,
259
+ revision: it.revision,
260
+ media: { mainMedia: { image: it.image }, coverMedia: { image: it.image } },
261
+ },
262
+ },
263
+ });
264
+ }
265
+
266
+ /**
267
+ * ONE-CALL seed: install → resolve staff → categories → services → CLASS sessions → images, in the
268
+ * correct order, keeping ids in memory (no hand-threading of scheduleId/resourceId across exec
269
+ * calls). DEFAULT path — call it once instead of the individual functions.
270
+ *
271
+ * @param plan {{
272
+ * services: [{ type:"APPOINTMENT"|"CLASS", name, description, tagLine?, price?, free?, duration?,
273
+ * capacity?, category?(name), staffMemberIds?,
274
+ * sessions?: [{ start, end, capacity? }], // CLASS only; local "YYYY-MM-DDThh:mm:ss"
275
+ * imageUrl? }], // a plain image url; imported to Wix Media here, optional
276
+ * staffResourceId?: string, // defaults to the fresh install's owner (queryStaff()[0].resourceId)
277
+ * }}
278
+ * @returns { services:[...createServices], categories:[{id,name}], resourceId, sessionsScheduled, imagesAttached }
279
+ */
280
+ async function setupBookings(ctx, { services = [], staffResourceId } = {}) {
281
+ await installBookingsApp(ctx);
282
+
283
+ let resourceId = staffResourceId;
284
+ if (!resourceId) {
285
+ const staff = await queryStaffWithRetry(ctx); // fresh install provisions the default owner async — poll
286
+ resourceId = staff[0]?.resourceId;
287
+ }
288
+
289
+ const catNames = [...new Set(services.map((s) => s.category).filter(Boolean))];
290
+ const cats = catNames.length ? await createCategories(ctx, catNames) : [];
291
+ const catIdByName = new Map(cats.map((c) => [c.name, c.id]));
292
+
293
+ const created = await createServices(ctx, services.map((s) => ({
294
+ type: s.type, name: s.name, description: s.description, tagLine: s.tagLine,
295
+ price: s.price, free: s.free, duration: s.duration, capacity: s.capacity,
296
+ categoryId: s.category ? catIdByName.get(s.category) : undefined,
297
+ staffMemberIds: s.staffMemberIds ?? (s.type === "APPOINTMENT" && resourceId ? [resourceId] : undefined),
298
+ })));
299
+
300
+ const sessions = [];
301
+ created.forEach((c, i) => {
302
+ const plan = services[i];
303
+ if (c.type === "CLASS" && Array.isArray(plan?.sessions)) {
304
+ for (const ses of plan.sessions) {
305
+ sessions.push({ scheduleId: c.scheduleId, resourceId, start: ses.start, end: ses.end, capacity: ses.capacity ?? plan.capacity });
306
+ }
307
+ }
308
+ });
309
+ const scheduled = sessions.length ? await scheduleClassSessions(ctx, sessions) : [];
310
+
311
+ let imagesAttached = 0;
312
+ for (let i = 0; i < created.length; i++) {
313
+ const url = services[i]?.imageUrl; // a plain image url (e.g. base44 generate_image) — imported here
314
+ if (!url || !created[i]?.id) continue;
315
+ try {
316
+ const file = await importImage(ctx, url, `${created[i].slug || "service"}.png`); // → Wix Media file id
317
+ await attachServiceImage(ctx, {
318
+ serviceId: created[i].id, revision: created[i].revision,
319
+ image: { id: file.id, url: file.url, width: 1024, height: 1024 },
320
+ });
321
+ imagesAttached++;
322
+ } catch { /* never block on image failure — leave the service text-only */ }
323
+ }
324
+
325
+ return { services: created, categories: cats, resourceId, sessionsScheduled: scheduled.length, imagesAttached };
326
+ }
327
+
328
+ module.exports = {
329
+ setupBookings,
330
+ installBookingsApp, listServices,
331
+ queryStaff, queryStaffWithRetry, createStaff, queryCategories, createCategories,
332
+ createServices, scheduleClassSessions, getService, importImage, attachServiceImage,
333
+ };