@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
@@ -11,6 +11,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
11
11
  import { Badge } from "@/components/ui/badge";
12
12
  import { Progress } from "@/components/ui/progress";
13
13
  import { cn } from "@/lib/utils";
14
+ import { useTranslations } from "next-intl";
14
15
 
15
16
  /**
16
17
  * Gift card status
@@ -72,35 +73,35 @@ function getStatusConfig(status: GiftCardStatus) {
72
73
  switch (status) {
73
74
  case "ACTIVE":
74
75
  return {
75
- label: "Active",
76
+ translationKey: "active",
76
77
  variant: "default" as const,
77
78
  color: "text-green-600",
78
79
  bgColor: "bg-green-100 dark:bg-green-900/30",
79
80
  };
80
81
  case "USED":
81
82
  return {
82
- label: "Fully Redeemed",
83
+ translationKey: "used",
83
84
  variant: "secondary" as const,
84
85
  color: "text-gray-600",
85
86
  bgColor: "bg-gray-100 dark:bg-gray-900/30",
86
87
  };
87
88
  case "EXPIRED":
88
89
  return {
89
- label: "Expired",
90
+ translationKey: "expired",
90
91
  variant: "destructive" as const,
91
92
  color: "text-red-600",
92
93
  bgColor: "bg-red-100 dark:bg-red-900/30",
93
94
  };
94
95
  case "DISABLED":
95
96
  return {
96
- label: "Disabled",
97
+ translationKey: "disabled",
97
98
  variant: "secondary" as const,
98
99
  color: "text-gray-600",
99
100
  bgColor: "bg-gray-100 dark:bg-gray-900/30",
100
101
  };
101
102
  default:
102
103
  return {
103
- label: "Unknown",
104
+ translationKey: "unknown",
104
105
  variant: "secondary" as const,
105
106
  color: "text-gray-600",
106
107
  bgColor: "bg-gray-100 dark:bg-gray-900/30",
@@ -136,6 +137,7 @@ export function GiftCardBalance({
136
137
  giftCard,
137
138
  className = "",
138
139
  }: GiftCardBalanceProps) {
140
+ const t = useTranslations("giftCardStatus");
139
141
  const statusConfig = getStatusConfig(giftCard.status);
140
142
  const initialAmount = parseFloat(giftCard.initialAmount.amount);
141
143
  const currentBalance = parseFloat(giftCard.balance.amount);
@@ -153,8 +155,8 @@ export function GiftCardBalance({
153
155
  </div>
154
156
  <div>
155
157
  <CardTitle className="text-lg flex items-center gap-2">
156
- Gift Card
157
- <Badge variant={statusConfig.variant}>{statusConfig.label}</Badge>
158
+ {t("title")}
159
+ <Badge variant={statusConfig.variant}>{t(statusConfig.translationKey as any)}</Badge>
158
160
  </CardTitle>
159
161
  <p className="text-sm text-muted-foreground font-mono mt-1">
160
162
  {giftCard.maskedCode}
@@ -168,7 +170,7 @@ export function GiftCardBalance({
168
170
  {/* Balance display */}
169
171
  <div className="space-y-2">
170
172
  <div className="flex justify-between items-baseline">
171
- <span className="text-sm text-muted-foreground">Available Balance</span>
173
+ <span className="text-sm text-muted-foreground">{t("availableBalance")}</span>
172
174
  <span className="text-2xl font-bold">
