@vendure-platform/create 0.4.0 → 0.5.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 (334) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +39 -39
  3. package/dist/add-to-existing.js +1 -1
  4. package/dist/assets/demo-data/initial-data.json +663 -663
  5. package/dist/assets/storefront/components.json +20 -20
  6. package/dist/assets/storefront/eslint.config.mjs +10 -10
  7. package/dist/assets/storefront/graphql.config.yml +1 -1
  8. package/dist/assets/storefront/messages/de.json +2 -0
  9. package/dist/assets/storefront/messages/en.json +2 -0
  10. package/dist/assets/storefront/next.config.ts +5 -4
  11. package/dist/assets/storefront/package.json +1 -1
  12. package/dist/assets/storefront/postcss.config.mjs +1 -1
  13. package/dist/assets/storefront/src/app/[locale]/(catalog)/catalog/page.tsx +17 -21
  14. package/dist/assets/storefront/src/app/[locale]/(catalog)/collection/[slug]/page.tsx +64 -42
  15. package/dist/assets/storefront/src/app/[locale]/(catalog)/layout.tsx +1 -5
  16. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/actions.ts +69 -65
  17. package/dist/assets/storefront/src/app/[locale]/(catalog)/product/[slug]/page.tsx +68 -37
  18. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/page.tsx +24 -27
  19. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-results.tsx +11 -9
  20. package/dist/assets/storefront/src/app/[locale]/(catalog)/search/search-term.tsx +4 -4
  21. package/dist/assets/storefront/src/app/[locale]/(scenario)/[...path]/page.tsx +1 -1
  22. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/accept-invitation-client.tsx +8 -13
  23. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/actions.ts +11 -14
  24. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/page.tsx +30 -33
  25. package/dist/assets/storefront/src/app/[locale]/accept-invitation/[token]/sign-out-and-retry.tsx +6 -6
  26. package/dist/assets/storefront/src/app/[locale]/account/addresses/actions.ts +30 -45
  27. package/dist/assets/storefront/src/app/[locale]/account/addresses/address-form.tsx +166 -159
  28. package/dist/assets/storefront/src/app/[locale]/account/addresses/addresses-client.tsx +35 -26
  29. package/dist/assets/storefront/src/app/[locale]/account/addresses/loading.tsx +1 -3
  30. package/dist/assets/storefront/src/app/[locale]/account/addresses/page.tsx +16 -18
  31. package/dist/assets/storefront/src/app/[locale]/account/approvals/actions.ts +5 -8
  32. package/dist/assets/storefront/src/app/[locale]/account/approvals/page.tsx +77 -69
  33. package/dist/assets/storefront/src/app/[locale]/account/company/actions.ts +10 -11
  34. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/actions.ts +6 -15
  35. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/address-form.tsx +57 -49
  36. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/addresses-client.tsx +23 -25
  37. package/dist/assets/storefront/src/app/[locale]/account/company/addresses/page.tsx +30 -30
  38. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/actions.ts +20 -29
  39. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-unit-form.tsx +32 -37
  40. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/business-units-client.tsx +32 -25
  41. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/page.tsx +24 -24
  42. package/dist/assets/storefront/src/app/[locale]/account/company/business-units/types.ts +7 -4
  43. package/dist/assets/storefront/src/app/[locale]/account/company/edit-company-form.tsx +11 -21
  44. package/dist/assets/storefront/src/app/[locale]/account/company/page.tsx +23 -28
  45. package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/page.tsx +24 -24
  46. package/dist/assets/storefront/src/app/[locale]/account/company/payment-methods/payment-methods-client.tsx +5 -7
  47. package/dist/assets/storefront/src/app/[locale]/account/company/roles/page.tsx +22 -25
  48. package/dist/assets/storefront/src/app/[locale]/account/company/roles/roles-client.tsx +11 -26
  49. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/actions.ts +6 -10
  50. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/page.tsx +18 -16
  51. package/dist/assets/storefront/src/app/[locale]/account/company/tax-ids/tax-ids-client.tsx +74 -67
  52. package/dist/assets/storefront/src/app/[locale]/account/company/users/actions.ts +16 -25
  53. package/dist/assets/storefront/src/app/[locale]/account/company/users/edit-user-form.tsx +27 -25
  54. package/dist/assets/storefront/src/app/[locale]/account/company/users/invite-user-form.tsx +27 -19
  55. package/dist/assets/storefront/src/app/[locale]/account/company/users/page.tsx +32 -29
  56. package/dist/assets/storefront/src/app/[locale]/account/company/users/users-client.tsx +35 -38
  57. package/dist/assets/storefront/src/app/[locale]/account/error.tsx +1 -3
  58. package/dist/assets/storefront/src/app/[locale]/account/layout.tsx +16 -22
  59. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/page.tsx +151 -103
  60. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-actions.ts +6 -7
  61. package/dist/assets/storefront/src/app/[locale]/account/orders/[code]/reorder-dialog.tsx +22 -48
  62. package/dist/assets/storefront/src/app/[locale]/account/orders/loading.tsx +8 -8
  63. package/dist/assets/storefront/src/app/[locale]/account/orders/page.tsx +101 -61
  64. package/dist/assets/storefront/src/app/[locale]/account/profile/actions.ts +32 -23
  65. package/dist/assets/storefront/src/app/[locale]/account/profile/change-password-form.tsx +6 -16
  66. package/dist/assets/storefront/src/app/[locale]/account/profile/edit-email-form.tsx +7 -17
  67. package/dist/assets/storefront/src/app/[locale]/account/profile/edit-profile-form.tsx +6 -16
  68. package/dist/assets/storefront/src/app/[locale]/account/profile/loading.tsx +12 -18
  69. package/dist/assets/storefront/src/app/[locale]/account/profile/page.tsx +15 -15
  70. package/dist/assets/storefront/src/app/[locale]/account/quick-order/file-upload-tab.tsx +2 -6
  71. package/dist/assets/storefront/src/app/[locale]/account/quick-order/manual-entry-tab.tsx +18 -15
  72. package/dist/assets/storefront/src/app/[locale]/account/quick-order/page.tsx +3 -6
  73. package/dist/assets/storefront/src/app/[locale]/account/quick-order/preview-table.tsx +10 -2
  74. package/dist/assets/storefront/src/app/[locale]/account/quick-order/save-to-list-dialog.tsx +24 -26
  75. package/dist/assets/storefront/src/app/[locale]/account/quick-order/sku-autocomplete.tsx +19 -10
  76. package/dist/assets/storefront/src/app/[locale]/account/quick-order/text-input-tab.tsx +3 -8
  77. package/dist/assets/storefront/src/app/[locale]/account/quotes/[id]/page.tsx +86 -59
  78. package/dist/assets/storefront/src/app/[locale]/account/quotes/actions.ts +38 -45
  79. package/dist/assets/storefront/src/app/[locale]/account/quotes/new/new-quote-form.tsx +3 -5
  80. package/dist/assets/storefront/src/app/[locale]/account/quotes/new/page.tsx +6 -6
  81. package/dist/assets/storefront/src/app/[locale]/account/quotes/page.tsx +92 -59
  82. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/activity-feed.tsx +152 -0
  83. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/add-item-search.tsx +132 -0
  84. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/add-member-form.tsx +140 -0
  85. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/members-panel.tsx +259 -0
  86. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/page.tsx +55 -0
  87. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/rename-dialog.tsx +117 -0
  88. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/[id]/shared-cart-detail-client.tsx +555 -0
  89. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/actions.ts +305 -0
  90. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/new/new-shared-cart-form.tsx +84 -0
  91. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/new/page.tsx +47 -0
  92. package/dist/assets/storefront/src/app/[locale]/account/shared-carts/page.tsx +276 -0
  93. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/page.tsx +22 -8
  94. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/[id]/shopping-list-detail-client.tsx +62 -49
  95. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/actions.ts +9 -4
  96. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/create-shopping-list-form.tsx +5 -11
  97. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/loading.tsx +1 -1
  98. package/dist/assets/storefront/src/app/[locale]/account/shopping-lists/page.tsx +32 -42
  99. package/dist/assets/storefront/src/app/[locale]/account/store-credits/actions.ts +8 -8
  100. package/dist/assets/storefront/src/app/[locale]/account/store-credits/claim-form.tsx +6 -10
  101. package/dist/assets/storefront/src/app/[locale]/account/store-credits/page.tsx +76 -55
  102. package/dist/assets/storefront/src/app/[locale]/account/store-credits/reveal-code.tsx +10 -6
  103. package/dist/assets/storefront/src/app/[locale]/account/verify-email/page.tsx +32 -39
  104. package/dist/assets/storefront/src/app/[locale]/cart/actions.ts +29 -23
  105. package/dist/assets/storefront/src/app/[locale]/cart/cart-items.tsx +111 -40
  106. package/dist/assets/storefront/src/app/[locale]/cart/cart-line-actions.tsx +9 -9
  107. package/dist/assets/storefront/src/app/[locale]/cart/cart-quantity-control.tsx +61 -48
  108. package/dist/assets/storefront/src/app/[locale]/cart/cart-remove-button.tsx +15 -9
  109. package/dist/assets/storefront/src/app/[locale]/cart/cart.tsx +38 -41
  110. package/dist/assets/storefront/src/app/[locale]/cart/code-input.tsx +8 -8
  111. package/dist/assets/storefront/src/app/[locale]/cart/order-summary.tsx +70 -29
  112. package/dist/assets/storefront/src/app/[locale]/cart/page.tsx +11 -11
  113. package/dist/assets/storefront/src/app/[locale]/cart/promotion-code.tsx +48 -42
  114. package/dist/assets/storefront/src/app/[locale]/cart/reapproval-confirm.tsx +22 -20
  115. package/dist/assets/storefront/src/app/[locale]/cart/request-quote-button.tsx +7 -16
  116. package/dist/assets/storefront/src/app/[locale]/checkout/actions.ts +39 -65
  117. package/dist/assets/storefront/src/app/[locale]/checkout/cancel-quote-checkout-button.tsx +5 -5
  118. package/dist/assets/storefront/src/app/[locale]/checkout/checkout-flow.tsx +202 -202
  119. package/dist/assets/storefront/src/app/[locale]/checkout/checkout-provider.tsx +142 -142
  120. package/dist/assets/storefront/src/app/[locale]/checkout/order-summary.tsx +158 -139
  121. package/dist/assets/storefront/src/app/[locale]/checkout/page.tsx +56 -47
  122. package/dist/assets/storefront/src/app/[locale]/checkout/steps/delivery-step.tsx +86 -88
  123. package/dist/assets/storefront/src/app/[locale]/checkout/steps/payment-step.tsx +90 -91
  124. package/dist/assets/storefront/src/app/[locale]/checkout/steps/review-step.tsx +166 -161
  125. package/dist/assets/storefront/src/app/[locale]/checkout/steps/shipping-address-step.tsx +573 -494
  126. package/dist/assets/storefront/src/app/[locale]/error.tsx +3 -5
  127. package/dist/assets/storefront/src/app/[locale]/forgot-password/actions.ts +10 -7
  128. package/dist/assets/storefront/src/app/[locale]/forgot-password/forgot-password-form.tsx +10 -25
  129. package/dist/assets/storefront/src/app/[locale]/forgot-password/page.tsx +5 -5
  130. package/dist/assets/storefront/src/app/[locale]/globals.css +48 -48
  131. package/dist/assets/storefront/src/app/[locale]/layout.tsx +53 -47
  132. package/dist/assets/storefront/src/app/[locale]/not-found.tsx +3 -5
  133. package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/order-confirmation.tsx +71 -44
  134. package/dist/assets/storefront/src/app/[locale]/order-confirmation/[code]/page.tsx +7 -5
  135. package/dist/assets/storefront/src/app/[locale]/page.tsx +33 -35
  136. package/dist/assets/storefront/src/app/[locale]/register/actions.ts +7 -7
  137. package/dist/assets/storefront/src/app/[locale]/register/page.tsx +22 -16
  138. package/dist/assets/storefront/src/app/[locale]/register/registration-form.tsx +22 -34
  139. package/dist/assets/storefront/src/app/[locale]/register-company/actions.ts +15 -19
  140. package/dist/assets/storefront/src/app/[locale]/register-company/page.tsx +17 -15
  141. package/dist/assets/storefront/src/app/[locale]/register-company/registration-form.tsx +9 -15
  142. package/dist/assets/storefront/src/app/[locale]/reset-password/actions.ts +7 -8
  143. package/dist/assets/storefront/src/app/[locale]/reset-password/page.tsx +18 -12
  144. package/dist/assets/storefront/src/app/[locale]/reset-password/reset-password-form.tsx +8 -16
  145. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/add-to-cart-button.tsx +10 -4
  146. package/dist/assets/storefront/src/app/[locale]/shopping-list/[code]/page.tsx +62 -30
  147. package/dist/assets/storefront/src/app/[locale]/sign-in/actions.ts +14 -16
  148. package/dist/assets/storefront/src/app/[locale]/sign-in/login-form.tsx +17 -30
  149. package/dist/assets/storefront/src/app/[locale]/sign-in/page.tsx +35 -30
  150. package/dist/assets/storefront/src/app/[locale]/verify/actions.ts +8 -8
  151. package/dist/assets/storefront/src/app/[locale]/verify/page.tsx +18 -14
  152. package/dist/assets/storefront/src/app/[locale]/verify/verify-content.tsx +25 -27
  153. package/dist/assets/storefront/src/app/[locale]/verify-pending/page.tsx +23 -23
  154. package/dist/assets/storefront/src/app/api/clear-company-context/route.ts +6 -3
  155. package/dist/assets/storefront/src/app/api/revalidate/route.ts +19 -28
  156. package/dist/assets/storefront/src/app/api/search-analytics/route.ts +1 -1
  157. package/dist/assets/storefront/src/components/commerce/account-nav-items.ts +44 -17
  158. package/dist/assets/storefront/src/components/commerce/account-sidebar.tsx +30 -22
  159. package/dist/assets/storefront/src/components/commerce/active-filters.tsx +1 -1
  160. package/dist/assets/storefront/src/components/commerce/add-to-shared-cart.tsx +247 -0
  161. package/dist/assets/storefront/src/components/commerce/approval-decision-form.tsx +9 -13
  162. package/dist/assets/storefront/src/components/commerce/approval-status-badge.tsx +23 -15
  163. package/dist/assets/storefront/src/components/commerce/business-unit-switcher.tsx +11 -11
  164. package/dist/assets/storefront/src/components/commerce/catalog-search-bar.tsx +2 -2
  165. package/dist/assets/storefront/src/components/commerce/category-product-section.tsx +28 -16
  166. package/dist/assets/storefront/src/components/commerce/category-sidebar.tsx +31 -18
  167. package/dist/assets/storefront/src/components/commerce/collection-hero-banner.tsx +2 -4
  168. package/dist/assets/storefront/src/components/commerce/company-state-badge.tsx +10 -16
  169. package/dist/assets/storefront/src/components/commerce/company-switcher.tsx +12 -12
  170. package/dist/assets/storefront/src/components/commerce/configurator-form.tsx +188 -79
  171. package/dist/assets/storefront/src/components/commerce/facet-filters.tsx +29 -49
  172. package/dist/assets/storefront/src/components/commerce/gift-card-pdp.tsx +4 -4
  173. package/dist/assets/storefront/src/components/commerce/gift-card-product-info.tsx +20 -30
  174. package/dist/assets/storefront/src/components/commerce/instant-search-dropdown.tsx +258 -227
  175. package/dist/assets/storefront/src/components/commerce/order-status-badge.tsx +27 -19
  176. package/dist/assets/storefront/src/components/commerce/packaging-unit-selector.tsx +27 -26
  177. package/dist/assets/storefront/src/components/commerce/permission-gate.tsx +4 -4
  178. package/dist/assets/storefront/src/components/commerce/price-range-filter.tsx +5 -5
  179. package/dist/assets/storefront/src/components/commerce/price-tiers.tsx +38 -21
  180. package/dist/assets/storefront/src/components/commerce/price.tsx +3 -3
  181. package/dist/assets/storefront/src/components/commerce/product-card-list.tsx +35 -14
  182. package/dist/assets/storefront/src/components/commerce/product-card.tsx +31 -16
  183. package/dist/assets/storefront/src/components/commerce/product-carousel.tsx +20 -12
  184. package/dist/assets/storefront/src/components/commerce/product-grid.tsx +14 -13
  185. package/dist/assets/storefront/src/components/commerce/product-image-carousel.tsx +10 -8
  186. package/dist/assets/storefront/src/components/commerce/product-info.tsx +129 -72
  187. package/dist/assets/storefront/src/components/commerce/product-list-expansion.tsx +102 -74
  188. package/dist/assets/storefront/src/components/commerce/product-list-view.tsx +1 -1
  189. package/dist/assets/storefront/src/components/commerce/product-sidebar.tsx +36 -11
  190. package/dist/assets/storefront/src/components/commerce/product-view-switcher.tsx +33 -21
  191. package/dist/assets/storefront/src/components/commerce/quote-action-buttons.tsx +54 -36
  192. package/dist/assets/storefront/src/components/commerce/quote-activity-timeline.tsx +126 -50
  193. package/dist/assets/storefront/src/components/commerce/quote-comment-form.tsx +9 -13
  194. package/dist/assets/storefront/src/components/commerce/quote-csv-export-button.tsx +6 -6
  195. package/dist/assets/storefront/src/components/commerce/quote-product-search.tsx +22 -18
  196. package/dist/assets/storefront/src/components/commerce/quote-request-form.tsx +28 -27
  197. package/dist/assets/storefront/src/components/commerce/quote-status-badge.tsx +26 -18
  198. package/dist/assets/storefront/src/components/commerce/quote-validity-indicator.tsx +7 -7
  199. package/dist/assets/storefront/src/components/commerce/quote-version-comparison.tsx +50 -30
  200. package/dist/assets/storefront/src/components/commerce/related-products.tsx +37 -28
  201. package/dist/assets/storefront/src/components/commerce/shared-cart-role.ts +39 -0
  202. package/dist/assets/storefront/src/components/commerce/shared-cart-status-badge.tsx +28 -0
  203. package/dist/assets/storefront/src/components/commerce/shop-by-category.tsx +8 -7
  204. package/dist/assets/storefront/src/components/commerce/shopping-list-toggle.tsx +14 -20
  205. package/dist/assets/storefront/src/components/commerce/sort-dropdown.tsx +9 -16
  206. package/dist/assets/storefront/src/components/commerce/store-credit-summary.tsx +30 -32
  207. package/dist/assets/storefront/src/components/commerce/subcollection-tiles.tsx +11 -13
  208. package/dist/assets/storefront/src/components/layout/auth-page-shell.tsx +2 -2
  209. package/dist/assets/storefront/src/components/layout/draft-mode-indicator.tsx +2 -7
  210. package/dist/assets/storefront/src/components/layout/draft-mode-link.tsx +4 -2
  211. package/dist/assets/storefront/src/components/layout/footer.tsx +70 -28
  212. package/dist/assets/storefront/src/components/layout/navbar/actions.ts +9 -12
  213. package/dist/assets/storefront/src/components/layout/navbar/cart-icon.tsx +9 -9
  214. package/dist/assets/storefront/src/components/layout/navbar/channel-picker-wrapper.tsx +4 -9
  215. package/dist/assets/storefront/src/components/layout/navbar/channel-picker.tsx +13 -8
  216. package/dist/assets/storefront/src/components/layout/navbar/currency-picker-wrapper.tsx +4 -4
  217. package/dist/assets/storefront/src/components/layout/navbar/currency-picker.tsx +11 -6
  218. package/dist/assets/storefront/src/components/layout/navbar/language-picker.tsx +7 -7
  219. package/dist/assets/storefront/src/components/layout/navbar/login-button.tsx +24 -21
  220. package/dist/assets/storefront/src/components/layout/navbar/mobile-nav.tsx +8 -8
  221. package/dist/assets/storefront/src/components/layout/navbar/nav-menu-items.tsx +4 -6
  222. package/dist/assets/storefront/src/components/layout/navbar/navbar-cart.tsx +1 -1
  223. package/dist/assets/storefront/src/components/layout/navbar/navbar-collections.tsx +41 -42
  224. package/dist/assets/storefront/src/components/layout/navbar/navbar-link.tsx +7 -10
  225. package/dist/assets/storefront/src/components/layout/navbar/navbar-user.tsx +18 -23
  226. package/dist/assets/storefront/src/components/layout/navbar/theme-switcher.tsx +9 -9
  227. package/dist/assets/storefront/src/components/layout/navbar.tsx +36 -31
  228. package/dist/assets/storefront/src/components/layout/search-input.tsx +35 -32
  229. package/dist/assets/storefront/src/components/providers/awaiting-approval-banner.tsx +12 -10
  230. package/dist/assets/storefront/src/components/providers/cart-count-server.tsx +14 -12
  231. package/dist/assets/storefront/src/components/providers/company-context-guard.tsx +4 -4
  232. package/dist/assets/storefront/src/components/providers/company-permissions-server.tsx +2 -6
  233. package/dist/assets/storefront/src/components/providers/theme-provider.tsx +3 -3
  234. package/dist/assets/storefront/src/components/shared/breadcrumbs.tsx +4 -1
  235. package/dist/assets/storefront/src/components/shared/country-select.tsx +63 -67
  236. package/dist/assets/storefront/src/components/shared/pagination.tsx +15 -28
  237. package/dist/assets/storefront/src/components/shared/prefetch-link.tsx +1 -1
  238. package/dist/assets/storefront/src/components/shared/skeletons/search-input-skeleton.tsx +1 -3
  239. package/dist/assets/storefront/src/components/ui/accordion.tsx +46 -51
  240. package/dist/assets/storefront/src/components/ui/alert-dialog.tsx +94 -121
  241. package/dist/assets/storefront/src/components/ui/alert.tsx +48 -54
  242. package/dist/assets/storefront/src/components/ui/aspect-ratio.tsx +5 -7
  243. package/dist/assets/storefront/src/components/ui/avatar.tsx +29 -44
  244. package/dist/assets/storefront/src/components/ui/badge.tsx +28 -37
  245. package/dist/assets/storefront/src/components/ui/breadcrumb.tsx +82 -89
  246. package/dist/assets/storefront/src/components/ui/button.tsx +43 -49
  247. package/dist/assets/storefront/src/components/ui/calendar.tsx +157 -198
  248. package/dist/assets/storefront/src/components/ui/card.tsx +53 -76
  249. package/dist/assets/storefront/src/components/ui/carousel.tsx +194 -212
  250. package/dist/assets/storefront/src/components/ui/chart.tsx +274 -317
  251. package/dist/assets/storefront/src/components/ui/checkbox.tsx +24 -27
  252. package/dist/assets/storefront/src/components/ui/collapsible.tsx +9 -21
  253. package/dist/assets/storefront/src/components/ui/command.tsx +114 -159
  254. package/dist/assets/storefront/src/components/ui/context-menu.tsx +167 -199
  255. package/dist/assets/storefront/src/components/ui/dialog.tsx +97 -114
  256. package/dist/assets/storefront/src/components/ui/drawer.tsx +93 -107
  257. package/dist/assets/storefront/src/components/ui/dropdown-menu.tsx +171 -206
  258. package/dist/assets/storefront/src/components/ui/empty-state.tsx +5 -5
  259. package/dist/assets/storefront/src/components/ui/field.tsx +196 -208
  260. package/dist/assets/storefront/src/components/ui/form.tsx +115 -139
  261. package/dist/assets/storefront/src/components/ui/hover-card.tsx +27 -33
  262. package/dist/assets/storefront/src/components/ui/input-otp.tsx +50 -59
  263. package/dist/assets/storefront/src/components/ui/input.tsx +17 -17
  264. package/dist/assets/storefront/src/components/ui/kbd-shortcut.tsx +3 -1
  265. package/dist/assets/storefront/src/components/ui/label.tsx +16 -19
  266. package/dist/assets/storefront/src/components/ui/menubar.tsx +193 -223
  267. package/dist/assets/storefront/src/components/ui/navigation-menu.tsx +123 -130
  268. package/dist/assets/storefront/src/components/ui/pagination.tsx +87 -108
  269. package/dist/assets/storefront/src/components/ui/popover.tsx +29 -35
  270. package/dist/assets/storefront/src/components/ui/progress.tsx +19 -26
  271. package/dist/assets/storefront/src/components/ui/radio-group.tsx +32 -38
  272. package/dist/assets/storefront/src/components/ui/resizable.tsx +37 -42
  273. package/dist/assets/storefront/src/components/ui/scroll-area.tsx +41 -47
  274. package/dist/assets/storefront/src/components/ui/select.tsx +130 -152
  275. package/dist/assets/storefront/src/components/ui/separator.tsx +21 -21
  276. package/dist/assets/storefront/src/components/ui/sheet.tsx +90 -111
  277. package/dist/assets/storefront/src/components/ui/sidebar.tsx +611 -649
  278. package/dist/assets/storefront/src/components/ui/skeleton.tsx +10 -10
  279. package/dist/assets/storefront/src/components/ui/slider.tsx +50 -55
  280. package/dist/assets/storefront/src/components/ui/sonner.tsx +20 -20
  281. package/dist/assets/storefront/src/components/ui/switch.tsx +23 -26
  282. package/dist/assets/storefront/src/components/ui/table.tsx +65 -97
  283. package/dist/assets/storefront/src/components/ui/tabs.tsx +39 -55
  284. package/dist/assets/storefront/src/components/ui/textarea.tsx +14 -14
  285. package/dist/assets/storefront/src/components/ui/toggle-group.tsx +56 -62
  286. package/dist/assets/storefront/src/components/ui/toggle.tsx +37 -38
  287. package/dist/assets/storefront/src/components/ui/tooltip.tsx +38 -46
  288. package/dist/assets/storefront/src/config/scenario-meta.ts +2 -2
  289. package/dist/assets/storefront/src/config/scenario.ts +14 -16
  290. package/dist/assets/storefront/src/contexts/auth-context.tsx +5 -6
  291. package/dist/assets/storefront/src/graphql-env.d.ts +18 -5
  292. package/dist/assets/storefront/src/graphql.ts +5 -5
  293. package/dist/assets/storefront/src/hooks/use-mobile.ts +13 -13
  294. package/dist/assets/storefront/src/i18n/locale-utils.ts +3 -3
  295. package/dist/assets/storefront/src/i18n/navigation.ts +4 -4
  296. package/dist/assets/storefront/src/i18n/request.ts +4 -4
  297. package/dist/assets/storefront/src/i18n/routing.ts +5 -5
  298. package/dist/assets/storefront/src/i18n/server.ts +3 -3
  299. package/dist/assets/storefront/src/lib/actions/switch-channel.ts +9 -9
  300. package/dist/assets/storefront/src/lib/actions/switch-currency.ts +7 -7
  301. package/dist/assets/storefront/src/lib/auth.ts +1 -1
  302. package/dist/assets/storefront/src/lib/backend-health.ts +3 -3
  303. package/dist/assets/storefront/src/lib/cart-count-context.tsx +10 -4
  304. package/dist/assets/storefront/src/lib/cart-guard-utils.ts +2 -1
  305. package/dist/assets/storefront/src/lib/cart-guard.ts +7 -6
  306. package/dist/assets/storefront/src/lib/channel-server.ts +4 -4
  307. package/dist/assets/storefront/src/lib/company-context.ts +1 -1
  308. package/dist/assets/storefront/src/lib/company-permissions-context.tsx +1 -5
  309. package/dist/assets/storefront/src/lib/currency-server.ts +4 -4
  310. package/dist/assets/storefront/src/lib/format.ts +16 -7
  311. package/dist/assets/storefront/src/lib/metadata.ts +39 -44
  312. package/dist/assets/storefront/src/lib/quantity.ts +2 -4
  313. package/dist/assets/storefront/src/lib/search-bar-context.tsx +7 -4
  314. package/dist/assets/storefront/src/lib/use-recent-searches.ts +8 -6
  315. package/dist/assets/storefront/src/lib/utils.ts +3 -3
  316. package/dist/assets/storefront/src/lib/vendure/actions.ts +18 -16
  317. package/dist/assets/storefront/src/lib/vendure/api.ts +44 -27
  318. package/dist/assets/storefront/src/lib/vendure/approval-utils.ts +9 -8
  319. package/dist/assets/storefront/src/lib/vendure/cached.ts +20 -15
  320. package/dist/assets/storefront/src/lib/vendure/mutations.ts +96 -0
  321. package/dist/assets/storefront/src/lib/vendure/queries.ts +157 -5
  322. package/dist/assets/storefront/src/middleware.ts +1 -1
  323. package/dist/assets/storefront/src/scenarios/customer/hero.tsx +2 -2
  324. package/dist/assets/storefront/src/scenarios/customer/meta.ts +1 -1
  325. package/dist/assets/storefront/src/schema.graphql +134 -0
  326. package/dist/bin/run.js +1 -1
  327. package/dist/cli.d.ts +1 -1
  328. package/dist/index.d.ts +5 -5
  329. package/dist/index.js +1 -1
  330. package/dist/plugin-registry.js +1 -1
  331. package/dist/scaffold-storefront-only.js +1 -1
  332. package/dist/scaffolder.js +1 -1
  333. package/dist/template-renderer.js +1 -1
  334. package/package.json +2 -2
