@wix/agent-skills 1.17.0 → 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 (246) 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 +14 -3
  9. package/skills/wix-app/references/SITE_PLUGIN.md +12 -0
  10. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +74 -68
  11. package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +79 -0
  12. package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +73 -0
  13. package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +52 -0
  14. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +184 -0
  15. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +12 -11
  16. package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +1 -0
  17. package/skills/wix-app/references/editor-react-component/BROWSER-SUPPORT.md +56 -0
  18. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +16 -8
  19. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +19 -1
  20. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +3 -0
  21. package/skills/wix-base44-connector/SKILL.md +133 -44
  22. package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +78 -0
  23. package/skills/wix-base44-connector/scripts/utils.js +216 -30
  24. package/skills/wix-headless/entry/skill.md +1 -1
  25. package/skills/wix-headless/references/CAPABILITIES.md +9 -1
  26. package/skills/wix-headless/references/DESIGN.md +1 -0
  27. package/skills/wix-headless/references/DISCOVERY.md +2 -1
  28. package/skills/wix-headless/references/IMAGE_GENERATION.md +1 -0
  29. package/skills/wix-headless/references/SDK_HANDOFF.md +4 -1
  30. package/skills/wix-headless/references/SEED.md +2 -1
  31. package/skills/wix-headless/references/SETUP.md +1 -0
  32. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +2 -2
  33. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +26 -8
  34. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +26 -10
  35. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +8 -8
  36. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +1 -1
  37. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +1 -1
  38. package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +254 -0
  39. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +13 -0
  40. package/skills/wix-headless/references/inline-recipes/setup-rentals.md +254 -0
  41. package/skills/wix-headless/references/non-astro.md +1 -1
  42. package/skills/wix-headless-fast/SKILL.md +166 -0
  43. package/skills/wix-headless-fast/entry/skill.md +99 -0
  44. package/skills/wix-headless-fast/install/deploy.mjs +433 -0
  45. package/skills/wix-headless-fast/install/fast-path.mjs +141 -0
  46. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +137 -0
  47. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +137 -0
  48. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +113 -0
  49. package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +42 -0
  50. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +123 -0
  51. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +70 -0
  52. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +99 -0
  53. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +76 -0
  54. package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +62 -0
  55. package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +40 -0
  56. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +99 -0
  57. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +24 -0
  58. package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +16156 -0
  59. package/skills/wix-headless-fast/references/blog/seed/SEED.md +72 -0
  60. package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +287 -0
  61. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +129 -0
  62. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +143 -0
  63. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +119 -0
  64. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +138 -0
  65. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +54 -0
  66. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +206 -0
  67. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +96 -0
  68. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +70 -0
  69. package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +40 -0
  70. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +76 -0
  71. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +22 -0
  72. package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +11962 -0
  73. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +61 -0
  74. package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +302 -0
  75. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +179 -0
  76. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +83 -0
  77. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +57 -0
  78. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +151 -0
  79. package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +75 -0
  80. package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +41 -0
  81. package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +11262 -0
  82. package/skills/wix-headless-fast/references/cms/seed/SEED.md +103 -0
  83. package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +324 -0
  84. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +149 -0
  85. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +66 -0
  86. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +176 -0
  87. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +118 -0
  88. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +117 -0
  89. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +59 -0
  90. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +136 -0
  91. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +128 -0
  92. package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +61 -0
  93. package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +40 -0
  94. package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +12 -0
  95. package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +80 -0
  96. package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +21 -0
  97. package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +11884 -0
  98. package/skills/wix-headless-fast/references/events/seed/SEED.md +86 -0
  99. package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +276 -0
  100. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +150 -0
  101. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +317 -0
  102. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +269 -0
  103. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +231 -0
  104. package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +135 -0
  105. package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +10593 -0
  106. package/skills/wix-headless-fast/references/forms/seed/SEED.md +108 -0
  107. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +320 -0
  108. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +63 -0
  109. package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +60 -0
  110. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +155 -0
  111. package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +78 -0
  112. package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +47 -0
  113. package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +66 -0
  114. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +88 -0
  115. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +53 -0
  116. package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +122 -0
  117. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +50 -0
  118. package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +21 -0
  119. package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +42 -0
  120. package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +10 -0
  121. package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +11 -0
  122. package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +10329 -0
  123. package/skills/wix-headless-fast/references/members/seed/SEED.md +39 -0
  124. package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +94 -0
  125. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +134 -0
  126. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +110 -0
  127. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +90 -0
  128. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +115 -0
  129. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +59 -0
  130. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +39 -0
  131. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +58 -0
  132. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +165 -0
  133. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +55 -0
  134. package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +40 -0
  135. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +30 -0
  136. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +21 -0
  137. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +29 -0
  138. package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +10197 -0
  139. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +78 -0
  140. package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +258 -0
  141. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +133 -0
  142. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +58 -0
  143. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +126 -0
  144. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +37 -0
  145. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +39 -0
  146. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +75 -0
  147. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +45 -0
  148. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +30 -0
  149. package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +41 -0
  150. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +40 -0
  151. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +21 -0
  152. package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +11226 -0
  153. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +71 -0
  154. package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +279 -0
  155. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +165 -0
  156. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +142 -0
  157. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +26 -0
  158. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +121 -0
  159. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +155 -0
  160. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +55 -0
  161. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +43 -0
  162. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +177 -0
  163. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +165 -0
  164. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +103 -0
  165. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +254 -0
  166. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +146 -0
  167. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +168 -0
  168. package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +47 -0
  169. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +22 -0
  170. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +12 -0
  171. package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +12560 -0
  172. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +81 -0
  173. package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +479 -0
  174. package/skills/wix-headless-fast/references/shared/CONTENT.md +22 -0
  175. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +73 -0
  176. package/skills/wix-headless-fast/references/shared/DESIGN.md +26 -0
  177. package/skills/wix-headless-fast/references/shared/app/styles/global.css +52 -0
  178. package/skills/wix-headless-fast/references/shared/app/wix/config.ts +15 -0
  179. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +27 -0
  180. package/skills/wix-headless-fast/references/shared/app/wix/money.ts +24 -0
  181. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +76 -0
  182. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +46 -0
  183. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +46 -0
  184. package/skills/wix-headless-fast/references/shared/seed/images.mjs +145 -0
  185. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +149 -0
  186. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +26 -0
  187. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +121 -0
  188. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +100 -0
  189. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +130 -0
  190. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +48 -0
  191. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +66 -0
  192. package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +103 -0
  193. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +48 -0
  194. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +140 -0
  195. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +80 -0
  196. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +101 -0
  197. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +221 -0
  198. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +215 -0
  199. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +109 -0
  200. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +46 -0
  201. package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +56 -0
  202. package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +23 -0
  203. package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +12247 -0
  204. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +85 -0
  205. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +415 -0
  206. package/skills/wix-manage/SKILL.md +18 -2
  207. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +141 -0
  208. package/skills/wix-manage/references/google-business-profile/connect-google-business-profile.md +134 -0
  209. package/skills/wix-manage/references/google-business-profile/manage-google-business-profile-locations.md +182 -0
  210. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +3 -0
  211. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +21 -6
  212. package/skills/wix-manage/references/stores/update-product-with-options.md +83 -0
  213. package/skills/wix-vibe-headless/SKILL.md +18 -4
  214. package/skills/wix-vibe-headless/install/deploy.cjs +12 -4
  215. package/skills/wix-vibe-headless/install/pin-agents-md.cjs +2 -1
  216. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
  217. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +3 -1
  218. package/skills/wix-vibe-headless/platforms/base44.md +23 -61
  219. package/skills/wix-vibe-headless/platforms/generic.md +6 -3
  220. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +43 -0
  221. package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +67 -0
  222. package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +128 -0
  223. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +5 -3
  224. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +250 -0
  225. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +12 -5
  226. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +41 -0
  227. package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.js +267 -0
  228. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +6 -3
  229. package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +470 -0
  230. package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +425 -0
  231. package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +187 -0
  232. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +238 -0
  233. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +108 -0
  234. package/skills/wix-vibe-headless/references/forms/seed/SEED.md +1762 -0
  235. package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +13 -0
  236. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +137 -84
  237. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +15 -8
  238. package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +37 -0
  239. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +18 -6
  240. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +30 -7
  241. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +46 -10
  242. package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +0 -110
  243. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +0 -51
  244. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +0 -49
  245. package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +0 -92
  246. package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +0 -139
