@wix/agent-skills 1.21.0 → 1.23.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 (269) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +71 -7
  3. package/skills/wix-app/references/APP_VALIDATION.md +16 -10
  4. package/skills/wix-app/references/DASHBOARD_PAGE.md +11 -25
  5. package/skills/wix-app/references/DATA_COLLECTION.md +16 -219
  6. package/skills/wix-app/references/SDK_MODULE_MAP.md +2 -0
  7. package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +182 -0
  8. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +97 -0
  9. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_COLLECTION.md +184 -0
  10. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +203 -0
  11. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_SETTINGS.md +44 -0
  12. package/skills/wix-app/references/dashboard-page/QUERY_AND_PAGING.md +182 -0
  13. package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +133 -0
  14. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +10 -5
  15. package/skills/wix-app/references/data-collection/LIFECYCLE.md +68 -0
  16. package/skills/wix-app/references/data-collection/PERMISSIONS.md +47 -0
  17. package/skills/wix-app/references/data-collection/SCHEMA.md +181 -0
  18. package/skills/wix-app/references/data-collection/WIX_DATA.md +38 -66
  19. package/skills/wix-headless-fast/SKILL.md +95 -230
  20. package/skills/wix-headless-fast/cold-start/bootstrap.mjs +156 -0
  21. package/skills/wix-headless-fast/{entry/skill.md → cold-start/cold-start.md} +17 -14
  22. package/skills/wix-headless-fast/guides/existing-site.md +78 -0
  23. package/skills/wix-headless-fast/guides/migration.md +43 -0
  24. package/skills/wix-headless-fast/guides/reference-mode.md +100 -0
  25. package/skills/wix-headless-fast/install/AGENTS.md +9 -4
  26. package/skills/wix-headless-fast/install/agents-md.mjs +5 -1
  27. package/skills/wix-headless-fast/install/attach.mjs +25 -47
  28. package/skills/wix-headless-fast/install/check.mjs +87 -0
  29. package/skills/wix-headless-fast/install/context.mjs +126 -0
  30. package/skills/wix-headless-fast/install/deploy.mjs +106 -80
  31. package/skills/wix-headless-fast/install/lock.mjs +55 -0
  32. package/skills/wix-headless-fast/install/setup.mjs +83 -23
  33. package/skills/wix-headless-fast/install/templates.mjs +168 -0
  34. package/skills/wix-manage/SKILL.md +3 -3
  35. package/skills/wix-manage/references/app-installation/install-wix-apps.md +68 -2
  36. package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +37 -4
  37. package/skills/wix-manage/references/ecommerce/ecom-pricing.md +29 -3
  38. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +27 -0
  39. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +83 -37
  40. package/skills/wix-manage/references/sites/site-import.md +101 -37
  41. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +0 -303
  42. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +0 -136
  43. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +0 -112
  44. package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +0 -42
  45. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +0 -31
  46. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +0 -22
  47. package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +0 -137
  48. package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +0 -78
  49. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +0 -117
  50. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +0 -52
  51. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +0 -47
  52. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +0 -58
  53. package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +0 -62
  54. package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +0 -40
  55. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +0 -97
  56. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +0 -24
  57. package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +0 -16156
  58. package/skills/wix-headless-fast/references/blog/rest/posts.ts +0 -42
  59. package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +0 -62
  60. package/skills/wix-headless-fast/references/blog/seed/SEED.md +0 -81
  61. package/skills/wix-headless-fast/references/blog/seed/read-site.mjs +0 -22
  62. package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +0 -287
  63. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +0 -277
  64. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +0 -144
  65. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +0 -119
  66. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +0 -32
  67. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +0 -23
  68. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +0 -218
  69. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +0 -169
  70. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +0 -121
  71. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +0 -92
  72. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +0 -90
  73. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +0 -57
  74. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +0 -72
  75. package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +0 -40
  76. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +0 -76
  77. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +0 -22
  78. package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +0 -11962
  79. package/skills/wix-headless-fast/references/bookings/rest/booking.ts +0 -113
  80. package/skills/wix-headless-fast/references/bookings/rest/services.ts +0 -55
  81. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +0 -71
  82. package/skills/wix-headless-fast/references/bookings/seed/read-site.mjs +0 -28
  83. package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +0 -302
  84. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +0 -355
  85. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +0 -50
  86. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +0 -37
  87. package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +0 -158
  88. package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +0 -93
  89. package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +0 -177
  90. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +0 -123
  91. package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +0 -75
  92. package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +0 -41
  93. package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +0 -11262
  94. package/skills/wix-headless-fast/references/cms/rest/items.ts +0 -125
  95. package/skills/wix-headless-fast/references/cms/seed/SEED.md +0 -112
  96. package/skills/wix-headless-fast/references/cms/seed/read-site.mjs +0 -20
  97. package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +0 -324
  98. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +0 -307
  99. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +0 -66
  100. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +0 -178
  101. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +0 -118
  102. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +0 -35
  103. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +0 -24
  104. package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +0 -130
  105. package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +0 -97
  106. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +0 -55
  107. package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +0 -113
  108. package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +0 -156
  109. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +0 -97
  110. package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +0 -61
  111. package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +0 -40
  112. package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +0 -12
  113. package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +0 -80
  114. package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +0 -21
  115. package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +0 -11884
  116. package/skills/wix-headless-fast/references/events/rest/events.ts +0 -57
  117. package/skills/wix-headless-fast/references/events/rest/registration.ts +0 -83
  118. package/skills/wix-headless-fast/references/events/seed/SEED.md +0 -95
  119. package/skills/wix-headless-fast/references/events/seed/read-site.mjs +0 -26
  120. package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +0 -276
  121. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +0 -340
  122. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +0 -85
  123. package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +0 -332
  124. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +0 -238
  125. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +0 -54
  126. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +0 -216
  127. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +0 -59
  128. package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +0 -135
  129. package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +0 -10593
  130. package/skills/wix-headless-fast/references/forms/rest/forms.ts +0 -34
  131. package/skills/wix-headless-fast/references/forms/rest/submissions.ts +0 -56
  132. package/skills/wix-headless-fast/references/forms/seed/SEED.md +0 -116
  133. package/skills/wix-headless-fast/references/forms/seed/read-site.mjs +0 -20
  134. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +0 -327
  135. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +0 -316
  136. package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +0 -60
  137. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +0 -155
  138. package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +0 -78
  139. package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +0 -47
  140. package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +0 -66
  141. package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +0 -105
  142. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +0 -59
  143. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +0 -53
  144. package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +0 -122
  145. package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +0 -43
  146. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +0 -29
  147. package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +0 -21
  148. package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +0 -42
  149. package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +0 -10
  150. package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +0 -11
  151. package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +0 -10329
  152. package/skills/wix-headless-fast/references/members/rest/auth.ts +0 -215
  153. package/skills/wix-headless-fast/references/members/rest/members.ts +0 -26
  154. package/skills/wix-headless-fast/references/members/seed/SEED.md +0 -39
  155. package/skills/wix-headless-fast/references/members/seed/read-site.mjs +0 -15
  156. package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +0 -94
  157. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +0 -315
  158. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +0 -110
  159. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +0 -90
  160. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +0 -35
  161. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +0 -85
  162. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +0 -32
  163. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +0 -21
  164. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +0 -31
  165. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +0 -82
  166. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +0 -58
  167. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +0 -177
  168. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +0 -78
  169. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +0 -81
  170. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +0 -55
  171. package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +0 -40
  172. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +0 -30
  173. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +0 -21
  174. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +0 -29
  175. package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +0 -10197
  176. package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +0 -75
  177. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +0 -86
  178. package/skills/wix-headless-fast/references/portfolio/seed/read-site.mjs +0 -20
  179. package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +0 -256
  180. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +0 -296
  181. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +0 -47
  182. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +0 -101
  183. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +0 -41
  184. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +0 -16
  185. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +0 -22
  186. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +0 -54
  187. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +0 -106
  188. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +0 -64
  189. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +0 -27
  190. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +0 -43
  191. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +0 -22
  192. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +0 -30
  193. package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +0 -41
  194. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +0 -40
  195. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +0 -21
  196. package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +0 -11226
  197. package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +0 -31
  198. package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +0 -20
  199. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +0 -79
  200. package/skills/wix-headless-fast/references/pricing-plans/seed/read-site.mjs +0 -21
  201. package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +0 -279
  202. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +0 -360
  203. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +0 -142
  204. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +0 -26
  205. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +0 -121
  206. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +0 -155
  207. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +0 -34
  208. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +0 -43
  209. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +0 -34
  210. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +0 -155
  211. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +0 -89
  212. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +0 -71
  213. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +0 -103
  214. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +0 -124
  215. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +0 -185
  216. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +0 -193
  217. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +0 -94
  218. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +0 -74
  219. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +0 -168
  220. package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +0 -47
  221. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +0 -22
  222. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +0 -12
  223. package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +0 -12560
  224. package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +0 -60
  225. package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +0 -169
  226. package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +0 -68
  227. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +0 -89
  228. package/skills/wix-headless-fast/references/restaurants/seed/read-site.mjs +0 -25
  229. package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +0 -499
  230. package/skills/wix-headless-fast/references/shared/CONTENT.md +0 -22
  231. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +0 -73
  232. package/skills/wix-headless-fast/references/shared/DESIGN.md +0 -26
  233. package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +0 -23
  234. package/skills/wix-headless-fast/references/shared/app/styles/global.css +0 -52
  235. package/skills/wix-headless-fast/references/shared/app/wix/config.ts +0 -15
  236. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +0 -81
  237. package/skills/wix-headless-fast/references/shared/app/wix/money.ts +0 -24
  238. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +0 -89
  239. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +0 -46
  240. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +0 -46
  241. package/skills/wix-headless-fast/references/shared/rest/client.ts +0 -155
  242. package/skills/wix-headless-fast/references/shared/rest/config.ts +0 -3
  243. package/skills/wix-headless-fast/references/shared/rest/media.ts +0 -65
  244. package/skills/wix-headless-fast/references/shared/seed/images.mjs +0 -145
  245. package/skills/wix-headless-fast/references/shared/seed/read-site.mjs +0 -99
  246. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +0 -612
  247. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +0 -26
  248. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +0 -155
  249. package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +0 -266
  250. package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +0 -95
  251. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +0 -136
  252. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +0 -48
  253. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +0 -39
  254. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +0 -34
  255. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +0 -121
  256. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +0 -107
  257. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +0 -138
  258. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +0 -314
  259. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +0 -133
  260. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +0 -147
  261. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +0 -289
  262. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +0 -169
  263. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +0 -65
  264. package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +0 -12247
  265. package/skills/wix-headless-fast/references/storefront/rest/cart.ts +0 -132
  266. package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +0 -123
  267. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +0 -130
  268. package/skills/wix-headless-fast/references/storefront/seed/read-site.mjs +0 -38
  269. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +0 -561