@@ -1,19 +1,19 @@
1
1
  'use server';
2
2
 
3
- import {client} from '@/lib/vendure/api';
3
+ import { cartGuard } from '@/lib/cart-guard';
4
+ import { getGuardError } from '@/lib/cart-guard-utils';
5
+ import { normalizeQuantity } from '@/lib/quantity';
6
+ import { client } from '@/lib/vendure/api';
4
7
  import {
5
- RemoveFromCartMutation,
6
8
  AdjustCartItemMutation,
7
9
  ApplyPromotionCodeMutation,
8
- RemovePromotionCodeMutation,
9
10
  ApplyStoreCreditToOrderMutation,
10
- RemoveStoreCreditFromOrderMutation,
11
11
  RemoveConfigurationFromOrderMutation,
12
+ RemoveFromCartMutation,
13
+ RemovePromotionCodeMutation,
14
+ RemoveStoreCreditFromOrderMutation,
12
15
  } from '@/lib/vendure/mutations';
13
- import {normalizeQuantity} from '@/lib/quantity';
14
- import {updateTag} from 'next/cache';
15
- import {cartGuard} from '@/lib/cart-guard';
16
- import {getGuardError} from '@/lib/cart-guard-utils';
16
+ import { updateTag } from 'next/cache';
17
17
 
