@doswiftly/cli 0.1.18 → 0.1.20

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 (277) hide show
  1. package/README.md +23 -323
  2. package/dist/commands/check.js +1 -1
  3. package/dist/commands/check.js.map +1 -1
  4. package/dist/commands/deploy.d.ts +20 -0
  5. package/dist/commands/deploy.d.ts.map +1 -1
  6. package/dist/commands/deploy.js +249 -17
  7. package/dist/commands/deploy.js.map +1 -1
  8. package/dist/commands/doctor.js +3 -3
  9. package/dist/commands/doctor.js.map +1 -1
  10. package/dist/commands/init.js +4 -4
  11. package/dist/commands/sdk.js +5 -5
  12. package/dist/commands/sdk.js.map +1 -1
  13. package/dist/commands/template.js +4 -4
  14. package/dist/commands/template.js.map +1 -1
  15. package/dist/commands/types.js +5 -5
  16. package/dist/commands/types.js.map +1 -1
  17. package/dist/commands/verify.js +2 -2
  18. package/dist/commands/verify.js.map +1 -1
  19. package/dist/lib/package-manager.d.ts +1 -1
  20. package/dist/lib/package-manager.js +1 -1
  21. package/package.json +4 -4
  22. package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
  23. package/templates/storefront-minimal/wrangler.toml +11 -0
  24. package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
  25. package/templates/storefront-nextjs/README.md +16 -12
  26. package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
  27. package/templates/storefront-nextjs/app/account/page.tsx +2 -2
  28. package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
  29. package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
  30. package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
  31. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
  32. package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
  33. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
  34. package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
  35. package/templates/storefront-nextjs/app/page.tsx +1 -1
  36. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
  37. package/templates/storefront-nextjs/app/products/page.tsx +2 -2
  38. package/templates/storefront-nextjs/app/search/page.tsx +1 -1
  39. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
  40. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
  41. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
  42. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
  43. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
  44. package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
  45. package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
  46. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
  47. package/templates/storefront-nextjs/components/providers.tsx +1 -1
  48. package/templates/storefront-nextjs/lib/currency.tsx +3 -3
  49. package/templates/storefront-nextjs/lib/format.ts +1 -1
  50. package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
  51. package/templates/storefront-nextjs/package.dev.json +1 -1
  52. package/templates/storefront-nextjs/package.json +1 -1
  53. package/templates/storefront-nextjs/package.json.template +1 -1
  54. package/templates/storefront-nextjs/wrangler.toml +11 -0
  55. package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
  56. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
  57. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
  58. package/templates/storefront-nextjs-shadcn/CLAUDE.md +172 -35
  59. package/templates/storefront-nextjs-shadcn/README.md +29 -162
  60. package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
  61. package/templates/storefront-nextjs-shadcn/app/[locale]/account/addresses/page.tsx +226 -0
  62. package/templates/storefront-nextjs-shadcn/app/[locale]/account/error.tsx +46 -0
  63. package/templates/storefront-nextjs-shadcn/app/[locale]/account/loading.tsx +19 -0
  64. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +89 -193
  65. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/loading.tsx +60 -0
  66. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/page.tsx +119 -0
  67. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
  68. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/page.tsx +101 -0
  69. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +9 -7
  70. package/templates/storefront-nextjs-shadcn/app/[locale]/account/settings/page.tsx +208 -0
  71. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +24 -17
  72. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
  73. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
  74. package/templates/storefront-nextjs-shadcn/app/[locale]/blog/[slug]/loading.tsx +17 -0
  75. package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +44 -3
  76. package/templates/storefront-nextjs-shadcn/app/[locale]/blog/loading.tsx +19 -0
  77. package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +2 -1
  78. package/templates/storefront-nextjs-shadcn/app/[locale]/cart/loading.tsx +26 -0
  79. package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +20 -13
  80. package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/category-products-client.tsx +58 -0
  81. package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/loading.tsx +32 -0
  82. package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/page.tsx +95 -0
  83. package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +21 -12
  84. package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/error.tsx +43 -0
  85. package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/loading.tsx +31 -0
  86. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +334 -253
  87. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
  88. package/templates/storefront-nextjs-shadcn/app/[locale]/collections/[handle]/loading.tsx +19 -0
  89. package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +6 -4
  90. package/templates/storefront-nextjs-shadcn/app/[locale]/collections/loading.tsx +18 -0
  91. package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +20 -12
  92. package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
  93. package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
  94. package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
  95. package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
  96. package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
  97. package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/error.tsx +43 -0
  98. package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/loading.tsx +29 -0
  99. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +17 -14
  100. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +18 -62
  101. package/templates/storefront-nextjs-shadcn/app/[locale]/products/loading.tsx +32 -0
  102. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
  103. package/templates/storefront-nextjs-shadcn/app/[locale]/products/products-client.tsx +450 -0
  104. package/templates/storefront-nextjs-shadcn/app/[locale]/search/loading.tsx +18 -0
  105. package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +27 -28
  106. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
  107. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
  108. package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
  109. package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
  110. package/templates/storefront-nextjs-shadcn/app/layout.tsx +8 -35
  111. package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
  112. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
  113. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
  114. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
  115. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +17 -13
  116. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +42 -30
  117. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
  118. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +18 -16
  119. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +37 -58
  120. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +85 -66
  121. package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
  122. package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
  123. package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
  124. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +10 -6
  125. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +9 -6
  126. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +8 -6
  127. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
  128. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +10 -8
  129. package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
  130. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +38 -20
  131. package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
  132. package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
  133. package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
  134. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +7 -5
  135. package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
  136. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +6 -4
  137. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +10 -9
  138. package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
  139. package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
  140. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
  141. package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
  142. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +22 -12
  143. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +18 -15
  144. package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
  145. package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
  146. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
  147. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
  148. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +13 -10
  149. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +10 -6
  150. package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
  151. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +3 -13
  152. package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
  153. package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
  154. package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
  155. package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
  156. package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
  157. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +41 -16
  158. package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
  159. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +7 -4
  160. package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
  161. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +52 -34
  162. package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
  163. package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
  164. package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
  165. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
  166. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
  167. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +32 -42
  168. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
  169. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
  170. package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
  171. package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
  172. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +4 -2
  173. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +72 -0
  174. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +87 -0
  175. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +140 -0
  176. package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
  177. package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
  178. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +11 -37
  179. package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
  180. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +179 -124
  181. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
  182. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
  183. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
  184. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
  185. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +44 -19
  186. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
  187. package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
  188. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
  189. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +26 -34
  190. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
  191. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
  192. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
  193. package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
  194. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +63 -0
  195. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
  196. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
  197. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +59 -72
  198. package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
  199. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
  200. package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
  201. package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
  202. package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
  203. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
  204. package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
  205. package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
  206. package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
  207. package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
  208. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
  209. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +4 -2
  210. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
  211. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +13387 -0
  212. package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
  213. package/templates/storefront-nextjs-shadcn/hooks/index.ts +3 -0
  214. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
  215. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
  216. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +34 -229
  217. package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
  218. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +16 -12
  219. package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
  220. package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
  221. package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
  222. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
  223. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
  224. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +33 -0
  225. package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
  226. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +720 -632
  227. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +88 -0
  228. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +132 -182
  229. package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
  230. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
  231. package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
  232. package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
  233. package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
  234. package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
  235. package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
  236. package/templates/storefront-nextjs-shadcn/package.json +14 -14
  237. package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
  238. package/templates/storefront-nextjs-shadcn/proxy.ts +115 -47
  239. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -56
  240. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
  241. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
  242. package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
  243. package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
  244. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
  245. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
  246. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
  247. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
  248. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
  249. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
  250. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
  251. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
  252. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +0 -215
  253. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +0 -128
  254. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +0 -80
  255. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +0 -171
  256. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +0 -78
  257. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +0 -192
  258. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
  259. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
  260. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
  261. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
  262. package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
  263. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
  264. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
  265. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
  266. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
  267. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
  268. package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
  269. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
  270. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
  271. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
  272. /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
  273. /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
  274. /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
  275. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
  276. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
  277. /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
