@wix/agent-skills 1.14.0 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/README.md +12 -1
  2. package/package.json +1 -1
  3. package/skills/README.md +23 -0
  4. package/skills/wix-app/SKILL.md +10 -2
  5. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
  6. package/skills/wix-app/references/CODE_QUALITY.md +1 -0
  7. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
  9. package/skills/wix-app/references/DATA_COLLECTION.md +5 -3
  10. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +25 -9
  11. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  12. package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
  13. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  31. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  32. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  33. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
  34. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  35. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
  36. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  37. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  38. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
  39. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +23 -105
  40. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  41. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  42. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +18 -16
  43. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  44. package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
  45. package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
  46. package/skills/wix-app/references/stores/INVENTORY.md +37 -0
  47. package/skills/wix-app/references/stores/QUERY.md +87 -0
  48. package/skills/wix-app/references/stores/WRITE.md +55 -0
  49. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  50. package/skills/wix-auth/SKILL.md +12 -0
  51. package/skills/wix-auth/references/device-flow.md +140 -0
  52. package/skills/wix-docs/SKILL.md +188 -0
  53. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  54. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  55. package/skills/wix-headless/SKILL.md +89 -130
  56. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  57. package/skills/wix-headless/entry/skill.md +76 -0
  58. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  59. package/skills/wix-headless/references/CAPABILITIES.md +86 -0
  60. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  61. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  62. package/skills/wix-headless/references/FEEDBACK.md +129 -0
  63. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  64. package/skills/wix-headless/references/SDK_HANDOFF.md +99 -0
  65. package/skills/wix-headless/references/SEED.md +45 -187
  66. package/skills/wix-headless/references/SETUP.md +32 -103
  67. package/skills/wix-headless/references/astro.md +98 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  72. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  73. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  74. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  75. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  76. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  77. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  78. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  79. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  80. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +213 -0
  81. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  83. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +232 -0
  84. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  85. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  86. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  87. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +192 -0
  88. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +155 -0
  89. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  90. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
  91. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +197 -0
  92. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +136 -0
  93. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  94. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  95. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  96. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  97. package/skills/wix-headless/references/managed/DEPLOYMENT.md +79 -0
  98. package/skills/wix-headless/references/non-astro.md +59 -0
  99. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  100. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +15 -0
  101. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  102. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +71 -0
  103. package/skills/wix-manage/SKILL.md +29 -3
  104. package/skills/wix-manage/references/analytics/query-site-analytics.md +298 -0
  105. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
  106. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  107. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
  108. package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
  109. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +299 -0
  110. package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
  111. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
  112. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +517 -0
  113. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +218 -0
  114. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  115. package/skills/wix-manage/references/sites/site-import.md +270 -0
  116. package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
  117. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +57 -0
  118. package/skills/wix-vibe-headless/SKILL.md +142 -0
  119. package/skills/wix-vibe-headless/platforms/README.md +22 -0
  120. package/skills/wix-vibe-headless/platforms/base44.md +142 -0
  121. package/skills/wix-vibe-headless/platforms/generic.md +87 -0
  122. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  123. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  124. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +157 -0
  125. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  126. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +217 -0
  127. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  128. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  129. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  130. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +136 -0
  131. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  132. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  133. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  134. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  135. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +223 -0
  136. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  137. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  138. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  139. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  140. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  141. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  142. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  143. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +164 -0
  144. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  145. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +146 -0
  146. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  147. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  148. package/skills/wix-headless/references/BUILD.md +0 -105
  149. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  150. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  151. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  152. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  153. package/skills/wix-headless/references/PLAN-create.md +0 -17
  154. package/skills/wix-headless/references/PLAN.md +0 -92
  155. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  156. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  157. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  158. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  159. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  160. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  161. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  162. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  163. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  164. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  165. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  166. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  167. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  168. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  169. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  170. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  171. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  172. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  173. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  174. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  175. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  176. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  177. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  178. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  179. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  180. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  181. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  182. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  183. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  184. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  185. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  186. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  187. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  188. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  189. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  190. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  191. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  192. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  193. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  194. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  195. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  196. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  197. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  198. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  199. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  200. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  201. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  202. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  203. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  204. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  205. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  206. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  207. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  208. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  209. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  210. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  211. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  212. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  213. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  214. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  215. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  216. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  217. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  218. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  219. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  220. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  221. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  222. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  223. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  224. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  225. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  226. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  227. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  228. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  229. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  230. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  231. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  232. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  233. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  234. package/skills/wix-headless/references/seed-recipes.md +0 -49
  235. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  236. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  237. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  238. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  239. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  240. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  241. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  242. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  243. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  244. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  245. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  246. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  247. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  248. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  249. package/skills/wix-headless/references/verticals/blog.md +0 -31
  250. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  251. package/skills/wix-headless/references/verticals/cms.md +0 -30
  252. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  253. package/skills/wix-headless/references/verticals/forms.md +0 -26
  254. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  255. package/skills/wix-headless/references/verticals/stores.md +0 -33
  256. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  257. package/skills/wix-headless/scripts/compose.mjs +0 -520
  258. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  259. package/skills/wix-headless/scripts/flatten.sh +0 -61
  260. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  261. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  262. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  263. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  264. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  265. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  266. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  267. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  268. package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