173
175
  {formatAmount(
174
176
  giftCard.balance.amount,
@@ -182,14 +184,14 @@ export function GiftCardBalance({
182
184
 
183
185
  <div className="flex justify-between text-xs text-muted-foreground">
184
186
  <span>
185
- Used:{" "}
187
+ {t("usedAmount")}{" "}
186
188
  {formatAmount(
187
189
  (initialAmount - currentBalance).toFixed(2),
188
190
  giftCard.balance.currencyCode
189
191
  )}
190
192
  </span>
191
193
  <span>
192
- Initial:{" "}
194
+ {t("initialAmount")}{" "}
193
195
  {formatAmount(
194
196
  giftCard.initialAmount.amount,
195
197
  giftCard.initialAmount.currencyCode
@@ -214,8 +216,9 @@ export function GiftCardBalance({
214
216
  <Calendar className="h-4 w-4 text-muted-foreground" />
215
217
  )}
216
218
  <span>
217
- {expiringSoon ? "Expires soon: " : "Expires: "}
218
- {formatDate(giftCard.expiresAt)}
219
+ {expiringSoon
220
+ ? t("expiresSoon", { date: formatDate(giftCard.expiresAt) })
221
+ : t("expiresOn", { date: formatDate(giftCard.expiresAt) })}
219
222
  </span>
220
223
  </div>
221
224
  )}
@@ -224,7 +227,7 @@ export function GiftCardBalance({
224
227
  {!giftCard.expiresAt && giftCard.status === "ACTIVE" && (
225
228
  <div className="flex items-center gap-2 text-sm text-muted-foreground">
226
229
  <CheckCircle className="h-4 w-4 text-green-500" />
227
- <span>No expiration date</span>
230
+ <span>{t("noExpiration")}</span>
228
231
  </div>
229
232
  )}
230
233
 
@@ -233,7 +236,7 @@ export function GiftCardBalance({
233
236
  <div className="pt-3 border-t border-border space-y-2">
234
237
  {giftCard.recipientName && (
235
238
  <p className="text-sm">
236
- <span className="text-muted-foreground">To: </span>
239
+ <span className="text-muted-foreground">{t("recipientLabel")} </span>
237
240
  <span className="font-medium">{giftCard.recipientName}</span>
238
241
  </p>
239
242
  )}
@@ -297,6 +300,7 @@ export function GiftCardBalanceCompact({
297
300
  status,
298
301
  className = "",
299
302
  }: GiftCardBalanceCompactProps) {
303
+ const t = useTranslations("giftCardStatus");
300
304
  const statusConfig = getStatusConfig(status);
301
305
 
302
306
  return (
@@ -310,7 +314,7 @@ export function GiftCardBalanceCompact({
310
314
  <CreditCard className="h-4 w-4 text-muted-foreground" />
311
315
  <span className="font-mono text-sm">{maskedCode}</span>
312
316
  <Badge variant={statusConfig.variant} className="text-xs">
313
- {statusConfig.label}
317
+ {t(statusConfig.translationKey as any)}
314
318
  </Badge>
315
319
  </div>
316
320
  <span className="font-semibold">
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useCallback } from "react";
4
+ import { useTranslations } from "next-intl";
4
5
  import { Input } from "@/components/ui/input";
5
6
  import { Button } from "@/components/ui/button";
6
7
  import { Badge } from "@/components/ui/badge";
@@ -89,6 +90,8 @@ export function GiftCardInput({
89
90
  disabled = false,
90
91
  className = "",
91
92
  }: GiftCardInputProps) {
93
+ const t = useTranslations("cart");
94
+ const tc = useTranslations("common");
92
95
  const [code, setCode] = useState("");
93
96
  const [isValidating, setIsValidating] = useState(false);
94
97
  const [validation, setValidation] = useState<GiftCardValidationResult | null>(
@@ -124,13 +127,13 @@ export function GiftCardInput({
124
127
  */
125
128
  const handleValidate = useCallback(async () => {
126
129
  if (!code || code.length < 4) {
127
- setError("Please enter a valid gift card code");
130
+ setError(t("giftCardInvalidCode"));
128
131
  return;
129
132
  }
130
133
 
131
134
  // Check if already applied
132
135
  if (appliedCodes.includes(code)) {
133
- setError("This gift card has already been applied");
136
+ setError(t("giftCardAlreadyApplied"));
134
137
  return;
135
138
  }
136
139
 
@@ -144,8 +147,8 @@ export function GiftCardInput({
144
147
  if (!result.valid && result.error) {
145
148
  setError(result.error.message);
146
149
  }
147
- } catch (err: any) {
148
- setError(err.message || "Failed to validate gift card");
150
+ } catch (err: unknown) {
151
+ setError(err instanceof Error ? err.message : t("giftCardValidationFailed"));
149
152
  } finally {
150
153
  setIsValidating(false);
151
154
  }
@@ -187,7 +190,7 @@ export function GiftCardInput({
187
190
  <Gift className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
188
191
  <Input
189
192
  type="text"
190
- placeholder="Gift card code (e.g., ABCD-EFGH-IJKL-MNOP)"
193
+ placeholder={t("giftCardCodePlaceholder")}
191
194
  value={code}
192
195
  onChange={handleCodeChange}
193
196
  onKeyDown={handleKeyDown}
@@ -207,10 +210,10 @@ export function GiftCardInput({
207
210
  ) : validation?.valid ? (
208
211
  <>
209
212
  <Check className="mr-2 h-4 w-4" />
210
- Apply
213
+ {tc("apply")}
211
214
  </>
212
215
  ) : (
213
- "Check"
216
+ t("giftCardCheck")
214
217
  )}
215
218
  </Button>
216
219
  </div>
@@ -229,7 +232,7 @@ export function GiftCardInput({
229
232
  <div className="flex items-center justify-between">
230
233
  <div className="flex items-center gap-2">
231
234
  <Check className="h-4 w-4" />
232
- <span>Gift card valid!</span>
235
+ <span>{t("giftCardValid")}</span>
233
236
  </div>
234
237
  {validation.availableBalance && (
235
238
  <span className="font-semibold">
@@ -244,7 +247,7 @@ export function GiftCardInput({
244
247
  ) : (
245
248
  <div className="flex items-center gap-2">
246
249
  <X className="h-4 w-4" />
247
- <span>{validation.error?.message || "Invalid gift card"}</span>
250
+ <span>{validation.error?.message || t("giftCardInvalid")}</span>
248
251
  </div>
249
252
  )}
250
253
  </div>
@@ -262,7 +265,7 @@ export function GiftCardInput({
262
265
  {appliedCodes.length > 0 && (
263
266
  <div className="space-y-2">
264
267
  <p className="text-sm font-medium text-muted-foreground">
265
- Applied gift cards:
268
+ {t("appliedGiftCards")}
266
269
  </p>
267
270
  <div className="flex flex-wrap gap-2">
268
271
  {appliedCodes.map((appliedCode) => (
@@ -1,14 +1,18 @@
1
1
  "use client";
2
2
 
3
- import Link from "next/link";
3
+ import { useTranslations } from "next-intl";
4
+ import { Link } from "@/i18n/navigation";
4
5
  import { cn } from "@/lib/utils";
5
6
  import { useCollections } from "@/lib/graphql/hooks";
7
+ import type { CollectionCardFields } from "@/lib/graphql/fragments";
6
8
 
7
9
  export interface CategoryGridProps {
8
10
  className?: string;
9
11
  }
10
12
 
11
13
  export function CategoryGrid({ className }: CategoryGridProps) {
14
+ const t = useTranslations("home");
15
+
12
16
  // Use collections for homepage marketing (not categories)
13
17
  // Collections are better for homepage because they're curated and flexible
14
18
  const { data, isLoading, error } = useCollections({
@@ -21,9 +25,9 @@ export function CategoryGrid({ className }: CategoryGridProps) {
21
25
  return (
22
26
  <section className={cn("container mx-auto px-4", className)}>
23
27
  <div className="mb-8 text-center">
24
- <h2 className="text-3xl font-bold text-foreground">Shop by Category</h2>
28
+ <h2 className="text-3xl font-bold text-foreground">{t("shopByCategory")}</h2>
25
29
  <p className="mt-2 text-muted-foreground">
26
- Find exactly what you're looking for
30
+ {t("shopByCategoryDescription")}
27
31
  </p>
28
32
  </div>
29
33
  <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4">
@@ -45,14 +49,14 @@ export function CategoryGrid({ className }: CategoryGridProps) {
45
49
  return (
46
50
  <section className={cn("container mx-auto px-4", className)}>
47
51
  <div className="mb-8 text-center">
48
- <h2 className="text-3xl font-bold text-foreground">Shop by Category</h2>
52
+ <h2 className="text-3xl font-bold text-foreground">{t("shopByCategory")}</h2>
49
53
  <p className="mt-2 text-muted-foreground">
50
- Find exactly what you're looking for
54
+ {t("shopByCategoryDescription")}
51
55
  </p>
52
56
  </div>
53
57
 
54
58
  <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4">
55
- {collections.map((collection: any) => (
59
+ {collections.map((collection: CollectionCardFields) => (
56
60
  <Link
57
61
  key={collection.id}
58
62
  href={`/collections/${collection.handle}`}
@@ -0,0 +1,21 @@
1
+ # Fragment for CollectionCard component.
2
+ #
3
+ # Minimal collection data for listing views: homepage,
4
+ # collection grid, navigation menus.
5
+ #
6
+ # Usage in components:
7
+ # import type { CollectionCardFieldsFragment } from '@/generated/graphql';
8
+ # interface Props { collection: CollectionCardFieldsFragment }
9
+
10
+ fragment CollectionCardFields on Collection {
11
+ id
12
+ handle
13
+ title
14
+ description
15
+ image {
16
+ url
17
+ altText
18
+ width
19
+ height
20
+ }
21
+ }
@@ -1,20 +1,10 @@
1
1
  "use client";
2
2
 
3
- import Link from "next/link";
3
+ import { Link } from "@/i18n/navigation";
4
4
  import { Button } from "@/components/ui/button";
5
5
  import { Card, CardContent } from "@/components/ui/card";
6
6
  import { useCollections } from "@/lib/graphql/hooks";
7
-
8
- export interface Collection {
9
- id: string;
10
- handle: string;
11
- title: string;
12
- description?: string;
13
- image?: {
14
- url: string;
15
- altText?: string | null;
16
- } | null;
17
- }
7
+ import type { CollectionCardFields } from "@/lib/graphql/fragments";
18
8
 
19
9
  export function FeaturedCollections() {
20
10
  // Fetch collections using GraphQL
@@ -61,7 +51,7 @@ export function FeaturedCollections() {
61
51
 
62
52
  {collections.length > 0 ? (
63
53
  <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
64
- {collections.map((collection: Collection) => (
54
+ {collections.map((collection: CollectionCardFields) => (
65
55
  <Link
66
56
  key={collection.id}
67
57
  href={`/collections/${collection.handle}`}
@@ -1,11 +1,15 @@
1
1
  "use client";
2
2
 
3
- import Link from "next/link";
3
+ import { useTranslations } from "next-intl";
4
+ import { Link } from "@/i18n/navigation";
4
5
  import { ProductGrid } from "@/components/product/product-grid";
5
6
  import { Button } from "@/components/ui/button";
6
7
  import { useProducts } from "@/lib/graphql/hooks";
7
8
 
8
9
  export function FeaturedProducts() {
10
+ const t = useTranslations("home");
11
+ const tc = useTranslations("common");
12
+
9
13
  // Fetch featured products using GraphQL
10
14
  const { data, isLoading, error } = useProducts({
11
15
  first: 8,
@@ -19,9 +23,9 @@ export function FeaturedProducts() {
19
23
  <section className="container mx-auto px-4">
20
24
  <div className="mb-8 flex items-center justify-between">
21
25
  <div>
22
- <h2 className="text-3xl font-bold text-foreground">Featured Products</h2>
26
+ <h2 className="text-3xl font-bold text-foreground">{t("featuredProducts")}</h2>
23
27
  <p className="mt-2 text-muted-foreground">
24
- Handpicked favorites just for you
28
+ {t("featuredDescription")}
25
29
  </p>
26
30
  </div>
27
31
  </div>
@@ -53,13 +57,13 @@ export function FeaturedProducts() {
53
57
  <section className="container mx-auto px-4">
54
58
  <div className="mb-8 flex items-center justify-between">
55
59
  <div>
56
- <h2 className="text-3xl font-bold text-foreground">Featured Products</h2>
60
+ <h2 className="text-3xl font-bold text-foreground">{t("featuredProducts")}</h2>
57
61
  <p className="mt-2 text-muted-foreground">
58
- Handpicked favorites just for you
62
+ {t("featuredDescription")}
59
63
  </p>
60
64
  </div>
61
65
  <Button variant="outline" asChild>
62
- <Link href="/products">View All</Link>
66
+ <Link href="/products">{tc("viewAll")}</Link>
63
67
  </Button>
64
68
  </div>
65
69
 
@@ -73,10 +77,10 @@ export function FeaturedProducts() {
73
77
  ) : (
74
78
  <div className="rounded-lg border border-border bg-muted/50 p-12 text-center">
75
79
  <p className="text-muted-foreground">
76
- No featured products available at the moment.
80
+ {t("noFeaturedProducts")}
77
81
  </p>
78
82
  <Button className="mt-4" asChild>
79
- <Link href="/products">Browse All Products</Link>
83
+ <Link href="/products">{t("browseAllProducts")}</Link>
80
84
  </Button>
81
85
  </div>
82
86
  )}
@@ -1,26 +1,31 @@
1
- import Link from "next/link";
1
+ import { useTranslations } from "next-intl";
2
+ import { Link } from "@/i18n/navigation";
2
3
  import { Button } from "@/components/ui/button";
3
4
 
4
5
  export function HeroSection() {
6
+ const t = useTranslations("home");
7
+
5
8
  return (
6
9
  <section className="container mx-auto px-4">
7
10
  <div className="relative overflow-hidden rounded-2xl bg-gradient-to-r from-primary/10 via-primary/5 to-background p-8 md:p-16">
8
11
  <div className="relative z-10 mx-auto max-w-3xl text-center">
9
12
  <h1 className="mb-4 text-4xl font-bold tracking-tight text-foreground sm:text-5xl md:text-6xl">
10
- Welcome to{" "}
11
- <span className="text-primary">
12
- {process.env.NEXT_PUBLIC_SITE_NAME || "My Store"}
13
- </span>
13
+ {t.rich("welcomeTo", {
14
+ storeName: process.env.NEXT_PUBLIC_SITE_NAME || "Store",
15
+ highlight: (chunks) => (
16
+ <span className="text-primary">{chunks}</span>
17
+ ),
18
+ })}
14
19
  </h1>
15
20
  <p className="mx-auto mb-8 max-w-2xl text-lg text-muted-foreground">
16
- Discover our curated collection of quality products. Shop with confidence and enjoy exceptional service.
21
+ {t("heroDescription")}
17
22
  </p>
18
23
  <div className="flex flex-col justify-center gap-4 sm:flex-row">
19
24
  <Button size="lg" asChild>
20
- <Link href="/products">Shop Now</Link>
25
+ <Link href="/products">{t("shopNow")}</Link>
21
26
  </Button>
22
27
  <Button size="lg" variant="outline" asChild>
23
- <Link href="/products">Browse Products</Link>
28
+ <Link href="/products">{t("browseProducts")}</Link>
24
29
  </Button>
25
30
  </div>
26
31
  </div>
@@ -1,7 +1,6 @@
1
1
  export { HeroSection } from './hero-section';
2
2
  export { FeaturedProducts } from './featured-products';
3
3
  export { FeaturedCollections } from './featured-collections';
4
- export type { Collection } from './featured-collections';
5
4
  export { CategoryGrid } from './category-grid';
6
5
  export type { CategoryGridProps } from './category-grid';
7
6
  export { NewsletterSignup } from './newsletter-signup';
@@ -5,12 +5,14 @@ import { Mail, Check } from "lucide-react";
5
5
  import { Input } from "@/components/ui/input";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import { cn } from "@/lib/utils";
8
+ import { useTranslations } from "next-intl";
8
9
 
9
10
  export interface NewsletterSignupProps {
10
11
  className?: string;
11
12
  }
12
13
 
13
14
  export function NewsletterSignup({ className }: NewsletterSignupProps) {
15
+ const t = useTranslations("footer");
14
16
  const [email, setEmail] = useState("");
15
17
  const [isLoading, setIsLoading] = useState(false);
16
18
  const [isSuccess, setIsSuccess] = useState(false);
@@ -20,7 +22,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
20
22
  e.preventDefault();
21
23
 
22
24
  if (!email || !email.includes("@")) {
23
- setError("Please enter a valid email address");
25
+ setError(t("invalidEmail"));
24
26
  return;
25
27
  }
26
28
 
@@ -42,7 +44,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
42
44
  setIsSuccess(false);
43
45
  }, 3000);
44
46
  } catch (err) {
45
- setError("Failed to subscribe. Please try again.");
47
+ setError(t("failedSubscribe"));
46
48
  } finally {
47
49
  setIsLoading(false);
48
50
  }
@@ -57,17 +59,17 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
57
59
  </div>
58
60
 
59
61
  <h2 className="mb-2 text-3xl font-bold text-foreground">
60
- Stay in the Loop
62
+ {t("stayInLoop")}
61
63
  </h2>
62
64
  <p className="mb-6 text-muted-foreground">
63
- Subscribe to our newsletter for exclusive offers, new arrivals, and insider news.
65
+ {t("newsletterLongDescription")}
64
66
  </p>
65
67
 
66
68
  {isSuccess ? (
67
69
  <div className="flex items-center justify-center gap-2 rounded-lg bg-green-50 p-4 text-green-700 dark:bg-green-950 dark:text-green-400">
68
70
  <Check className="h-5 w-5" />
69
71
  <span className="font-medium">
70
- Thanks for subscribing! Check your email for confirmation.
72
+ {t("thanksForSubscribing")}
71
73
  </span>
72
74
  </div>
73
75
  ) : (
@@ -75,7 +77,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
75
77
  <div className="flex flex-col gap-3 sm:flex-row">
76
78
  <Input
77
79
  type="email"
78
- placeholder="Enter your email"
80
+ placeholder={t("emailPlaceholder")}
79
81
  value={email}
80
82
  onChange={(e) => setEmail(e.target.value)}
81
83
  disabled={isLoading}
@@ -88,7 +90,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
88
90
  size="lg"
89
91
  className="sm:w-auto"
90
92
  >
91
- {isLoading ? "Subscribing..." : "Subscribe"}
93
+ {isLoading ? t("subscribing") : t("subscribe")}
92
94
  </Button>
93
95
  </div>
94
96
 
@@ -97,7 +99,7 @@ export function NewsletterSignup({ className }: NewsletterSignupProps) {
97
99
  )}
98
100
 
99
101
  <p className="mt-3 text-xs text-muted-foreground">
100
- By subscribing, you agree to our Privacy Policy and consent to receive updates.
102
+ {t("privacyConsent")}
101
103
  </p>
102
104
  </form>
103
105
  )}
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import { useHydrated } from "@doswiftly/storefront-sdk/react";
4
+ import type { ReactNode } from "react";
5
+
6
+ interface HydratedProps {
7
+ children: ReactNode;
8
+ fallback?: ReactNode;
9
+ }
10
+
11
+ /**
12
+ * Wrapper component that only renders children after client hydration.
13
+ * Use for content that depends on browser-only state (localStorage, Zustand persist).
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Hydrated fallback={<Skeleton className="w-20 h-8" />}>
18
+ * <CartIcon />
19
+ * </Hydrated>
20
+ * ```
21
+ */
22
+ export function Hydrated({ children, fallback = null }: HydratedProps) {
23
+ return useHydrated() ? children : fallback;
24
+ }