@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,40 +1,48 @@
1
- import {Badge} from '@/components/ui/badge';
1
+ import { Badge } from '@/components/ui/badge';
2
2
  import {
3
+ Ban,
4
+ CheckCircle,
5
+ Clock,
3
6
  FileEdit,
7
+ MessageSquare,
4
8
  Search,
5
- Clock,
6
9
  Send,
7
- MessageSquare,
8
- CheckCircle,
9
- XCircle,
10
- Ban,
11
10
  TimerOff,
11
+ XCircle,
12
12
  type LucideIcon,
13
13
  } from 'lucide-react';
14
14
 
15
15
  const STATUS_CONFIG: Record<string, { color: string; label: string; icon: LucideIcon }> = {
16
- DRAFT: {color: 'bg-gray-100 text-gray-800', label: 'Draft', icon: FileEdit},
17
- IN_REVIEW: {color: 'bg-amber-100 text-amber-800', label: 'In Review', icon: Search},
18
- AWAITING_APPROVAL: {color: 'bg-amber-100 text-amber-800', label: 'Awaiting Approval', icon: Clock},
19
- QUOTED: {color: 'bg-blue-100 text-blue-800', label: 'Quoted', icon: Send},
20
- CHANGES_REQUESTED: {color: 'bg-orange-100 text-orange-800', label: 'Changes Requested', icon: MessageSquare},
21
- ACCEPTED: {color: 'bg-green-100 text-green-800', label: 'Accepted', icon: CheckCircle},
22
- REJECTED: {color: 'bg-red-100 text-red-800', label: 'Rejected', icon: XCircle},
23
- CANCELLED: {color: 'bg-red-100 text-red-800', label: 'Cancelled', icon: Ban},
24
- EXPIRED: {color: 'bg-gray-100 text-gray-600', label: 'Expired', icon: TimerOff},
16
+ DRAFT: { color: 'bg-gray-100 text-gray-800', label: 'Draft', icon: FileEdit },
17
+ IN_REVIEW: { color: 'bg-amber-100 text-amber-800', label: 'In Review', icon: Search },
18
+ AWAITING_APPROVAL: { color: 'bg-amber-100 text-amber-800', label: 'Awaiting Approval', icon: Clock },
19
+ QUOTED: { color: 'bg-blue-100 text-blue-800', label: 'Quoted', icon: Send },
20
+ CHANGES_REQUESTED: {
21
+ color: 'bg-orange-100 text-orange-800',
22
+ label: 'Changes Requested',
23
+ icon: MessageSquare,
24
+ },
25
+ ACCEPTED: { color: 'bg-green-100 text-green-800', label: 'Accepted', icon: CheckCircle },
26
+ REJECTED: { color: 'bg-red-100 text-red-800', label: 'Rejected', icon: XCircle },
27
+ CANCELLED: { color: 'bg-red-100 text-red-800', label: 'Cancelled', icon: Ban },
28
+ EXPIRED: { color: 'bg-gray-100 text-gray-600', label: 'Expired', icon: TimerOff },
25
29
  };
26
30
 
27
31
  interface QuoteStatusBadgeProps {
28
32
  status: string;
29
33
  }
30
34
 