@@ -1,42 +1,25 @@
1
1
  "use client";
2
2
 
3
3
  import { useState } from "react";
4
- import { useRouter } from "next/navigation";
5
- import Link from "next/link";
4
+ import { useRouter, Link } from "@/i18n/navigation";
5
+ import { useTranslations } from "next-intl";
6
6
  import { z } from "zod";
7
- import { useMutation } from "@tanstack/react-query";
8
- import { getGraphQLClient } from "@/lib/graphql/client";
9
- import { CustomerCreateDocument } from "@/generated/graphql";
7
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
8
+ import { useExecute } from "@/lib/graphql/client";
9
+ import { CustomerCreateDocument, type CustomerCreateMutation } from "@/generated/graphql";
10
+ import { useAuthStore } from "@doswiftly/storefront-sdk/react";
11
+ import { createAuthTokenClient } from "@doswiftly/storefront-sdk";
12
+
13
+ const { setToken: setAuthToken } = createAuthTokenClient();
10
14
  import { Button } from "@/components/ui/button";
11
15
  import { Input } from "@/components/ui/input";
12
16
 
13
- // Zod validation schema for registration form
14
- const registerSchema = z.object({
15
- firstName: z
16
- .string()
17
- .min(1, "First name is required")
18
- .min(2, "First name must be at least 2 characters")
19
- .max(50, "First name must be less than 50 characters"),
20
- lastName: z
21
- .string()
22
- .min(1, "Last name is required")
23
- .min(2, "Last name must be at least 2 characters")
24
- .max(50, "Last name must be less than 50 characters"),
25
- email: z
26
- .string()
27
- .min(1, "Email is required")
28
- .email("Please enter a valid email address"),
29
- password: z
30
- .string()
31
- .min(1, "Password is required")
32
- .min(8, "Password must be at least 8 characters")
33
- .regex(
34
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
35
- "Password must contain at least one uppercase letter, one lowercase letter, and one number"
36
- ),
37
- });
38
-
39
- type RegisterFormData = z.infer<typeof registerSchema>;
17
+ type RegisterFormData = {
18
+ firstName: string;
19
+ lastName: string;
20
+ email: string;
21
+ password: string;
22
+ };
40
23
 
