@doswiftly/cli 0.1.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 (453) hide show
  1. package/README.md +357 -0
  2. package/bin/doswiftly.js +2 -0
  3. package/dist/commands/auth-github.d.ts +6 -0
  4. package/dist/commands/auth-github.d.ts.map +1 -0
  5. package/dist/commands/auth-github.js +89 -0
  6. package/dist/commands/auth-github.js.map +1 -0
  7. package/dist/commands/auth-token.d.ts +12 -0
  8. package/dist/commands/auth-token.d.ts.map +1 -0
  9. package/dist/commands/auth-token.js +43 -0
  10. package/dist/commands/auth-token.js.map +1 -0
  11. package/dist/commands/auth.d.ts +22 -0
  12. package/dist/commands/auth.d.ts.map +1 -0
  13. package/dist/commands/auth.js +348 -0
  14. package/dist/commands/auth.js.map +1 -0
  15. package/dist/commands/check.d.ts +5 -0
  16. package/dist/commands/check.d.ts.map +1 -0
  17. package/dist/commands/check.js +234 -0
  18. package/dist/commands/check.js.map +1 -0
  19. package/dist/commands/config.d.ts +3 -0
  20. package/dist/commands/config.d.ts.map +1 -0
  21. package/dist/commands/config.js +104 -0
  22. package/dist/commands/config.js.map +1 -0
  23. package/dist/commands/deploy.d.ts +37 -0
  24. package/dist/commands/deploy.d.ts.map +1 -0
  25. package/dist/commands/deploy.js +580 -0
  26. package/dist/commands/deploy.js.map +1 -0
  27. package/dist/commands/dev.d.ts +8 -0
  28. package/dist/commands/dev.d.ts.map +1 -0
  29. package/dist/commands/dev.js +83 -0
  30. package/dist/commands/dev.js.map +1 -0
  31. package/dist/commands/doctor.d.ts +5 -0
  32. package/dist/commands/doctor.d.ts.map +1 -0
  33. package/dist/commands/doctor.js +363 -0
  34. package/dist/commands/doctor.js.map +1 -0
  35. package/dist/commands/domain.d.ts +13 -0
  36. package/dist/commands/domain.d.ts.map +1 -0
  37. package/dist/commands/domain.js +128 -0
  38. package/dist/commands/domain.js.map +1 -0
  39. package/dist/commands/env.d.ts +25 -0
  40. package/dist/commands/env.d.ts.map +1 -0
  41. package/dist/commands/env.js +228 -0
  42. package/dist/commands/env.js.map +1 -0
  43. package/dist/commands/init.d.ts +11 -0
  44. package/dist/commands/init.d.ts.map +1 -0
  45. package/dist/commands/init.js +1028 -0
  46. package/dist/commands/init.js.map +1 -0
  47. package/dist/commands/inspect.d.ts +12 -0
  48. package/dist/commands/inspect.d.ts.map +1 -0
  49. package/dist/commands/inspect.js +162 -0
  50. package/dist/commands/inspect.js.map +1 -0
  51. package/dist/commands/migrate.d.ts +18 -0
  52. package/dist/commands/migrate.d.ts.map +1 -0
  53. package/dist/commands/migrate.js +355 -0
  54. package/dist/commands/migrate.js.map +1 -0
  55. package/dist/commands/preview.d.ts +29 -0
  56. package/dist/commands/preview.d.ts.map +1 -0
  57. package/dist/commands/preview.js +199 -0
  58. package/dist/commands/preview.js.map +1 -0
  59. package/dist/commands/proxy.d.ts +9 -0
  60. package/dist/commands/proxy.d.ts.map +1 -0
  61. package/dist/commands/proxy.js +37 -0
  62. package/dist/commands/proxy.js.map +1 -0
  63. package/dist/commands/sdk.d.ts +5 -0
  64. package/dist/commands/sdk.d.ts.map +1 -0
  65. package/dist/commands/sdk.js +82 -0
  66. package/dist/commands/sdk.js.map +1 -0
  67. package/dist/commands/template.d.ts +107 -0
  68. package/dist/commands/template.d.ts.map +1 -0
  69. package/dist/commands/template.js +1309 -0
  70. package/dist/commands/template.js.map +1 -0
  71. package/dist/commands/types.d.ts +5 -0
  72. package/dist/commands/types.d.ts.map +1 -0
  73. package/dist/commands/types.js +82 -0
  74. package/dist/commands/types.js.map +1 -0
  75. package/dist/commands/update.d.ts +2 -0
  76. package/dist/commands/update.d.ts.map +1 -0
  77. package/dist/commands/update.js +103 -0
  78. package/dist/commands/update.js.map +1 -0
  79. package/dist/commands/upgrade.d.ts +18 -0
  80. package/dist/commands/upgrade.d.ts.map +1 -0
  81. package/dist/commands/upgrade.js +55 -0
  82. package/dist/commands/upgrade.js.map +1 -0
  83. package/dist/commands/verify.d.ts +5 -0
  84. package/dist/commands/verify.d.ts.map +1 -0
  85. package/dist/commands/verify.js +232 -0
  86. package/dist/commands/verify.js.map +1 -0
  87. package/dist/commands/whoami.d.ts +5 -0
  88. package/dist/commands/whoami.d.ts.map +1 -0
  89. package/dist/commands/whoami.js +60 -0
  90. package/dist/commands/whoami.js.map +1 -0
  91. package/dist/config/types.d.ts +173 -0
  92. package/dist/config/types.d.ts.map +1 -0
  93. package/dist/config/types.js +48 -0
  94. package/dist/config/types.js.map +1 -0
  95. package/dist/index.d.ts +3 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +416 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/lib/api-url.d.ts +14 -0
  100. package/dist/lib/api-url.d.ts.map +1 -0
  101. package/dist/lib/api-url.js +24 -0
  102. package/dist/lib/api-url.js.map +1 -0
  103. package/dist/lib/api.d.ts +67 -0
  104. package/dist/lib/api.d.ts.map +1 -0
  105. package/dist/lib/api.js +36 -0
  106. package/dist/lib/api.js.map +1 -0
  107. package/dist/lib/config.d.ts +39 -0
  108. package/dist/lib/config.d.ts.map +1 -0
  109. package/dist/lib/config.js +195 -0
  110. package/dist/lib/config.js.map +1 -0
  111. package/dist/lib/env-storage.d.ts +140 -0
  112. package/dist/lib/env-storage.d.ts.map +1 -0
  113. package/dist/lib/env-storage.js +464 -0
  114. package/dist/lib/env-storage.js.map +1 -0
  115. package/dist/lib/errors.d.ts +61 -0
  116. package/dist/lib/errors.d.ts.map +1 -0
  117. package/dist/lib/errors.js +204 -0
  118. package/dist/lib/errors.js.map +1 -0
  119. package/dist/lib/i18n.d.ts +99 -0
  120. package/dist/lib/i18n.d.ts.map +1 -0
  121. package/dist/lib/i18n.js +184 -0
  122. package/dist/lib/i18n.js.map +1 -0
  123. package/dist/lib/logger.d.ts +95 -0
  124. package/dist/lib/logger.d.ts.map +1 -0
  125. package/dist/lib/logger.js +168 -0
  126. package/dist/lib/logger.js.map +1 -0
  127. package/dist/lib/package-manager.d.ts +91 -0
  128. package/dist/lib/package-manager.d.ts.map +1 -0
  129. package/dist/lib/package-manager.js +205 -0
  130. package/dist/lib/package-manager.js.map +1 -0
  131. package/dist/lib/proxy-server.d.ts +24 -0
  132. package/dist/lib/proxy-server.d.ts.map +1 -0
  133. package/dist/lib/proxy-server.js +173 -0
  134. package/dist/lib/proxy-server.js.map +1 -0
  135. package/dist/lib/select-with-back.d.ts +34 -0
  136. package/dist/lib/select-with-back.d.ts.map +1 -0
  137. package/dist/lib/select-with-back.js +94 -0
  138. package/dist/lib/select-with-back.js.map +1 -0
  139. package/dist/lib/shared-api-client.d.ts +40 -0
  140. package/dist/lib/shared-api-client.d.ts.map +1 -0
  141. package/dist/lib/shared-api-client.js +92 -0
  142. package/dist/lib/shared-api-client.js.map +1 -0
  143. package/dist/lib/wizard-engine.d.ts +128 -0
  144. package/dist/lib/wizard-engine.d.ts.map +1 -0
  145. package/dist/lib/wizard-engine.js +168 -0
  146. package/dist/lib/wizard-engine.js.map +1 -0
  147. package/package.json +85 -0
  148. package/templates/storefront-minimal/.env.example +10 -0
  149. package/templates/storefront-minimal/.github/workflows/build-template.yml +109 -0
  150. package/templates/storefront-minimal/app/globals.css +18 -0
  151. package/templates/storefront-minimal/app/layout.tsx +26 -0
  152. package/templates/storefront-minimal/app/page.tsx +93 -0
  153. package/templates/storefront-minimal/lib/graphql-client.ts +23 -0
  154. package/templates/storefront-minimal/next.config.ts +15 -0
  155. package/templates/storefront-minimal/open-next.config.ts +3 -0
  156. package/templates/storefront-minimal/package.json +30 -0
  157. package/templates/storefront-minimal/postcss.config.mjs +5 -0
  158. package/templates/storefront-minimal/tailwind.config.ts +14 -0
  159. package/templates/storefront-minimal/tsconfig.json +27 -0
  160. package/templates/storefront-minimal/wrangler.toml +9 -0
  161. package/templates/storefront-nextjs/.env.example +68 -0
  162. package/templates/storefront-nextjs/.github/workflows/build-template.yml +109 -0
  163. package/templates/storefront-nextjs/.github/workflows/deploy.yml +25 -0
  164. package/templates/storefront-nextjs/.github/workflows/preview.yml +22 -0
  165. package/templates/storefront-nextjs/README.md +520 -0
  166. package/templates/storefront-nextjs/app/account/orders/page.tsx +216 -0
  167. package/templates/storefront-nextjs/app/account/page.tsx +167 -0
  168. package/templates/storefront-nextjs/app/auth/login/page.tsx +135 -0
  169. package/templates/storefront-nextjs/app/auth/register/page.tsx +228 -0
  170. package/templates/storefront-nextjs/app/cart/page.tsx +263 -0
  171. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +200 -0
  172. package/templates/storefront-nextjs/app/categories/page.tsx +58 -0
  173. package/templates/storefront-nextjs/app/checkout/page.tsx +351 -0
  174. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +158 -0
  175. package/templates/storefront-nextjs/app/collections/page.tsx +61 -0
  176. package/templates/storefront-nextjs/app/globals.css +98 -0
  177. package/templates/storefront-nextjs/app/layout.tsx +39 -0
  178. package/templates/storefront-nextjs/app/page.tsx +136 -0
  179. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +119 -0
  180. package/templates/storefront-nextjs/app/products/page.tsx +107 -0
  181. package/templates/storefront-nextjs/app/search/page.tsx +127 -0
  182. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +94 -0
  183. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +77 -0
  184. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +29 -0
  185. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +217 -0
  186. package/templates/storefront-nextjs/components/commerce/pagination.tsx +62 -0
  187. package/templates/storefront-nextjs/components/commerce/product-actions.tsx +135 -0
  188. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +109 -0
  189. package/templates/storefront-nextjs/components/commerce/product-price.tsx +375 -0
  190. package/templates/storefront-nextjs/components/commerce/search-input.tsx +178 -0
  191. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +64 -0
  192. package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +210 -0
  193. package/templates/storefront-nextjs/components/layout/footer.tsx +107 -0
  194. package/templates/storefront-nextjs/components/layout/header.tsx +104 -0
  195. package/templates/storefront-nextjs/components/providers.tsx +62 -0
  196. package/templates/storefront-nextjs/lib/auth/routes.ts +52 -0
  197. package/templates/storefront-nextjs/lib/currency.tsx +140 -0
  198. package/templates/storefront-nextjs/lib/format.ts +159 -0
  199. package/templates/storefront-nextjs/lib/graphql-queries.ts +629 -0
  200. package/templates/storefront-nextjs/lib/hooks.ts +30 -0
  201. package/templates/storefront-nextjs/middleware.ts +80 -0
  202. package/templates/storefront-nextjs/next.config.ts +37 -0
  203. package/templates/storefront-nextjs/open-next.config.ts +3 -0
  204. package/templates/storefront-nextjs/package.dev.json +30 -0
  205. package/templates/storefront-nextjs/package.json +32 -0
  206. package/templates/storefront-nextjs/package.json.template +32 -0
  207. package/templates/storefront-nextjs/postcss.config.mjs +8 -0
  208. package/templates/storefront-nextjs/tailwind.config.ts +111 -0
  209. package/templates/storefront-nextjs/tsconfig.json +27 -0
  210. package/templates/storefront-nextjs/wrangler.toml +9 -0
  211. package/templates/storefront-nextjs-shadcn/.env.example +68 -0
  212. package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +109 -0
  213. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +25 -0
  214. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +22 -0
  215. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +282 -0
  216. package/templates/storefront-nextjs-shadcn/CLAUDE.md +96 -0
  217. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +190 -0
  218. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +263 -0
  219. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +135 -0
  220. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +142 -0
  221. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +448 -0
  222. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +307 -0
  223. package/templates/storefront-nextjs-shadcn/README.md +195 -0
  224. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +245 -0
  225. package/templates/storefront-nextjs-shadcn/app/about/page.tsx +34 -0
  226. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +215 -0
  227. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +484 -0
  228. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +128 -0
  229. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/tracking/page.tsx +206 -0
  230. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +80 -0
  231. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +107 -0
  232. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +195 -0
  233. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +87 -0
  234. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +125 -0
  235. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +131 -0
  236. package/templates/storefront-nextjs-shadcn/app/auth/login/page.tsx +24 -0
  237. package/templates/storefront-nextjs-shadcn/app/auth/register/page.tsx +20 -0
  238. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +323 -0
  239. package/templates/storefront-nextjs-shadcn/app/blog/page.tsx +159 -0
  240. package/templates/storefront-nextjs-shadcn/app/brands/[slug]/page.tsx +170 -0
  241. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +73 -0
  242. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +165 -0
  243. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +78 -0
  244. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +75 -0
  245. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +1752 -0
  246. package/templates/storefront-nextjs-shadcn/app/checkout/success/[orderId]/page.tsx +256 -0
  247. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +74 -0
  248. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +75 -0
  249. package/templates/storefront-nextjs-shadcn/app/contact/page.tsx +114 -0
  250. package/templates/storefront-nextjs-shadcn/app/error.tsx +90 -0
  251. package/templates/storefront-nextjs-shadcn/app/globals.css +125 -0
  252. package/templates/storefront-nextjs-shadcn/app/layout.tsx +57 -0
  253. package/templates/storefront-nextjs-shadcn/app/not-found.tsx +68 -0
  254. package/templates/storefront-nextjs-shadcn/app/page.tsx +21 -0
  255. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +246 -0
  256. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +343 -0
  257. package/templates/storefront-nextjs-shadcn/app/products/page.tsx +25 -0
  258. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +192 -0
  259. package/templates/storefront-nextjs-shadcn/app/returns/page.tsx +77 -0
  260. package/templates/storefront-nextjs-shadcn/app/robots.ts +53 -0
  261. package/templates/storefront-nextjs-shadcn/app/search/page.tsx +16 -0
  262. package/templates/storefront-nextjs-shadcn/app/search/search-client.tsx +47 -0
  263. package/templates/storefront-nextjs-shadcn/app/shipping/page.tsx +62 -0
  264. package/templates/storefront-nextjs-shadcn/app/sitemap.ts +144 -0
  265. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +179 -0
  266. package/templates/storefront-nextjs-shadcn/codegen.ts +51 -0
  267. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +348 -0
  268. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +144 -0
  269. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +258 -0
  270. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +107 -0
  271. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +132 -0
  272. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +188 -0
  273. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +305 -0
  274. package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +240 -0
  275. package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +177 -0
  276. package/templates/storefront-nextjs-shadcn/components/blog/index.ts +8 -0
  277. package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +119 -0
  278. package/templates/storefront-nextjs-shadcn/components/brand/brand-grid.tsx +64 -0
  279. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +140 -0
  280. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +48 -0
  281. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +112 -0
  282. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +84 -0
  283. package/templates/storefront-nextjs-shadcn/components/cart/index.ts +17 -0
  284. package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +121 -0
  285. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +162 -0
  286. package/templates/storefront-nextjs-shadcn/components/checkout/index.ts +25 -0
  287. package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +187 -0
  288. package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +160 -0
  289. package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +154 -0
  290. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +225 -0
  291. package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +62 -0
  292. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +158 -0
  293. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +174 -0
  294. package/templates/storefront-nextjs-shadcn/components/commerce/variant-selector.tsx +210 -0
  295. package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +97 -0
  296. package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +187 -0
  297. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +151 -0
  298. package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +166 -0
  299. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +245 -0
  300. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +246 -0
  301. package/templates/storefront-nextjs-shadcn/components/discount/index.ts +19 -0
  302. package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +113 -0
  303. package/templates/storefront-nextjs-shadcn/components/error/index.ts +7 -0
  304. package/templates/storefront-nextjs-shadcn/components/filters/attribute-filter.tsx +153 -0
  305. package/templates/storefront-nextjs-shadcn/components/filters/checkbox-group-filter.tsx +167 -0
  306. package/templates/storefront-nextjs-shadcn/components/filters/color-swatch-filter.tsx +176 -0
  307. package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +220 -0
  308. package/templates/storefront-nextjs-shadcn/components/filters/index.ts +36 -0
  309. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +193 -0
  310. package/templates/storefront-nextjs-shadcn/components/filters/toggle-filter.tsx +132 -0
  311. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +321 -0
  312. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +309 -0
  313. package/templates/storefront-nextjs-shadcn/components/gift-card/index.ts +24 -0
  314. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +72 -0
  315. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +107 -0
  316. package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +85 -0
  317. package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +34 -0
  318. package/templates/storefront-nextjs-shadcn/components/home/index.ts +8 -0
  319. package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +108 -0
  320. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +133 -0
  321. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +341 -0
  322. package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +128 -0
  323. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +147 -0
  324. package/templates/storefront-nextjs-shadcn/components/layout/index.ts +9 -0
  325. package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +211 -0
  326. package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +95 -0
  327. package/templates/storefront-nextjs-shadcn/components/layout/theme-switcher.tsx +192 -0
  328. package/templates/storefront-nextjs-shadcn/components/loyalty/index.ts +11 -0
  329. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +93 -0
  330. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +177 -0
  331. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +250 -0
  332. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +217 -0
  333. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-badge.tsx +106 -0
  334. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +131 -0
  335. package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +196 -0
  336. package/templates/storefront-nextjs-shadcn/components/order/index.ts +11 -0
  337. package/templates/storefront-nextjs-shadcn/components/order/order-tracking.tsx +200 -0
  338. package/templates/storefront-nextjs-shadcn/components/order/shipment-card.tsx +407 -0
  339. package/templates/storefront-nextjs-shadcn/components/order/tracking-status.tsx +222 -0
  340. package/templates/storefront-nextjs-shadcn/components/order/tracking-timeline.tsx +205 -0
  341. package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +161 -0
  342. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +250 -0
  343. package/templates/storefront-nextjs-shadcn/components/product/discount-badge.tsx +196 -0
  344. package/templates/storefront-nextjs-shadcn/components/product/index.ts +41 -0
  345. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +147 -0
  346. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +217 -0
  347. package/templates/storefront-nextjs-shadcn/components/product/product-gallery.tsx +143 -0
  348. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +83 -0
  349. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +155 -0
  350. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +158 -0
  351. package/templates/storefront-nextjs-shadcn/components/product/product-quantity-selector.tsx +111 -0
  352. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +238 -0
  353. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +58 -0
  354. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +169 -0
  355. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +220 -0
  356. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +338 -0
  357. package/templates/storefront-nextjs-shadcn/components/product/review-summary.tsx +143 -0
  358. package/templates/storefront-nextjs-shadcn/components/product/sale-countdown.tsx +166 -0
  359. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +213 -0
  360. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +57 -0
  361. package/templates/storefront-nextjs-shadcn/components/product/stock-indicator.tsx +91 -0
  362. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +103 -0
  363. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +8 -0
  364. package/templates/storefront-nextjs-shadcn/components/providers/query-provider.tsx +260 -0
  365. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +13 -0
  366. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +26 -0
  367. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +608 -0
  368. package/templates/storefront-nextjs-shadcn/components/returns/return-status-card.tsx +554 -0
  369. package/templates/storefront-nextjs-shadcn/components/search/index.ts +8 -0
  370. package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +140 -0
  371. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +58 -0
  372. package/templates/storefront-nextjs-shadcn/components/search/search-suggestions.tsx +43 -0
  373. package/templates/storefront-nextjs-shadcn/components/seo/index.ts +12 -0
  374. package/templates/storefront-nextjs-shadcn/components/seo/json-ld.tsx +56 -0
  375. package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +167 -0
  376. package/templates/storefront-nextjs-shadcn/components/shipping/index.ts +16 -0
  377. package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +337 -0
  378. package/templates/storefront-nextjs-shadcn/components/ui/accordion.tsx +153 -0
  379. package/templates/storefront-nextjs-shadcn/components/ui/alert.tsx +59 -0
  380. package/templates/storefront-nextjs-shadcn/components/ui/badge.tsx +34 -0
  381. package/templates/storefront-nextjs-shadcn/components/ui/button.tsx +51 -0
  382. package/templates/storefront-nextjs-shadcn/components/ui/card.tsx +77 -0
  383. package/templates/storefront-nextjs-shadcn/components/ui/checkbox.tsx +30 -0
  384. package/templates/storefront-nextjs-shadcn/components/ui/dialog.tsx +137 -0
  385. package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +207 -0
  386. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +67 -0
  387. package/templates/storefront-nextjs-shadcn/components/ui/input.tsx +65 -0
  388. package/templates/storefront-nextjs-shadcn/components/ui/label.tsx +26 -0
  389. package/templates/storefront-nextjs-shadcn/components/ui/pagination.tsx +205 -0
  390. package/templates/storefront-nextjs-shadcn/components/ui/radio-group.tsx +44 -0
  391. package/templates/storefront-nextjs-shadcn/components/ui/select.tsx +160 -0
  392. package/templates/storefront-nextjs-shadcn/components/ui/separator.tsx +28 -0
  393. package/templates/storefront-nextjs-shadcn/components/ui/skeleton.tsx +20 -0
  394. package/templates/storefront-nextjs-shadcn/components/ui/spinner.tsx +82 -0
  395. package/templates/storefront-nextjs-shadcn/components/ui/tabs.tsx +119 -0
  396. package/templates/storefront-nextjs-shadcn/components/ui/toast.tsx +96 -0
  397. package/templates/storefront-nextjs-shadcn/components/wishlist/index.ts +9 -0
  398. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +148 -0
  399. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +47 -0
  400. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +165 -0
  401. package/templates/storefront-nextjs-shadcn/components.json +19 -0
  402. package/templates/storefront-nextjs-shadcn/generated/.gitkeep +2 -0
  403. package/templates/storefront-nextjs-shadcn/graphql/.gitkeep +31 -0
  404. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +168 -0
  405. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +160 -0
  406. package/templates/storefront-nextjs-shadcn/hooks/index.ts +9 -0
  407. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +310 -0
  408. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +286 -0
  409. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +110 -0
  410. package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.test.ts +173 -0
  411. package/templates/storefront-nextjs-shadcn/hooks/use-filter-params.ts +298 -0
  412. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +220 -0
  413. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +57 -0
  414. package/templates/storefront-nextjs-shadcn/lib/config.ts +46 -0
  415. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +254 -0
  416. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +464 -0
  417. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +328 -0
  418. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +295 -0
  419. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +27 -0
  420. package/templates/storefront-nextjs-shadcn/lib/format.test.ts +397 -0
  421. package/templates/storefront-nextjs-shadcn/lib/format.ts +226 -0
  422. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +109 -0
  423. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +1183 -0
  424. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +267 -0
  425. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +30 -0
  426. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +89 -0
  427. package/templates/storefront-nextjs-shadcn/lib/utils.ts +6 -0
  428. package/templates/storefront-nextjs-shadcn/next.config.ts +47 -0
  429. package/templates/storefront-nextjs-shadcn/open-next.config.ts +3 -0
  430. package/templates/storefront-nextjs-shadcn/package.dev.json +30 -0
  431. package/templates/storefront-nextjs-shadcn/package.json +60 -0
  432. package/templates/storefront-nextjs-shadcn/package.json.template +46 -0
  433. package/templates/storefront-nextjs-shadcn/postcss.config.mjs +8 -0
  434. package/templates/storefront-nextjs-shadcn/proxy.ts +80 -0
  435. package/templates/storefront-nextjs-shadcn/public/icons/payment/apple-pay.svg +8 -0
  436. package/templates/storefront-nextjs-shadcn/public/icons/payment/bank-transfer.svg +10 -0
  437. package/templates/storefront-nextjs-shadcn/public/icons/payment/blik.svg +6 -0
  438. package/templates/storefront-nextjs-shadcn/public/icons/payment/cash-on-delivery.svg +11 -0
  439. package/templates/storefront-nextjs-shadcn/public/icons/payment/google-pay.svg +11 -0
  440. package/templates/storefront-nextjs-shadcn/public/icons/payment/mastercard.svg +7 -0
  441. package/templates/storefront-nextjs-shadcn/public/icons/payment/paypal.svg +7 -0
  442. package/templates/storefront-nextjs-shadcn/public/icons/payment/payu.svg +7 -0
  443. package/templates/storefront-nextjs-shadcn/public/icons/payment/przelewy24.svg +7 -0
  444. package/templates/storefront-nextjs-shadcn/public/icons/payment/stripe.svg +4 -0
  445. package/templates/storefront-nextjs-shadcn/public/icons/payment/visa.svg +5 -0
  446. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +66 -0
  447. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +56 -0
  448. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +184 -0
  449. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +103 -0
  450. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +291 -0
  451. package/templates/storefront-nextjs-shadcn/tailwind.config.ts +111 -0
  452. package/templates/storefront-nextjs-shadcn/tsconfig.json +27 -0
  453. package/templates/storefront-nextjs-shadcn/wrangler.toml +9 -0