31
- export function QuoteStatusBadge({status}: QuoteStatusBadgeProps) {
32
- const config = STATUS_CONFIG[status] || {color: 'bg-gray-100 text-gray-800', label: status, icon: Clock};
35
+ export function QuoteStatusBadge({ status }: QuoteStatusBadgeProps) {
36
+ const config = STATUS_CONFIG[status] || {
37
+ color: 'bg-gray-100 text-gray-800',
38
+ label: status,
39
+ icon: Clock,
40
+ };
33
41
  const Icon = config.icon;
34
42
 
35
43
  return (
36
44
  <Badge className={config.color} variant="secondary">
37
- <Icon className="h-3 w-3 mr-1"/>
45
+ <Icon className="h-3 w-3 mr-1" />
38
46
  {config.label}
39
47
  </Badge>
40
48
  );
@@ -1,17 +1,15 @@
1
1
  'use client';
2
2
 
3
- import {useLocale} from 'next-intl';
4
- import {formatDate} from '@/lib/format';
3
+ import { formatDate } from '@/lib/format';
4
+ import { useLocale } from 'next-intl';
5
5
 
6
6
  interface QuoteValidityIndicatorProps {
7
7
  validUntil: string;
8
8
  }
9
9
 
10
- export function QuoteValidityIndicator({validUntil}: QuoteValidityIndicatorProps) {
10
+ export function QuoteValidityIndicator({ validUntil }: QuoteValidityIndicatorProps) {
11
11
  const locale = useLocale();
12
- const daysRemaining = Math.ceil(
13
- (new Date(validUntil).getTime() - Date.now()) / (1000 * 60 * 60 * 24),
14
- );
12
+ const daysRemaining = Math.ceil((new Date(validUntil).getTime() - Date.now()) / (1000 * 60 * 60 * 24));
15
13
 
16
14
  if (daysRemaining < 0) {
17
15
  return <span className="text-red-600 font-medium">Expired</span>;
@@ -23,7 +21,9 @@ export function QuoteValidityIndicator({validUntil}: QuoteValidityIndicatorProps
23
21
 
24
22
  if (daysRemaining <= 7) {
25
23
  return (
26
- <span className={`font-medium ${daysRemaining <= 3 ? 'text-amber-600' : 'text-muted-foreground'}`}>
24
+ <span
25
+ className={`font-medium ${daysRemaining <= 3 ? 'text-amber-600' : 'text-muted-foreground'}`}
26
+ >
27
27
  Expires in {daysRemaining} day{daysRemaining !== 1 ? 's' : ''}
28
28
  </span>
29
29
  );
@@ -1,8 +1,8 @@
1
- import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card';
2
- import {Price} from '@/components/commerce/price';
3
- import {Badge} from '@/components/ui/badge';
4
- import type {QuoteDetail, QuoteVersion} from '@/lib/vendure/queries';
5
- import {getTranslations} from 'next-intl/server';
1
+ import { Price } from '@/components/commerce/price';
2
+ import { Badge } from '@/components/ui/badge';
3
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import type { QuoteDetail, QuoteVersion } from '@/lib/vendure/queries';
5
+ import { getTranslations } from 'next-intl/server';
6
6
 
7
7
  interface VersionComparisonProps {
8
8
  currentVersion: NonNullable<QuoteDetail['currentVersion']>;
@@ -10,27 +10,30 @@ interface VersionComparisonProps {
10
10
  currencyCode: string;
11
11
  }
12
12
 
13
- export async function QuoteVersionComparison({currentVersion, versions, currencyCode}: VersionComparisonProps) {
13
+ export async function QuoteVersionComparison({
14
+ currentVersion,
15
+ versions,
16
+ currencyCode,
17
+ }: VersionComparisonProps) {
14
18
  const t = await getTranslations('Quotes');
15
19
  const hasMultipleVersions = versions.length > 1;
16
- const v1 = versions.find(v => v.versionNumber === 1);
17
- const v1ItemsBySku = new Map(
18
- v1?.lineItems.map(item => [item.sku, item]) ?? [],
19
- );
20
+ const v1 = versions.find((v) => v.versionNumber === 1);
21
+ const v1ItemsBySku = new Map(v1?.lineItems.map((item) => [item.sku, item]) ?? []);
20
22
 
21
23
  return (
22
24
  <Card>
23
25
  <CardHeader>
24
- <CardTitle>{t('lineItemsCardTitle', {version: currentVersion.versionNumber})}</CardTitle>
26
+ <CardTitle>{t('lineItemsCardTitle', { version: currentVersion.versionNumber })}</CardTitle>
25
27
  </CardHeader>
26
28
  <CardContent>
27
29
  <div className="space-y-3">
28
30
  {currentVersion.lineItems.map((item) => {
29
31
  const v1Item = v1ItemsBySku.get(item.sku);
30
- const hasPriceDiff = hasMultipleVersions && v1Item && v1Item.unitPrice !== item.unitPrice;
32
+ const hasPriceDiff =
33
+ hasMultipleVersions && v1Item && v1Item.unitPrice !== item.unitPrice;
31
34
  const isNew = hasMultipleVersions && !v1Item;
32
35
  const diff = hasPriceDiff ? item.unitPrice - v1Item.unitPrice : 0;
33
- const pctDiff = hasPriceDiff ? ((diff / v1Item.unitPrice) * 100) : 0;
36
+ const pctDiff = hasPriceDiff ? (diff / v1Item.unitPrice) * 100 : 0;
34
37
 
35
38
  return (
36
39
  <div key={item.id} className="flex gap-4">
@@ -38,20 +41,27 @@ export async function QuoteVersionComparison({currentVersion, versions, currency
38
41
  <div className="flex items-center gap-2 flex-wrap">
39
42
  <p className="font-medium">{item.productName}</p>
40
43
  {isNew && (
41
- <Badge variant="secondary" className="text-xs">{t('lineItemNew')}</Badge>
42
- )}
43
- {item.productVariant?.stockLevel && item.productVariant.stockLevel !== 'IN_STOCK' && (
44
- <span className={`text-[10px] font-medium px-1.5 py-0.5 rounded ${
45
- item.productVariant.stockLevel === 'OUT_OF_STOCK'
46
- ? 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300'
47
- : 'bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300'
48
- }`}>
49
- {item.productVariant.stockLevel === 'OUT_OF_STOCK' ? t('lineItemOutOfStock') : t('lineItemLowStock')}
50
- </span>
44
+ <Badge variant="secondary" className="text-xs">
45
+ {t('lineItemNew')}
46
+ </Badge>
51
47
  )}
48
+ {item.productVariant?.stockLevel &&
49
+ item.productVariant.stockLevel !== 'IN_STOCK' && (
50
+ <span
51
+ className={`text-[10px] font-medium px-1.5 py-0.5 rounded ${
52
+ item.productVariant.stockLevel === 'OUT_OF_STOCK'
53
+ ? 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300'
54
+ : 'bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300'
55
+ }`}
56
+ >
57
+ {item.productVariant.stockLevel === 'OUT_OF_STOCK'
58
+ ? t('lineItemOutOfStock')
59
+ : t('lineItemLowStock')}
60
+ </span>
61
+ )}
52
62
  </div>
53
63
  <p className="text-sm text-muted-foreground">
54
- {t('lineItemSku', {sku: item.sku})}
64
+ {t('lineItemSku', { sku: item.sku })}
55
65
  </p>
56
66
  {item.note && (
57
67
  <p className="text-sm text-muted-foreground italic mt-1">
@@ -61,19 +71,29 @@ export async function QuoteVersionComparison({currentVersion, versions, currency
61
71
  </div>
62
72
  <div className="text-right">
63
73
  <p className="font-medium">
64
- <Price value={item.unitPrice * item.quantity} currencyCode={currencyCode}/>
74
+ <Price
75
+ value={item.unitPrice * item.quantity}
76
+ currencyCode={currencyCode}
77
+ />
65
78
  </p>
66
79
  <p className="text-sm text-muted-foreground">
67
- {t('lineItemQty', {qty: item.quantity})} &times; <Price value={item.unitPrice} currencyCode={currencyCode}/>
80
+ {t('lineItemQty', { qty: item.quantity })} &times;{' '}
81
+ <Price value={item.unitPrice} currencyCode={currencyCode} />
68
82
  </p>
69
83
  {hasPriceDiff && (
70
84
  <div className="mt-1">
71
85
  <p className="text-xs text-muted-foreground line-through">
72
- <Price value={v1Item.unitPrice} currencyCode={currencyCode}/>
86
+ <Price value={v1Item.unitPrice} currencyCode={currencyCode} />
73
87
  </p>
74
- <p className={`text-xs font-medium ${diff < 0 ? 'text-green-600' : 'text-red-600'}`}>
75
- {diff < 0 ? '' : '+'}<Price value={diff} currencyCode={currencyCode}/>
76
- {' '}({pctDiff > 0 ? '+' : ''}{pctDiff.toFixed(1)}%)
88
+ <p
89
+ className={`text-xs font-medium ${
90
+ diff < 0 ? 'text-green-600' : 'text-red-600'
91
+ }`}
92
+ >
93
+ {diff < 0 ? '' : '+'}
94
+ <Price value={diff} currencyCode={currencyCode} /> (
95
+ {pctDiff > 0 ? '+' : ''}
96
+ {pctDiff.toFixed(1)}%)
77
97
  </p>
78
98
  </div>
79
99
  )}
@@ -1,10 +1,10 @@
1
- import { ProductCarousel } from "@/components/commerce/product-carousel";
2
- import { cacheLife, cacheTag } from "next/cache";
3
- import { publicClient } from "@/lib/vendure/api";
4
- import { GetCollectionProductsQuery } from "@/lib/vendure/queries";
5
- import { readFragment } from "@/graphql";
6
- import { ProductCardFragment } from "@/lib/vendure/fragments";
7
- import { getTranslations } from "next-intl/server";
1
+ import { ProductCarousel } from '@/components/commerce/product-carousel';
2
+ import { readFragment } from '@/graphql';
3
+ import { publicClient } from '@/lib/vendure/api';
4
+ import { ProductCardFragment } from '@/lib/vendure/fragments';
5
+ import { GetCollectionProductsQuery } from '@/lib/vendure/queries';
6
+ import { getTranslations } from 'next-intl/server';
7
+ import { cacheLife, cacheTag } from 'next/cache';
8
8
  export interface RelatedProductsProps {
9
9
  collectionSlug: string;
10
10
  currentProductId: string;
@@ -12,32 +12,46 @@ export interface RelatedProductsProps {
12
12
  channelToken?: string;
13
13
  }
14
14
 
15
- async function getRelatedProducts(collectionSlug: string, locale: string, currentProductId: string, channelToken?: string) {
16
- 'use cache'
17
- cacheLife('hours')
18
- cacheTag(`related-products-${collectionSlug}-${locale}${channelToken ? `-${channelToken}` : ''}`)
15
+ async function getRelatedProducts(
16
+ collectionSlug: string,
17
+ locale: string,
18
+ currentProductId: string,
19
+ channelToken?: string,
20
+ ) {
21
+ 'use cache';
22
+ cacheLife('hours');
23
+ cacheTag(`related-products-${collectionSlug}-${locale}${channelToken ? `-${channelToken}` : ''}`);
19
24
 
20
- const [result, error] = await publicClient.query(GetCollectionProductsQuery, {
21
- slug: collectionSlug,
22
- input: {
23
- collectionSlug: collectionSlug,
24
- take: 13, // Fetch extra to account for filtering out current product
25
- skip: 0,
26
- groupByProduct: true
27
- }
28
- }, {languageCode: locale, ...(channelToken ? {channelToken} : {})});
25
+ const [result, error] = await publicClient.query(
26
+ GetCollectionProductsQuery,
27
+ {
28
+ slug: collectionSlug,
29
+ input: {
30
+ collectionSlug: collectionSlug,
31
+ take: 13, // Fetch extra to account for filtering out current product
32
+ skip: 0,
33
+ groupByProduct: true,
34
+ },
35
+ },
36
+ { languageCode: locale, ...(channelToken ? { channelToken } : {}) },
37
+ );
29
38
  if (error) return [];
30
39
 
31
40
  // Filter out the current product and limit to 12
32
41
  return result.data.search.items
33
- .filter(item => {
42
+ .filter((item) => {
34
43
  const product = readFragment(ProductCardFragment, item);
35
44
  return product.productId !== currentProductId;
36
45
  })
37
46
  .slice(0, 12);
38
47
  }
39
48
 
40
- export async function RelatedProducts({ collectionSlug, currentProductId, locale, channelToken }: RelatedProductsProps) {
49
+ export async function RelatedProducts({
50
+ collectionSlug,
51
+ currentProductId,
52
+ locale,
53
+ channelToken,
54
+ }: RelatedProductsProps) {
41
55
  const [products, t] = await Promise.all([
42
56
  getRelatedProducts(collectionSlug, locale, currentProductId, channelToken),
43
57
  getTranslations('Product'),
@@ -47,10 +61,5 @@ export async function RelatedProducts({ collectionSlug, currentProductId, locale
47
61
  return null;
48
62
  }
49
63
 
50
- return (
51
- <ProductCarousel
52
- title={t('relatedProducts')}
53
- products={products}
54
- />
55
- );
64
+ return <ProductCarousel title={t('relatedProducts')} products={products} />;
56
65
  }
@@ -0,0 +1,39 @@
1
+ // Shared-cart role helpers. Roles are ordered view < edit < checkout; the
2
+ // server is the source of truth for permissions, these drive UI gating only.
3
+ import type { SharedCartRole } from '@/app/[locale]/account/shared-carts/actions';
4
+
5
+ const ROLE_RANK: Record<SharedCartRole, number> = { view: 0, edit: 1, checkout: 2 };
6
+
7
+ // Company permission to create and own a shared cart. Most buyers hold this; it also lets you
8
+ // govern carts you own, but nothing else.
9
+ export const USE_SHARED_CARTS_PERMISSION = 'UseSharedCarts';
10
+
11
+ // Company permission to govern *any* cart in the company — manage members, transfer, rename,
12
+ // delete — regardless of ownership. Admin-level; holding it also lets you create.
13
+ export const MANAGE_SHARED_CARTS_PERMISSION = 'ManageSharedCarts';
14
+
15
+ export function canCreateSharedCarts(permissions: readonly string[] | null | undefined): boolean {
16
+ return (
17
+ permissions?.some((p) => p === USE_SHARED_CARTS_PERMISSION || p === MANAGE_SHARED_CARTS_PERMISSION) ??
18
+ false
19
+ );
20
+ }
21
+
22
+ export function canManageSharedCarts(permissions: readonly string[] | null | undefined): boolean {
23
+ return permissions?.includes(MANAGE_SHARED_CARTS_PERMISSION) ?? false;
24
+ }
25
+
26
+ export const ROLE_OPTIONS: ReadonlyArray<{ value: SharedCartRole; label: string; hint: string }> = [
27
+ { value: 'view', label: 'Viewer', hint: 'Can view the cart and activity' },
28
+ { value: 'edit', label: 'Editor', hint: 'Can add, change, and remove items' },
29
+ { value: 'checkout', label: 'Checkout', hint: 'Can edit and place the order' },
30
+ ];
31
+
32
+ export function roleLabel(role: string): string {
33
+ return ROLE_OPTIONS.find((o) => o.value === role)?.label ?? role;
34
+ }
35
+
36
+ export function roleAtLeast(role: SharedCartRole | undefined, min: SharedCartRole): boolean {
37
+ if (!role) return false;
38
+ return ROLE_RANK[role] >= ROLE_RANK[min];
39
+ }
@@ -0,0 +1,28 @@
1
+ import { Badge } from '@/components/ui/badge';
2
+ import { CheckCircle2, CircleDot, Lock, type LucideIcon } from 'lucide-react';
3
+
4
+ const STATUS_CONFIG: Record<string, { color: string; label: string; icon: LucideIcon }> = {
5
+ Open: { color: 'bg-emerald-100 text-emerald-800', label: 'Open', icon: CircleDot },
6
+ Locked: { color: 'bg-amber-100 text-amber-800', label: 'Checkout in progress', icon: Lock },
7
+ Closed: { color: 'bg-gray-100 text-gray-600', label: 'Closed', icon: CheckCircle2 },
8
+ };
9
+
10
+ interface SharedCartStatusBadgeProps {
11
+ status: string;
12
+ }
13
+
14
+ export function SharedCartStatusBadge({ status }: SharedCartStatusBadgeProps) {
15
+ const config = STATUS_CONFIG[status] ?? {
16
+ color: 'bg-gray-100 text-gray-800',
17
+ label: status,
18
+ icon: CircleDot,
19
+ };
20
+ const Icon = config.icon;
21
+
22
+ return (
23
+ <Badge className={config.color} variant="secondary">
24
+ <Icon className="h-3 w-3 mr-1" />
25
+ {config.label}
26
+ </Badge>
27
+ );
28
+ }
@@ -1,11 +1,12 @@
1
+ import type { ResultOf } from '@/graphql';
2
+ import { Link } from '@/i18n/navigation';
3
+ import type { GetTopCollectionsQuery } from '@/lib/vendure/queries';
4
+ import { Layers } from 'lucide-react';
1
5
  import Image from 'next/image';
2
- import {Layers} from 'lucide-react';
3
- import {Link} from '@/i18n/navigation';
4
- import type {ResultOf} from '@/graphql';
5
- import type {GetTopCollectionsQuery} from '@/lib/vendure/queries';
6
6
 
7
- export type ShopByCategoryCollection =
8
- ResultOf<typeof GetTopCollectionsQuery>['collections']['items'][number];
7
+ export type ShopByCategoryCollection = ResultOf<
8
+ typeof GetTopCollectionsQuery
9
+ >['collections']['items'][number];
9
10
 
10
11
  export interface ShopByCategoryProps {
11
12
  collections: ShopByCategoryCollection[];
@@ -18,7 +19,7 @@ export interface ShopByCategoryProps {
18
19
  * this component is layout-neutral so it can be used on the homepage, a
19
20
  * standalone catalog landing page, or elsewhere.
20
21
  */
21
- export function ShopByCategory({collections}: ShopByCategoryProps) {
22
+ export function ShopByCategory({ collections }: ShopByCategoryProps) {
22
23
  if (!collections.length) return null;
23
24
 
24
25
  return (
@@ -1,16 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { useState, useTransition } from 'react';
4
- import { Heart, Check, Plus } from 'lucide-react';
5
- import { Button } from '@/components/ui/button';
6
- import {
7
- Popover,
8
- PopoverContent,
9
- PopoverTrigger,
10
- } from '@/components/ui/popover';
3
+ import type { ShoppingListInfo } from '@/app/[locale]/account/shopping-lists/actions';
11
4
  import { addToShoppingList, removeFromShoppingList } from '@/app/[locale]/account/shopping-lists/actions';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
7
+ import { Check, Heart, Plus } from 'lucide-react';
8
+ import { useState, useTransition } from 'react';
12
9
  import { toast } from 'sonner';
13
- import type { ShoppingListInfo } from '@/app/[locale]/account/shopping-lists/actions';
14
10
 
15
11
  interface ShoppingListToggleProps {
16
12
  variantId: string;
@@ -18,14 +14,19 @@ interface ShoppingListToggleProps {
18
14
  initialIsInAnyList: boolean;
19
15
  }
20
16
 
21
- export function ShoppingListToggle({ variantId, shoppingLists, initialIsInAnyList }: ShoppingListToggleProps) {
17
+ export function ShoppingListToggle({
18
+ variantId,
19
+ shoppingLists,
20
+ initialIsInAnyList,
21
+ }: ShoppingListToggleProps) {
22
22
  const [lists, setLists] = useState(shoppingLists);
23
23
  const [isPending, startTransition] = useTransition();
24
24
  const [open, setOpen] = useState(false);
25
25
  // Track adds when we have no list data (0 lists → server creates default)
26
26
  const [addedToDefault, setAddedToDefault] = useState(false);
27
27
 
28
- const isInAnyList = addedToDefault || lists.some((list) => list.items.some((item) => item.variantId === variantId));
28
+ const isInAnyList =
29
+ addedToDefault || lists.some((list) => list.items.some((item) => item.variantId === variantId));
29
30
 
30
31
  const handleAdd = (listId: string, listName: string) => {
31
32
  startTransition(async () => {
@@ -112,12 +113,7 @@ export function ShoppingListToggle({ variantId, shoppingLists, initialIsInAnyLis
112
113
  return (
113
114
  <Popover open={open} onOpenChange={setOpen}>
114
115
  <PopoverTrigger asChild>
115
- <Button
116
- variant="outline"
117
- size="icon"
118
- disabled={isPending}
119
- aria-label="Add to shopping list"
120
- >
116
+ <Button variant="outline" size="icon" disabled={isPending} aria-label="Add to shopping list">
121
117
  <Heart
122
118
  className={`h-5 w-5 transition-colors ${
123
119
  isInAnyList ? 'fill-red-500 text-red-500' : 'text-muted-foreground'
@@ -137,9 +133,7 @@ export function ShoppingListToggle({ variantId, shoppingLists, initialIsInAnyLis
137
133
  type="button"
138
134
  disabled={isPending}
139
135
  onClick={() =>
140
- isInList
141
- ? handleRemove(list.id, list.name)
142
- : handleAdd(list.id, list.name)
136
+ isInList ? handleRemove(list.id, list.name) : handleAdd(list.id, list.name)
143
137
  }
144
138
  className="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground disabled:opacity-50"
145
139
  >
@@ -1,16 +1,9 @@
1
1
  'use client';
2
2
 
3
-
4
- import {
5
- Select,
6
- SelectContent,
7
- SelectItem,
8
- SelectTrigger,
9
- SelectValue,
10
- } from '@/components/ui/select';
11
- import {useSearchParams} from "next/navigation";
12
- import {usePathname, useRouter} from "@/i18n/navigation";
13
- import {useTranslations} from 'next-intl';
3
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
4
+ import { usePathname, useRouter } from '@/i18n/navigation';
5
+ import { useTranslations } from 'next-intl';
6
+ import { useSearchParams } from 'next/navigation';
14
7
 
15
8
  export function SortDropdown() {
16
9
  const t = useTranslations('Sort');
@@ -19,10 +12,10 @@ export function SortDropdown() {
19
12
  const router = useRouter();
20
13
 
21
14
  const sortOptions = [
22
- {value: 'name-asc', label: t('nameAsc')},
23
- {value: 'name-desc', label: t('nameDesc')},
24
- {value: 'price-asc', label: t('priceAsc')},
25
- {value: 'price-desc', label: t('priceDesc')},
15
+ { value: 'name-asc', label: t('nameAsc') },
16
+ { value: 'name-desc', label: t('nameDesc') },
17
+ { value: 'price-asc', label: t('priceAsc') },
18
+ { value: 'price-desc', label: t('priceDesc') },
26
19
  ];
27
20
 
28
21
  const currentSort = searchParams.get('sort') || 'name-asc';
@@ -37,7 +30,7 @@ export function SortDropdown() {
37
30
  return (
38
31
  <Select value={currentSort} onValueChange={handleSortChange}>
39
32
  <SelectTrigger className="w-[180px]">
40
- <SelectValue placeholder={t('placeholder')}/>
33
+ <SelectValue placeholder={t('placeholder')} />
41
34
  </SelectTrigger>
42
35
  <SelectContent>
43
36
  {sortOptions.map((option) => (
@@ -2,44 +2,42 @@
2
2
 
3
3
  import { Price } from '@/components/commerce/price';
4
4
  import { maskCode } from '@/lib/format';
5
+ import type { StoreCredit } from '@/lib/vendure/queries';
5
6
  import { Wallet } from 'lucide-react';
6
7
  import { useTranslations } from 'next-intl';
7
- import type { StoreCredit } from '@/lib/vendure/queries';
8
8
 
9
9
  interface StoreCreditSummaryProps {
10
- credits: StoreCredit[];
11
- totalAfterCredit: number | null | undefined;
12
- currencyCode: string;
10
+ credits: StoreCredit[];
11
+ totalAfterCredit: number | null | undefined;
12
+ currencyCode: string;
13
13
  }
14
14
 
15
15
  export function StoreCreditSummary({ credits, totalAfterCredit, currencyCode }: StoreCreditSummaryProps) {
16
- const t = useTranslations('Cart');
17
- if (credits.length === 0) return null;
16
+ const t = useTranslations('Cart');
17
+ if (credits.length === 0) return null;
18
18
 
19
- return (
20
- <div className="space-y-3">
21
- <div className="flex items-center gap-2">
22
- <Wallet className="h-5 w-5 text-muted-foreground" />
23
- <h4 className="font-medium">{t('storeCreditsApplied')}</h4>
24
- </div>
25
- <div className="text-sm space-y-1">
26
- {credits.map((credit) => (
27
- <div key={credit.code} className="flex justify-between">
28
- <span className="text-muted-foreground">
29
- {maskCode(credit.code)}
30
- </span>
31
- <span className="text-green-600">
32
- -<Price value={credit.amountDeducted} currencyCode={currencyCode} />
33
- </span>
34
- </div>
35
- ))}
36
- {totalAfterCredit != null && (
37
- <div className="flex justify-between font-medium pt-1">
38
- <span>{t('amountToPay')}</span>
39
- <Price value={totalAfterCredit} currencyCode={currencyCode} />
40
- </div>
41
- )}
42
- </div>
43
- </div>
44
- );
19
+ return (
20
+ <div className="space-y-3">
21
+ <div className="flex items-center gap-2">
22
+ <Wallet className="h-5 w-5 text-muted-foreground" />
23
+ <h4 className="font-medium">{t('storeCreditsApplied')}</h4>
24
+ </div>
25
+ <div className="text-sm space-y-1">
26
+ {credits.map((credit) => (
27
+ <div key={credit.code} className="flex justify-between">
28
+ <span className="text-muted-foreground">{maskCode(credit.code)}</span>
29
+ <span className="text-green-600">
30
+ -<Price value={credit.amountDeducted} currencyCode={currencyCode} />
31
+ </span>
32
+ </div>
33
+ ))}
34
+ {totalAfterCredit != null && (
35
+ <div className="flex justify-between font-medium pt-1">
36
+ <span>{t('amountToPay')}</span>
37
+ <Price value={totalAfterCredit} currencyCode={currencyCode} />
38
+ </div>
39
+ )}
40
+ </div>
41
+ </div>
42
+ );
45
43
  }