41
24
  export interface RegisterFormProps {
42
25
  onSuccess?: () => void;
@@ -45,12 +28,38 @@ export interface RegisterFormProps {
45
28
 
46
29
  /**
47
30
  * RegisterForm - Reusable registration form component with Zod validation
48
- *
31
+ *
49
32
  * Validates user input before submission and displays field-level errors
50
33
  */
51
34
  export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFormProps) {
35
+ const t = useTranslations("auth");
52
36
  const router = useRouter();
53
37
 
38
+ const registerSchema = z.object({
39
+ firstName: z
40
+ .string()
41
+ .min(1, t("validation.firstNameMinLength"))
42
+ .min(2, t("validation.firstNameMinLength"))
43
+ .max(50, t("validation.firstNameMaxLength")),
44
+ lastName: z
45
+ .string()
46
+ .min(1, t("validation.lastNameMinLength"))
47
+ .min(2, t("validation.lastNameMinLength"))
48
+ .max(50, t("validation.lastNameMaxLength")),
49
+ email: z
50
+ .string()
51
+ .min(1, t("validation.emailInvalid"))
52
+ .email(t("validation.emailInvalid")),
53
+ password: z
54
+ .string()
55
+ .min(1, t("validation.passwordMinLength"))
56
+ .min(8, t("validation.passwordMinLength"))
57
+ .regex(
58
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
59
+ t("validation.passwordRequirements"),
60
+ ),
61
+ });
62
+
54
63
  const [email, setEmail] = useState("");
55
64
  const [password, setPassword] = useState("");
56
65
  const [firstName, setFirstName] = useState("");
@@ -58,7 +67,9 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
58
67
  const [error, setError] = useState("");
59
68
  const [fieldErrors, setFieldErrors] = useState<Partial<Record<keyof RegisterFormData, string>>>({});
60
69
 
61
- const client = getGraphQLClient();
70
+ const queryClient = useQueryClient();
71
+ const { setAuth } = useAuthStore();
72
+ const execute = useExecute();
62
73
 
63
74
  const registerMutation = useMutation({
64
75
  mutationFn: async (input: {
@@ -67,7 +78,10 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
67
78
  firstName: string;
68
79
  lastName: string;
69
80
  }) => {
70
- return client.request(CustomerCreateDocument, { input });
81
+ return execute<CustomerCreateMutation>(
82
+ CustomerCreateDocument.toString(),
83
+ { input },
84
+ );
71
85
  },
72
86
  });
73
87
 
@@ -105,45 +119,50 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
105
119
  });
