@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,343 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { useProduct } from "@/lib/graphql/hooks";
5
+ import { useCurrencyStore } from "@/stores/currency-store";
6
+ import { ProductGallery } from "@/components/product/product-gallery";
7
+ import { ProductPrice } from "@/components/product/product-price";
8
+ import { ProductVariantSelector } from "@/components/product/product-variant-selector";
9
+ import { ProductQuantitySelector } from "@/components/product/product-quantity-selector";
10
+ import { AddToCartButton } from "@/components/product/add-to-cart-button";
11
+ import { StockIndicator } from "@/components/product/stock-indicator";
12
+ import { SimilarProducts } from "@/components/product/similar-products";
13
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
14
+ import { Badge } from "@/components/ui/badge";
15
+ import { Gift } from "lucide-react";
16
+ import { shopConfig } from "@/lib/config";
17
+
18
+ export interface Product {
19
+ id: string;
20
+ handle: string;
21
+ title: string;
22
+ description?: string | null;
23
+ vendor?: string | null;
24
+ productType?: string | null;
25
+ type?: string | null;
26
+ collectRecipientInfo?: boolean;
27
+ tags?: string[];
28
+ images: Array<{
29
+ url: string;
30
+ altText?: string | null;
31
+ }>;
32
+ variants: Array<{
33
+ id: string;
34
+ title: string;
35
+ available: boolean;
36
+ selectedOptions: Array<{
37
+ name: string;
38
+ value: string;
39
+ }>;
40
+ price: {
41
+ amount: string;
42
+ currencyCode: string;
43
+ };
44
+ compareAtPrice?: {
45
+ amount: string;
46
+ currencyCode: string;
47
+ } | null;
48
+ image?: {
49
+ url: string;
50
+ altText?: string | null;
51
+ } | null;
52
+ }>;
53
+ priceRange: {
54
+ minVariantPrice: {
55
+ amount: string;
56
+ currencyCode: string;
57
+ };
58
+ maxVariantPrice: {
59
+ amount: string;
60
+ currencyCode: string;
61
+ };
62
+ };
63
+ }
64
+
65
+ export interface ProductClientProps {
66
+ product: Product;
67
+ similarProducts?: any[];
68
+ }
69
+
70
+ /**
71
+ * Product Client Component
72
+ *
73
+ * Handles interactive features and currency-aware price display.
74
+ *
75
+ * Flow:
76
+ * 1. Receives initial product data from Server Component (base currency)
77
+ * 2. Checks if user's preferred currency differs from SSR currency
78
+ * 3. If different, refetches product with preferred currency
79
+ * 4. Uses suppressHydrationWarning on price elements to prevent mismatch
80
+ *
81
+ * Requirements: 2.2, 3.2, 6.4
82
+ */
83
+ export function ProductClient({ product: initialProduct, similarProducts = [] }: ProductClientProps) {
84
+ const [selectedVariant, setSelectedVariant] = useState(initialProduct.variants[0]);
85
+ const [quantity, setQuantity] = useState(1);
86
+
87
+ // Get user's preferred currency from store
88
+ const currency = useCurrencyStore((s: any) => s.currency);
89
+ const isHydrated = useCurrencyStore((s: any) => s.isHydrated);
90
+
91
+ // Check if SSR currency matches user's preferred currency
92
+ const ssrCurrency = initialProduct.priceRange?.minVariantPrice?.currencyCode;
93
+ const currencyMatches = !isHydrated || !currency || currency === ssrCurrency;
94
+
95
+ // Refetch product with current currency from store
96
+ // Query key includes currency, so it auto-refetches when currency changes
97
+ const { data, isFetching } = useProduct(initialProduct.handle, {
98
+ // Only enable after hydration to avoid SSR/client mismatch
99
+ enabled: isHydrated,
100
+ // Only use SSR data as placeholder if currency matches
101
+ // Otherwise, don't show stale data from wrong currency
102
+ placeholderData: currencyMatches ? { product: initialProduct } : undefined,
103
+ // Don't use stale data - always refetch when currency changes
104
+ staleTime: 0,
105
+ });
106
+
107
+ // Use fetched product if available, otherwise use initial (SSR) data only if currency matches
108
+ const product = data?.product || (currencyMatches ? initialProduct : null);
109
+
110
+ // Update selected variant when product changes (currency switch)
111
+ // IMPORTANT: This useEffect must be called BEFORE any conditional returns
112
+ // to comply with React Hooks rules (hooks must be called in the same order every render)
113
+ useEffect(() => {
114
+ if (product?.variants.length > 0) {
115
+ // Try to maintain the same variant by matching ID
116
+ const matchingVariant = product.variants.find(
117
+ (v: any) => v.id === selectedVariant.id
118
+ );
119
+ if (matchingVariant) {
120
+ setSelectedVariant(matchingVariant);
121
+ } else {
122
+ // Fallback to first variant if ID doesn't match
123
+ setSelectedVariant(product.variants[0]);
124
+ }
125
+ }
126
+ }, [product, selectedVariant.id]);
127
+
128
+ // Show loading state if we don't have data in correct currency yet
129
+ // This check comes AFTER all hooks to prevent "Rendered fewer hooks than expected" error
130
+ if (!product) {
131
+ return (
132
+ <div className="space-y-16">
133
+ <div className="grid gap-8 lg:grid-cols-2 lg:gap-12">
134
+ <div className="aspect-square animate-pulse bg-muted rounded-lg" />
135
+ <div className="space-y-6">
136
+ <div className="h-10 w-3/4 animate-pulse bg-muted rounded" />
137
+ <div className="h-8 w-1/4 animate-pulse bg-muted rounded" />
138
+ <div className="h-24 animate-pulse bg-muted rounded" />
139
+ </div>
140
+ </div>
141
+ </div>
142
+ );
143
+ }
144
+
145
+ const isOnSale = selectedVariant.compareAtPrice &&
146
+ parseFloat(selectedVariant.compareAtPrice.amount) >
147
+ parseFloat(selectedVariant.price.amount);
148
+
149
+ return (
150
+ <div className="space-y-16">
151
+ {/* Product Details */}
152
+ <div className="grid gap-8 lg:grid-cols-2 lg:gap-12">
153
+ {/* Gallery */}
154
+ <div>
155
+ <ProductGallery images={product.images} productTitle={product.title} />
156
+ </div>
157
+
158
+ {/* Info */}
159
+ <div className="space-y-6">
160
+ {/* Title & Badges */}
161
+ <div>
162
+ <div className="mb-2 flex flex-wrap gap-2">
163
+ {product.type === "GIFT_CARD" && (
164
+ <Badge variant="default">
165
+ <Gift className="mr-1 h-3 w-3" />
166
+ Karta podarunkowa
167
+ </Badge>
168
+ )}
169
+ {product.tags?.includes("new") && product.type !== "GIFT_CARD" && (
170
+ <Badge variant="default">NEW</Badge>
171
+ )}
172
+ {isOnSale && <Badge variant="destructive">SALE</Badge>}
173
+ </div>
174
+ <h1 className="text-3xl font-bold text-foreground lg:text-4xl">
175
+ {product.title}
176
+ </h1>
177
+ {product.vendor && (
178
+ <p className="mt-2 text-sm text-muted-foreground">
179
+ by {product.vendor}
180
+ </p>
181
+ )}
182
+ </div>
183
+
184
+ {/* Price - suppressHydrationWarning prevents mismatch during currency switch */}
185
+ <div suppressHydrationWarning>
186
+ <ProductPrice
187
+ price={selectedVariant.price}
188
+ compareAtPrice={selectedVariant.compareAtPrice || undefined}
189
+ showCompareAt
190
+ size="xl"
191
+ />
192
+ </div>
193
+
194
+ {/* Stock */}
195
+ <StockIndicator
196
+ available={selectedVariant.available}
197
+ variant="text"
198
+ />
199
+
200
+ {/* Description */}
201
+ {product.description && (
202
+ <div className="prose prose-sm max-w-none text-muted-foreground">
203
+ <p>{product.description}</p>
204
+ </div>
205
+ )}
206
+
207
+ {/* Variant Selector */}
208
+ {product.variants.length > 1 && (
209
+ <ProductVariantSelector
210
+ variants={product.variants}
211
+ selectedVariantId={selectedVariant.id}
212
+ onVariantChange={setSelectedVariant}
213
+ />
214
+ )}
215
+
216
+ {/* Quantity & Add to Cart */}
217
+ <div className="space-y-4">
218
+ <div className="flex items-center gap-4">
219
+ <ProductQuantitySelector
220
+ value={quantity}
221
+ onChange={setQuantity}
222
+ min={1}
223
+ max={99}
224
+ disabled={!selectedVariant.available}
225
+ />
226
+ <AddToCartButton
227
+ variantId={selectedVariant.id}
228
+ productId={product.id}
229
+ productHandle={product.handle}
230
+ productTitle={product.title}
231
+ variantTitle={selectedVariant.title}
232
+ price={selectedVariant.price}
233
+ image={selectedVariant.image || product.images[0]}
234
+ available={selectedVariant.available}
235
+ quantity={quantity}
236
+ fullWidth
237
+ size="lg"
238
+ />
239
+ </div>
240
+ </div>
241
+
242
+ {/* Product Details */}
243
+ {(product.vendor || product.productType) && (
244
+ <div className="border-t border-border pt-6">
245
+ <h3 className="mb-4 text-sm font-semibold text-foreground">
246
+ Product Details
247
+ </h3>
248
+ <dl className="space-y-2 text-sm">
249
+ {product.vendor && (
250
+ <div className="flex justify-between">
251
+ <dt className="text-muted-foreground">Vendor</dt>
252
+ <dd className="font-medium text-foreground">
253
+ {product.vendor}
254
+ </dd>
255
+ </div>
256
+ )}
257
+ {product.productType && (
258
+ <div className="flex justify-between">
259
+ <dt className="text-muted-foreground">Type</dt>
260
+ <dd className="font-medium text-foreground">
261
+ {product.productType}
262
+ </dd>
263
+ </div>
264
+ )}
265
+ </dl>
266
+ </div>
267
+ )}
268
+ </div>
269
+ </div>
270
+
271
+ {/* Additional Info Tabs */}
272
+ <Tabs defaultValue="description" className="w-full">
273
+ <TabsList className="w-full justify-start">
274
+ <TabsTrigger value="description">Description</TabsTrigger>
275
+ <TabsTrigger value="details">Details</TabsTrigger>
276
+ <TabsTrigger value="shipping">Shipping</TabsTrigger>
277
+ </TabsList>
278
+
279
+ <TabsContent value="description" className="mt-6">
280
+ <div className="prose prose-sm max-w-none">
281
+ <p>{product.description || "No description available."}</p>
282
+ </div>
283
+ </TabsContent>
284
+
285
+ <TabsContent value="details" className="mt-6">
286
+ <dl className="grid gap-4 sm:grid-cols-2">
287
+ <div>
288
+ <dt className="font-medium text-foreground">Product ID</dt>
289
+ <dd className="mt-1 text-sm text-muted-foreground">
290
+ {product.id}
291
+ </dd>
292
+ </div>
293
+ <div>
294
+ <dt className="font-medium text-foreground">Handle</dt>
295
+ <dd className="mt-1 text-sm text-muted-foreground">
296
+ {product.handle}
297
+ </dd>
298
+ </div>
299
+ {product.vendor && (
300
+ <div>
301
+ <dt className="font-medium text-foreground">Vendor</dt>
302
+ <dd className="mt-1 text-sm text-muted-foreground">
303
+ {product.vendor}
304
+ </dd>
305
+ </div>
306
+ )}
307
+ {product.productType && (
308
+ <div>
309
+ <dt className="font-medium text-foreground">Type</dt>
310
+ <dd className="mt-1 text-sm text-muted-foreground">
311
+ {product.productType}
312
+ </dd>
313
+ </div>
314
+ )}
315
+ </dl>
316
+ </TabsContent>
317
+
318
+ <TabsContent value="shipping" className="mt-6">
319
+ <div className="space-y-4 text-sm">
320
+ <p className="text-muted-foreground">
321
+ Free shipping on orders over ${shopConfig.shipping.freeShippingThreshold}.
322
+ Standard delivery takes {shopConfig.shipping.standardDeliveryDays.min}-{shopConfig.shipping.standardDeliveryDays.max} business days.
323
+ </p>
324
+ <ul className="list-inside list-disc space-y-2 text-muted-foreground">
325
+ {shopConfig.shipping.features.map((feature, index) => (
326
+ <li key={index}>{feature}</li>
327
+ ))}
328
+ </ul>
329
+ </div>
330
+ </TabsContent>
331
+ </Tabs>
332
+
333
+ {/* Similar Products */}
334
+ {similarProducts.length > 0 && (
335
+ <SimilarProducts
336
+ products={similarProducts}
337
+ title="You might also like"
338
+ columns={4}
339
+ />
340
+ )}
341
+ </div>
342
+ );
343
+ }
@@ -0,0 +1,25 @@
1
+ import { Suspense } from "react";
2
+ import { Breadcrumbs } from "@/components/layout/breadcrumbs";
3
+ import { Spinner } from "@/components/ui/spinner";
4
+ import { ProductsClient } from "./products-client";
5
+
6
+ export default function ProductsPage() {
7
+ return (
8
+ <div className="container mx-auto px-4 py-8">
9
+ {/* Breadcrumbs */}
10
+ <Breadcrumbs className="mb-6" />
11
+
12
+ {/* Page Header */}
13
+ <div className="mb-8">
14
+ <h1 className="text-3xl font-bold text-foreground">All Products</h1>
15
+ <p className="mt-2 text-muted-foreground">
16
+ Browse our complete collection of products
17
+ </p>
18
+ </div>
19
+
20
+ <Suspense fallback={<Spinner />}>
21
+ <ProductsClient />
22
+ </Suspense>
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,192 @@
1
+ "use client";
2
+
3
+ import { Suspense } from "react";
4
+ import { useSearchParams, useRouter } from "next/navigation";
5
+ import { ProductGrid } from "@/components/product/product-grid";
6
+ import { ProductFilters } from "@/components/product/product-filters";
7
+ import { ProductSort, type SortOption } from "@/components/product/product-sort";
8
+ import { Pagination } from "@/components/ui/pagination";
9
+ import { Skeleton } from "@/components/ui/skeleton";
10
+ import { useProducts, useCategories } from "@/lib/graphql/hooks";
11
+
12
+ export function ProductsClient() {
13
+ const searchParams = useSearchParams();
14
+ const router = useRouter();
15
+
16
+ // Parse URL parameters (all lowercase)
17
+ const page = parseInt(searchParams.get("page") || "1", 10);
18
+ const sort = (searchParams.get("sort") as SortOption) || "relevance";
19
+ const categories = searchParams.get("categories")?.split(",").filter(Boolean) || [];
20
+ const priceMin = searchParams.get("price_min");
21
+ const priceMax = searchParams.get("price_max");
22
+
23
+ const limit = 20;
24
+
25
+ // Fetch categories for filter sidebar
26
+ const { data: categoriesData } = useCategories();
27
+ const allCategories = categoriesData?.categories ?? [];
28
+
29
+ // Build GraphQL query string for filters
30
+ let queryString = "";
31
+ if (categories.length > 0) {
32
+ // Use category handles in query
33
+ queryString = categories.map(cat => `category:${cat}`).join(" OR ");
34
+ }
35
+
36
+ // Fetch products using GraphQL
37
+ // Backend should normalize sort values (e.g., "price-asc" → PRICE + reverse: false)
38
+ const { data, isLoading, error } = useProducts({
39
+ first: limit,
40
+ query: queryString || undefined,
41
+ sortKey: sort as any, // Backend normalizes this
42
+ });
43
+
44
+ const products = data?.products ?? [];
45
+ const totalCount = data?.totalCount ?? 0;
46
+ const totalPages = Math.ceil(totalCount / limit);
47
+
48
+ // Build selected filters object
49
+ const selectedFilters: Record<string, any> = {};
50
+ if (categories.length > 0) {
51
+ selectedFilters.categories = categories;
52
+ }
53
+ if (priceMin) {
54
+ selectedFilters.price_min = priceMin;
55
+ }
56
+ if (priceMax) {
57
+ selectedFilters.price_max = priceMax;
58
+ }
59
+
60
+ // Update URL with new filters
61
+ const updateFilters = (updates: Record<string, any>) => {
62
+ const newParams = new URLSearchParams(searchParams.toString());
63
+
64
+ Object.entries(updates).forEach(([key, value]) => {
65
+ if (value === null || value === undefined || value === "") {
66
+ newParams.delete(key);
67
+ } else if (Array.isArray(value)) {
68
+ if (value.length > 0) {
69
+ newParams.set(key, value.join(","));
70
+ } else {
71
+ newParams.delete(key);
72
+ }
73
+ } else {
74
+ newParams.set(key, value.toString());
75
+ }
76
+ });
77
+
78
+ // Reset to page 1 when filters change
79
+ newParams.set("page", "1");
80
+ router.push(`/products?${newParams.toString()}`);
81
+ };
82
+
83
+ // Handle filter changes
84
+ const handleFilterChange = (filterId: string, value: any) => {
85
+ if (filterId === "categories") {
86
+ // Toggle category in array
87
+ const newCategories = categories.includes(value)
88
+ ? categories.filter(c => c !== value)
89
+ : [...categories, value];
90
+ updateFilters({ categories: newCategories });
91
+ } else if (filterId === "price_min") {
92
+ updateFilters({ price_min: value });
93
+ } else if (filterId === "price_max") {
94
+ updateFilters({ price_max: value });
95
+ }
96
+ };
97
+
98
+ // Build filter options from categories
99
+ const filterOptions = [
100
+ {
101
+ id: "categories",
102
+ label: "Categories",
103
+ type: "checkbox" as const,
104
+ options: allCategories.map((category: any) => ({
105
+ label: category.name,
106
+ value: category.slug,
107
+ count: category.productCount || 0,
108
+ })),
109
+ },
110
+ {
111
+ id: "price",
112
+ label: "Price Range",
113
+ type: "range" as const,
114
+ min: 0,
115
+ max: 1000,
116
+ },
117
+ ];
118
+
119
+ return (
120
+ <div className="flex flex-col gap-8 lg:flex-row">
121
+ {/* Sidebar Filters */}
122
+ <aside className="w-full lg:w-64 lg:flex-shrink-0">
123
+ <div className="sticky top-4">
124
+ <h2 className="mb-4 text-lg font-semibold text-foreground">
125
+ Filters
126
+ </h2>
127
+ <Suspense fallback={<Skeleton className="h-96 w-full" />}>
128
+ <ProductFilters
129
+ filters={filterOptions}
130
+ selectedFilters={selectedFilters}
131
+ onFilterChange={handleFilterChange}
132
+ onClearAll={() => router.push("/products")}
133
+ />
134
+ </Suspense>
135
+ </div>
136
+ </aside>
137
+
138
+ {/* Main Content */}
139
+ <div className="flex-1">
140
+ {/* Sort & Results Count */}
141
+ <div className="mb-6 flex flex-col justify-between gap-4 sm:flex-row sm:items-center">
142
+ <p className="text-sm text-muted-foreground">
143
+ {totalCount > 0
144
+ ? `Showing ${(page - 1) * limit + 1}-${Math.min(page * limit, totalCount)} of ${totalCount} products`
145
+ : "No products found"}
146
+ </p>
147
+ <ProductSort
148
+ value={sort}
149
+ onChange={(newSort) => {
150
+ const newParams = new URLSearchParams(searchParams.toString());
151
+ newParams.set("sort", newSort);
152
+ router.push(`/products?${newParams.toString()}`);
153
+ }}
154
+ />
155
+ </div>
156
+
157
+ {/* Products Grid */}
158
+ {isLoading ? (
159
+ <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
160
+ {Array.from({ length: 6 }).map((_, i) => (
161
+ <Skeleton key={i} className="aspect-square w-full" />
162
+ ))}
163
+ </div>
164
+ ) : (
165
+ <ProductGrid
166
+ products={products}
167
+ columns={3}
168
+ priorityCount={6}
169
+ showBadges
170
+ emptyMessage="No products match your filters"
171
+ onResetFilters={() => router.push("/products")}
172
+ />
173
+ )}
174
+
175
+ {/* Pagination */}
176
+ {totalPages > 1 && (
177
+ <div className="mt-8 flex justify-center">
178
+ <Pagination
179
+ currentPage={page}
180
+ totalPages={totalPages}
181
+ onPageChange={(newPage: number) => {
182
+ const newParams = new URLSearchParams(searchParams.toString());
183
+ newParams.set("page", newPage.toString());
184
+ router.push(`/products?${newParams.toString()}`);
185
+ }}
186
+ />
187
+ </div>
188
+ )}
189
+ </div>
190
+ </div>
191
+ );
192
+ }
@@ -0,0 +1,77 @@
1
+ import { Breadcrumbs } from "@/components/layout/breadcrumbs";
2
+ import { RotateCcw, CheckCircle, XCircle } from "lucide-react";
3
+
4
+ export default function ReturnsPage() {
5
+ return (
6
+ <div className="container mx-auto px-4 py-8">
7
+ <Breadcrumbs className="mb-6" />
8
+
9
+ <div className="mx-auto max-w-3xl">
10
+ <h1 className="mb-6 text-4xl font-bold text-foreground">Returns & Refunds</h1>
11
+
12
+ <div className="space-y-8">
13
+ <div>
14
+ <div className="mb-4 flex items-center gap-3">
15
+ <RotateCcw className="h-6 w-6 text-primary" />
16
+ <h2 className="text-2xl font-semibold text-foreground">Return Policy</h2>
17
+ </div>
18
+ <p className="text-muted-foreground">
19
+ We want you to be completely satisfied with your purchase. If you're not happy with your order, you can return it within 30 days of delivery for a full refund.
20
+ </p>
21
+ </div>
22
+
23
+ <div>
24
+ <div className="mb-4 flex items-center gap-3">
25
+ <CheckCircle className="h-6 w-6 text-green-600" />
26
+ <h2 className="text-2xl font-semibold text-foreground">Eligible Returns</h2>
27
+ </div>
28
+ <ul className="list-inside list-disc space-y-2 text-muted-foreground">
29
+ <li>Items must be unused and in original condition</li>
30
+ <li>Items must be in original packaging</li>
31
+ <li>Return within 30 days of delivery</li>
32
+ <li>Include proof of purchase</li>
33
+ </ul>
34
+ </div>
35
+
36
+ <div>
37
+ <div className="mb-4 flex items-center gap-3">
38
+ <XCircle className="h-6 w-6 text-red-600" />
39
+ <h2 className="text-2xl font-semibold text-foreground">Non-Returnable Items</h2>
40
+ </div>
41
+ <ul className="list-inside list-disc space-y-2 text-muted-foreground">
42
+ <li>Personalized or custom-made items</li>
43
+ <li>Perishable goods</li>
44
+ <li>Intimate or sanitary goods</li>
45
+ <li>Sale or clearance items</li>
46
+ </ul>
47
+ </div>
48
+
49
+ <div>
50
+ <h2 className="mb-4 text-2xl font-semibold text-foreground">How to Return</h2>
51
+ <ol className="list-inside list-decimal space-y-2 text-muted-foreground">
52
+ <li>Contact our customer service team to initiate a return</li>
53
+ <li>Pack the item securely in its original packaging</li>
54
+ <li>Include your order number and reason for return</li>
55
+ <li>Ship the package to the address provided</li>
56
+ <li>Refund will be processed within 5-7 business days after we receive your return</li>
57
+ </ol>
58
+ </div>
59
+
60
+ <div>
61
+ <h2 className="mb-4 text-2xl font-semibold text-foreground">Refund Policy</h2>
62
+ <p className="text-muted-foreground">
63
+ Once we receive and inspect your return, we'll send you an email notification. If approved, your refund will be processed and automatically applied to your original payment method within 5-7 business days.
64
+ </p>
65
+ </div>
66
+
67
+ <div>
68
+ <h2 className="mb-4 text-2xl font-semibold text-foreground">Exchanges</h2>
69
+ <p className="text-muted-foreground">
70
+ We only replace items if they are defective or damaged. If you need to exchange an item, contact our customer service team.
71
+ </p>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ );
77
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * robots.txt Configuration
3
+ *
4
+ * Controls search engine crawler access to the site.
5
+ *
6
+ * Requirements: 13.5
7
+ */
8
+
9
+ import type { MetadataRoute } from "next";
10
+
11
+ const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com";
12
+
13
+ /**
14
+ * Generate robots.txt rules
15
+ *
16
+ * Allows:
17
+ * - All public pages (products, collections, categories)
18
+ *
19
+ * Disallows:
20
+ * - API routes (/api/*)
21
+ * - Checkout pages (/checkout/*)
22
+ * - Account pages (/account/*)
23
+ * - Admin routes (/admin/*)
24
+ * - Cart page (/cart)
25
+ * - Auth pages (/auth/*)
26
+ */
27
+ export default function robots(): MetadataRoute.Robots {
28
+ return {
29
+ rules: [
30
+ {
31
+ userAgent: "*",
32
+ allow: "/",
33
+ disallow: [
34
+ "/api/",
35
+ "/checkout/",
36
+ "/account/",
37
+ "/admin/",
38
+ "/cart",
39
+ "/auth/",
40
+ "/_next/",
41
+ "/private/",
42
+ ],
43
+ },
44
+ // Block bad bots explicitly
45
+ {
46
+ userAgent: ["GPTBot", "ChatGPT-User", "CCBot", "anthropic-ai"],
47
+ disallow: ["/"],
48
+ },
49
+ ],
50
+ sitemap: `${SITE_URL}/sitemap.xml`,
51
+ host: SITE_URL,
52
+ };
53
+ }