@@ -0,0 +1,81 @@
1
+ # Restaurants — seeding
2
+
3
+ Seed by **running `seed-restaurants.mjs` with a plan file** — don't hand-write the REST calls.
4
+ The script mints its own site token via the Wix CLI (logged-in session + `wix.config.json`
5
+ required), installs the Restaurants apps, builds each menu bottom-up (items → sections →
6
+ menu, all visible), imports+attaches images, and configures ordering/reservations.
7
+
8
+ ```bash
9
+ # from the project root (where wix.config.json lives):
10
+ node <SKILL_ROOT>/references/restaurants/seed/seed-restaurants.mjs plan.json
11
+ ```
12
+
13
+ `plan.json` is plain data — write it from the brief. **Default to one menu with ~3 sections
14
+ of 2–3 items each** (the seed shows the shape; the owner adds the rest in the dashboard),
15
+ every item with an image (a menu without photos looks broken) — the default is an
16
+ `imagePrompt` (AI-generated, ~1 Wix AI credit per image, account-billed): brand-contextual —
17
+ subject, aesthetic/mood, palette, lighting — always ending "no text, no watermarks". At least one image in the set shows the real subject of the business — the actual product/space/service, not abstract decoration. For an asset the user actually
18
+ supplied use `imagePath` (a file on this machine — uploaded to Wix Media) or `imageUrl`
19
+ (their own hosted URL; verify it with `curl -sI` → 200) — never a stock-photo or guessed URL. Images resolve in parallel and never
20
+ block the seed, a failed image leaves that item text-only — and both add-ons on for a
21
+ restaurant that takes orders and reservations:
22
+
23
+ ```json
24
+ {
25
+ "menus": [
26
+ { "name": "Dinner", "description": "Evening menu", "sections": [
27
+ { "name": "Antipasti", "description": "To start", "items": [
28
+ { "name": "Bruschetta al Pomodoro", "description": "Grilled sourdough, San Marzano tomatoes, basil.",
29
+ "price": 9.5, "imageUrl": "https://…" },
30
+ { "name": "Burrata", "description": "Creamy burrata, heirloom tomatoes, olive oil.",
31
+ "price": 14, "imageUrl": "https://…" }
32
+ ] },
33
+ { "name": "Mains", "items": [
34
+ { "name": "Tagliatelle al Ragù", "description": "Slow-braised beef ragù, parmigiano.",
35
+ "price": 22, "imageUrl": "https://…" },
36
+ { "name": "Branzino", "description": "Whole roasted sea bass, lemon, herbs.",
37
+ "price": 28, "imageUrl": "https://…" }
38
+ ] },
39
+ { "name": "Dolci", "items": [
40
+ { "name": "Tiramisù", "description": "Espresso-soaked ladyfingers, mascarpone.",
41
+ "price": 10, "imageUrl": "https://…" }
42
+ ] }
43
+ ] }
44
+ ],
45
+ "ordering": { "address": {
46
+ "name": "Trattoria Lumina", "timeZone": "America/New_York",
47
+ "address": { "country": "US", "subdivision": "US-NY", "city": "New York", "postalCode": "10012",
48
+ "streetAddress": { "number": "18", "name": "Prince Street" },
49
+ "formattedAddress": "18 Prince Street, New York, NY 10012" } } },
50
+ "reservations": { "partySize": { "min": 1, "max": 10 } }
51
+ }
52
+ ```
53
+
54
+ - `price` — a number; stored as a decimal string in the **site currency** (never send one).
55
+ - `ordering` — installs the Orders app, which **auto-provisions** a working setup (ENABLED
56
+ operation, Pickup + Delivery methods, every menu orderable); the seed verifies it. The
57
+ `address` is **required for real ordering** — without one, ordering is "testing only" and
58
+ checkout breaks. If the brief names no address, pass `"ordering": true` and the result
59
+ carries a note to flag the owner. Completing a **paid** order additionally needs a premium
60
+ plan + a connected payment method (dashboard) — mention it, don't treat it as a failure.
61
+ - `reservations` — installs Table Reservations, which auto-provisions a default reservation
62
+ location; `partySize` (or a full `configuration`) is a partial PATCH. The final
63
+ **enable-online-reservations toggle is premium-only**: on a free site the result carries
64
+ `reservations.premiumRequired: true` — expected, tell the owner, don't retry.
65
+ - Fresh Menus installs ship a sample "Dinner Menu"; when THIS run installs the app the seed
66
+ removes that sample (`sampleMenuRemoved: true`). On a site that already had the app,
67
+ **seeding is strictly additive — never delete or overwrite existing content**; ask first
68
+ if a cleanup seems needed.
69
+
70
+ ## Escape hatch — individual functions
71
+ `setupRestaurants` composes exported steps — `installMenusApp`, `installOrdersApp`,
72
+ `installTableReservationsApp`, `removeSampleMenu`, `createMenu`, `importImage`,
73
+ `attachItemImages`, `setBusinessLocation`, `listOperationsWithRetry`, `enableOperation`,
74
+ `queryMenuOrderingSettings`, `updateMenuOrderingSettings`, `listReservationLocationsWithRetry`,
75
+ `updateReservationLocation`, `enableOnlineReservations`, plus `makeCtx()` — import them only
76
+ for a partial re-seed.
77
+
78
+ ## Reference
79
+ Unexpected shape or an uncovered operation → read the live Wix API reference; the
80
+ authoritative source recipes are `wix-headless/references/inline-recipes/setup-restaurants.md`,
81
+ `setup-restaurant-orders.md`, and `setup-restaurant-reservations.md`.
@@ -0,0 +1,479 @@
1
+ // Restaurants seed — a BUILD-TIME script, never shipped in the app. Run from the project
2
+ // root (where wix.config.json lives) with a plan file:
3
+ //
4
+ // node <SKILL_ROOT>/references/restaurants/seed/seed-restaurants.mjs plan.json
5
+ //
6
+ // It mints its own site token via the Wix CLI, installs the Wix Restaurants Menus app (plus
7
+ // Orders / Table Reservations when the plan asks), builds each menu BOTTOM-UP (bulk items →
8
+ // bulk sections → menu — every child exists before its parent, visible:true at every level),
9
+ // imports+attaches item images, and configures the add-ons. Prints a JSON result to stdout.
10
+ //
11
+ // Plan shape (see SEED.md):
12
+ // { "menus": [{ "name", "description"?, "sections": [{ "name", "description"?,
13
+ // "items": [{ "name", "description"?, "price", "imageUrl"? | "imagePrompt"? }] }] }],
14
+ // "ordering"?: true | { "address"? }, // menu-first add-on; address is STEP 0
15
+ // "reservations"?: true | { "partySize"? { "min","max" }, "address"? } }
16
+ //
17
+ // Seeding is ADDITIVE — with ONE recipe-sanctioned exception: when THIS run installs the
18
+ // Menus app onto a site that didn't have it, the install's own sample "Dinner Menu" is
19
+ // removed (it's provably not owner content). Nothing else is ever deleted. Unexpected
20
+ // shapes → read the live API reference; authoritative source recipes:
21
+ // wix-headless/references/inline-recipes/setup-restaurants.md, setup-restaurant-orders.md,
22
+ // setup-restaurant-reservations.md.
23
+ import { execFileSync } from "node:child_process";
24
+ import { readFileSync } from "node:fs";
25
+ import { resolveItemImages } from "../../shared/seed/images.mjs";
26
+
27
+ const API = "https://www.wixapis.com";
28
+ const MENUS_APP_ID = "b278a256-2757-4f19-9313-c05c783bec92";
29
+ const ORDERS_APP_ID = "9a5d83fd-8570-482e-81ab-cfa88942ee60";
30
+ const TABLE_RESERVATIONS_APP_ID = "f9c07de2-5341-40c6-b096-8eb39de391fb";
31
+
32
+ export function makeCtx({ cwd = process.cwd() } = {}) {
33
+ const config = JSON.parse(readFileSync(`${cwd}/wix.config.json`, "utf8"));
34
+ const siteId = config.siteId ?? config.projectId;
35
+ if (!siteId) throw new Error("wix.config.json has no siteId — is this a Wix CLI project?");
36
+ const token = execFileSync("npx", ["@wix/cli@latest", "token", "--site", siteId], {
37
+ encoding: "utf8",
38
+ cwd,
39
+ }).trim();
40
+ if (!token) throw new Error("The Wix CLI returned no token — run `npx @wix/cli@latest login` first.");
41
+ return { token, siteId };
42
+ }
43
+
44
+ async function req(ctx, path, { method = "POST", body } = {}) {
45
+ const res = await fetch(API + path, {
46
+ method,
47
+ headers: {
48
+ Authorization: `Bearer ${ctx.token}`,
49
+ "wix-site-id": ctx.siteId,
50
+ "Content-Type": "application/json",
51
+ },
52
+ body: body ? JSON.stringify(body) : undefined,
53
+ });
54
+ const json = await res.json().catch(() => ({}));
55
+ if (!res.ok) throw new Error(`${method} ${path} -> ${res.status}: ${JSON.stringify(json).slice(0, 400)}`);
56
+ return json;
57
+ }
58
+
59
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
60
+
61
+ // ---- app installs --------------------------------------------------------------------------------
62
+
63
+ // docs: https://dev.wix.com/docs/api-reference/articles/work-with-wix-apis/platform/about-apps-created-by-wix.md
64
+ async function installApp(ctx, appDefId) {
65
+ try {
66
+ await req(ctx, "/apps-installer-service/v1/app-instance/install", { body: {
67
+ tenant: { tenantType: "SITE", id: ctx.siteId },
68
+ appInstance: { appDefId, enabled: true },
69
+ } });
70
+ } catch {
71
+ /* already installed is fine */
72
+ }
73
+ }
74
+ export async function installMenusApp(ctx) { return installApp(ctx, MENUS_APP_ID); }
75
+ export async function installOrdersApp(ctx) { return installApp(ctx, ORDERS_APP_ID); }
76
+ export async function installTableReservationsApp(ctx) { return installApp(ctx, TABLE_RESERVATIONS_APP_ID); }
77
+
78
+ /** True when the Menus API answers — i.e. the app is already on the site. */
79
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/list-menus.md
80
+ export async function menusAppPresent(ctx) {
81
+ try {
82
+ await req(ctx, "/restaurants/menus/v1/menus", { method: "GET" });
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ // ---- menu (setup-restaurants.md) -----------------------------------------------------------------
90
+ // Everything is Restaurants Menus V1 on /restaurants/menus/v1/... . REST flattens the
91
+ // protobuf wrappers: plain values ("visible": true), never {"value": …}.
92
+
93
+ /**
94
+ * Recipe STEP 0 — a FRESH Menus-app install ships a populated sample "Dinner Menu"
95
+ * (~4 sections, ~21 items) that would render next to the seeded menu. Call ONLY when this
96
+ * run installed the app onto a site that didn't have it (menusAppPresent was false) — then
97
+ * everything present is provably the install's own sample. Polls briefly (the sample
98
+ * provisions async), then deletes children before parents. No-op when nothing appears.
99
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/list-items.md
100
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/sections/list-sections.md
101
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/bulk-delete-items.md
102
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/sections/bulk-delete-sections.md
103
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/delete-menu.md
104
+ */
105
+ export async function removeSampleMenu(ctx, { tries = 8, delayMs = 2000 } = {}) {
106
+ let menus = [];
107
+ for (let i = 0; i < tries; i++) {
108
+ const r = await req(ctx, "/restaurants/menus/v1/menus", { method: "GET" });
109
+ menus = r.menus ?? [];
110
+ if (menus.length) break;
111
+ if (i < tries - 1) await sleep(delayMs);
112
+ }
113
+ if (!menus.length) return { removed: false };
114
+
115
+ const itemsRes = await req(ctx, "/restaurants/menus/v1/items", { method: "GET" });
116
+ const sectionsRes = await req(ctx, "/restaurants/menus/v1/sections", { method: "GET" });
117
+ const itemIds = (itemsRes.items ?? []).map((it) => it.id).filter(Boolean);
118
+ const sectionIds = (sectionsRes.sections ?? []).map((s) => s.id).filter(Boolean);
119
+ if (itemIds.length) {
120
+ await req(ctx, "/restaurants/menus/v1/bulk/items/delete", { method: "DELETE", body: { ids: itemIds } });
121
+ }
122
+ if (sectionIds.length) {
123
+ await req(ctx, "/restaurants/menus/v1/bulk/sections/delete", { method: "DELETE", body: { ids: sectionIds } });
124
+ }
125
+ for (const m of menus) {
126
+ await req(ctx, `/restaurants/menus/v1/menus/${m.id}`, { method: "DELETE" }); // no bulk delete for menus
127
+ }
128
+ return { removed: true, menus: menus.map((m) => m.name) };
129
+ }
130
+
131
+ /**
132
+ * Build ONE menu BOTTOM-UP (items → sections → menu) in three bulk phases.
133
+ * price -> priceInfo.price as a decimal STRING; currency is the site's (send none).
134
+ * visible:true is baked in at every level — required to render on the live site.
135
+ * Returns { menuId, name, sectionIds, itemIds, items: [{ id, revision, price }] } — the
136
+ * per-item revision/price feed the image pass (Update Item is a full replace).
137
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/bulk-create-items.md
138
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/sections/bulk-create-sections.md
139
+ * docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/create-menu.md
140
+ */
141
+ export async function createMenu(ctx, menu) {
142
+ // STEP 1 — bulk-create every item across all sections in ONE request.
143
+ const flat = [];
144
+ menu.sections.forEach((sec, si) => (sec.items || []).forEach((it) => flat.push({ ...it, _section: si })));
145
+ const itemRes = await req(ctx, "/restaurants/menus/v1/bulk/items/create", {
146
+ body: {
147
+ items: flat.map((it) => ({
148
+ name: it.name,
149
+ ...(it.description ? { description: it.description } : {}),
150
+ priceInfo: { price: String(it.price) },
151
+ visible: true,
152
+ })),
153
+ returnEntity: true,
154
+ },
155
+ });
156
+ // created items are under results[].item (results[].itemMetadata.success is the per-item flag)
157
+ const createdItems = (itemRes.results ?? []).map((r) => r.item);
158
+ const itemIdsBySection = menu.sections.map(() => []);
159
+ flat.forEach((it, i) => {
160
+ const id = createdItems[i]?.id;
161
+ if (id) itemIdsBySection[it._section].push(id);
162
+ });
163
+
164
+ // STEP 2 — bulk-create sections, each carrying the itemIds of its items in display order.
165
+ const secRes = await req(ctx, "/restaurants/menus/v1/bulk/sections/create", {
166
+ body: {
167
+ sections: menu.sections.map((sec, si) => ({
168
+ name: sec.name,
169
+ ...(sec.description ? { description: sec.description } : {}),
170
+ visible: true,
171
+ itemIds: itemIdsBySection[si],
172
+ })),
173
+ returnEntity: true,
174
+ },
175
+ });
176
+ const sectionIds = (secRes.results ?? []).map((r) => r.item?.id);
177
+
178
+ // STEP 3 — create the menu (single create wraps in `menu`), carrying its sectionIds.
179
+ // businessLocationId omitted -> binds to the site's default (main) location.
180
+ const menuRes = await req(ctx, "/restaurants/menus/v1/menus", {
181
+ body: {
182
+ menu: {
183
+ name: menu.name,
184
+ ...(menu.description ? { description: menu.description } : {}),
185
+ visible: true,
186
+ sectionIds,
187
+ },
188
+ },
189
+ });
190
+ return {
191
+ menuId: menuRes.menu?.id,
192
+ name: menu.name,
193
+ sectionIds,
194
+ itemIds: createdItems.map((it) => it?.id),
195
+ items: createdItems.map((it, i) => ({ id: it?.id, revision: it?.revision, price: flat[i]?.price })),
196
+ };
197
+ }
198
+
199
+ // Restaurants binds an item image by Wix Media file ID — an external url must be imported
200
+ // first; a plan `imagePrompt` is generated (Wix AI, 1 credit) then imported. Both live in the
201
+ // shared util (parallel, resilient, never blocks the seed).
202
+ export { importImage } from "../../shared/seed/images.mjs";
203
+
204
+ // Image pass. Update Item is a FULL-ENTITY REPLACE with NO field mask — each entry MUST echo
205
+ // the item's current `revision` AND `priceInfo`, or it fails 428 MISSING_ITEM_PRICING and the
206
+ // image does NOT apply. `image` is an OBJECT { id, url, height, width } (never a bare string);
207
+ // the binding field is the Wix Media file `id`.
208
+ // items: [{ id, revision, price, image: { id, url, height, width } }]
209
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/bulk-update-item.md
210
+ export async function attachItemImages(ctx, items) {
211
+ return req(ctx, "/restaurants/menus/v1/bulk/items/update", {
212
+ body: {
213
+ items: items.map((it) => ({
214
+ item: {
215
+ id: it.id,
216
+ revision: it.revision,
217
+ priceInfo: { price: String(it.price) },
218
+ image: it.image,
219
+ },
220
+ })),
221
+ },
222
+ });
223
+ }
224
+
225
+ // ---- business location (shared STEP 0 for ordering + reservations) ------------------------------
226
+ // Update Location is a FULL OVERRIDE — send the WHOLE `location` object (omitted fields are
227
+ // wiped), echo `default:true` (omitting it 400s CHANGE_DEFAULT_FORBIDDEN) and the current
228
+ // `revision`. address.country is a 2-letter ISO code. Without a real address, ordering is
229
+ // "testing only" and checkout breaks — a placeholder must be flagged to the owner.
230
+ // location: { name, timeZone, email?, phone?, address: { country, subdivision, city,
231
+ // postalCode, streetAddress: { number, name }, formattedAddress } }
232
+ // docs: https://dev.wix.com/docs/api-reference/business-management/locations/list-locations.md
233
+ // docs: https://dev.wix.com/docs/api-reference/business-management/locations/create-location.md
234
+ // docs: https://dev.wix.com/docs/api-reference/business-management/locations/update-location.md
235
+ export async function setBusinessLocation(ctx, location) {
236
+ const list = await req(ctx, "/locations/v1/locations", { method: "GET" });
237
+ const def = (list.locations ?? []).find((l) => l.default);
238
+ if (!def) {
239
+ // No default location at all (a bare site can have none) -> CREATE one; operations
240
+ // auto-bind on first-location-add.
241
+ const r = await req(ctx, "/locations/v1/locations", { body: { location: { ...location, default: true } } });
242
+ return r.location;
243
+ }
244
+ const r = await req(ctx, `/locations/v1/locations/${def.id}`, {
245
+ method: "PUT",
246
+ body: { location: { ...location, id: def.id, revision: def.revision, default: true } },
247
+ });
248
+ return r.location;
249
+ }
250
+
251
+ // ---- online ordering (setup-restaurant-orders.md) ------------------------------------------------
252
+ // The Orders-app install AUTO-provisions a working setup (an ENABLED operation with Pickup +
253
+ // Delivery attached, every menu ordering-enabled) — these helpers VERIFY it; they never POST
254
+ // an operation. Each micro-service is on its OWN host prefix — do not normalize.
255
+
256
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/operations/list-operations.md
257
+ export async function listOperations(ctx) {
258
+ const r = await req(ctx, "/restaurants-operations/v1/operations", { method: "GET" });
259
+ return r.operations ?? [];
260
+ }
261
+
262
+ // A fresh install provisions the operation ASYNC — poll until it lands.
263
+ export async function listOperationsWithRetry(ctx, { tries = 15, delayMs = 2000 } = {}) {
264
+ for (let i = 0; i < tries; i++) {
265
+ const ops = await listOperations(ctx).catch(() => []);
266
+ if (ops.length) return ops;
267
+ if (i < tries - 1) await sleep(delayMs);
268
+ }
269
+ return [];
270
+ }
271
+
272
+ // Normally already ENABLED — only PATCH when DISABLED/PAUSED_UNTIL. revision mandatory + current.
273
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/operations/update-operation.md
274
+ export async function enableOperation(ctx, operationId, revision) {
275
+ return req(ctx, `/restaurants-operations/v1/operations/${operationId}`, {
276
+ method: "PATCH",
277
+ body: { operation: { revision, onlineOrderingStatus: "ENABLED" } },
278
+ });
279
+ }
280
+
281
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/menu-ordering-settings/query-menu-ordering-settings.md
282
+ export async function queryMenuOrderingSettings(ctx) {
283
+ const r = await req(ctx, "/menu-ordering-settings/v1/menu-ordering-settings/query", { body: { query: {} } });
284
+ return r.menuOrderingSettings ?? [];
285
+ }
286
+
287
+ // Only when an entry shows onlineOrderingEnabled:false / operationId:"none".
288
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/menu-ordering-settings/update-menu-ordering-settings.md
289
+ export async function updateMenuOrderingSettings(ctx, settingsId, patch) {
290
+ return req(ctx, `/menu-ordering-settings/v1/menu-ordering-settings/${settingsId}`, {
291
+ method: "PATCH",
292
+ body: { menuOrderingSettings: patch },
293
+ });
294
+ }
295
+
296
+ // ---- table reservations (setup-restaurant-reservations.md) ---------------------------------------
297
+ // The install AUTO-provisions one default reservation location with a complete config; the
298
+ // one thing OFF is onlineReservationsEnabled (premium-gated). A reservation location cannot
299
+ // be created via this API — discover, configure, enable. Post-Jan-2026 field names:
300
+ // partySize (not partiesSize), approval (not manualApproval).
301
+
302
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/list-reservation-locations.md
303
+ export async function listReservationLocations(ctx) {
304
+ const r = await req(ctx, "/table-reservations/reservation-locations/v1/reservation-locations", { method: "GET" });
305
+ return r.reservationLocations ?? [];
306
+ }
307
+
308
+ export async function listReservationLocationsWithRetry(ctx, { tries = 15, delayMs = 2000 } = {}) {
309
+ for (let i = 0; i < tries; i++) {
310
+ const locs = await listReservationLocations(ctx).catch(() => []);
311
+ if (locs.length) return locs;
312
+ if (i < tries - 1) await sleep(delayMs);
313
+ }
314
+ return [];
315
+ }
316
+
317
+ // Partial PATCH; revision mandatory; works on a non-premium site. The `location` object
318
+ // (address/name) is IMMUTABLE here — only touch `configuration`.
319
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/update-reservation-location.md
320
+ export async function updateReservationLocation(ctx, reservationLocationId, revision, configuration) {
321
+ return req(ctx, `/table-reservations/reservation-locations/v1/reservation-locations/${reservationLocationId}`, {
322
+ method: "PATCH",
323
+ body: { reservationLocation: { id: reservationLocationId, revision, configuration } },
324
+ });
325
+ }
326
+
327
+ // PREMIUM-ONLY: on a non-premium site this THROWS `428 PREMIUM_ONLY` — expected and
328
+ // non-fatal; the caller records it and continues (never retry-spiral, never fail the seed).
329
+ // docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/update-reservation-location.md
330
+ export async function enableOnlineReservations(ctx, reservationLocationId, revision) {
331
+ return req(ctx, `/table-reservations/reservation-locations/v1/reservation-locations/${reservationLocationId}`, {
332
+ method: "PATCH",
333
+ body: {
334
+ reservationLocation: {
335
+ id: reservationLocationId,
336
+ revision,
337
+ configuration: { onlineReservations: { onlineReservationsEnabled: true } },
338
+ },
339
+ },
340
+ });
341
+ }
342
+
343
+ /**
344
+ * ONE-CALL seed: Menus install (+ sample cleanup when fresh) → menus bottom-up → images →
345
+ * ordering add-on → reservations add-on, ids threaded in memory. The default path.
346
+ */
347
+ export async function setupRestaurants(ctx, plan) {
348
+ const menusPlan = plan.menus ?? [];
349
+ const wasPresent = await menusAppPresent(ctx);
350
+ await installMenusApp(ctx);
351
+ let sampleMenuRemoved = false;
352
+ if (!wasPresent) {
353
+ const r = await removeSampleMenu(ctx).catch(() => ({ removed: false }));
354
+ sampleMenuRemoved = r.removed === true;
355
+ }
356
+
357
+ const createdMenus = [];
358
+ for (const m of menusPlan) createdMenus.push(await createMenu(ctx, m));
359
+
360
+ // image pass — resolve each item's image (import by url / generate by prompt) in ONE
361
+ // parallel wave (restaurants binds by file id), then bulk full-replace with revision +
362
+ // priceInfo echoed. Never block on image failure.
363
+ let imagesAttached = 0;
364
+ const imageItems = [];
365
+ menusPlan.forEach((m, mi) => {
366
+ const flat = m.sections.flatMap((s) => s.items || []);
367
+ flat.forEach((it, i) => {
368
+ const created = createdMenus[mi]?.items?.[i];
369
+ if ((it.imageUrl || it.imagePrompt) && created?.id) {
370
+ imageItems.push({ ...created, imageUrl: it.imageUrl, imagePrompt: it.imagePrompt, name: it.name });
371
+ }
372
+ });
373
+ });
374
+ const files = await resolveItemImages(ctx, imageItems.map((it) => ({
375
+ url: it.imageUrl,
376
+ path: it.imagePath,
377
+ prompt: it.imagePrompt,
378
+ displayName: `${it.name || "item"}.png`,
379
+ })));
380
+ const toAttach = imageItems
381
+ .map((it, i) => (files[i]
382
+ ? { id: it.id, revision: it.revision ?? "1", price: it.price,
383
+ image: { id: files[i].id, url: files[i].url, width: 1024, height: 1024 } }
384
+ : null))
385
+ .filter(Boolean);
386
+ if (toAttach.length) {
387
+ try {
388
+ await attachItemImages(ctx, toAttach);
389
+ imagesAttached = toAttach.length;
390
+ } catch {
391
+ /* the items stay text-only */
392
+ }
393
+ }
394
+
395
+ // shared STEP 0 address — set once across both add-ons.
396
+ let locationSet = false;
397
+ const ensureLocation = async (address) => {
398
+ if (address && !locationSet) {
399
+ await setBusinessLocation(ctx, address);
400
+ locationSet = true;
401
+ }
402
+ };
403
+
404
+ const ordering = { enabled: false, addressSet: false };
405
+ if (plan.ordering) {
406
+ const cfg = typeof plan.ordering === "object" ? plan.ordering : {};
407
+ await installOrdersApp(ctx); // auto-provisions operation + methods + per-menu settings
408
+ await ensureLocation(cfg.address);
409
+ ordering.addressSet = locationSet;
410
+ const ops = await listOperationsWithRetry(ctx);
411
+ const op = ops.find((o) => o.default) ?? ops[0];
412
+ if (!op) throw new Error("No ordering operation appeared — the Orders app install may not have completed; re-run the seed.");
413
+ if (op.onlineOrderingStatus !== "ENABLED") await enableOperation(ctx, op.id, op.revision);
414
+ // confirm each menu is orderable (auto-created + auto-enabled per menu; PATCH only when off)
415
+ const settings = await queryMenuOrderingSettings(ctx);
416
+ for (const s of settings) {
417
+ if (s.onlineOrderingEnabled === false || s.operationId === "none") {
418
+ await updateMenuOrderingSettings(ctx, s.id, {
419
+ revision: s.revision,
420
+ operationId: op.id,
421
+ onlineOrderingEnabled: true,
422
+ availability: { type: "ALWAYS_AVAILABLE", timeZone: cfg.address?.timeZone ?? "America/New_York" },
423
+ }).catch(() => {});
424
+ }
425
+ }
426
+ ordering.enabled = true;
427
+ ordering.operationId = op.id;
428
+ if (!ordering.addressSet) ordering.note = "No address in the plan — ordering is 'testing only' until the owner sets the real business address.";
429
+ }
430
+
431
+ const reservations = { enabled: false };
432
+ if (plan.reservations) {
433
+ const cfg = typeof plan.reservations === "object" ? plan.reservations : {};
434
+ await installTableReservationsApp(ctx); // auto-provisions the default reservation location
435
+ await ensureLocation(cfg.address);
436
+ let [loc] = await listReservationLocationsWithRetry(ctx);
437
+ if (!loc) throw new Error("No reservation location appeared — the Table Reservations install may not have completed; re-run the seed.");
438
+ const configuration = cfg.configuration ?? (cfg.partySize ? { onlineReservations: { partySize: cfg.partySize } } : null);
439
+ if (configuration) {
440
+ await updateReservationLocation(ctx, loc.id, loc.revision, configuration);
441
+ [loc] = await listReservationLocations(ctx); // re-read for the bumped revision
442
+ }
443
+ reservations.reservationLocationId = loc.id;
444
+ try {
445
+ await enableOnlineReservations(ctx, loc.id, loc.revision);
446
+ reservations.enabled = true;
447
+ } catch {
448
+ // 428 PREMIUM_ONLY on a free site — expected; record, don't fail.
449
+ reservations.premiumRequired = true;
450
+ }
451
+ }
452
+
453
+ return {
454
+ menus: createdMenus.map(({ items, ...m }) => m),
455
+ imagesAttached,
456
+ sampleMenuRemoved,
457
+ ordering,
458
+ reservations,
459
+ };
460
+ }
461
+
462
+ // ---- CLI entry ----------------------------------------------------------------------------------
463
+
464
+ const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
465
+ if (invokedDirectly) {
466
+ const planPath = process.argv[2];
467
+ if (!planPath) {
468
+ console.error("usage: node seed-restaurants.mjs <plan.json> (run from the project root)");
469
+ process.exit(1);
470
+ }
471
+ const plan = JSON.parse(readFileSync(planPath, "utf8"));
472
+ const ctx = makeCtx();
473
+ setupRestaurants(ctx, plan)
474
+ .then((result) => console.log(JSON.stringify(result, null, 2)))
475
+ .catch((e) => {
476
+ console.error(e.message);
477
+ process.exit(1);
478
+ });
479
+ }
@@ -0,0 +1,22 @@
1
+ # Content floor — the copy you write on every authored surface
2
+
3
+ **A fallback, not an override.** If the user supplied copy, voice, or tone, use theirs. The
4
+ one exception is the first rule — a comprehension floor, not a style preference.
5
+
6
+ - **Say what the business is, literally, above the fold.** A first-time visitor must know
7
+ what the site *is* and can *do* from the hero alone. The business category ("ceramics
8
+ studio", "trattoria", "yoga studio") appears verbatim in the H1, the kicker above it, or
9
+ the first clause of the subtitle. A witty H1 is welcome — beside an explicit category,
10
+ never instead of one.
11
+ - **The subtitle leads with the offer** — the verb the visitor cares about comes early
12
+ ("Book…", "Order…", "Browse…"), flavor after.
13
+ - **Kickers carry information or don't exist.** "Handmade in Portland · Since 2019" earns its
14
+ place; "The Collection" above a Collection heading doesn't. A voice-led H1 pairs with an
15
+ informative kicker (that's where the category goes).
16
+ - **Titles and subtitles end without a period.** Paragraphs keep theirs.
17
+ - **CTA labels are short, concrete, and not all-caps** — the actual action, matching the one
18
+ primary CTA per page (`DESIGN.md`).
19
+ - **No fluff.** Every line moves a visitor's understanding forward — the H1 and subtitle most
20
+ of all.
21
+ - **A caption describes what its image actually shows.** A slogan overlaid on a photo is not
22
+ a caption; if none is warranted, write none.
@@ -0,0 +1,73 @@
1
+ # Custom operations
2
+
3
+ Shipped vertical code covers its advertised contract. A brief can also require an operation
4
+ outside that contract: a member submission, a file derived in the browser, moderation, a
5
+ custom business write, or a third-party service.
6
+
7
+ ## Decide the boundary first
8
+
9
+ 1. **Caller-permitted Wix operation** — add one function to the appropriate `src/wix/<vertical>/`
10
+ data layer. Use the documented SDK method; components call that function, never the SDK.
11
+ 2. **Privileged Wix operation** — on Wix-managed Astro, create a narrow `src/pages/api/` endpoint.
12
+ Authenticate/authorize the caller and validate the exact input before elevating the one SDK
13
+ call with `auth.elevate()`. On another stack, the equivalent belongs on a server using
14
+ server-only credentials. Never expose an elevated method or credential to the browser.
15
+ 3. **No documented Wix path** — simplify the feature to a supported design. Do not infer a
16
+ contract from generated types, SDK implementation files, or `node_modules`.
17
+
18
+ ## The research rule
19
+
20
+ Read the relevant shipped playbook first. For a genuine gap, use `wix-docs` to read the
21
+ official method or feature guide, then implement that contract. One documentation lookup and
22
+ one type check are enough before coding. Investigate further only after a concrete error.
23
+
24
+ ## User-created public content
25
+
26
+ Use the CMS vertical when a visitor or member submits content that the app must later list or
27
+ show publicly. Seed the collection with the appropriate permission preset and never trust the
28
+ browser to choose an owner, collection, or privileged action.
29
+
30
+ For a small browser-created artifact, the official Wix-managed Astro pattern is a validated
31
+ server endpoint that writes the contribution with elevation. For binary media, first confirm
32
+ the Media Files API's authorization requirements: generating a media upload URL requires
33
+ Manage Media Manager permission, so it is not a browser-side member call. Validate identity,
34
+ MIME type, size, and destination before any elevated call. The authoritative references are:
35
+
36
+ - `wix-docs`: **Upload Images to CMS** for the secure CMS contribution flow.
37
+ - `wix-docs`: **Generate File Upload URL** and **Upload API** for Media Manager files.
38
+
39
+ Keep the endpoint specific to the allowed contribution. An endpoint that accepts arbitrary
40
+ SDK method names, collection ids, or upload destinations is a privilege-escalation bug.
41
+
42
+ ## Optional Media Upload capability
43
+
44
+ Use this only when a site actually needs a normal file upload (not a small canvas artifact
45
+ stored as a CMS data URL). Put the policy in the site plan; the Fast deployer ships the shared
46
+ client helper and Astro endpoint once, plus a generated read-only policy module:
47
+
48
+ ```json
49
+ {
50
+ "capabilities": {
51
+ "mediaUpload": {
52
+ "policies": [{
53
+ "id": "gallery-artwork",
54
+ "accept": ["image/png", "image/jpeg"],
55
+ "maxBytes": 5242880
56
+ }]
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ The generated policy lives at `src/wix/media-upload/policies.generated.ts`; do not hand-edit
63
+ it. The shared browser helper is `src/wix/media-upload/client.ts`:
64
+
65
+ ```ts
66
+ const media = await uploadMedia("gallery-artwork", pngBlob, "my-drawing.png");
67
+ // media.id and media.url are the Wix Media reference to store in the intended CMS image field.
68
+ ```
69
+
70
+ The browser can select only the **named policy** and the bytes. The deployed API route fixes
71
+ the destination and elevates only `files.generateFileUploadUrl`. A vertical uses the same
72
+ capability by putting its own policy in its plan. Do not copy, fork, or widen the endpoint in
73
+ vertical code.