106
120
 
107
121
  // Check for user errors first
108
- const userErrors = mutationResult?.customerCreate?.userErrors ||
109
- mutationResult?.customerCreate?.customerUserErrors || [];
110
-
122
+ const userErrors = mutationResult?.customerCreate?.userErrors || [];
123
+
111
124
  if (userErrors.length > 0) {
112
- setError(userErrors[0].message || "Registration failed");
125
+ setError(userErrors[0].message || t("registerFailed"));
113
126
  return;
114
127
  }
115
128
 
116
129
  if (mutationResult?.customerCreate?.customer) {
117
- // Registration successful - token is already in the response
118
130
  const accessToken = mutationResult?.customerCreate?.customerAccessToken?.accessToken;
119
-
131
+ const customer = mutationResult.customerCreate.customer;
132
+
120
133
  if (accessToken) {
121
- // Store token in httpOnly cookie via API route
122
- const tokenResponse = await fetch("/api/auth/set-token", {
123
- method: "POST",
124
- headers: { "Content-Type": "application/json" },
125
- body: JSON.stringify({ token: accessToken }),
126
- });
134
+ // Store token in Zustand store (for client-side GraphQL requests)
135
+ setAuth(
136
+ {
137
+ id: customer.id,
138
+ email: customer.email,
139
+ firstName: customer.firstName || undefined,
140
+ lastName: customer.lastName || undefined,
141
+ phone: customer.phone || undefined,
142
+ },
143
+ accessToken,
144
+ );
145
+
146
+ // Store token in httpOnly cookie (for SSR/middleware)
147
+ await setAuthToken(accessToken);
148
+
149
+ // Invalidate queries so authenticated data loads fresh
150
+ queryClient.invalidateQueries();
127
151
 
128
- if (tokenResponse.ok) {
129
- if (onSuccess) {
130
- onSuccess();
131
- } else {
132
- router.push(redirectTo);
133
- }
152
+ if (onSuccess) {
153
+ onSuccess();
134
154
  } else {
135
- setError("Registration successful but session setup failed. Please try logging in.");
155
+ router.push(redirectTo);
136
156
  }
137
157
  } else {
138
- // No token in response - redirect to login
139
- setError("Registration successful! Please log in with your credentials.");
158
+ setError(t("registerSuccess"));
140
159
  setTimeout(() => router.push("/auth/login"), 2000);
141
160
  }
142
161
  } else {
143
- setError("Registration failed. Please try again.");
162
+ setError(t("registerFailed"));
144
163
  }
145
164
  } catch (err) {
146
- setError("An error occurred. Please try again.");
165
+ setError(t("unexpectedError"));
147
166
  }
148
167
  };
149
168
 
@@ -161,7 +180,7 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
161
180
  htmlFor="firstName"
162
181
  className="text-sm font-medium text-foreground"
163
182
  >
164
- First Name
183
+ {t("firstName")}
165
184
  </label>
166
185
  <Input
167
186
  id="firstName"
@@ -186,7 +205,7 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
186
205
  htmlFor="lastName"
187
206
  className="text-sm font-medium text-foreground"
188
207
  >
189
- Last Name
208
+ {t("lastName")}
190
209
  </label>
191
210
  <Input
192
211
  id="lastName"
@@ -212,7 +231,7 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
212
231
  htmlFor="email"
213
232
  className="text-sm font-medium text-foreground"
214
233
  >
