@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,228 @@
1
+ "use client";
2
+
3
+ import { useState, FormEvent } from "react";
4
+ import Link from "next/link";
5
+ import { useRouter } from "next/navigation";
6
+ import { useRegister } from "@doswiftly/commerce-sdk/graphql/react";
7
+ import { AuthGuard } from "@/components/auth/auth-guard";
8
+
9
+ function RegisterForm() {
10
+ const router = useRouter();
11
+ const register = useRegister();
12
+
13
+ const [formData, setFormData] = useState({
14
+ firstName: "",
15
+ lastName: "",
16
+ email: "",
17
+ password: "",
18
+ confirmPassword: "",
19
+ acceptsMarketing: false,
20
+ });
21
+ const [error, setError] = useState<string | null>(null);
22
+
23
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
24
+ const { name, value, type, checked } = e.target;
25
+ setFormData((prev) => ({
26
+ ...prev,
27
+ [name]: type === "checkbox" ? checked : value,
28
+ }));
29
+ };
30
+
31
+ const handleSubmit = async (e: FormEvent) => {
32
+ e.preventDefault();
33
+ setError(null);
34
+
35
+ // Validate passwords match
36
+ if (formData.password !== formData.confirmPassword) {
37
+ setError("Passwords do not match");
38
+ return;
39
+ }
40
+
41
+ // Validate password length
42
+ if (formData.password.length < 8) {
43
+ setError("Password must be at least 8 characters");
44
+ return;
45
+ }
46
+
47
+ try {
48
+ const result = await register.mutateAsync({
49
+ input: {
50
+ email: formData.email,
51
+ password: formData.password,
52
+ firstName: formData.firstName || undefined,
53
+ lastName: formData.lastName || undefined,
54
+ acceptsMarketing: formData.acceptsMarketing,
55
+ },
56
+ });
57
+
58
+ // Check for user errors
59
+ const userErrors = result.customerCreate?.userErrors;
60
+ if (userErrors && userErrors.length > 0) {
61
+ setError(userErrors[0].message);
62
+ return;
63
+ }
64
+
65
+ // Success - redirect to account
66
+ router.push("/account");
67
+ } catch (err) {
68
+ setError("Registration failed. Please try again.");
69
+ }
70
+ };
71
+
72
+ return (
73
+ <div className="container mx-auto flex min-h-[60vh] items-center justify-center px-4 py-8">
74
+ <div className="w-full max-w-md">
75
+ <h1 className="mb-8 text-center text-3xl font-bold text-gray-900">
76
+ Create Account
77
+ </h1>
78
+
79
+ {error && (
80
+ <div className="mb-6 rounded-lg bg-red-50 p-4 text-red-700">
81
+ {error}
82
+ </div>
83
+ )}
84
+
85
+ <form onSubmit={handleSubmit} className="space-y-4">
86
+ <div className="grid grid-cols-2 gap-4">
87
+ <div>
88
+ <label
89
+ htmlFor="firstName"
90
+ className="mb-2 block text-sm font-medium text-gray-900"
91
+ >
92
+ First Name
93
+ </label>
94
+ <input
95
+ type="text"
96
+ id="firstName"
97
+ name="firstName"
98
+ value={formData.firstName}
99
+ onChange={handleChange}
100
+ className="w-full rounded-lg border border-gray-300 px-4 py-3 focus:border-gray-900 focus:outline-none"
101
+ placeholder="John"
102
+ />
103
+ </div>
104
+ <div>
105
+ <label
106
+ htmlFor="lastName"
107
+ className="mb-2 block text-sm font-medium text-gray-900"
108
+ >
109
+ Last Name
110
+ </label>
111
+ <input
112
+ type="text"
113
+ id="lastName"
114
+ name="lastName"
115
+ value={formData.lastName}
116
+ onChange={handleChange}
117
+ className="w-full rounded-lg border border-gray-300 px-4 py-3 focus:border-gray-900 focus:outline-none"
118
+ placeholder="Doe"
119
+ />
120
+ </div>
121
+ </div>
122
+
123
+ <div>
124
+ <label
125
+ htmlFor="email"
126
+ className="mb-2 block text-sm font-medium text-gray-900"
127
+ >
128
+ Email *
129
+ </label>
130
+ <input
131
+ type="email"
132
+ id="email"
133
+ name="email"
134
+ value={formData.email}
135
+ onChange={handleChange}
136
+ required
137
+ className="w-full rounded-lg border border-gray-300 px-4 py-3 focus:border-gray-900 focus:outline-none"
138
+ placeholder="your@email.com"
139
+ />
140
+ </div>
141
+
142
+ <div>
143
+ <label
144
+ htmlFor="password"
145
+ className="mb-2 block text-sm font-medium text-gray-900"
146
+ >
147
+ Password *
148
+ </label>
149
+ <input
150
+ type="password"
151
+ id="password"
152
+ name="password"
153
+ value={formData.password}
154
+ onChange={handleChange}
155
+ required
156
+ minLength={8}
157
+ className="w-full rounded-lg border border-gray-300 px-4 py-3 focus:border-gray-900 focus:outline-none"
158
+ placeholder="••••••••"
159
+ />
160
+ <p className="mt-1 text-xs text-gray-500">
161
+ Must be at least 8 characters
162
+ </p>
163
+ </div>
164
+
165
+ <div>
166
+ <label
167
+ htmlFor="confirmPassword"
168
+ className="mb-2 block text-sm font-medium text-gray-900"
169
+ >
170
+ Confirm Password *
171
+ </label>
172
+ <input
173
+ type="password"
174
+ id="confirmPassword"
175
+ name="confirmPassword"
176
+ value={formData.confirmPassword}
177
+ onChange={handleChange}
178
+ required
179
+ minLength={8}
180
+ className="w-full rounded-lg border border-gray-300 px-4 py-3 focus:border-gray-900 focus:outline-none"
181
+ placeholder="••••••••"
182
+ />
183
+ </div>
184
+
185
+ <div className="flex items-center gap-2">
186
+ <input
187
+ type="checkbox"
188
+ id="acceptsMarketing"
189
+ name="acceptsMarketing"
190
+ checked={formData.acceptsMarketing}
191
+ onChange={handleChange}
192
+ className="h-4 w-4 rounded border-gray-300"
193
+ />
194
+ <label htmlFor="acceptsMarketing" className="text-sm text-gray-600">
195
+ I want to receive marketing emails
196
+ </label>
197
+ </div>
198
+
199
+ <button
200
+ type="submit"
201
+ disabled={register.isPending}
202
+ className="w-full rounded-lg bg-gray-900 py-3 font-medium text-white transition hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50"
203
+ >
204
+ {register.isPending ? "Creating account..." : "Create Account"}
205
+ </button>
206
+ </form>
207
+
208
+ <p className="mt-6 text-center text-sm text-gray-600">
209
+ Already have an account?{" "}
210
+ <Link
211
+ href="/auth/login"
212
+ className="font-medium text-gray-900 hover:underline"
213
+ >
214
+ Login
215
+ </Link>
216
+ </p>
217
+ </div>
218
+ </div>
219
+ );
220
+ }
221
+
222
+ export default function RegisterPage() {
223
+ return (
224
+ <AuthGuard requireGuest>
225
+ <RegisterForm />
226
+ </AuthGuard>
227
+ );
228
+ }
@@ -0,0 +1,263 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+ import { Trash2, Plus, Minus, ShoppingCart, Loader2 } from "lucide-react";
5
+ import { useCartManager } from "@doswiftly/commerce-sdk/graphql/react";
6
+
7
+ /**
8
+ * CartPage - Shopping cart with real GraphQL data
9
+ *
10
+ * Uses useCartManager hook - all cart operations are handled automatically.
11
+ * No manual state management needed!
12
+ */
13
+ export default function CartPage() {
14
+ const {
15
+ cart,
16
+ lines,
17
+ totalQuantity,
18
+ cartLoading,
19
+ loading,
20
+ removeItem,
21
+ removeItemLoading,
22
+ updateQuantity,
23
+ updateQuantityLoading,
24
+ clearCart,
25
+ } = useCartManager();
26
+
27
+ // Show loading state
28
+ if (cartLoading && !cart) {
29
+ return (
30
+ <div className="container mx-auto px-4 py-16">
31
+ <div className="flex items-center justify-center">
32
+ <Loader2 className="h-8 w-8 animate-spin text-primary" />
33
+ <span className="ml-2 text-gray-600">Loading cart...</span>
34
+ </div>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ // Empty cart state
40
+ if (!cart || totalQuantity === 0) {
41
+ return (
42
+ <div className="container mx-auto px-4 py-8">
43
+ <h1 className="mb-8 text-3xl font-bold text-gray-900">Shopping Cart</h1>
44
+ <div className="py-16 text-center">
45
+ <ShoppingCart className="mx-auto mb-4 h-16 w-16 text-gray-300" />
46
+ <h2 className="mb-2 text-xl font-semibold text-gray-900">
47
+ Your cart is empty
48
+ </h2>
49
+ <p className="mb-8 text-gray-600">
50
+ Looks like you haven&apos;t added anything to your cart yet.
51
+ </p>
52
+ <Link href="/products" className="btn btn-primary">
53
+ Continue Shopping
54
+ </Link>
55
+ </div>
56
+ </div>
57
+ );
58
+ }
59
+
60
+ // Cart line type from GraphQL
61
+ type CartLine = (typeof lines)[number];
62
+
63
+ return (
64
+ <div className="container mx-auto px-4 py-8">
65
+ <div className="mb-8 flex items-center justify-between">
66
+ <h1 className="text-3xl font-bold text-gray-900">
67
+ Shopping Cart ({totalQuantity}{" "}
68
+ {totalQuantity === 1 ? "item" : "items"})
69
+ </h1>
70
+ <button
71
+ onClick={clearCart}
72
+ className="text-sm text-gray-500 hover:text-red-500"
73
+ >
74
+ Clear cart
75
+ </button>
76
+ </div>
77
+
78
+ <div className="grid gap-8 lg:grid-cols-3">
79
+ {/* Cart Items */}
80
+ <div className="lg:col-span-2">
81
+ <div className="space-y-4">
82
+ {(lines as CartLine[]).map((line) => (
83
+ <div key={line.id} className="card flex gap-4">
84
+ {/* Product Image */}
85
+ <div className="h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg bg-gray-100">
86
+ {line.merchandise?.image?.url ? (
87
+ <img
88
+ src={line.merchandise.image.url}
89
+ alt={
90
+ line.merchandise.image.altText || line.merchandise.title
91
+ }
92
+ className="h-full w-full object-cover"
93
+ />
94
+ ) : (
95
+ <div className="flex h-full items-center justify-center text-xs text-gray-400">
96
+ No image
97
+ </div>
98
+ )}
99
+ </div>
100
+
101
+ {/* Product Info */}
102
+ <div className="flex flex-1 flex-col">
103
+ <div className="flex justify-between">
104
+ <div>
105
+ <h3 className="font-medium text-gray-900">
106
+ {line.merchandise?.title}
107
+ </h3>
108
+ {/* Selected Options (size, color, etc.) */}
109
+ {line.merchandise?.selectedOptions?.length > 0 && (
110
+ <p className="mt-1 text-sm text-gray-500">
111
+ {line.merchandise.selectedOptions
112
+ .map((opt) => `${opt.name}: ${opt.value}`)
113
+ .join(", ")}
114
+ </p>
115
+ )}
116
+ </div>
117
+ <button
118
+ onClick={() => removeItem(line.id)}
119
+ disabled={removeItemLoading}
120
+ className="text-gray-400 hover:text-red-500 disabled:opacity-50"
121
+ >
122
+ {removeItemLoading ? (
123
+ <Loader2 className="h-5 w-5 animate-spin" />
124
+ ) : (
125
+ <Trash2 className="h-5 w-5" />
126
+ )}
127
+ </button>
128
+ </div>
129
+
130
+ <div className="mt-auto flex items-center justify-between">
131
+ {/* Quantity Controls */}
132
+ <div className="flex items-center rounded-lg border border-gray-300">
133
+ <button
134
+ onClick={() =>
135
+ updateQuantity(line.id, line.quantity - 1)
136
+ }
137
+ disabled={updateQuantityLoading || line.quantity <= 1}
138
+ className="p-2 text-gray-600 hover:text-primary disabled:opacity-50"
139
+ >
140
+ <Minus className="h-4 w-4" />
141
+ </button>
142
+ <span className="w-12 text-center">
143
+ {updateQuantityLoading ? (
144
+ <Loader2 className="mx-auto h-4 w-4 animate-spin" />
145
+ ) : (
146
+ line.quantity
147
+ )}
148
+ </span>
149
+ <button
150
+ onClick={() =>
151
+ updateQuantity(line.id, line.quantity + 1)
152
+ }
153
+ disabled={updateQuantityLoading}
154
+ className="p-2 text-gray-600 hover:text-primary disabled:opacity-50"
155
+ >
156
+ <Plus className="h-4 w-4" />
157
+ </button>
158
+ </div>
159
+
160
+ {/* Line Total */}
161
+ <span className="font-semibold text-gray-900">
162
+ {line.cost?.totalAmount?.amount}{" "}
163
+ {line.cost?.totalAmount?.currencyCode}
164
+ </span>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ ))}
169
+ </div>
170
+ </div>
171
+
172
+ {/* Order Summary */}
173
+ <div className="lg:col-span-1">
174
+ <div className="card sticky top-24">
175
+ <h2 className="mb-4 text-lg font-semibold text-gray-900">
176
+ Order Summary
177
+ </h2>
178
+
179
+ <div className="space-y-3 text-sm">
180
+ <div className="flex justify-between">
181
+ <span className="text-gray-600">Subtotal</span>
182
+ <span className="text-gray-900">
183
+ {cart.cost?.subtotalAmount?.amount}{" "}
184
+ {cart.cost?.subtotalAmount?.currencyCode}
185
+ </span>
186
+ </div>
187
+
188
+ {cart.cost?.totalTaxAmount && (
189
+ <div className="flex justify-between">
190
+ <span className="text-gray-600">Tax</span>
191
+ <span className="text-gray-900">
192
+ {cart.cost.totalTaxAmount.amount}{" "}
193
+ {cart.cost.totalTaxAmount.currencyCode}
194
+ </span>
195
+ </div>
196
+ )}
197
+
198
+ <div className="flex justify-between">
199
+ <span className="text-gray-600">Shipping</span>
200
+ <span className="text-gray-500 italic">
201
+ Calculated at checkout
202
+ </span>
203
+ </div>
204
+
205
+ {/* Discount Codes */}
206
+ {cart.discountCodes?.length > 0 && (
207
+ <div className="border-t border-gray-200 pt-3">
208
+ <span className="text-xs font-medium uppercase text-gray-500">
209
+ Applied Discounts
210
+ </span>
211
+ {cart.discountCodes.map((discount) => (
212
+ <div
213
+ key={discount.code}
214
+ className="mt-1 flex items-center justify-between"
215
+ >
216
+ <span className="text-green-600">{discount.code}</span>
217
+ {!discount.applicable && (
218
+ <span className="text-xs text-red-500">
219
+ Not applicable
220
+ </span>
221
+ )}
222
+ </div>
223
+ ))}
224
+ </div>
225
+ )}
226
+
227
+ <div className="border-t border-gray-200 pt-3">
228
+ <div className="flex justify-between font-semibold">
229
+ <span className="text-gray-900">Total</span>
230
+ <span className="text-primary">
231
+ {cart.cost?.totalAmount?.amount}{" "}
232
+ {cart.cost?.totalAmount?.currencyCode}
233
+ </span>
234
+ </div>
235
+ </div>
236
+ </div>
237
+
238
+ {/* Checkout Button */}
239
+ {cart.checkoutUrl ? (
240
+ <a
241
+ href={cart.checkoutUrl}
242
+ className="btn btn-primary mt-6 w-full"
243
+ >
244
+ {loading ? (
245
+ <Loader2 className="mr-2 h-5 w-5 animate-spin" />
246
+ ) : null}
247
+ Proceed to Checkout
248
+ </a>
249
+ ) : (
250
+ <Link href="/checkout" className="btn btn-primary mt-6 w-full">
251
+ Proceed to Checkout
252
+ </Link>
253
+ )}
254
+
255
+ <Link href="/products" className="btn btn-outline mt-3 w-full">
256
+ Continue Shopping
257
+ </Link>
258
+ </div>
259
+ </div>
260
+ </div>
261
+ </div>
262
+ );
263
+ }
@@ -0,0 +1,200 @@
1
+ import Link from "next/link";
2
+ import { notFound } from "next/navigation";
3
+ import {
4
+ useCategory,
5
+ useProducts,
6
+ } from "@doswiftly/commerce-sdk/graphql/server";
7
+ import { ProductSortKeys } from "@doswiftly/commerce-sdk/graphql";
8
+ import { Pagination } from "@/components/commerce/pagination";
9
+ import { SortSelect } from "@/components/commerce/sort-select";
10
+
11
+ interface CategoryPageProps {
12
+ params: Promise<{ slug: string }>;
13
+ searchParams: Promise<{
14
+ after?: string;
15
+ before?: string;
16
+ sort?: string;
17
+ order?: string;
18
+ }>;
19
+ }
20
+
21
+ export default async function CategoryPage({
22
+ params,
23
+ searchParams,
24
+ }: CategoryPageProps) {
25
+ const { slug } = await params;
26
+ const { after, sort, order } = await searchParams;
27
+
28
+ // Fetch category details by slug
29
+ const categoryData = await useCategory({ slug });
30
+ const category = categoryData.category;
31
+
32
+ if (!category) {
33
+ notFound();
34
+ }
35
+
36
+ // Build sort options from URL params
37
+ const sortKey =
38
+ (sort as keyof typeof ProductSortKeys) || ProductSortKeys.Relevance;
39
+ const reverse = order === "asc" ? false : true;
40
+
41
+ // Fetch products for this category using query filter
42
+ const { products, pageInfo } = await useProducts({
43
+ first: 12,
44
+ after,
45
+ query: `category:${category.slug}`, // Filter by category slug
46
+ sortKey: ProductSortKeys[sortKey as keyof typeof ProductSortKeys],
47
+ reverse,
48
+ });
49
+
50
+ return (
51
+ <div className="container mx-auto px-4 py-8">
52
+ {/* Breadcrumb */}
53
+ <nav className="mb-4 text-sm">
54
+ <ol className="flex items-center gap-2">
55
+ <li>
56
+ <Link href="/" className="text-gray-500 hover:text-gray-700">
57
+ Home
58
+ </Link>
59
+ </li>
60
+ <li className="text-gray-400">/</li>
61
+ <li>
62
+ <Link
63
+ href="/categories"
64
+ className="text-gray-500 hover:text-gray-700"
65
+ >
66
+ Categories
67
+ </Link>
68
+ </li>
69
+ {category.parent && (
70
+ <>
71
+ <li className="text-gray-400">/</li>
72
+ <li>
73
+ <Link
74
+ href={`/categories/${category.parent.slug}`}
75
+ className="text-gray-500 hover:text-gray-700"
76
+ >
77
+ {category.parent.name}
78
+ </Link>
79
+ </li>
80
+ </>
81
+ )}
82
+ <li className="text-gray-400">/</li>
83
+ <li className="text-gray-900">{category.name}</li>
84
+ </ol>
85
+ </nav>
86
+
87
+ {/* Category Header */}
88
+ <div className="mb-8">
89
+ <h1 className="text-3xl font-bold text-gray-900">{category.name}</h1>
90
+ {category.description && (
91
+ <p className="mt-2 text-gray-600">{category.description}</p>
92
+ )}
93
+ <p className="mt-1 text-sm text-gray-500">
94
+ {category.productCount}{" "}
95
+ {category.productCount === 1 ? "product" : "products"}
96
+ </p>
97
+ </div>
98
+
99
+ {/* Subcategories */}
100
+ {category.children && category.children.length > 0 && (
101
+ <div className="mb-8">
102
+ <h2 className="mb-4 text-lg font-semibold text-gray-900">
103
+ Subcategories
104
+ </h2>
105
+ <div className="flex flex-wrap gap-2">
106
+ {category.children.map(
107
+ (child: { id: string; name: string; slug: string }) => (
108
+ <Link
109
+ key={child.id}
110
+ href={`/categories/${child.slug}`}
111
+ className="rounded-full border border-gray-200 px-4 py-2 text-sm text-gray-700 hover:border-gray-900 hover:text-gray-900"
112
+ >
113
+ {child.name}
114
+ </Link>
115
+ )
116
+ )}
117
+ </div>
118
+ </div>
119
+ )}
120
+
121
+ {/* Sort Options */}
122
+ <div className="mb-6 flex items-center justify-between">
123
+ <p className="text-sm text-gray-500">
124
+ Showing {products.length} of {category.productCount} products
125
+ </p>
126
+ <SortSelect currentSort={sort} currentOrder={order} />
127
+ </div>
128
+
129
+ {/* Products Grid */}
130
+ <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4 lg:gap-6">
131
+ {products.length === 0 ? (
132
+ <div className="col-span-full py-12 text-center text-gray-500">
133
+ No products in this category
134
+ </div>
135
+ ) : (
136
+ products.map((product) => {
137
+ const price = product.priceRange?.minVariantPrice;
138
+ const compareAtPrice = product.compareAtPriceRange?.minVariantPrice;
139
+ const hasDiscount =
140
+ compareAtPrice &&
141
+ price &&
142
+ parseFloat(compareAtPrice.amount) > parseFloat(price.amount);
143
+
144
+ return (
145
+ <Link
146
+ key={product.id}
147
+ href={`/products/${product.handle}`}
148
+ className="group"
149
+ >
150
+ <div className="relative aspect-square overflow-hidden rounded-lg bg-gray-100">
151
+ {product.featuredImage?.url ? (
152
+ <img
153
+ src={product.featuredImage.url}
154
+ alt={product.featuredImage.altText || product.title}
155
+ className="h-full w-full object-cover transition group-hover:scale-105"
156
+ />
157
+ ) : (
158
+ <div className="flex h-full items-center justify-center">
159
+ <span className="text-4xl text-gray-400">📷</span>
160
+ </div>
161
+ )}
162
+ {hasDiscount && (
163
+ <span className="absolute left-2 top-2 rounded bg-red-500 px-2 py-1 text-xs font-medium text-white">
164
+ Sale
165
+ </span>
166
+ )}
167
+ </div>
168
+ <div className="mt-3">
169
+ <h3 className="text-sm font-medium text-gray-900 group-hover:text-gray-700">
170
+ {product.title}
171
+ </h3>
172
+ {price && (
173
+ <div className="mt-1 flex items-center gap-2">
174
+ <span className="text-sm font-medium text-gray-900">
175
+ {price.currencyCode} {price.amount}
176
+ </span>
177
+ {hasDiscount && compareAtPrice && (
178
+ <span className="text-sm text-gray-500 line-through">
179
+ {compareAtPrice.currencyCode} {compareAtPrice.amount}
180
+ </span>
181
+ )}
182
+ </div>
183
+ )}
184
+ </div>
185
+ </Link>
186
+ );
187
+ })
188
+ )}
189
+ </div>
190
+
191
+ {/* Pagination */}
192
+ <Pagination
193
+ hasMore={pageInfo.hasNextPage}
194
+ endCursor={pageInfo.endCursor}
195
+ currentCursor={after}
196
+ totalShown={products.length}
197
+ />
198
+ </div>
199
+ );
200
+ }