18
18
  function invalidateCart() {
19
19
  updateTag('cart');
@@ -25,7 +25,7 @@ export async function removeFromCart(lineId: string) {
25
25
  const guardError = getGuardError(guard);
26
26
  if (guardError) return { success: false, error: guardError };
27
27
 
28
- const [result, error] = await client.mutate(RemoveFromCartMutation, {lineId});
28
+ const [result, error] = await client.mutate(RemoveFromCartMutation, { lineId });
29
29
  if (error) {
30
30
  return { success: false, error: 'Failed to remove item from cart' };
31
31
  }
@@ -33,9 +33,12 @@ export async function removeFromCart(lineId: string) {
33
33
  const data = result.data.removeOrderLine;
34
34
  return {
35
35
  success: false,
36
- error: 'interceptorError' in data ? data.interceptorError
37
- : 'message' in data ? data.message
38
- : 'Failed to remove item',
36
+ error:
37
+ 'interceptorError' in data
38
+ ? data.interceptorError
39
+ : 'message' in data
40
+ ? data.message
41
+ : 'Failed to remove item',
39
42
  };
40
43
  }
41
44
  invalidateCart();
@@ -52,7 +55,7 @@ export async function removeConfigurationFromCart(productConfigurationId: string
52
55
  const guardError = getGuardError(guard);
53
56
  if (guardError) return { success: false, error: guardError };
54
57
 
55
- const [, error] = await client.mutate(RemoveConfigurationFromOrderMutation, {productConfigurationId});
58
+ const [, error] = await client.mutate(RemoveConfigurationFromOrderMutation, { productConfigurationId });
56
59
  if (error) {
57
60
  return { success: false, error: error.message || 'Failed to remove configuration from cart' };
58
61
  }
@@ -67,7 +70,7 @@ export async function adjustQuantity(lineId: string, quantity: number) {
67
70
 
68
71
  const nextQuantity = normalizeQuantity(quantity);
69
72
 
70
- const [result, error] = await client.mutate(AdjustCartItemMutation, {lineId, quantity: nextQuantity});
73
+ const [result, error] = await client.mutate(AdjustCartItemMutation, { lineId, quantity: nextQuantity });
71
74
 
72
75
  if (error) {
73
76
  return {
@@ -80,15 +83,18 @@ export async function adjustQuantity(lineId: string, quantity: number) {
80
83
  const data = result.data.adjustOrderLine;
81
84
  return {
82
85
  success: false,
83
- error: 'interceptorError' in data ? data.interceptorError
84
- : 'message' in data ? data.message
85
- : 'Failed to update cart quantity',
86
+ error:
87
+ 'interceptorError' in data
88
+ ? data.interceptorError
89
+ : 'message' in data
90
+ ? data.message
91
+ : 'Failed to update cart quantity',
86
92
  };
87
93
  }
88
94
 
89
95
  invalidateCart();
90
96
 
91
- return {success: true};
97
+ return { success: true };
92
98
  }
93
99
 
94
100
  export async function applyCode(code: string): Promise<{ success: boolean; error?: string }> {
@@ -98,14 +104,14 @@ export async function applyCode(code: string): Promise<{ success: boolean; error
98
104
  if (guardError) return { success: false, error: guardError };
99
105
 
100
106
  // Try coupon code first
101
- const [couponRes, couponError] = await client.mutate(ApplyPromotionCodeMutation, {couponCode: code});
107
+ const [couponRes, couponError] = await client.mutate(ApplyPromotionCodeMutation, { couponCode: code });
102
108
  if (!couponError && couponRes.data.applyCouponCode.__typename === 'Order') {
103
109
  invalidateCart();
104
110
  return { success: true };
105
111
  }
106
112
 
107
113
  // Fall back to store credit / gift card
108
- const [creditRes, creditError] = await client.mutate(ApplyStoreCreditToOrderMutation, {code});
114
+ const [creditRes, creditError] = await client.mutate(ApplyStoreCreditToOrderMutation, { code });
109
115
  if (creditError) {
110
116
  return { success: false, error: 'Invalid code. Please check and try again.' };
111
117
  }
@@ -122,7 +128,7 @@ export async function removePromotionCode(formData: FormData): Promise<void> {
122
128
  const guard = await cartGuard();
123
129
  if (getGuardError(guard)) return;
124
130
 
125
- await client.mutate(RemovePromotionCodeMutation, {couponCode: code});
131
+ await client.mutate(RemovePromotionCodeMutation, { couponCode: code });
126
132
  invalidateCart();
127
133
  }
128
134
 
@@ -132,7 +138,7 @@ export async function applyStoreCredit(formData: FormData): Promise<void> {
132
138
  const guard = await cartGuard();
133
139
  if (getGuardError(guard)) return;
134
140
 
135
- await client.mutate(ApplyStoreCreditToOrderMutation, {code});
141
+ await client.mutate(ApplyStoreCreditToOrderMutation, { code });
136
142
  invalidateCart();
137
143
  }
138
144
 
@@ -142,6 +148,6 @@ export async function removeStoreCredit(formData: FormData): Promise<void> {
142
148
  const guard = await cartGuard();
143
149
  if (getGuardError(guard)) return;
144
150
 
145
- await client.mutate(RemoveStoreCreditFromOrderMutation, {code});
151
+ await client.mutate(RemoveStoreCreditFromOrderMutation, { code });
146
152
  invalidateCart();
147
153
  }
@@ -1,14 +1,14 @@
1
1
  'use client';
2
2
 
3
+ import { Price } from '@/components/commerce/price';
4
+ import { Badge } from '@/components/ui/badge';
5
+ import { EmptyState } from '@/components/ui/empty-state';
6
+ import { Link } from '@/i18n/navigation';
7
+ import { ClipboardCheck, Gift, Package, ShoppingCart } from 'lucide-react';
8
+ import { useTranslations } from 'next-intl';
3
9
  import Image from 'next/image';
4
- import {Link} from '@/i18n/navigation';
5
- import {Badge} from '@/components/ui/badge';
6
- import {EmptyState} from '@/components/ui/empty-state';
7
- import {ClipboardCheck, Gift, Package, ShoppingCart} from 'lucide-react';
8
- import {Price} from '@/components/commerce/price';
9
- import {CartQuantityControl} from './cart-quantity-control';
10
- import {CartRemoveButton} from './cart-remove-button';
11
- import {useTranslations} from 'next-intl';
10
+ import { CartQuantityControl } from './cart-quantity-control';
11
+ import { CartRemoveButton } from './cart-remove-button';
12
12
 
13
13
  type GiftCardInput = {
14
14
  value: number;
@@ -53,7 +53,17 @@ type ActiveOrder = {
53
53
  }>;
54
54
  };
55
55
 
56
- export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved, readOnly}: { activeOrder: ActiveOrder | null; awaitingApprovalOrderCode?: string; wasApproved?: boolean; readOnly?: boolean }) {
56
+ export function CartItems({
57
+ activeOrder,
58
+ awaitingApprovalOrderCode,
59
+ wasApproved,
60
+ readOnly,
61
+ }: {
62
+ activeOrder: ActiveOrder | null;
63
+ awaitingApprovalOrderCode?: string;
64
+ wasApproved?: boolean;
65
+ readOnly?: boolean;
66
+ }) {
57
67
  const t = useTranslations('Cart');
58
68
 
59
69
  if (!activeOrder || activeOrder.lines.length === 0) {
@@ -63,15 +73,15 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
63
73
  <EmptyState
64
74
  icon={ClipboardCheck}
65
75
  heading={t('orderPendingApproval')}
66
- body={t('orderPendingApprovalMessage', {code: awaitingApprovalOrderCode})}
67
- action={{label: t('viewMyRequests'), href: '/account/approvals'}}
76
+ body={t('orderPendingApprovalMessage', { code: awaitingApprovalOrderCode })}
77
+ action={{ label: t('viewMyRequests'), href: '/account/approvals' }}
68
78
  />
69
79
  ) : (
70
80
  <EmptyState
71
81
  icon={ShoppingCart}
72
82
  heading={t('empty')}
73
83
  body={t('emptyMessage')}
74
- action={{label: t('continueShopping'), href: '/'}}
84
+ action={{ label: t('continueShopping'), href: '/' }}
75
85
  />
76
86
  )}
77
87
  </div>
@@ -115,9 +125,16 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
115
125
  <Badge className="text-sm gap-1.5 px-2.5 py-0.5 bg-primary text-primary-foreground">
116
126
  <Package className="h-3.5 w-3.5" />
117
127
  {line.customFields.packagingUnit.name}
118
- {line.customFields.packagingUnitConversionFactor && line.customFields.packagingUnitConversionFactor > 1 && (
119
- <span className="opacity-80">· {t('pcs', {count: line.customFields.packagingUnitConversionFactor})}</span>
120
- )}
128
+ {line.customFields.packagingUnitConversionFactor &&
129
+ line.customFields.packagingUnitConversionFactor > 1 && (
130
+ <span className="opacity-80">
131
+ ·{' '}
132
+ {t('pcs', {
133
+ count: line.customFields
134
+ .packagingUnitConversionFactor,
135
+ })}
136
+ </span>
137
+ )}
121
138
  </Badge>
122
139
  )}
123
140
  </div>
@@ -127,21 +144,22 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
127
144
  <div className="flex items-center gap-1 text-sm text-muted-foreground">
128
145
  <Gift className="h-3.5 w-3.5" />
129
146
  <span>{t('giftCard')}</span>
130
- {gc.templateName && (
131
- <span>— {gc.templateName}</span>
132
- )}
147
+ {gc.templateName && <span>— {gc.templateName}</span>}
133
148
  </div>
134
149
  <p className="text-sm text-muted-foreground">
135
- {t('value')}<Price value={gc.value} currencyCode={activeOrder.currencyCode} />
150
+ {t('value')}
151
+ <Price value={gc.value} currencyCode={activeOrder.currencyCode} />
136
152
  </p>
137
153
  {gc.recipientEmailAddress && (
138
154
  <p className="text-sm text-muted-foreground">
139
- {t('to')}{gc.recipientEmailAddress}
155
+ {t('to')}
156
+ {gc.recipientEmailAddress}
140
157
  </p>
141
158
  )}
142
159
  {gc.senderName && (
143
160
  <p className="text-sm text-muted-foreground">
144
- {t('from')}{gc.senderName}
161
+ {t('from')}
162
+ {gc.senderName}
145
163
  </p>
146
164
  )}
147
165
  {gc.message && (
@@ -151,7 +169,8 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
151
169
  )}
152
170
  {gc.deliveryDate && (
153
171
  <p className="text-sm text-muted-foreground">
154
- {t('delivery')}{new Date(gc.deliveryDate).toLocaleDateString()}
172
+ {t('delivery')}
173
+ {new Date(gc.deliveryDate).toLocaleDateString()}
155
174
  </p>
156
175
  )}
157
176
  </div>
@@ -163,14 +182,30 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
163
182
  </p>
164
183
  )}
165
184
  <p className="text-sm text-muted-foreground mt-1">
166
- {t('sku', {sku: line.productVariant.sku})}
185
+ {t('sku', { sku: line.productVariant.sku })}
167
186
  </p>
168
187
  <p className="text-sm text-muted-foreground mt-2 sm:hidden">
169
- <Price value={line.unitPrice} currencyCode={activeOrder.currencyCode}/> {t('each')}
188
+ <Price
189
+ value={line.unitPrice}
190
+ currencyCode={activeOrder.currencyCode}
191
+ />{' '}
192
+ {t('each')}
170
193
  {line.unitPrice < line.productVariant.price && (
171
194
  <span className="ml-1">
172
- <span className="line-through opacity-60"><Price value={line.productVariant.price} currencyCode={activeOrder.currencyCode}/></span>
173
- {' '}<span className="text-green-600 font-medium">{t('savePercent', {percent: Math.round((1 - line.unitPrice / line.productVariant.price) * 100)})}</span>
195
+ <span className="line-through opacity-60">
196
+ <Price
197
+ value={line.productVariant.price}
198
+ currencyCode={activeOrder.currencyCode}
199
+ />
200
+ </span>{' '}
201
+ <span className="text-green-600 font-medium">
202
+ {t('savePercent', {
203
+ percent: Math.round(
204
+ (1 - line.unitPrice / line.productVariant.price) *
205
+ 100,
206
+ ),
207
+ })}
208
+ </span>
174
209
  </span>
175
210
  )}
176
211
  </p>
@@ -179,24 +214,40 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
179
214
 
180
215
  <div className="flex items-center gap-3 mt-4">
181
216
  {!gc && !readOnly && (
182
- <CartQuantityControl lineId={line.id} quantity={line.quantity} requiresReapproval={wasApproved}/>
217
+ <CartQuantityControl
218
+ lineId={line.id}
219
+ quantity={line.quantity}
220
+ requiresReapproval={wasApproved}
221
+ />
183
222
  )}
184
223
  {!gc && readOnly && (
185
- <span className="text-sm text-muted-foreground">{t('qty', {quantity: line.quantity})}</span>
186
- )}
187
-
188
- {line.customFields?.packagingUnitConversionFactor && line.customFields.packagingUnitConversionFactor > 1 && (
189
- <span className="text-xs text-muted-foreground">
190
- {t('equalsNPcs', {count: line.quantity * line.customFields.packagingUnitConversionFactor})}
224
+ <span className="text-sm text-muted-foreground">
225
+ {t('qty', { quantity: line.quantity })}
191
226
  </span>
192
227
  )}
193
228
 
194
- {!readOnly && <CartRemoveButton lineId={line.id} requiresReapproval={wasApproved} />}
229
+ {line.customFields?.packagingUnitConversionFactor &&
230
+ line.customFields.packagingUnitConversionFactor > 1 && (
231
+ <span className="text-xs text-muted-foreground">
232
+ {t('equalsNPcs', {
233
+ count:
234
+ line.quantity *
235
+ line.customFields.packagingUnitConversionFactor,
236
+ })}
237
+ </span>
238
+ )}
239
+
240
+ {!readOnly && (
241
+ <CartRemoveButton lineId={line.id} requiresReapproval={wasApproved} />
242
+ )}
195
243
 
196
244
  <div className="sm:hidden ml-auto">
197
245
  <p className="font-semibold text-lg">
198
- <Price value={line.linePrice}
199
- currencyCode={activeOrder.currencyCode} taxLabel="excl" />
246
+ <Price
247
+ value={line.linePrice}
248
+ currencyCode={activeOrder.currencyCode}
249
+ taxLabel="excl"
250
+ />
200
251
  </p>
201
252
  </div>
202
253
  </div>
@@ -204,17 +255,37 @@ export function CartItems({activeOrder, awaitingApprovalOrderCode, wasApproved,
204
255
 
205
256
  <div className="hidden sm:block text-right flex-shrink-0">
206
257
  <p className="font-semibold text-lg">
207
- <Price value={line.linePrice} currencyCode={activeOrder.currencyCode} taxLabel="excl" />
258
+ <Price
259
+ value={line.linePrice}
260
+ currencyCode={activeOrder.currencyCode}
261
+ taxLabel="excl"
262
+ />
208
263
  </p>
209
264
  {!gc && (
210
265
  <>
211
266
  <p className="text-sm text-muted-foreground mt-1">
212
- <Price value={line.unitPrice} currencyCode={activeOrder.currencyCode}/> {t('each')}
267
+ <Price
268
+ value={line.unitPrice}
269
+ currencyCode={activeOrder.currencyCode}
270
+ />{' '}
271
+ {t('each')}
213
272
  </p>
214
273
  {line.unitPrice < line.productVariant.price && (
215
274
  <p className="text-xs mt-0.5">
216
- <span className="line-through text-muted-foreground/60"><Price value={line.productVariant.price} currencyCode={activeOrder.currencyCode}/></span>
217
- {' '}<span className="text-green-600 font-medium">{t('savePercent', {percent: Math.round((1 - line.unitPrice / line.productVariant.price) * 100)})}</span>
275
+ <span className="line-through text-muted-foreground/60">
276
+ <Price
277
+ value={line.productVariant.price}
278
+ currencyCode={activeOrder.currencyCode}
279
+ />
280
+ </span>{' '}
281
+ <span className="text-green-600 font-medium">
282
+ {t('savePercent', {
283
+ percent: Math.round(
284
+ (1 - line.unitPrice / line.productVariant.price) *
285
+ 100,
286
+ ),
287
+ })}
288
+ </span>
218
289
  </p>
219
290
  )}
220
291
  </>
@@ -1,11 +1,11 @@
1
1
  'use client';
2
2
 
3
- import {Button} from '@/components/ui/button';
4
- import {Minus, Plus, X} from 'lucide-react';
5
- import {removeFromCart, adjustQuantity} from './actions';
6
- import {useTransition} from 'react';
3
+ import { Button } from '@/components/ui/button';
4
+ import { Minus, Plus, X } from 'lucide-react';
5
+ import { useTransition } from 'react';
6
+ import { adjustQuantity, removeFromCart } from './actions';
7
7
 
8
- export function QuantityControls({lineId, quantity}: { lineId: string; quantity: number }) {
8
+ export function QuantityControls({ lineId, quantity }: { lineId: string; quantity: number }) {
9
9
  const [isPending, startTransition] = useTransition();
10
10
 
11
11
  return (
@@ -18,7 +18,7 @@ export function QuantityControls({lineId, quantity}: { lineId: string; quantity:
18
18
  disabled={quantity <= 1 || isPending}
19
19
  onClick={() => startTransition(() => void adjustQuantity(lineId, quantity - 1))}
20
20
  >
21
- <Minus className="h-4 w-4"/>
21
+ <Minus className="h-4 w-4" />
22
22
  </Button>
23
23
 
24
24
  <span className="w-12 text-center font-medium">{quantity}</span>
@@ -31,13 +31,13 @@ export function QuantityControls({lineId, quantity}: { lineId: string; quantity:
31
31
  disabled={isPending}
32
32
  onClick={() => startTransition(() => void adjustQuantity(lineId, quantity + 1))}
33
33
  >
34
- <Plus className="h-4 w-4"/>
34
+ <Plus className="h-4 w-4" />
35
35
  </Button>
36
36
  </div>
37
37
  );
38
38
  }
39
39
 
40
- export function RemoveLineButton({lineId}: { lineId: string }) {
40
+ export function RemoveLineButton({ lineId }: { lineId: string }) {
41
41
  const [isPending, startTransition] = useTransition();
42
42
 
43
43
  return (
@@ -49,7 +49,7 @@ export function RemoveLineButton({lineId}: { lineId: string }) {
49
49
  disabled={isPending}
50
50
  onClick={() => startTransition(() => void removeFromCart(lineId))}
51
51
  >
52
- <X className="h-5 w-5"/>
52
+ <X className="h-5 w-5" />
53
53
  </Button>
54
54
  );
55
55
  }
@@ -1,27 +1,35 @@
1
1
  'use client';
2
2
 
3
- import {useCallback, useEffect, useRef, useState, useTransition} from 'react';
4
- import {useRouter} from '@/i18n/navigation';
5
- import {Minus, Plus} from 'lucide-react';
6
- import {toast} from 'sonner';
7
-
8
- import {adjustQuantity} from './actions';
9
- import {useReapprovalConfirm} from './reapproval-confirm';
10
- import {Button} from '@/components/ui/button';
11
- import {Input} from '@/components/ui/input';
12
- import {MIN_ORDER_QUANTITY, normalizeQuantity, sanitizeQuantityInput} from '@/lib/quantity';
13
- import {useTranslations} from 'next-intl';
3
+ import { useRouter } from '@/i18n/navigation';
4
+ import { Minus, Plus } from 'lucide-react';
5
+ import { useCallback, useEffect, useRef, useState, useTransition } from 'react';
6
+ import { toast } from 'sonner';
7
+
8
+ import { Button } from '@/components/ui/button';
9
+ import { Input } from '@/components/ui/input';
10
+ import { MIN_ORDER_QUANTITY, normalizeQuantity, sanitizeQuantityInput } from '@/lib/quantity';
11
+ import { useTranslations } from 'next-intl';
12
+ import { adjustQuantity } from './actions';
13
+ import { useReapprovalConfirm } from './reapproval-confirm';
14
14
 
15
15
  const DEBOUNCE_MS = 300;
16
16
 
17
- export function CartQuantityControl({lineId, quantity, requiresReapproval}: { lineId: string; quantity: number; requiresReapproval?: boolean }) {
17
+ export function CartQuantityControl({
18
+ lineId,
19
+ quantity,
20
+ requiresReapproval,
21
+ }: {
22
+ lineId: string;
23
+ quantity: number;
24
+ requiresReapproval?: boolean;
25
+ }) {
18
26
  const t = useTranslations('Cart');
19
27
  const router = useRouter();
20
28
  const [isPending, startTransition] = useTransition();
21
29
  const [quantityInput, setQuantityInput] = useState(String(quantity));
22
30
  const skipBlurSubmitRef = useRef(false);
23
31
  const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
24
- const {guardCartChange, dialog} = useReapprovalConfirm(requiresReapproval);
32
+ const { guardCartChange, dialog } = useReapprovalConfirm(requiresReapproval);
25
33
 
26
34
  useEffect(() => {
27
35
  setQuantityInput(String(quantity));
@@ -29,39 +37,42 @@ export function CartQuantityControl({lineId, quantity, requiresReapproval}: { li
29
37
 
30
38
  const normalizedQuantity = normalizeQuantity(quantityInput);
31
39
 
32
- const submitQuantity = useCallback((nextValue: number | string) => {
33
- const nextQuantity = normalizeQuantity(nextValue);
34
- const rawParsed = typeof nextValue === 'number' ? nextValue : parseInt(String(nextValue), 10);
35
- if (Number.isFinite(rawParsed) && rawParsed < MIN_ORDER_QUANTITY) {
36
- toast.info(t('minimumQuantity', {min: MIN_ORDER_QUANTITY}));
37
- }
38
- setQuantityInput(String(nextQuantity));
39
-
40
- if (nextQuantity === quantity) {
41
- return;
42
- }
43
-
44
- // Clear any pending debounce
45
- if (debounceRef.current) clearTimeout(debounceRef.current);
46
-
47
- debounceRef.current = setTimeout(() => {
48
- guardCartChange(() => {
49
- startTransition(async () => {
50
- const result = await adjustQuantity(lineId, nextQuantity);
51
-
52
- if (!result.success) {
53
- setQuantityInput(String(quantity));
54
- toast.error(t('unableToUpdateQuantity'), {
55
- description: result.error,
56
- });
57
- return;
58
- }
59
-
60
- router.refresh();
40
+ const submitQuantity = useCallback(
41
+ (nextValue: number | string) => {
42
+ const nextQuantity = normalizeQuantity(nextValue);
43
+ const rawParsed = typeof nextValue === 'number' ? nextValue : parseInt(String(nextValue), 10);
44
+ if (Number.isFinite(rawParsed) && rawParsed < MIN_ORDER_QUANTITY) {
45
+ toast.info(t('minimumQuantity', { min: MIN_ORDER_QUANTITY }));
46
+ }
47
+ setQuantityInput(String(nextQuantity));
48
+
49
+ if (nextQuantity === quantity) {
50
+ return;
51
+ }
52
+
53
+ // Clear any pending debounce
54
+ if (debounceRef.current) clearTimeout(debounceRef.current);
55
+
56
+ debounceRef.current = setTimeout(() => {
57
+ guardCartChange(() => {
58
+ startTransition(async () => {
59
+ const result = await adjustQuantity(lineId, nextQuantity);
60
+
61
+ if (!result.success) {
62
+ setQuantityInput(String(quantity));
63
+ toast.error(t('unableToUpdateQuantity'), {
64
+ description: result.error,
65
+ });
66
+ return;
67
+ }
68
+
69
+ router.refresh();
70
+ });
61
71
  });
62
- });
63
- }, DEBOUNCE_MS);
64
- }, [lineId, quantity, guardCartChange, router]);
72
+ }, DEBOUNCE_MS);
73
+ },
74
+ [lineId, quantity, guardCartChange, router],
75
+ );
65
76
 
66
77
  // Cleanup on unmount
67
78
  useEffect(() => {
@@ -86,7 +97,7 @@ export function CartQuantityControl({lineId, quantity, requiresReapproval}: { li
86
97
  }}
87
98
  onClick={() => submitQuantity(normalizedQuantity - 1)}
88
99
  >
89
- <Minus className="h-4 w-4"/>
100
+ <Minus className="h-4 w-4" />
90
101
  </Button>
91
102
 
92
103
  <Input
@@ -113,7 +124,9 @@ export function CartQuantityControl({lineId, quantity, requiresReapproval}: { li
113
124
  const result = await adjustQuantity(lineId, nextQuantity);
114
125
  if (!result.success) {
115
126
  setQuantityInput(String(quantity));
116
- toast.error(t('unableToUpdateQuantity'), { description: result.error });
127
+ toast.error(t('unableToUpdateQuantity'), {
128
+ description: result.error,
129
+ });
117
130
  return;
118
131
  }
119
132
  router.refresh();
@@ -150,7 +163,7 @@ export function CartQuantityControl({lineId, quantity, requiresReapproval}: { li
150
163
  }}
151
164
  onClick={() => submitQuantity(normalizedQuantity + 1)}
152
165
  >
153
- <Plus className="h-4 w-4"/>
166
+ <Plus className="h-4 w-4" />
154
167
  </Button>
155
168
  </div>
156
169
  </>
@@ -1,16 +1,22 @@
1
1
  'use client';
2
2
 
3
- import {useTransition} from 'react';
4
- import {useRouter} from '@/i18n/navigation';
5
- import {X} from 'lucide-react';
6
- import {Button} from '@/components/ui/button';
7
- import {removeFromCart} from './actions';
8
- import {useReapprovalConfirm} from './reapproval-confirm';
3
+ import { Button } from '@/components/ui/button';
4
+ import { useRouter } from '@/i18n/navigation';
5
+ import { X } from 'lucide-react';
6
+ import { useTransition } from 'react';
7
+ import { removeFromCart } from './actions';
8
+ import { useReapprovalConfirm } from './reapproval-confirm';
9
9
 
10
- export function CartRemoveButton({lineId, requiresReapproval}: {lineId: string; requiresReapproval?: boolean}) {
10
+ export function CartRemoveButton({
11
+ lineId,
12
+ requiresReapproval,
13
+ }: {
14
+ lineId: string;
15
+ requiresReapproval?: boolean;
16
+ }) {
11
17
  const router = useRouter();
12
18
  const [isPending, startTransition] = useTransition();
13
- const {guardCartChange, dialog} = useReapprovalConfirm(requiresReapproval);
19
+ const { guardCartChange, dialog } = useReapprovalConfirm(requiresReapproval);
14
20
 
15
21
  return (
16
22
  <>
@@ -29,7 +35,7 @@ export function CartRemoveButton({lineId, requiresReapproval}: {lineId: string;
29
35
  });
30
36
  }}
31
37
  >
32
- <X className="h-4 w-4"/>
38
+ <X className="h-4 w-4" />
33
39
  </Button>
34
40
  </>
35
41
  );