215
- Email
234
+ {t("email")}
216
235
  </label>
217
236
  <Input
218
237
  id="email"
@@ -237,7 +256,7 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
237
256
  htmlFor="password"
238
257
  className="text-sm font-medium text-foreground"
239
258
  >
240
- Password
259
+ {t("password")}
241
260
  </label>
242
261
  <Input
243
262
  id="password"
@@ -257,7 +276,7 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
257
276
  </p>
258
277
  ) : (
259
278
  <p className="text-xs text-muted-foreground">
260
- Must be at least 8 characters with uppercase, lowercase, and number
279
+ {t("passwordRequirements")}
261
280
  </p>
262
281
  )}
263
282
  </div>
@@ -267,16 +286,16 @@ export function RegisterForm({ onSuccess, redirectTo = "/account" }: RegisterFor
267
286
  className="w-full"
268
287
  disabled={registerMutation.isPending}
269
288
  >
270
- {registerMutation.isPending ? "Creating account..." : "Create Account"}
289
+ {registerMutation.isPending ? t("creatingAccount") : t("createAccount")}
271
290
  </Button>
272
291
 
273
292
  <div className="text-center text-sm text-muted-foreground">
274
- Already have an account?{" "}
293
+ {t("hasAccount")}{" "}
275
294
  <Link
276
295
  href="/auth/login"
277
296
  className="font-medium text-primary hover:underline"
278
297
  >
279
- Sign in
298
+ {t("signIn")}
280
299
  </Link>
281
300
  </div>
282
301
  </form>
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import Image from 'next/image';
10
- import Link from 'next/link';
10
+ import { Link } from '@/i18n/navigation';
11
11
  import { Calendar, Clock, Eye, User } from 'lucide-react';
12
12
  import { Card, CardContent, CardFooter } from '@/components/ui/card';
13
13
  import { Badge } from '@/components/ui/badge';
@@ -6,7 +6,7 @@
6
6
  * Sidebar with categories, tags, recent posts, and search.
7
7
  */
8
8
 
9
- import Link from 'next/link';
9
+ import { Link } from '@/i18n/navigation';
10
10
  import { Search, FolderOpen, Tag, TrendingUp } from 'lucide-react';
11
11
  import { Input } from '@/components/ui/input';
12
12
  import { Badge } from '@/components/ui/badge';
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import Link from "next/link";
3
+ import { Link } from "@/i18n/navigation";
4
4
  import Image from "next/image";
5
5
  import { Card } from "@/components/ui/card";
6
6
 
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useEffect } from "react";
4
+ import { useRouter } from "@/i18n/navigation";
5
+ import { useTranslations } from "next-intl";
4
6
  import { X, ShoppingBag, Loader2 } from "lucide-react";
5
7
  import { useCartStore } from "@/stores/cart-store";
6
8
  import { useCartSync } from "@/hooks/use-cart-sync";