@@ -1,543 +0,0 @@
1
- import { useState, useEffect, useRef } from "react";
2
- import { currentCart } from "@wix/ecom";
3
- import { redirects } from "@wix/redirects";
4
- import { trackEvent } from "../utils/analytics";
5
-
6
- // ── Types ──
7
-
8
- interface DescriptionLine {
9
- name?: { translated?: string };
10
- plainText?: { translated?: string };
11
- colorInfo?: { translated?: string; code?: string };
12
- }
13
-
14
- interface Availability {
15
- status?: string; // "AVAILABLE" | "NOT_AVAILABLE" | "NOT_FOUND" | "PARTIALLY_AVAILABLE"
16
- quantityAvailable?: number;
17
- }
18
-
19
- interface Modifier {
20
- label?: { translated?: string };
21
- quantity?: number;
22
- price?: { formattedConvertedAmount?: string };
23
- }
24
-
25
- interface ModifierGroup {
26
- name?: { translated?: string };
27
- modifiers?: Modifier[];
28
- }
29
-
30
- // Local structural type. The SDK's lineItem types use `string | null` for
31
- // translated fields; we keep this local shape narrower for ergonomic
32
- // destructuring and use `as unknown as LineItem[]` at SDK boundaries.
33
- // Narrowing is safe because we null-check each field at the use site.
34
- interface LineItem {
35
- _id?: string | null;
36
- productName?: { translated?: string };
37
- quantity?: number;
38
- price?: { amount?: string; formattedConvertedAmount?: string };
39
- fullPrice?: { formattedConvertedAmount?: string };
40
- lineItemPrice?: { amount?: string; formattedConvertedAmount?: string };
41
- image?: string; // "wix:image://v1/<mediaId>/..." — always a string, NOT an object
42
- catalogReference?: { catalogItemId?: string };
43
- descriptionLines?: DescriptionLine[];
44
- availability?: Availability;
45
- modifierGroups?: ModifierGroup[];
46
- // The SDK returns `url` as an absolute URL string (e.g.
47
- // "https://<site>.wixsite.com/<name>/product-page/<slug>"); REST returns
48
- // an object `{ relativePath, url }`. Handle both in `resolveProductHref`.
49
- url?: string | { relativePath?: string; url?: string };
50
- }
51
-
52
- interface CartSummary {
53
- subtotal?: string;
54
- discount?: string;
55
- total?: string;
56
- discountNames: string[];
57
- }
58
-
59
- // No server-side props — cart is per-visitor and must not break SSR caching.
60
- // Always fetched client-side on mount.
61
-
62
- // ── Helpers ──
63
-
64
- /**
65
- * Extract display text from a description line. Handles both plainText and
66
- * colorInfo types. colorInfo lines carry a human-readable name (e.g. "Dark Blue")
67
- * alongside the hex code — use the translated name, not the code.
68
- * Renders as "Title: Value", or just the title/value if one is missing.
69
- */
70
- function formatDescriptionLine(line: DescriptionLine): string {
71
- const title = line.name?.translated;
72
- const value = line.plainText?.translated ?? line.colorInfo?.translated;
73
- if (title && value) return `${title}: ${value}`;
74
- return title ?? value ?? "";
75
- }
76
-
77
- /**
78
- * Resolve a cart line item image to a renderable CDN URL.
79
- * The ecom SDK returns lineItem.image as a plain string "wix:image://v1/<mediaId>/..."
80
- * (NOT an object { url } despite what the REST docs say).
81
- * Parse it to a Wix static CDN URL with resizing.
82
- */
83
- function resolveCartImage(image: string | undefined, width: number, height: number): string | undefined {
84
- if (!image) return undefined;
85
- if (image.startsWith("wix:image://")) {
86
- const match = image.match(/wix:image:\/\/v1\/([^/]+)/);
87
- if (match) {
88
- return `https://static.wixstatic.com/media/${match[1]}/v1/fill/w_${width},h_${height},al_c,q_80/${match[1]}`;
89
- }
90
- }
91
- // Direct CDN URL — use as-is
92
- return image;
93
- }
94
-
95
- /** Whether a line item is unavailable (out of stock, deleted, or not found) */
96
- function isItemUnavailable(item: LineItem): boolean {
97
- const status = item.availability?.status;
98
- return status === "NOT_AVAILABLE" || status === "NOT_FOUND";
99
- }
100
-
101
- /**
102
- * Build a same-site link to the product detail page for this line item.
103
- *
104
- * `item.url` shape varies by caller:
105
- * - `@wix/ecom` currentCart SDK: absolute URL string like
106
- * "https://<site>.wixsite.com/<name>/product-page/<slug>"
107
- * - REST: object `{ relativePath: "/product-page/<slug>", url: "..." }`
108
- *
109
- * Extract the slug after `/product-page/` either way and rewrite to the
110
- * headless `/products/<slug>` route. Returns undefined when the URL is
111
- * absent or doesn't follow the Stores route pattern.
112
- */
113
- function resolveProductHref(item: LineItem): string | undefined {
114
- const url = item.url;
115
- if (!url) return undefined;
116
- const str = typeof url === "string" ? url : (url.relativePath ?? url.url);
117
- if (!str) return undefined;
118
- const match = str.match(/\/product-page\/([^/?#]+)/);
119
- return match ? `/products/${match[1]}` : undefined;
120
- }
121
-
122
- // Cached cart snapshot so re-navigation to /cart renders the previous state
123
- // instantly, while the authoritative fetch runs in the background. Kept in
124
- // sessionStorage (per-tab, cleared on close) so logout/device-sharing never
125
- // shows a stale cart to a different identity.
126
- const CART_CACHE_KEY = "cart:last-snapshot";
127
- type CartSnapshot = { lineItems: LineItem[]; summary: CartSummary };
128
-
129
- function readCartSnapshot(): CartSnapshot | null {
130
- if (typeof window === "undefined") return null;
131
- try {
132
- const raw = window.sessionStorage.getItem(CART_CACHE_KEY);
133
- if (!raw) return null;
134
- const parsed = JSON.parse(raw);
135
- if (!Array.isArray(parsed?.lineItems)) return null;
136
- return parsed as CartSnapshot;
137
- } catch {
138
- return null;
139
- }
140
- }
141
-
142
- function writeCartSnapshot(snapshot: CartSnapshot): void {
143
- if (typeof window === "undefined") return;
144
- try {
145
- window.sessionStorage.setItem(CART_CACHE_KEY, JSON.stringify(snapshot));
146
- } catch {
147
- /* storage full / denied — ignore, fall back to fetch-only */
148
- }
149
- }
150
-
151
- /**
152
- * Extract cart-level totals + applied discount names from a cart response.
153
- *
154
- * `priceSummary` exposes:
155
- * - `subtotal` — sum of line items BEFORE cart-level discounts (each line already
156
- * reflects per-line automatic discounts, so this is "what the items cost")
157
- * - `discount` — cart-level discount amount (automatic discounts, promo codes)
158
- * - `total` — subtotal − discount + shipping + tax (shipping + tax are
159
- * typically zero before checkout is created)
160
- *
161
- * `appliedDiscounts[]` lists every discount Wix applied; `discountName` is the
162
- * merchant-facing label (e.g. "Spring Sale 20% off").
163
- *
164
- * Fallback: if `priceSummary.subtotal` is missing (some early-stage carts return
165
- * an empty summary until checkout is created), compute it client-side from
166
- * `lineItemPrice.amount` × rows. Shows something instead of a blank slot.
167
- */
168
- function extractSummary(
169
- // `lineItems` is `any[]` — the SDK's LineItem shape (string | null on
170
- // translated fields) is structurally incompatible with our narrower local
171
- // LineItem. We only read `lineItemPrice.amount` here, which is loose anyway.
172
- cart: { priceSummary?: any; appliedDiscounts?: any[]; lineItems?: any[]; currency?: string } | undefined,
173
- ): CartSummary {
174
- const ps = cart?.priceSummary;
175
- let subtotal = ps?.subtotal?.formattedConvertedAmount as string | undefined;
176
- const discount = ps?.discount?.formattedConvertedAmount as string | undefined;
177
- const total = ps?.total?.formattedConvertedAmount as string | undefined;
178
-
179
- // Fallback subtotal from line items when priceSummary is empty.
180
- if (!subtotal && cart?.lineItems?.length) {
181
- const sum = cart.lineItems.reduce((acc, item) => {
182
- const amt = item.lineItemPrice?.amount;
183
- return amt ? acc + Number(amt) : acc;
184
- }, 0);
185
- if (sum > 0) {
186
- try {
187
- subtotal = new Intl.NumberFormat(undefined, {
188
- style: "currency",
189
- currency: cart.currency ?? "USD",
190
- }).format(sum);
191
- } catch {
192
- subtotal = sum.toFixed(2);
193
- }
194
- }
195
- }
196
-
197
- const discountNames = (cart?.appliedDiscounts ?? [])
198
- .map((d: any) => d?.discountName || d?.coupon?.name || d?.merchantDiscount?.discountName)
199
- .filter(Boolean);
200
-
201
- return { subtotal, discount, total, discountNames };
202
- }
203
-
204
- // ── Component ──
205
-
206
- export default function CartView() {
207
- // Hydrate from the last cart snapshot so re-navigating to /cart renders the
208
- // previous state immediately — no empty/loading flash. The authoritative
209
- // fetch in useEffect below reconciles shortly after.
210
- const cached = typeof window !== "undefined" ? readCartSnapshot() : null;
211
- const [items, setItems] = useState<LineItem[]>(cached?.lineItems ?? []);
212
- const [summary, setSummary] = useState<CartSummary>(
213
- cached?.summary ?? { discountNames: [] },
214
- );
215
- // `loading` is only true when there's no cached snapshot to render from.
216
- const [loading, setLoading] = useState(!cached);
217
- const [checkingOut, setCheckingOut] = useState(false);
218
- const qtyTimers = useRef(new Map<string, ReturnType<typeof setTimeout>>());
219
-
220
- useEffect(() => {
221
- loadCart();
222
- }, []);
223
-
224
- // getCurrentCart() returns the cart object directly — NOT wrapped in { cart }.
225
- // Totals + applied discounts all live under priceSummary + appliedDiscounts.
226
- const loadCart = async () => {
227
- try {
228
- const cart = await currentCart.getCurrentCart();
229
- const lineItems = (cart.lineItems as unknown as LineItem[]) ?? [];
230
- const nextSummary = extractSummary(cart);
231
- setItems(lineItems);
232
- setSummary(nextSummary);
233
- writeCartSnapshot({ lineItems, summary: nextSummary });
234
- } catch {
235
- // Leave any cached state in place on transient errors.
236
- if (!cached) {
237
- setItems([]);
238
- setSummary({ discountNames: [] });
239
- }
240
- } finally {
241
- setLoading(false);
242
- }
243
- };
244
-
245
- const handleUpdateQuantity = (itemId: string, quantity: number) => {
246
- // Optimistic: update local state immediately
247
- setItems((prev) =>
248
- prev.map((it) => (it._id === itemId ? { ...it, quantity } : it))
249
- );
250
-
251
- // Debounce API call — rapid clicks coalesce into one request
252
- const prev = qtyTimers.current.get(itemId);
253
- if (prev) clearTimeout(prev);
254
- qtyTimers.current.set(
255
- itemId,
256
- setTimeout(async () => {
257
- qtyTimers.current.delete(itemId);
258
- try {
259
- const { cart } = await currentCart.updateCurrentCartLineItemQuantity([
260
- { _id: itemId, quantity },
261
- ]);
262
- if (!cart) return;
263
- window.dispatchEvent(new CustomEvent("cart-updated", { detail: { cart } }));
264
- const nextItems = (cart.lineItems as unknown as LineItem[]) ?? [];
265
- const nextSummary = extractSummary(cart);
266
- setItems(nextItems);
267
- setSummary(nextSummary);
268
- writeCartSnapshot({ lineItems: nextItems, summary: nextSummary });
269
- } catch {
270
- await loadCart();
271
- }
272
- }, 300)
273
- );
274
- };
275
-
276
- const handleRemoveItem = async (itemId: string) => {
277
- const removed = items.find((item) => item._id === itemId);
278
- if (removed) {
279
- trackEvent("RemoveFromCart", {
280
- id: removed.catalogReference?.catalogItemId ?? removed._id,
281
- name: removed.productName?.translated,
282
- price: removed.price?.amount ? Number(removed.price.amount) : undefined,
283
- quantity: removed.quantity,
284
- origin: "Cart",
285
- });
286
- }
287
-
288
- // Optimistic: remove from local state immediately
289
- setItems((prev) => prev.filter((it) => it._id !== itemId));
290
-
291
- try {
292
- const { cart } = await currentCart.removeLineItemsFromCurrentCart([itemId]);
293
- if (!cart) return;
294
- window.dispatchEvent(new CustomEvent("cart-updated", { detail: { cart } }));
295
- const nextItems = (cart.lineItems as unknown as LineItem[]) ?? [];
296
- const nextSummary = extractSummary(cart);
297
- setItems(nextItems);
298
- setSummary(nextSummary);
299
- writeCartSnapshot({ lineItems: nextItems, summary: nextSummary });
300
- } catch {
301
- await loadCart();
302
- }
303
- };
304
-
305
- const handleCheckout = async () => {
306
- setCheckingOut(true);
307
- try {
308
- trackEvent("InitiateCheckout", {
309
- contents: items.map((item) => ({
310
- id: item.catalogReference?.catalogItemId ?? item._id,
311
- name: item.productName?.translated,
312
- price: item.price?.amount ? Number(item.price.amount) : undefined,
313
- quantity: item.quantity,
314
- })),
315
- origin: "Cart",
316
- });
317
-
318
- const { checkoutId } = await currentCart.createCheckoutFromCurrentCart({
319
- channelType: currentCart.ChannelType.WEB,
320
- });
321
-
322
- const { redirectSession } = await redirects.createRedirectSession({
323
- ecomCheckout: { checkoutId },
324
- callbacks: {
325
- postFlowUrl: window.location.origin,
326
- thankYouPageUrl: `${window.location.origin}/thank-you`,
327
- cartPageUrl: `${window.location.origin}/cart`,
328
- },
329
- });
330
-
331
- if (redirectSession?.fullUrl) {
332
- window.location.href = redirectSession.fullUrl;
333
- }
334
- } catch {
335
- setCheckingOut(false);
336
- }
337
- };
338
-
339
- // ── Render ──
340
-
341
- if (loading) {
342
- return <p className="cart-empty">Loading cart...</p>;
343
- }
344
-
345
- if (items.length === 0) {
346
- return (
347
- <div className="cart-empty">
348
- <p>Your cart is empty.</p>
349
- <a href="/products" className="checkout-btn">Browse Products</a>
350
- </div>
351
- );
352
- }
353
-
354
- const hasUnavailable = items.some(isItemUnavailable);
355
-
356
- return (
357
- <div className="grid grid-cols-1 md:grid-cols-[1fr_22rem] gap-12 md:gap-16 md:items-start">
358
- {/* ── Left column: line items ── */}
359
- <div className="flex flex-col gap-6">
360
- {items.map((item) => {
361
- const unavailable = isItemUnavailable(item);
362
- const maxQty = item.availability?.quantityAvailable ?? 99;
363
- const hasDiscount =
364
- item.fullPrice?.formattedConvertedAmount &&
365
- item.fullPrice.formattedConvertedAmount !== item.price?.formattedConvertedAmount;
366
-
367
- return (
368
- <div
369
- key={item._id}
370
- className={`flex gap-6 pb-6 border-b border-rule last:border-0${unavailable ? " unavailable" : ""}`}
371
- >
372
- {/* Product image + name link back to the product detail page
373
- for available items. Unavailable lines render as plain text. */}
374
- {(() => {
375
- const productHref = !unavailable ? resolveProductHref(item) : undefined;
376
- const imgSrc = resolveCartImage(item.image, 160, 160);
377
- const imageNode = imgSrc ? (
378
- <img src={imgSrc} alt={item.productName?.translated ?? ""} className="cart-item-image" />
379
- ) : (
380
- <div className="cart-item-image cart-item-image-placeholder" aria-hidden="true" />
381
- );
382
- return productHref ? (
383
- <a href={productHref} className="cart-item-image-link" aria-label={item.productName?.translated ?? "Product"}>
384
- {imageNode}
385
- </a>
386
- ) : imageNode;
387
- })()}
388
-
389
- <div className="cart-item-info">
390
- <h3 className="cart-item-name">
391
- {(() => {
392
- const productHref = !unavailable ? resolveProductHref(item) : undefined;
393
- return productHref ? (
394
- <a href={productHref} className="cart-item-name-link">
395
- {item.productName?.translated}
396
- </a>
397
- ) : item.productName?.translated;
398
- })()}
399
- </h3>
400
-
401
- {/* Description lines — option selections (Size, Color, etc.)
402
- Both plainText and colorInfo types are handled by formatDescriptionLine. */}
403
- {(item.descriptionLines ?? []).map((line, i) => {
404
- const text = formatDescriptionLine(line);
405
- return text ? (
406
- <p key={i} className="cart-item-option">{text}</p>
407
- ) : null;
408
- })}
409
-
410
- {/* Modifier groups — extras/add-ons */}
411
- {(item.modifierGroups ?? []).map((group, gi) => (
412
- <div key={gi} className="cart-item-modifiers">
413
- {(group.modifiers ?? []).map((mod, mi) => (
414
- <p key={mi} className="cart-item-option">
415
- {mod.label?.translated}
416
- {mod.quantity && mod.quantity > 1 ? ` ×${mod.quantity}` : ""}
417
- {mod.price?.formattedConvertedAmount
418
- ? ` (+${mod.price.formattedConvertedAmount})`
419
- : ""}
420
- </p>
421
- ))}
422
- </div>
423
- ))}
424
-
425
- {/* Quantity selector or unavailable label */}
426
- {unavailable ? (
427
- <p className="cart-item-unavailable">
428
- {item.availability?.status === "NOT_FOUND"
429
- ? "This item is no longer available"
430
- : "Out of Stock"}
431
- </p>
432
- ) : (
433
- <div className="cart-item-qty">
434
- <button
435
- className="qty-btn"
436
- disabled={!item.quantity || item.quantity <= 1}
437
- onClick={() =>
438
- item._id && handleUpdateQuantity(item._id, (item.quantity ?? 1) - 1)
439
- }
440
- >
441
- −
442
- </button>
443
- <span className="qty-value">{item.quantity}</span>
444
- <button
445
- className="qty-btn"
446
- disabled={(item.quantity ?? 0) >= maxQty}
447
- onClick={() =>
448
- item._id && handleUpdateQuantity(item._id, (item.quantity ?? 1) + 1)
449
- }
450
- >
451
- +
452
- </button>
453
- </div>
454
- )}
455
- </div>
456
-
457
- {/* Price display — THREE fields, each with a different meaning:
458
- - fullPrice: per-unit price BEFORE discount (strikethrough when discounted)
459
- - price: per-unit price AFTER discount (the actual unit price)
460
- - lineItemPrice: total for the line (price × quantity)
461
- Show fullPrice and price side by side (both per-unit).
462
- Show lineItemPrice below ONLY when qty > 1 (otherwise it equals price). */}
463
- <div className="cart-item-actions">
464
- <div className="cart-item-prices">
465
- {hasDiscount && (
466
- <span className="cart-item-full-price">{item.fullPrice!.formattedConvertedAmount}</span>
467
- )}
468
- <span className="cart-item-unit-price">{item.price?.formattedConvertedAmount}</span>
469
- </div>
470
- {(item.quantity ?? 1) > 1 && (
471
- <p className="cart-item-line-total">
472
- {item.lineItemPrice?.formattedConvertedAmount}
473
- </p>
474
- )}
475
- <button
476
- onClick={() => item._id && handleRemoveItem(item._id)}
477
- className="cart-item-remove"
478
- >
479
- Remove
480
- </button>
481
- </div>
482
- </div>
483
- );
484
- })}
485
- </div>
486
-
487
- {/* ── Right column: order summary ──
488
- Always render `Subtotal`. Three ways discounts surface — all values
489
- come from the API, we don't compute amounts client-side:
490
- 1. Cart-level discount with amount (coupons, cart-level promos) →
491
- `priceSummary.discount` populated → render `cart-discount` row
492
- with amount + optional name.
493
- 2. Applied-promotion name only (common for line-item automatic
494
- discounts, where `priceSummary.discount` stays empty but
495
- `appliedDiscounts[].discountName` carries the promo label) →
496
- render `cart-applied-discounts` row: name only, no amount.
497
- Per-line savings show inline via fullPrice/price strikethrough.
498
- 3. No discount → no row.
499
- Render `Total` when it differs from subtotal.
500
- See extractSummary() above. */}
501
- <div className="cart-summary">
502
- <div className="flex justify-between items-baseline">
503
- <span>Subtotal</span>
504
- <span>{summary.subtotal}</span>
505
- </div>
506
- {summary.discount ? (
507
- <div className="cart-discount">
508
- <span>
509
- Discount
510
- {summary.discountNames.length > 0 && (
511
- <span className="cart-discount-name"> · {summary.discountNames.join(", ")}</span>
512
- )}
513
- </span>
514
- <span className="cart-discount-amount">−{summary.discount}</span>
515
- </div>
516
- ) : summary.discountNames.length > 0 ? (
517
- <div className="cart-applied-discounts">
518
- <span>Applied discount</span>
519
- <span className="cart-applied-discounts-name">{summary.discountNames.join(", ")}</span>
520
- </div>
521
- ) : null}
522
- {summary.total && summary.total !== summary.subtotal && (
523
- <div className="cart-total">
524
- <span>Total</span>
525
- <span>{summary.total}</span>
526
- </div>
527
- )}
528
- {hasUnavailable && (
529
- <p className="cart-item-unavailable">
530
- Remove unavailable items before checking out.
531
- </p>
532
- )}
533
- <button
534
- onClick={handleCheckout}
535
- disabled={checkingOut || hasUnavailable}
536
- className="checkout-btn"
537
- >
538
- {checkingOut ? "Redirecting to checkout..." : "Proceed to Checkout"}
539
- </button>
540
- </div>
541
- </div>
542
- );
543
- }
@@ -1,13 +0,0 @@
1
- ---
2
- import Layout from "../layouts/Layout.astro";
3
- import CartView from "../components/CartView.tsx";
4
- ---
5
-
6
- <Layout title="Cart">
7
- <main class="py-16">
8
- <div class="max-w-wide mx-auto px-8">
9
- <h1 class="font-display text-4xl mb-12">Your Cart</h1>
10
- <CartView client:load />
11
- </div>
12
- </main>
13
- </Layout>