@@ -1,155 +0,0 @@
1
- // Slide-over cart with quantity stepper, remove, live subtotal, and Wix-hosted checkout.
2
- // Mount ONCE per page, as-is; opens via useCart().openCart() / CartButton. Styled from the
3
- // @theme tokens.
4
- import { useEffect, useRef } from "react";
5
- import { useCart } from "../../hooks/storefront/useCart";
6
-
7
- export default function CartDrawer() {
8
- const { cart, open, closeCart, busy, error, updateQuantity, removeLine, checkout } = useCart();
9
-
10
- // The overlay contract, done here rather than assumed from CSS: Escape closes (the backdrop
11
- // click is pointer-only), the page behind stops scrolling, focus moves into the panel on open
12
- // and returns to whatever opened it on close. Hooks run before the early return below.
13
- const panelRef = useRef<HTMLElement>(null);
14
- useEffect(() => {
15
- if (!open) return;
16
- const opener = document.activeElement;
17
- const previousOverflow = document.body.style.overflow;
18
- document.body.style.overflow = "hidden";
19
- panelRef.current?.focus();
20
- const onKey = (e: KeyboardEvent) => e.key === "Escape" && closeCart();
21
- document.addEventListener("keydown", onKey);
22
- return () => {
23
- document.removeEventListener("keydown", onKey);
24
- document.body.style.overflow = previousOverflow;
25
- if (opener instanceof HTMLElement) opener.focus();
26
- };
27
- }, [open, closeCart]);
28
-
29
- if (!open) return null;
30
- const lines = cart?.lines ?? [];
31
-
32
- return (
33
- <div
34
- className="fixed inset-0 z-50 flex justify-end bg-foreground/40 backdrop-blur-[2px]"
35
- onClick={closeCart}
36
- >
37
- <aside
38
- ref={panelRef}
39
- tabIndex={-1}
40
- role="dialog"
41
- aria-modal="true"
42
- aria-label="Cart"
43
- onClick={(e) => e.stopPropagation()}
44
- className="flex h-full w-full max-w-md flex-col bg-background text-foreground shadow-2xl outline-none"
45
- >
46
- <div className="flex items-center justify-between border-b border-border px-6 py-5">
47
- <span className="text-base font-semibold">
48
- Cart{cart && cart.itemCount > 0 ? ` (${cart.itemCount})` : ""}
49
- </span>
50
- <button
51
- type="button"
52
- aria-label="Close cart"
53
- onClick={closeCart}
54
- className="text-xl leading-none text-muted-foreground transition-colors hover:text-foreground"
55
- >
56
- ×
57
- </button>
58
- </div>
59
-
60
- <div className="flex-1 overflow-y-auto px-6 py-5">
61
- {lines.length === 0 && (
62
- <p className="py-16 text-center text-sm text-muted-foreground">Your cart is empty.</p>
63
- )}
64
- {lines.map((line) => (
65
- <div className="mb-6 flex gap-4" key={line.lineItemId}>
66
- {line.imageUrl ? (
67
- <img
68
- src={line.imageUrl}
69
- alt=""
70
- loading="lazy"
71
- className="h-16 w-16 flex-shrink-0 rounded-md bg-secondary object-cover"
72
- />
73
- ) : (
74
- <div aria-hidden="true" className="h-16 w-16 flex-shrink-0 rounded-md bg-secondary" />
75
- )}
76
- <div className="min-w-0 flex-1">
77
- <p className="text-sm font-medium">{line.productName}</p>
78
- {line.descriptionLines.map((d) => (
79
- <p className="mt-0.5 text-xs text-muted-foreground" key={d}>
80
- {d}
81
- </p>
82
- ))}
83
- {line.subscription && (
84
- <p className="mt-0.5 text-xs text-muted-foreground">Subscription: {line.subscription}</p>
85
- )}
86
- {line.status !== "IN_STOCK" && (
87
- <p className="mt-1 text-xs text-red-600">No longer available at this quantity</p>
88
- )}
89
- <div className="mt-2 flex items-center">
90
- <div className="inline-flex items-center gap-3 rounded-full border border-border px-2 py-0.5">
91
- <button
92
- type="button"
93
- aria-label="Decrease quantity"
94
- disabled={busy || line.quantity <= 1}
95
- onClick={() => updateQuantity(line.lineItemId, line.quantity - 1).catch(() => {})}
96
- className="px-1 text-base disabled:opacity-40"
97
- >
98
- −
99
- </button>
100
- <span className="text-sm tabular-nums">{line.quantity}</span>
101
- <button
102
- type="button"
103
- aria-label="Increase quantity"
104
- disabled={busy}
105
- onClick={() => updateQuantity(line.lineItemId, line.quantity + 1).catch(() => {})}
106
- className="px-1 text-base disabled:opacity-40"
107
- >
108
- +
109
- </button>
110
- </div>
111
- <button
112
- type="button"
113
- disabled={busy}
114
- onClick={() => removeLine(line.lineItemId).catch(() => {})}
115
- className="ml-3 text-xs text-muted-foreground underline transition-colors hover:text-foreground disabled:opacity-40"
116
- >
117
- Remove
118
- </button>
119
- </div>
120
- </div>
121
- <p className="text-sm font-medium">{line.linePrice}</p>
122
- </div>
123
- ))}
124
- </div>
125
-
126
- {lines.length > 0 && (
127
- <div className="border-t border-border px-6 py-5">
128
- {cart?.discount && (
129
- <div className="mb-2 flex justify-between text-sm">
130
- <span className="text-muted-foreground">Discount</span>
131
- <span>−{cart.discount}</span>
132
- </div>
133
- )}
134
- {cart?.subtotal && (
135
- <div className="mb-1 flex justify-between text-sm">
136
- <span className="text-muted-foreground">Subtotal</span>
137
- <strong className="font-semibold">{cart.subtotal}</strong>
138
- </div>
139
- )}
140
- <p className="mb-4 text-xs text-muted-foreground">Shipping and taxes are calculated at checkout.</p>
141
- {error && <p className="mb-3 text-xs text-red-600">{error}</p>}
142
- <button
143
- type="button"
144
- disabled={busy}
145
- onClick={() => checkout().catch(() => {})}
146
- className="w-full rounded-full bg-primary py-3.5 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-50"
147
- >
148
- {busy ? "One moment…" : "Continue to secure checkout"}
149
- </button>
150
- </div>
151
- )}
152
- </aside>
153
- </div>
154
- );
155
- }
@@ -1,266 +0,0 @@
1
- // The gallery's filter layout — wire as-is, your grid goes inside:
2
- //
3
- // <FilterPanel shop={shop}>
4
- // …your loading / empty / error states and your grid of tiles…
5
- // </FilterPanel>
6
- //
7
- // Renders: a toolbar (result count, sort, and under `md` a Filters button); active-filter chips;
8
- // then on `md`+ a two-column layout — a 16rem sidebar of collapsible groups (Price as a two-handle
9
- // slider with numeric fields, Availability, one group per option facet with swatches or pills) beside
10
- // your results, so expanded facets never push the products below the fold. Under `md` the same
11
- // groups live in a bottom sheet: changes are staged and commit on Apply; dismissing keeps the
12
- // current results. Sidebar changes commit immediately (a price pair on release/blur, valid only).
13
- // Styled from the @theme tokens.
14
- import { useEffect, useRef, useState, type ReactNode } from "react";
15
- import { SORTS, type ShopFilters, type UseShop } from "../../hooks/storefront/useShop";
16
- import type { CatalogSort } from "../../wix/storefront/catalog";
17
-
18
- interface Draft {
19
- min: string;
20
- max: string;
21
- inStockOnly: boolean;
22
- choiceIds: string[];
23
- }
24
-
25
- const draftFrom = (shop: UseShop): Draft => ({
26
- min: String(shop.filters.minPrice ?? ""),
27
- max: String(shop.filters.maxPrice ?? ""),
28
- inStockOnly: !!shop.filters.inStockOnly,
29
- choiceIds: shop.selectedChoiceIds,
30
- });
31
-
32
- function validPricePair(min: string, max: string): boolean {
33
- if (min && !Number.isFinite(Number(min))) return false;
34
- if (max && !Number.isFinite(Number(max))) return false;
35
- if (min && max && Number(min) > Number(max)) return false;
36
- return true;
37
- }
38
-
39
- export default function FilterPanel({ shop, children }: { shop: UseShop; children?: ReactNode }) {
40
- const [sheetOpen, setSheetOpen] = useState(false);
41
- const [staged, setStaged] = useState<Draft>(() => draftFrom(shop));
42
- const panelRef = useRef<HTMLElement>(null);
43
-
44
- // The sheet is an overlay: root-level, scrim, scroll lock, Escape, focus in and back.
45
- useEffect(() => {
46
- if (!sheetOpen) return;
47
- setStaged(draftFrom(shop));
48
- const opener = document.activeElement;
49
- const previousOverflow = document.body.style.overflow;
50
- document.body.style.overflow = "hidden";
51
- panelRef.current?.focus();
52
- const onKey = (e: KeyboardEvent) => e.key === "Escape" && setSheetOpen(false);
53
- document.addEventListener("keydown", onKey);
54
- return () => {
55
- document.removeEventListener("keydown", onKey);
56
- document.body.style.overflow = previousOverflow;
57
- if (opener instanceof HTMLElement) opener.focus();
58
- };
59
- // eslint-disable-next-line react-hooks/exhaustive-deps
60
- }, [sheetOpen]);
61
-
62
- const commit = (d: Draft) => {
63
- if (!validPricePair(d.min, d.max)) return;
64
- const next: ShopFilters = {
65
- ...shop.filters,
66
- minPrice: d.min || undefined,
67
- maxPrice: d.max || undefined,
68
- inStockOnly: d.inStockOnly || undefined,
69
- };
70
- const same =
71
- next.minPrice === shop.filters.minPrice &&
72
- next.maxPrice === shop.filters.maxPrice &&
73
- !!next.inStockOnly === !!shop.filters.inStockOnly;
74
- if (!same) shop.setFilters(next);
75
- for (const id of d.choiceIds) if (!shop.selectedChoiceIds.includes(id)) shop.toggleChoice(id);
76
- for (const id of shop.selectedChoiceIds) if (!d.choiceIds.includes(id)) shop.toggleChoice(id);
77
- };
78
-
79
- const activeCount =
80
- shop.selectedChoiceIds.length + (shop.filters.inStockOnly ? 1 : 0) + (shop.filters.minPrice || shop.filters.maxPrice ? 1 : 0);
81
- const choiceName = (id: string) => shop.facets.flatMap((f) => f.choices).find((c) => c.id === id)?.name ?? id;
82
-
83
- return (
84
- <div className="flex flex-col gap-4">
85
- {/* Toolbar */}
86
- <div className="flex flex-wrap items-center justify-between gap-3">
87
- <p className="text-sm text-muted-foreground" aria-live="polite">
88
- {shop.total != null ? `${shop.total} ${shop.total === 1 ? "product" : "products"}` : ""}
89
- </p>
90
- <div className="flex items-center gap-2">
91
- <button type="button" onClick={() => setSheetOpen(true)} className="rounded-md border border-border px-3 py-1.5 text-sm md:hidden">
92
- Filters{activeCount ? ` (${activeCount})` : ""}
93
- </button>
94
- <label className="sr-only" htmlFor="shop-sort">Sort by</label>
95
- <select id="shop-sort" value={shop.sort} onChange={(e) => shop.setSort(e.target.value as CatalogSort)}
96
- className="rounded-md border border-border bg-background px-2 py-1.5 text-sm">
97
- {Object.entries(SORTS).map(([key, { label }]) => <option key={key} value={key}>{label}</option>)}
98
- </select>
99
- </div>
100
- </div>
101
-
102
- {/* Active chips */}
103
- {shop.hasActiveFilters && (
104
- <div className="flex flex-wrap items-center gap-1.5" aria-label="Active filters">
105
- {shop.selectedChoiceIds.map((id) => (
106
- <button key={id} type="button" onClick={() => shop.toggleChoice(id)} className="rounded-full bg-secondary px-2.5 py-1 text-xs text-foreground">
107
- {choiceName(id)} ×
108
- </button>
109
- ))}
110
- {shop.filters.inStockOnly && (
111
- <button type="button" onClick={() => shop.setFilters({ ...shop.filters, inStockOnly: undefined })} className="rounded-full bg-secondary px-2.5 py-1 text-xs">In stock ×</button>
112
- )}
113
- {(shop.filters.minPrice || shop.filters.maxPrice) && (
114
- <button type="button" onClick={() => shop.setFilters({ ...shop.filters, minPrice: undefined, maxPrice: undefined })} className="rounded-full bg-secondary px-2.5 py-1 text-xs">
115
- Price {shop.filters.minPrice ?? shop.priceRange?.min ?? "…"}–{shop.filters.maxPrice ?? shop.priceRange?.max ?? "…"} ×
116
- </button>
117
- )}
118
- <button type="button" onClick={shop.clearFilters} className="text-xs text-muted-foreground underline">Clear all</button>
119
- </div>
120
- )}
121
-
122
- {/* Sidebar + results */}
123
- <div className="md:grid md:grid-cols-[16rem_minmax(0,1fr)] md:gap-8">
124
- <aside className="hidden md:block" aria-label="Filters">
125
- <Groups shop={shop} draft={draftFrom(shop)} onChange={commit} immediate />
126
- </aside>
127
- <div className="min-w-0">{children}</div>
128
- </div>
129
-
130
- {/* Sheet — small screens; staged until Apply */}
131
- {sheetOpen && (
132
- <div className="fixed inset-0 z-50 flex items-end bg-foreground/40 md:hidden" onClick={() => setSheetOpen(false)}>
133
- <section ref={panelRef} tabIndex={-1} role="dialog" aria-modal="true" aria-label="Filters" onClick={(e) => e.stopPropagation()}
134
- className="flex max-h-[85vh] w-full flex-col rounded-t-2xl bg-background text-foreground outline-none">
135
- <div className="flex items-center justify-between px-5 pt-5">
136
- <h2 className="text-base font-semibold">Filters</h2>
137
- <button type="button" aria-label="Close filters" onClick={() => setSheetOpen(false)} className="text-xl leading-none">×</button>
138
- </div>
139
- <div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">
140
- <Groups shop={shop} draft={staged} onChange={setStaged} />
141
- </div>
142
- <div className="flex gap-2 border-t border-border bg-background px-5 py-3">
143
- <button type="button" onClick={() => setStaged({ min: "", max: "", inStockOnly: false, choiceIds: [] })}
144
- className="flex-1 rounded-full border border-border py-2.5 text-sm">Clear</button>
145
- <button type="button" onClick={() => { commit(staged); setSheetOpen(false); }}
146
- className="flex-1 rounded-full bg-primary py-2.5 text-sm font-semibold text-primary-foreground">Apply</button>
147
- </div>
148
- </section>
149
- </div>
150
- )}
151
- </div>
152
- );
153
- }
154
-
155
- // The filter groups — one markup for the sidebar (immediate commit) and the sheet (staged).
156
- function Groups({ shop, draft, onChange, immediate = false }: { shop: UseShop; draft: Draft; onChange: (d: Draft) => void; immediate?: boolean }) {
157
- const toggle = (id: string) =>
158
- onChange({ ...draft, choiceIds: draft.choiceIds.includes(id) ? draft.choiceIds.filter((x) => x !== id) : [...draft.choiceIds, id] });
159
- return (
160
- <div className="flex flex-col divide-y divide-border">
161
- <Group title="Price">
162
- <PriceControl
163
- range={shop.priceRange}
164
- min={draft.min}
165
- max={draft.max}
166
- onCommit={(min, max) => onChange({ ...draft, min, max })}
167
- immediate={immediate}
168
- />
169
- </Group>
170
- <Group title="Availability">
171
- <label className="flex items-center gap-2 text-sm">
172
- <input type="checkbox" checked={draft.inStockOnly} onChange={(e) => onChange({ ...draft, inStockOnly: e.target.checked })} />
173
- In stock only
174
- </label>
175
- </Group>
176
- {shop.facets.map((facet) => (
177
- <Group key={facet.name} title={facet.name} count={facet.choices.filter((c) => draft.choiceIds.includes(c.id)).length}>
178
- <div className="flex flex-wrap gap-1.5">
179
- {facet.choices.map((c) => {
180
- const on = draft.choiceIds.includes(c.id);
181
- return facet.isColor && c.colorCode ? (
182
- <button key={c.id} type="button" aria-pressed={on} aria-label={c.name} title={c.name} onClick={() => toggle(c.id)}
183
- className={`h-7 w-7 rounded-full border-2 ${on ? "border-foreground" : "border-border"}`} style={{ backgroundColor: c.colorCode }} />
184
- ) : (
185
- <button key={c.id} type="button" aria-pressed={on} onClick={() => toggle(c.id)}
186
- className={`rounded-full border px-3 py-1 text-sm ${on ? "border-foreground bg-foreground text-background" : "border-border bg-background text-foreground"}`}>
187
- {c.name}
188
- </button>
189
- );
190
- })}
191
- </div>
192
- </Group>
193
- ))}
194
- </div>
195
- );
196
- }
197
-
198
- // A collapsible group — native <details>, open by default, the selected count on the summary.
199
- function Group({ title, count = 0, children }: { title: string; count?: number; children: ReactNode }) {
200
- return (
201
- <details open className="group py-3">
202
- <summary className="flex cursor-pointer list-none items-center justify-between text-sm font-medium">
203
- <span>{title}{count ? <span className="ml-1.5 text-xs text-muted-foreground">({count})</span> : null}</span>
204
- <span aria-hidden="true" className="text-muted-foreground transition-transform group-open:rotate-180">⌄</span>
205
- </summary>
206
- <div className="pt-3">{children}</div>
207
- </details>
208
- );
209
- }
210
-
211
- // Two-handle range slider bounded by the scope's real prices, with the numeric pair under it.
212
- // Local drafts while dragging/typing; a VALID pair commits on release / blur / Enter (immediate mode)
213
- // or on every change (staged mode, where Apply commits later).
214
- function PriceControl({ range, min, max, onCommit, immediate }: {
215
- range: UseShop["priceRange"]; min: string; max: string; onCommit: (min: string, max: string) => void; immediate: boolean;
216
- }) {
217
- const [lo, setLo] = useState(min);
218
- const [hi, setHi] = useState(max);
219
- useEffect(() => { setLo(min); setHi(max); }, [min, max]);
220
- const bounds = range ?? null;
221
- const loN = lo === "" ? (bounds?.min ?? 0) : Number(lo);
222
- const hiN = hi === "" ? (bounds?.max ?? 0) : Number(hi);
223
- const step = bounds ? Math.max(1, Math.round((bounds.max - bounds.min) / 100)) : 1;
224
- const pct = (v: number) => (bounds ? ((v - bounds.min) / (bounds.max - bounds.min)) * 100 : 0);
225
- const publish = (a: string, b: string) => {
226
- // at the bounds the filter is "no bound" — omit it rather than sending the catalog's own extremes
227
- const A = bounds && a !== "" && Number(a) <= bounds.min ? "" : a;
228
- const B = bounds && b !== "" && Number(b) >= bounds.max ? "" : b;
229
- if (validPricePair(A, B)) onCommit(A, B);
230
- };
231
- const setFromSlider = (which: "lo" | "hi", v: number) => {
232
- const a = which === "lo" ? String(Math.min(v, hiN)) : lo;
233
- const b = which === "hi" ? String(Math.max(v, loN)) : hi;
234
- setLo(a); setHi(b);
235
- if (!immediate) publish(a, b);
236
- };
237
- const release = () => immediate && publish(lo, hi);
238
- const thumb = "pointer-events-none absolute inset-x-0 top-0 h-6 w-full appearance-none bg-transparent [&::-webkit-slider-thumb]:pointer-events-auto [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-background [&::-moz-range-thumb]:pointer-events-auto [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-foreground [&::-moz-range-thumb]:bg-background";
239
- return (
240
- <div className="flex flex-col gap-3">
241
- {bounds && (
242
- <div className="relative h-6" aria-hidden={false}>
243
- <div className="absolute inset-x-0 top-1/2 h-1 -translate-y-1/2 rounded bg-secondary" />
244
- <div className="absolute top-1/2 h-1 -translate-y-1/2 rounded bg-foreground" style={{ left: `${pct(loN)}%`, right: `${100 - pct(hiN)}%` }} />
245
- <input type="range" aria-label="Minimum price" min={bounds.min} max={bounds.max} step={step} value={loN}
246
- onChange={(e) => setFromSlider("lo", Number(e.target.value))} onPointerUp={release} onKeyUp={release} className={thumb} />
247
- <input type="range" aria-label="Maximum price" min={bounds.min} max={bounds.max} step={step} value={hiN}
248
- onChange={(e) => setFromSlider("hi", Number(e.target.value))} onPointerUp={release} onKeyUp={release} className={thumb} />
249
- </div>
250
- )}
251
- <div className="flex items-center gap-2">
252
- <label className="sr-only" htmlFor="filter-min">Minimum price</label>
253
- <input id="filter-min" inputMode="decimal" placeholder={bounds ? String(bounds.min) : "Min"} value={lo}
254
- onChange={(e) => { setLo(e.target.value); if (!immediate) publish(e.target.value, hi); }}
255
- onBlur={release} onKeyDown={(e) => e.key === "Enter" && release()}
256
- className="w-full min-w-0 rounded-md border border-border bg-background px-2 py-1.5 text-sm" />
257
- <span className="text-muted-foreground">–</span>
258
- <label className="sr-only" htmlFor="filter-max">Maximum price</label>
259
- <input id="filter-max" inputMode="decimal" placeholder={bounds ? String(bounds.max) : "Max"} value={hi}
260
- onChange={(e) => { setHi(e.target.value); if (!immediate) publish(lo, e.target.value); }}
261
- onBlur={release} onKeyDown={(e) => e.key === "Enter" && release()}
262
- className="w-full min-w-0 rounded-md border border-border bg-background px-2 py-1.5 text-sm" />
263
- </div>
264
- </div>
265
- );
266
- }
@@ -1,95 +0,0 @@
1
- // The purchase controls for one product — option groups (swatches for a color option, pills
2
- // otherwise, out-of-stock choices disabled), choice modifiers, an optional quantity stepper, and
3
- // the buy button gated by the hook: disabled with a plain reason ("Choose Size") until every
4
- // choice is made, "Pre-order" when the resolved variant is pre-orderable, the hook's error inline.
5
- // QuickAdd's picker and your product page mount the same component, so buying behaves the same
6
- // in the gallery and on the PDP. Wire as-is; style via the tokens.
7
- //
8
- // const d = useProductDetail({ initial }); // the PDP
9
- // <OptionPicker detail={d} showQuantity />
10
- //
11
- // Price, name, gallery, description, and layout stay yours — render d.price / d.compareAtPrice
12
- // beside this (the range until every option is picked, then the variant's price).
13
- import type { UseProductDetail } from "../../hooks/storefront/useProductDetail";
14
-
15
- export default function OptionPicker({
16
- detail: d,
17
- showQuantity = false,
18
- onAdded,
19
- }: {
20
- detail: UseProductDetail;
21
- /** The PDP shows a quantity stepper; a tile picker adds one. */
22
- showQuantity?: boolean;
23
- /** Called after a successful add (a tile picker closes itself here). */
24
- onAdded?: () => void;
25
- }) {
26
- if (!d.product) return null;
27
- return (
28
- <div className="flex flex-col gap-3">
29
- {d.optionGroups.map((g) => (
30
- <fieldset key={g.id}>
31
- <legend className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">{g.name}</legend>
32
- <div className="flex flex-wrap gap-1.5">
33
- {g.choices.map((c) =>
34
- g.isColor && c.colorCode ? (
35
- <button key={c.choiceId} type="button" aria-label={c.name} title={c.name} aria-pressed={c.selected}
36
- disabled={!c.inStock} onClick={() => d.selectOption(g.name, c.name)}
37
- className={`h-8 w-8 rounded-full border-2 disabled:opacity-30 ${c.selected ? "border-foreground" : "border-border"}`}
38
- style={{ backgroundColor: c.colorCode }} />
39
- ) : (
40
- <button key={c.choiceId} type="button" aria-pressed={c.selected} disabled={!c.inStock}
41
- onClick={() => d.selectOption(g.name, c.name)}
42
- className={`rounded-full border px-3 py-1 text-sm disabled:line-through disabled:opacity-40 ${c.selected ? "border-foreground bg-foreground text-background" : "border-border"}`}>
43
- {c.name}
44
- </button>
45
- ),
46
- )}
47
- </div>
48
- </fieldset>
49
- ))}
50
- {d.product.modifiers.filter((m) => m.type === "choices").map((m) => (
51
- <fieldset key={m.key}>
52
- <legend className="mb-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
53
- {m.name}{m.mandatory ? " *" : ""}
54
- </legend>
55
- <div className="flex flex-wrap gap-1.5">
56
- {m.choices.map((c) => (
57
- <button key={c.key} type="button" aria-pressed={d.modifierValues[m.key] === c.key}
58
- onClick={() => d.setModifier(m.key, c.key)}
59
- className={`rounded-full border px-3 py-1 text-sm ${d.modifierValues[m.key] === c.key ? "border-foreground bg-foreground text-background" : "border-border"}`}>
60
- {c.name}
61
- </button>
62
- ))}
63
- </div>
64
- </fieldset>
65
- ))}
66
- {d.product.modifiers.filter((m) => m.type === "text").map((m) => (
67
- <label key={m.key} className="flex flex-col gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
68
- {m.name}{m.mandatory ? " *" : ""}
69
- <input type="text" value={d.modifierValues[m.key] ?? ""} onChange={(e) => d.setModifier(m.key, e.target.value)}
70
- className="rounded-md border border-border bg-background px-3 py-2 text-sm normal-case tracking-normal text-foreground" />
71
- </label>
72
- ))}
73
- <div className="flex items-stretch gap-2">
74
- {showQuantity && (
75
- <div className="flex items-center rounded-full border border-border">
76
- <button type="button" aria-label="Decrease quantity" disabled={d.quantity <= 1} onClick={() => d.setQuantity(d.quantity - 1)}
77
- className="px-3 py-2 text-sm disabled:opacity-40">−</button>
78
- <span className="min-w-6 text-center text-sm tabular-nums">{d.quantity}</span>
79
- <button type="button" aria-label="Increase quantity" onClick={() => d.setQuantity(d.quantity + 1)} className="px-3 py-2 text-sm">+</button>
80
- </div>
81
- )}
82
- <button
83
- type="button"
84
- disabled={!d.canAdd || d.adding}
85
- onClick={() => d.add().then(() => onAdded?.()).catch(() => {})}
86
- className="flex-1 rounded-full bg-primary py-2.5 text-sm font-semibold text-primary-foreground disabled:opacity-50"
87
- >
88
- {d.adding ? "Adding…" : d.isPreorder ? "Pre-order" : "Add to cart"}
89
- </button>
90
- </div>
91
- {d.blockedReason && !d.canAdd && <p className="text-xs text-muted-foreground">{d.blockedReason}</p>}
92
- {d.error && <p className="text-xs text-red-600">{d.error}</p>}
93
- </div>
94
- );
95
- }
@@ -1,136 +0,0 @@
1
- // The card's purchase control — the three gallery purchase paths, decided from the product:
2
- // • no options → Direct Add: one click, the cheapest variant, quantity 1
3
- // • options / choice mods → Quick Add: a picker on the card (bottom sheet on small screens)
4
- // • free-text modifier → the product page (the gallery can't collect the text)
5
- // Mount as the LAST ROW of the tile's text block (under name and price), as a direct child of the
6
- // tile root that carries `relative flex flex-col` — the picker anchors to that root and takes its
7
- // width, and the control pins itself to the tile's bottom (mt-auto) so the action row lines up
8
- // across a grid row whether or not a neighbour carries swatches or a struck price. Never overlay
9
- // it on the image, never wrap it in a narrower positioned box.
10
- // <QuickAdd product={p} /> Wire as-is; style via the tokens.
11
- import { useEffect, useRef, useState } from "react";
12
- import { useCart } from "../../hooks/storefront/useCart";
13
- import { useProductDetail } from "../../hooks/storefront/useProductDetail";
14
- import type { ProductSummary } from "../../wix/storefront/types";
15
- import OptionPicker from "./OptionPicker";
16
-
17
- export default function QuickAdd({ product }: { product: ProductSummary }) {
18
- return (
19
- // A tile is often one big <a>. A click inside the buy control must never become a navigation
20
- // to the product page — this boundary cancels the link's default for everything below it
21
- // (the button, the picker, the sheet). Still: keep the control OUTSIDE the tile's link.
22
- <div
23
- className="contents"
24
- onClick={(e) => {
25
- e.preventDefault();
26
- e.stopPropagation();
27
- }}
28
- >
29
- <QuickAddControl product={product} />
30
- </div>
31
- );
32
- }
33
-
34
- function QuickAddControl({ product }: { product: ProductSummary }) {
35
- const { addToCart, busy } = useCart();
36
- const [open, setOpen] = useState(false);
37
- const [error, setError] = useState<string | null>(null);
38
-
39
- if (product.availability === "OUT_OF_STOCK" && !product.preorder) {
40
- return <span className="mt-auto block pt-3 text-sm text-muted-foreground">Out of stock</span>;
41
- }
42
- if (product.quickAddable) {
43
- return (
44
- <div className="mt-auto pt-3">
45
- <button
46
- type="button"
47
- disabled={busy}
48
- onClick={() =>
49
- addToCart(product.id, product.minPriceVariantId, 1).catch((e) => setError(e instanceof Error ? e.message : String(e)))
50
- }
51
- className="w-full rounded-full border border-foreground py-2 text-sm font-medium transition-colors hover:bg-foreground hover:text-background disabled:opacity-50"
52
- >
53
- {product.preorder ? "Pre-order" : "Add to cart"}
54
- </button>
55
- {error && <p className="mt-1 text-xs text-red-600">{error}</p>}
56
- </div>
57
- );
58
- }
59
- // Options to pick, or a single variant that isn't plainly in stock (pre-order, partial stock):
60
- // the picker resolves it through useProductDetail, exactly as the PDP would.
61
- return (
62
- <div className="mt-auto pt-3">
63
- <button
64
- type="button"
65
- onClick={() => setOpen(true)}
66
- className="w-full rounded-full border border-foreground py-2 text-sm font-medium transition-colors hover:bg-foreground hover:text-background"
67
- >
68
- {product.optionsSummary ? "Choose options" : product.preorder ? "Pre-order" : "Add to cart"}
69
- </button>
70
- {open && <QuickAddPicker product={product} onClose={() => setOpen(false)} />}
71
- </div>
72
- );
73
- }
74
-
75
- // The picker fetches the full product only when it opens — cards never carry PDP data. Its controls
76
- // are the shipped OptionPicker, the same component the PDP mounts.
77
- function QuickAddPicker({ product, onClose }: { product: ProductSummary; onClose: () => void }) {
78
- const d = useProductDetail({ slug: product.slug });
79
- const panelRef = useRef<HTMLElement>(null);
80
-
81
- // Overlay contract: Escape closes, scroll locks under md, focus moves in and back.
82
- useEffect(() => {
83
- const opener = document.activeElement;
84
- const isSmall = window.matchMedia("(max-width: 767px)").matches;
85
- const previousOverflow = document.body.style.overflow;
86
- if (isSmall) document.body.style.overflow = "hidden";
87
- panelRef.current?.focus();
88
- const onKey = (e: KeyboardEvent) => e.key === "Escape" && onClose();
89
- document.addEventListener("keydown", onKey);
90
- return () => {
91
- document.removeEventListener("keydown", onKey);
92
- document.body.style.overflow = previousOverflow;
93
- if (opener instanceof HTMLElement) opener.focus();
94
- };
95
- }, [onClose]);
96
-
97
- const needsPdp = (d.product?.modifiers ?? []).some((m) => m.type === "text");
98
-
99
- return (
100
- <>
101
- <div className="fixed inset-0 z-40 bg-foreground/40 md:hidden" onClick={onClose} />
102
- <section
103
- ref={panelRef}
104
- tabIndex={-1}
105
- role="dialog"
106
- aria-modal="true"
107
- aria-label={`Choose options for ${product.name}`}
108
- // md+: anchored to the tile (its nearest `relative` ancestor), the tile's full width — and never
109
- // narrower than 18rem even when mounted inside a small wrapper.
110
- className="fixed inset-x-0 bottom-0 z-50 max-h-[85vh] overflow-y-auto rounded-t-2xl bg-background p-5 text-foreground shadow-2xl outline-none md:absolute md:inset-x-auto md:bottom-0 md:right-0 md:w-[max(100%,18rem)] md:max-h-none md:rounded-lg md:p-4"
111
- >
112
- <div className="mb-3 flex items-start justify-between gap-3">
113
- <div className="min-w-0">
114
- <p className="truncate text-sm font-semibold">{product.name}</p>
115
- <p className="text-sm">
116
- {d.price}
117
- {d.compareAtPrice && <span className="ml-2 text-muted-foreground line-through">{d.compareAtPrice}</span>}
118
- </p>
119
- </div>
120
- <button type="button" aria-label="Close" onClick={onClose} className="text-xl leading-none text-muted-foreground">×</button>
121
- </div>
122
-
123
- {!d.product && !d.notFound && <p className="text-sm text-muted-foreground">Loading options…</p>}
124
- {d.notFound && <p className="text-sm text-muted-foreground">This product isn't available anymore.</p>}
125
-
126
- {d.product && needsPdp && (
127
- <a href={`/products/${product.slug}`} className="block rounded-full bg-primary py-2.5 text-center text-sm font-semibold text-primary-foreground">
128
- Customize on the product page
129
- </a>
130
- )}
131
-
132
- {d.product && !needsPdp && <OptionPicker detail={d} onAdded={onClose} />}
133
- </section>
134
- </>
135
- );
136
- }