@@ -22,6 +24,10 @@ export interface CartDrawerProps {
22
24
  * Mutations go through useCartActions (server-only).
23
25
  */
24
26
  export function CartDrawer({ className }: CartDrawerProps) {
27
+ const router = useRouter();
28
+ const t = useTranslations("cart");
29
+ const tc = useTranslations("common");
30
+
25
31
  // UI state from Zustand
26
32
  const { isOpen, closeCart } = useCartStore();
27
33
 
@@ -45,9 +51,7 @@ export function CartDrawer({ className }: CartDrawerProps) {
45
51
 
46
52
  const handleCheckout = () => {
47
53
  closeCart();
48
- if (typeof window !== 'undefined') {
49
- window.location.href = "/checkout";
50
- }
54
+ router.push("/checkout");
51
55
  };
52
56
 
53
57
  if (!isOpen) return null;
@@ -70,14 +74,14 @@ export function CartDrawer({ className }: CartDrawerProps) {
70
74
  )}
71
75
  role="dialog"
72
76
  aria-modal="true"
73
- aria-label="Shopping cart"
77
+ aria-label={t("title")}
74
78
  >
75
79
  {/* Header */}
76
80
  <div className="flex items-center justify-between border-b border-border p-4">
77
81
  <div className="flex items-center gap-2">
78
82
  <ShoppingBag className="h-5 w-5" />
79
83
  <h2 className="text-lg font-semibold">
80
- Shopping Cart
84
+ {t("title")}
81
85
  {totalQuantity > 0 && (
82
86
  <span className="ml-2 text-sm font-normal text-muted-foreground">
83
87
  ({totalQuantity})
@@ -90,7 +94,7 @@ export function CartDrawer({ className }: CartDrawerProps) {
90
94
  size="sm"
91
95
  onClick={closeCart}
92
96
  className="h-8 w-8 p-0"
93
- aria-label="Close cart"
97
+ aria-label={tc("close")}
94
98
  >
95
99
  <X className="h-5 w-5" />
96
100
  </Button>
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { useRouter } from "next/navigation";
3
+ import { useRouter } from "@/i18n/navigation";
4
4
  import { ShoppingCart } from "lucide-react";
5
5
  import { useCartSync } from "@/hooks/use-cart-sync";
6
+ import { useHydrated } from "@doswiftly/storefront-sdk/react";
6
7
  import { Button } from "@/components/ui/button";
7
8
  import { cn } from "@/lib/utils";
8
9
 
@@ -15,11 +16,13 @@ export interface CartIconProps {
15
16
  /**
16
17
  * CartIcon - Cart button with item count badge
17
18
  *
18
- * Client Component that reads cart count from server (source of truth)
19
+ * Client Component that reads cart count from server (source of truth).
20
+ * Badge is guarded by isHydrated to prevent hydration mismatch.
19
21
  */
20
22
  export function CartIcon({ className, navigateToCart = true }: CartIconProps) {
21
23
  const router = useRouter();
22
- const { totalQuantity, isLoading } = useCartSync();
24
+ const isHydrated = useHydrated();
25
+ const { totalQuantity } = useCartSync();
23
26
 
24
27
  const handleClick = () => {
25
28
  if (navigateToCart) {
@@ -33,12 +36,12 @@ export function CartIcon({ className, navigateToCart = true }: CartIconProps) {
33
36
  size="sm"
34
37
  onClick={handleClick}
35
38
  className={cn("relative p-2", className)}
36
- aria-label={`Shopping cart with ${totalQuantity} items`}
39
+ aria-label={`Shopping cart${isHydrated && totalQuantity > 0 ? ` with ${totalQuantity} items` : ""}`}
37
40
  >
38
41
  <ShoppingCart className="h-5 w-5" />
39
42
 
40
- {/* Badge */}
41
- {totalQuantity > 0 && (
43
+ {/* Badge — only rendered after hydration to prevent mismatch */}
44
+ {isHydrated && totalQuantity > 0 && (
42
45
  <span className="absolute -right-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-primary text-xs font-medium text-primary-foreground">
43
46
  {totalQuantity > 99 ? "99+" : totalQuantity}
44
47
  </span>
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
 
3
3
  import { X, Gift } from "lucide-react";
4
- import Link from "next/link";
4
+ import { Link } from "@/i18n/navigation";
5
+ import { useTranslations } from "next-intl";
5
6
  import { ProductImage } from "@/components/product/product-image";
6
7
  import { ProductQuantitySelector } from "@/components/product/product-quantity-selector";
7
8
  import { Button } from "@/components/ui/button";
8
9
  import { cn } from "@/lib/utils";
9
- import { formatPrice, formatAmount } from "@/lib/format";
10
+ import { formatPrice, formatAmount } from "@doswiftly/storefront-sdk";
10
11
  import type { CartItemData } from "@/hooks/use-cart-sync";
11
12
 
12
13
  export interface CartItemProps {
@@ -25,6 +26,7 @@ export function CartItem({
25
26
  onRemove,
26
27
  className,
27
28
  }: CartItemProps) {
29
+ const t = useTranslations("cart");
28
30
  const itemTotal = parseFloat(item.price.amount) * item.quantity;
29
31
 
30
32
  return (
@@ -56,7 +58,7 @@ export function CartItem({
56
58
  {item.productType === "GIFT_CARD" && (
57
59
  <p className="mt-1 flex items-center gap-1 text-xs text-primary">
58
60
  <Gift className="h-3 w-3" />
59
- Karta podarunkowa
61
+ {t("giftCardLabel")}
60
62
  </p>
61
63
  )}
62
64
  {item.variantTitle && (
@@ -72,7 +74,7 @@ export function CartItem({
72
74
  size="sm"
73
75
  onClick={() => onRemove(item.lineId)}
74
76
  className="h-8 w-8 p-0"
75
- aria-label="Remove item"
77
+ aria-label={t("remove")}
76
78
  >
77
79
  <X className="h-4 w-4" />
78
80
  </Button>
@@ -94,7 +96,7 @@ export function CartItem({
94
96
  </p>
95
97
  {item.quantity > 1 && (
96
98
  <p className="text-xs text-muted-foreground">
97
- {formatPrice(item.price)} each
99
+ {formatPrice(item.price)} {t("each")}
98
100
  </p>
99
101
  )}
100
102
  </div>
@@ -103,7 +105,7 @@ export function CartItem({
103
105
  {/* Out of stock warning */}
104
106
  {!item.available && (
105
107
  <p className="mt-2 text-xs text-destructive">
106
- This item is currently out of stock
108
+ {t("outOfStockWarning")}
107
109
  </p>
108
110
  )}
109
111
  </div>
@@ -0,0 +1,53 @@
1
+ # Fragment for CartItem / CartLine component.
2
+ #
3
+ # Maps to the CartItemData interface in use-cart-sync.ts.
4
+ # Used by: CartDrawer, CartPage, MiniCart.
5
+ #
6
+ # Usage in components:
7
+ # import type { CartLineFieldsFragment } from '@/generated/graphql';
8
+ # interface Props { line: CartLineFieldsFragment }
9
+ #
10
+ # Note: This is a UI-focused subset. The full Cart fragment
11
+ # from storefront-operations includes cost aggregates and
12
+ # discount allocations for the cart summary.
13
+
14
+ fragment CartLineFields on CartLine {
15
+ id
16
+ quantity
17
+ productId
18
+ productTitle
19
+ productHandle
20
+ productType
21
+ merchandise {
22
+ id
23
+ title
24
+ available
25
+ quantityAvailable
26
+ price {
27
+ amount
28
+ currencyCode
29
+ }
30
+ image {
31
+ url
32
+ altText
33
+ }
34
+ selectedOptions {
35
+ name
36
+ value
37
+ }
38
+ }
39
+ cost {
40
+ amountPerQuantity {
41
+ amount
42
+ currencyCode
43
+ }
44
+ totalAmount {
45
+ amount
46
+ currencyCode
47
+ }
48
+ compareAtAmountPerQuantity {
49
+ amount
50
+ currencyCode
51
+ }
52
+ }
53
+ }
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
+ import { useTranslations } from "next-intl";
3
4
  import { Button } from "@/components/ui/button";
4
5
  import { cn } from "@/lib/utils";
5
- import { formatAmount } from "@/lib/format";
6
+ import { formatAmount } from "@doswiftly/storefront-sdk";
6
7
 
7
8
  export interface CartSummaryProps {
8
9
  subtotal: number;
@@ -26,6 +27,7 @@ export function CartSummary({
26
27
  onCheckout,
27
28
  className,
28
29
  }: CartSummaryProps) {
30
+ const t = useTranslations("cart");
29
31
  const displayTotal = total ?? subtotal;
30
32
  const hasDiscount = (totalDiscount ?? 0) > 0;
31
33
 
@@ -33,7 +35,7 @@ export function CartSummary({
33
35
  <div className={cn("space-y-4", className)}>
34
36
  {/* Subtotal */}
35
37
  <div className="flex items-center justify-between border-t border-border pt-4">
36
- <span className="text-base font-medium text-foreground">Subtotal</span>
38
+ <span className="text-base font-medium text-foreground">{t("subtotal")}</span>
37
39
  <span className={cn("text-lg font-semibold text-foreground", hasDiscount && "text-muted-foreground line-through text-base")}>
38
40
  {formatAmount(subtotal, currencyCode)}
39
41
  </span>
@@ -43,11 +45,11 @@ export function CartSummary({
43
45
  {hasDiscount && (
44
46
  <>
45
47
  <div className="flex items-center justify-between text-sm text-green-600 dark:text-green-400">
46
- <span>Discount</span>
48
+ <span>{t("discount")}</span>
47
49
  <span>-{formatAmount(totalDiscount!, currencyCode)}</span>
48
50
  </div>
49
51
  <div className="flex items-center justify-between">
50
- <span className="text-base font-medium text-foreground">Total</span>
52
+ <span className="text-base font-medium text-foreground">{t("total")}</span>
51
53
  <span className="text-lg font-semibold text-foreground">
52
54
  {formatAmount(displayTotal, currencyCode)}
53
55
  </span>
@@ -57,12 +59,12 @@ export function CartSummary({
57
59
 
58
60
  {/* Item count */}
59
61
  <p className="text-sm text-muted-foreground">
60
- {itemCount} {itemCount === 1 ? "item" : "items"} in cart
62
+ {t("itemCount", { count: itemCount })} {t("inCart")}
61
63
  </p>
62
64
 
63
65
  {/* Shipping note */}
64
66
  <p className="text-xs text-muted-foreground">
65
- Shipping and taxes calculated at checkout
67
+ {t("shippingTaxNote")}
66
68
  </p>
67
69
 
68
70
  {/* Checkout button */}
@@ -72,12 +74,12 @@ export function CartSummary({
72
74
  className="w-full"
73
75
  disabled={itemCount === 0}
74
76
  >
75
- Proceed to Checkout
77
+ {t("proceedToCheckout")}
76
78
  </Button>
77
79
 
78
80
  {/* Continue shopping */}
79
81
  <Button variant="outline" size="lg" className="w-full" asChild>
80
- <a href="/products">Continue Shopping</a>
82
+ <a href="/products">{t("continueShopping")}</a>
81
83
  </Button>
82
84
  </div>
83
85
  );
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { Tag, Check, X } from "lucide-react";
5
6
  import { Input } from "@/components/ui/input";
6
7
  import { Button } from "@/components/ui/button";
@@ -22,6 +23,8 @@ export function PromoCodeInput({
22
23
  onRemove,
23
24
  className,
24
25
  }: PromoCodeInputProps) {
26
+ const t = useTranslations("cart");
27
+ const tc = useTranslations("common");
25
28
  const [code, setCode] = useState("");
26
29
  const [isLoading, setIsLoading] = useState(false);
27
30
  const [error, setError] = useState<string | null>(null);
@@ -38,13 +41,13 @@ export function PromoCodeInput({
38
41
  const result = await onApply(code.trim());
39
42
 
40
43
  if (result.success) {
41
- setSuccess(result.message || "Promo code applied successfully!");
44
+ setSuccess(result.message || t("promoApplied"));
42
45
  setCode("");
43
46
  } else {
44
- setError(result.message || "Invalid promo code");
47
+ setError(result.message || t("promoError"));
45
48
  }
46
49
  } catch (err) {
47
- setError("Failed to apply promo code");
50
+ setError(t("promoError"));
48
51
  } finally {
49
52
  setIsLoading(false);
50
53
  }
@@ -88,7 +91,7 @@ export function PromoCodeInput({
88
91
  <Tag className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
89
92
  <Input
90
93
  type="text"
91
- placeholder="Enter promo code"
94
+ placeholder={t("enterPromoCode")}
92
95
  value={code}
93
96
  onChange={(e) => setCode(e.target.value.toUpperCase())}
94
97
  onKeyDown={(e) => e.key === "Enter" && handleApply()}
@@ -101,7 +104,7 @@ export function PromoCodeInput({
101
104
  disabled={!code.trim() || isLoading}
102
105
  size="default"
103
106
  >
104
- {isLoading ? "Applying..." : "Apply"}
107
+ {isLoading ? t("applying") : tc("apply")}
105
108
  </Button>
106
109
  </div>
107
110