@@ -0,0 +1,160 @@
1
+ "use client";
2
+
3
+ import { useState, useCallback } from "react";
4
+ import { cn } from "@/lib/utils";
5
+ import { CreditCard, AlertCircle, Loader2 } from "lucide-react";
6
+ import { PaymentMethodCard, PaymentMethod } from "./payment-method-card";
7
+ import { Alert, AlertDescription } from "@/components/ui/alert";
8
+ import { Button } from "@/components/ui/button";
9
+
10
+ export interface PaymentStepProps {
11
+ /** Available payment methods from checkout */
12
+ availablePaymentMethods: PaymentMethod[];
13
+ /** Currently selected payment method ID */
14
+ selectedPaymentMethodId?: string | null;
15
+ /** Callback when payment method is selected */
16
+ onPaymentMethodSelect: (paymentMethodId: string) => void | Promise<void>;
17
+ /** Whether the mutation is loading */
18
+ isLoading?: boolean;
19
+ /** Error message to display */
20
+ error?: string | null;
21
+ /** Whether checkout is ready for payment */
22
+ checkoutReady?: boolean;
23
+ /** Callback to proceed to next step */
24
+ onContinue?: () => void;
25
+ className?: string;
26
+ }
27
+
28
+ /**
29
+ * PaymentStep - Checkout step for selecting payment method
30
+ *
31
+ * Features:
32
+ * - Lists available payment methods with icons
33
+ * - Pre-selects default or previously selected method
34
+ * - Shows loading state during mutation
35
+ * - Error handling with user feedback
36
+ *
37
+ * Requirements: R23.1, R23.2, R23.5
38
+ */
39
+ export function PaymentStep({
40
+ availablePaymentMethods,
41
+ selectedPaymentMethodId,
42
+ onPaymentMethodSelect,
43
+ isLoading = false,
44
+ error,
45
+ checkoutReady = false,
46
+ onContinue,
47
+ className,
48
+ }: PaymentStepProps) {
49
+ const [localSelectedId, setLocalSelectedId] = useState<string | null>(
50
+ selectedPaymentMethodId ||
51
+ availablePaymentMethods.find((m) => m.isDefault)?.id ||
52
+ availablePaymentMethods[0]?.id ||
53
+ null
54
+ );
55
+
56
+ const handleSelect = useCallback(
57
+ async (method: PaymentMethod) => {
58
+ setLocalSelectedId(method.id);
59
+ await onPaymentMethodSelect(method.id);
60
+ },
61
+ [onPaymentMethodSelect]
62
+ );
63
+
64
+ // No payment methods available
65
+ if (availablePaymentMethods.length === 0) {
66
+ return (
67
+ <div className={cn("space-y-4", className)}>
68
+ <div className="flex items-center gap-2">
69
+ <CreditCard className="h-5 w-5 text-muted-foreground" />
70
+ <h3 className="text-lg font-semibold text-foreground">Payment Method</h3>
71
+ </div>
72
+
73
+ <Alert variant="destructive">
74
+ <AlertCircle className="h-4 w-4" />
75
+ <AlertDescription>
76
+ No payment methods are available. Please contact support.
77
+ </AlertDescription>
78
+ </Alert>
79
+ </div>
80
+ );
81
+ }
82
+
83
+ const selectedMethod = availablePaymentMethods.find(
84
+ (m) => m.id === (selectedPaymentMethodId || localSelectedId)
85
+ );
86
+
87
+ return (
88
+ <div className={cn("space-y-4", className)}>
89
+ {/* Header */}
90
+ <div className="flex items-center gap-2">
91
+ <CreditCard className="h-5 w-5 text-muted-foreground" />
92
+ <h3 className="text-lg font-semibold text-foreground">Payment Method</h3>
93
+ </div>
94
+
95
+ {/* Description */}
96
+ <p className="text-sm text-muted-foreground">
97
+ Choose how you'd like to pay for your order.
98
+ </p>
99
+
100
+ {/* Error Alert */}
101
+ {error && (
102
+ <Alert variant="destructive">
103
+ <AlertCircle className="h-4 w-4" />
104
+ <AlertDescription>{error}</AlertDescription>
105
+ </Alert>
106
+ )}
107
+
108
+ {/* Payment Methods List */}
109
+ <div className="space-y-3">
110
+ {availablePaymentMethods.map((method) => (
111
+ <PaymentMethodCard
112
+ key={method.id}
113
+ method={method}
114
+ selected={
115
+ method.id === (selectedPaymentMethodId || localSelectedId)
116
+ }
117
+ onSelect={handleSelect}
118
+ disabled={isLoading}
119
+ />
120
+ ))}
121
+ </div>
122
+
123
+ {/* Loading Indicator */}
124
+ {isLoading && (
125
+ <div className="flex items-center justify-center gap-2 text-sm text-muted-foreground">
126
+ <Loader2 className="h-4 w-4 animate-spin" />
127
+ <span>Updating payment method...</span>
128
+ </div>
129
+ )}
130
+
131
+ {/* Continue Button */}
132
+ {onContinue && (
133
+ <Button
134
+ onClick={onContinue}
135
+ disabled={!selectedMethod || isLoading || !checkoutReady}
136
+ className="w-full"
137
+ >
138
+ {isLoading ? (
139
+ <>
140
+ <Loader2 className="h-4 w-4 animate-spin mr-2" />
141
+ Processing...
142
+ </>
143
+ ) : (
144
+ "Continue to Review"
145
+ )}
146
+ </Button>
147
+ )}
148
+
149
+ {/* Selected Method Info */}
150
+ {selectedMethod && (
151
+ <p className="text-xs text-muted-foreground text-center">
152
+ Selected: {selectedMethod.name}
153
+ {selectedMethod.description && ` - ${selectedMethod.description}`}
154
+ </p>
155
+ )}
156
+ </div>
157
+ );
158
+ }
159
+
160
+ export default PaymentStep;
@@ -0,0 +1,154 @@
1
+ "use client";
2
+
3
+ import { cn } from "@/lib/utils";
4
+ import { Receipt } from "lucide-react";
5
+
6
+ /**
7
+ * Types matching GraphQL schema
8
+ */
9
+ export interface Money {
10
+ amount: string;
11
+ currencyCode: string;
12
+ }
13
+
14
+ export interface TaxLine {
15
+ title: string;
16
+ rate: number;
17
+ price: Money;
18
+ }
19
+
20
+ export interface TaxBreakdownProps {
21
+ taxLines: TaxLine[];
22
+ totalTax: Money;
23
+ className?: string;
24
+ /** Whether to show the header */
25
+ showHeader?: boolean;
26
+ /** Compact mode for smaller displays */
27
+ compact?: boolean;
28
+ }
29
+
30
+ /**
31
+ * TaxBreakdown - Display tax line items with rates and amounts
32
+ *
33
+ * Features:
34
+ * - Shows each tax line with name, rate, and amount
35
+ * - Shows total tax
36
+ * - Supports compact mode for checkout summary
37
+ *
38
+ * Requirements: R36.1, R36.2, R36.3
39
+ */
40
+ export function TaxBreakdown({
41
+ taxLines,
42
+ totalTax,
43
+ className,
44
+ showHeader = true,
45
+ compact = false,
46
+ }: TaxBreakdownProps) {
47
+ const formatPrice = (money: Money) => {
48
+ const amount = parseFloat(money.amount);
49
+ return new Intl.NumberFormat("pl-PL", {
50
+ style: "currency",
51
+ currency: money.currencyCode,
52
+ }).format(amount);
53
+ };
54
+
55
+ const formatRate = (rate: number) => {
56
+ return `${(rate * 100).toFixed(0)}%`;
57
+ };
58
+
59
+ // No tax lines to show
60
+ if (taxLines.length === 0) {
61
+ return null;
62
+ }
63
+
64
+ // Compact mode - just show total
65
+ if (compact) {
66
+ return (
67
+ <div className={cn("flex items-center justify-between", className)}>
68
+ <span className="text-sm text-muted-foreground">Tax</span>
69
+ <span className="text-sm font-medium text-foreground">
70
+ {formatPrice(totalTax)}
71
+ </span>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ // Full breakdown
77
+ return (
78
+ <div className={cn("space-y-2", className)}>
79
+ {showHeader && (
80
+ <div className="flex items-center gap-2">
81
+ <Receipt className="h-4 w-4 text-muted-foreground" />
82
+ <h4 className="text-sm font-medium text-foreground">Tax Breakdown</h4>
83
+ </div>
84
+ )}
85
+
86
+ <div className="space-y-1.5 rounded-lg border border-border bg-muted/30 p-3">
87
+ {taxLines.map((taxLine, index) => (
88
+ <div
89
+ key={`${taxLine.title}-${index}`}
90
+ className="flex items-center justify-between text-sm"
91
+ >
92
+ <span className="text-muted-foreground">
93
+ {taxLine.title}
94
+ <span className="ml-1 text-xs">({formatRate(taxLine.rate)})</span>
95
+ </span>
96
+ <span className="font-medium text-foreground">
97
+ {formatPrice(taxLine.price)}
98
+ </span>
99
+ </div>
100
+ ))}
101
+
102
+ {/* Total if multiple tax lines */}
103
+ {taxLines.length > 1 && (
104
+ <>
105
+ <div className="border-t border-border my-2" />
106
+ <div className="flex items-center justify-between text-sm">
107
+ <span className="font-medium text-foreground">Total Tax</span>
108
+ <span className="font-semibold text-foreground">
109
+ {formatPrice(totalTax)}
110
+ </span>
111
+ </div>
112
+ </>
113
+ )}
114
+ </div>
115
+
116
+ {/* Tax exemption note (for B2B) */}
117
+ {parseFloat(totalTax.amount) === 0 && taxLines.length === 0 && (
118
+ <p className="text-xs text-muted-foreground">
119
+ Tax exempt order
120
+ </p>
121
+ )}
122
+ </div>
123
+ );
124
+ }
125
+
126
+ /**
127
+ * TaxSummaryLine - Simple tax line for order summary
128
+ */
129
+ export function TaxSummaryLine({
130
+ totalTax,
131
+ label = "Tax",
132
+ className,
133
+ }: {
134
+ totalTax: Money;
135
+ label?: string;
136
+ className?: string;
137
+ }) {
138
+ const formatPrice = (money: Money) => {
139
+ const amount = parseFloat(money.amount);
140
+ return new Intl.NumberFormat("pl-PL", {
141
+ style: "currency",
142
+ currency: money.currencyCode,
143
+ }).format(amount);
144
+ };
145
+
146
+ return (
147
+ <div className={cn("flex items-center justify-between text-sm", className)}>
148
+ <span className="text-muted-foreground">{label}</span>
149
+ <span className="text-foreground">{formatPrice(totalTax)}</span>
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export default TaxBreakdown;
@@ -0,0 +1,225 @@
1
+ "use client";
2
+
3
+ import { useState, useRef, useEffect } from "react";
4
+ import { ChevronDown, Check, Globe } from "lucide-react";
5
+ import { useCurrencyStore } from "@/stores/currency-store";
6
+ import { useQueryClient } from "@tanstack/react-query";
7
+
8
+ // ============================================================================
9
+ // CURRENCY DATA
10
+ // ============================================================================
11
+
12
+ const CURRENCY_SYMBOLS: Record<string, string> = {
13
+ PLN: "zł",
14
+ EUR: "€",
15
+ USD: "$",
16
+ GBP: "£",
17
+ CHF: "CHF",
18
+ CZK: "Kč",
19
+ SEK: "kr",
20
+ NOK: "kr",
21
+ DKK: "kr",
22
+ JPY: "¥",
23
+ CNY: "¥",
24
+ AUD: "A$",
25
+ CAD: "C$",
26
+ };
27
+
28
+ const CURRENCY_NAMES: Record<string, string> = {
29
+ PLN: "Polski złoty",
30
+ EUR: "Euro",
31
+ USD: "US Dollar",
32
+ GBP: "British Pound",
33
+ CHF: "Swiss Franc",
34
+ CZK: "Czech Koruna",
35
+ SEK: "Swedish Krona",
36
+ NOK: "Norwegian Krone",
37
+ DKK: "Danish Krone",
38
+ JPY: "Japanese Yen",
39
+ CNY: "Chinese Yuan",
40
+ AUD: "Australian Dollar",
41
+ CAD: "Canadian Dollar",
42
+ };
43
+
44
+ // ============================================================================
45
+ // TYPES
46
+ // ============================================================================
47
+
48
+ interface CurrencySelectorProps {
49
+ className?: string;
50
+ variant?: "default" | "compact";
51
+ }
52
+
53
+ // ============================================================================
54
+ // COMPONENT
55
+ // ============================================================================
56
+
57
+ /**
58
+ * CurrencySelector - Dropdown for selecting preferred currency
59
+ *
60
+ * Features:
61
+ * - Shows current currency with symbol
62
+ * - Lists all supported currencies from Shop
63
+ * - Persists selection to HTTP cookie (SSR-safe)
64
+ * - Invalidates React Query cache on change (refetches prices)
65
+ * - Handles hydration properly (no flash)
66
+ * - Works seamlessly with Server-Side Rendering
67
+ *
68
+ * Technical Details:
69
+ * - Currency stored in cookie (not localStorage)
70
+ * - Cookie accessible by both client and server
71
+ * - GraphQL requests automatically include X-Preferred-Currency header
72
+ * - Cache invalidation ensures immediate price updates
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * <CurrencySelector />
77
+ * <CurrencySelector variant="compact" />
78
+ * ```
79
+ */
80
+ export function CurrencySelector({
81
+ className = "",
82
+ variant = "default",
83
+ }: CurrencySelectorProps) {
84
+ const [isOpen, setIsOpen] = useState(false);
85
+ const dropdownRef = useRef<HTMLDivElement>(null);
86
+ const queryClient = useQueryClient();
87
+
88
+ // Get currency state from store
89
+ const currency = useCurrencyStore((state) => state.currency);
90
+ const supportedCurrencies = useCurrencyStore((state) => state.supportedCurrencies);
91
+ const setCurrency = useCurrencyStore((state) => state.setCurrency);
92
+ const isHydrated = useCurrencyStore((state) => state.isHydrated);
93
+
94
+ // Handle currency change
95
+ const handleCurrencyChange = (newCurrency: string) => {
96
+ // Update Zustand store (which automatically writes to cookie)
97
+ setCurrency(newCurrency);
98
+
99
+ // Close dropdown
100
+ setIsOpen(false);
101
+
102
+ // Invalidate all React Query cache to refetch with new currency
103
+ // This ensures prices are immediately updated across the app
104
+ queryClient.invalidateQueries();
105
+ };
106
+
107
+ // Close dropdown when clicking outside
108
+ useEffect(() => {
109
+ const handleClickOutside = (event: MouseEvent) => {
110
+ if (
111
+ dropdownRef.current &&
112
+ !dropdownRef.current.contains(event.target as Node)
113
+ ) {
114
+ setIsOpen(false);
115
+ }
116
+ };
117
+
118
+ if (isOpen) {
119
+ document.addEventListener("mousedown", handleClickOutside);
120
+ return () =>
121
+ document.removeEventListener("mousedown", handleClickOutside);
122
+ }
123
+ }, [isOpen]);
124
+
125
+ // Don't render until hydrated (prevents flash)
126
+ if (!isHydrated) {
127
+ return (
128
+ <div className={`flex items-center gap-2 ${className}`}>
129
+ <Globe className="h-4 w-4 text-muted-foreground" />
130
+ <span className="text-sm text-muted-foreground">Loading...</span>
131
+ </div>
132
+ );
133
+ }
134
+
135
+ const currentSymbol = currency ? (CURRENCY_SYMBOLS[currency] || currency) : "USD";
136
+ const currentName = currency ? (CURRENCY_NAMES[currency] || currency) : "US Dollar";
137
+
138
+ if (variant === "compact") {
139
+ return (
140
+ <div ref={dropdownRef} className={`relative ${className}`}>
141
+ <button
142
+ onClick={() => setIsOpen(!isOpen)}
143
+ className="flex items-center gap-1 rounded-md px-2 py-1 text-sm hover:bg-muted"
144
+ aria-label="Select currency"
145
+ >
146
+ <span className="font-medium">{currentSymbol}</span>
147
+ <ChevronDown className="h-3 w-3" />
148
+ </button>
149
+
150
+ {isOpen && (
151
+ <div className="absolute right-0 top-full z-50 mt-2 w-48 rounded-md border border-border bg-background shadow-lg">
152
+ <div className="max-h-64 overflow-y-auto p-1">
153
+ {supportedCurrencies.map((code) => (
154
+ <button
155
+ key={code}
156
+ onClick={() => handleCurrencyChange(code)}
157
+ className="flex w-full items-center justify-between rounded-sm px-3 py-2 text-sm hover:bg-muted"
158
+ >
159
+ <span className="flex items-center gap-2">
160
+ <span className="font-medium">
161
+ {CURRENCY_SYMBOLS[code] || code}
162
+ </span>
163
+ <span className="text-muted-foreground">{code}</span>
164
+ </span>
165
+ {currency === code && (
166
+ <Check className="h-4 w-4 text-primary" />
167
+ )}
168
+ </button>
169
+ ))}
170
+ </div>
171
+ </div>
172
+ )}
173
+ </div>
174
+ );
175
+ }
176
+
177
+ return (
178
+ <div ref={dropdownRef} className={`relative ${className}`}>
179
+ <button
180
+ onClick={() => setIsOpen(!isOpen)}
181
+ className="flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm hover:bg-muted"
182
+ aria-label="Select currency"
183
+ >
184
+ <Globe className="h-4 w-4 text-muted-foreground" />
185
+ <span className="font-medium">{currentSymbol}</span>
186
+ <span className="text-muted-foreground">{currency}</span>
187
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
188
+ </button>
189
+
190
+ {isOpen && (
191
+ <div className="absolute right-0 top-full z-50 mt-2 w-64 rounded-md border border-border bg-background shadow-lg">
192
+ <div className="border-b border-border p-3">
193
+ <p className="text-sm font-medium text-foreground">
194
+ Select Currency
195
+ </p>
196
+ <p className="text-xs text-muted-foreground">
197
+ Prices will be displayed in your selected currency
198
+ </p>
199
+ </div>
200
+ <div className="max-h-64 overflow-y-auto p-1">
201
+ {supportedCurrencies.map((code) => (
202
+ <button
203
+ key={code}
204
+ onClick={() => handleCurrencyChange(code)}
205
+ className="flex w-full items-center justify-between rounded-sm px-3 py-2 text-sm hover:bg-muted"
206
+ >
207
+ <span className="flex items-center gap-2">
208
+ <span className="font-medium">
209
+ {CURRENCY_SYMBOLS[code] || code}
210
+ </span>
211
+ <span className="text-muted-foreground">
212
+ {CURRENCY_NAMES[code] || code}
213
+ </span>
214
+ </span>
215
+ {currency === code && (
216
+ <Check className="h-4 w-4 text-primary" />
217
+ )}
218
+ </button>
219
+ ))}
220
+ </div>
221
+ </div>
222
+ )}
223
+ </div>
224
+ );
225
+ }
@@ -0,0 +1,62 @@
1
+ "use client";
2
+
3
+ import { useSearchParams, usePathname } from "next/navigation";
4
+ import Link from "next/link";
5
+
6
+ interface PaginationProps {
7
+ hasMore: boolean;
8
+ endCursor?: string | null;
9
+ currentCursor?: string;
10
+ totalShown: number;
11
+ }
12
+
13
+ /**
14
+ * Pagination - URL-based pagination that preserves filters
15
+ */
16
+ export function Pagination({
17
+ hasMore,
18
+ endCursor,
19
+ currentCursor,
20
+ totalShown,
21
+ }: PaginationProps) {
22
+ const pathname = usePathname();
23
+ const searchParams = useSearchParams();
24
+
25
+ // Build URL preserving current filters
26
+ const buildUrl = (cursor?: string | null) => {
27
+ const params = new URLSearchParams(searchParams.toString());
28
+
29
+ if (cursor) {
30
+ params.set("after", cursor);
31
+ } else {
32
+ params.delete("after");
33
+ }
34
+
35
+ const queryString = params.toString();
36
+ return queryString ? `${pathname}?${queryString}` : pathname;
37
+ };
38
+
39
+ return (
40
+ <div className="mt-12 flex justify-center gap-2">
41
+ <Link
42
+ href={buildUrl()}
43
+ className={`btn btn-outline ${
44
+ !currentCursor ? "pointer-events-none opacity-50" : ""
45
+ }`}
46
+ >
47
+ First Page
48
+ </Link>
49
+ <span className="flex items-center px-4 text-gray-600">
50
+ Showing {totalShown} products
51
+ </span>
52
+ <Link
53
+ href={hasMore && endCursor ? buildUrl(endCursor) : "#"}
54
+ className={`btn btn-outline ${
55
+ !hasMore ? "pointer-events-none opacity-50" : ""
56
+ }`}
57
+ >
58
+ Next
59
+ </Link>
60
+ </div>
61
+ );
62
+ }