@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,58 @@
1
+ "use client";
2
+
3
+ import { ProductGrid, type ProductCardProduct } from "@/components/product/product-grid";
4
+ import { EmptyProducts } from "@/components/ui/empty-state";
5
+
6
+ export interface SearchResultsProps {
7
+ query: string;
8
+ results: ProductCardProduct[];
9
+ }
10
+
11
+ /**
12
+ * SearchResults - Display search results
13
+ */
14
+ export function SearchResults({ query, results }: SearchResultsProps) {
15
+ if (!query) {
16
+ return (
17
+ <div className="rounded-lg border border-border bg-muted/50 p-12 text-center">
18
+ <p className="text-muted-foreground">
19
+ Enter a search term to find products
20
+ </p>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ if (results.length === 0) {
26
+ return (
27
+ <div className="space-y-6">
28
+ <EmptyProducts />
29
+
30
+ {/* Search tips */}
31
+ <div className="rounded-lg border border-border bg-muted/50 p-6">
32
+ <h3 className="mb-3 font-semibold text-foreground">Search Tips:</h3>
33
+ <ul className="space-y-2 text-sm text-muted-foreground">
34
+ <li>• Check your spelling</li>
35
+ <li>• Try more general keywords</li>
36
+ <li>• Try different keywords</li>
37
+ <li>• Browse our categories instead</li>
38
+ </ul>
39
+ </div>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ return (
45
+ <div className="space-y-6">
46
+ <p className="text-sm text-muted-foreground">
47
+ Found {results.length} {results.length === 1 ? "result" : "results"} for "{query}"
48
+ </p>
49
+
50
+ <ProductGrid
51
+ products={results}
52
+ columns={4}
53
+ priorityCount={4}
54
+ showBadges
55
+ />
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,43 @@
1
+ "use client";
2
+
3
+ import { Search } from "lucide-react";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ export interface SearchSuggestionsProps {
7
+ suggestions: string[];
8
+ onSelect: (suggestion: string) => void;
9
+ className?: string;
10
+ }
11
+
12
+ /**
13
+ * SearchSuggestions - Dropdown with search suggestions
14
+ */
15
+ export function SearchSuggestions({
16
+ suggestions,
17
+ onSelect,
18
+ className,
19
+ }: SearchSuggestionsProps) {
20
+ return (
21
+ <div
22
+ className={cn(
23
+ "absolute top-full z-50 mt-2 w-full rounded-lg border border-border bg-background shadow-lg",
24
+ className
25
+ )}
26
+ >
27
+ <ul className="max-h-80 overflow-y-auto py-2">
28
+ {suggestions.map((suggestion) => (
29
+ <li key={`suggestion-${suggestion.toLowerCase().replace(/\s+/g, '-')}`}>
30
+ <button
31
+ type="button"
32
+ onClick={() => onSelect(suggestion)}
33
+ className="flex w-full items-center gap-3 px-4 py-2 text-left text-sm transition-colors hover:bg-accent"
34
+ >
35
+ <Search className="h-4 w-4 text-muted-foreground" />
36
+ <span className="text-foreground">{suggestion}</span>
37
+ </button>
38
+ </li>
39
+ ))}
40
+ </ul>
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SEO Components
3
+ *
4
+ * Components and utilities for structured data and search engine optimization.
5
+ */
6
+
7
+ export { JsonLd } from "./json-ld";
8
+ export {
9
+ buildProductJsonLd,
10
+ buildBreadcrumbJsonLd,
11
+ buildOrganizationJsonLd,
12
+ } from "./product-json-ld";
@@ -0,0 +1,56 @@
1
+ /**
2
+ * JsonLd Component - Type-safe structured data for SEO
3
+ *
4
+ * Renders JSON-LD structured data in a script tag with proper sanitization.
5
+ * Requirements: 13.3
6
+ */
7
+
8
+ import type { Thing, WithContext } from "schema-dts";
9
+
10
+ interface JsonLdProps<T extends Thing> {
11
+ /** Schema.org structured data object */
12
+ data: WithContext<T>;
13
+ /** Optional key for multiple JSON-LD blocks */
14
+ id?: string;
15
+ }
16
+
17
+ /**
18
+ * Sanitize JSON-LD content to prevent XSS attacks
19
+ *
20
+ * Replaces < and > characters with their Unicode equivalents
21
+ * to prevent script injection via malicious data.
22
+ */
23
+ function sanitizeJsonLd(json: string): string {
24
+ return json
25
+ .replace(/</g, "\\u003c")
26
+ .replace(/>/g, "\\u003e");
27
+ }
28
+
29
+ /**
30
+ * JsonLd - Render JSON-LD structured data script tag
31
+ *
32
+ * Features:
33
+ * - Type-safe schema data via schema-dts types
34
+ * - XSS sanitization (< and > character escaping)
35
+ * - Multiple schemas per page support
36
+ * - Server-side rendering compatible
37
+ *
38
+ * Usage:
39
+ * ```tsx
40
+ * <JsonLd data={buildProductJsonLd(product)} />
41
+ * ```
42
+ */
43
+ export function JsonLd<T extends Thing>({ data, id }: JsonLdProps<T>) {
44
+ const json = JSON.stringify(data);
45
+ const sanitized = sanitizeJsonLd(json);
46
+
47
+ return (
48
+ <script
49
+ type="application/ld+json"
50
+ id={id}
51
+ dangerouslySetInnerHTML={{ __html: sanitized }}
52
+ />
53
+ );
54
+ }
55
+
56
+ export default JsonLd;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Product JSON-LD Builder - Schema.org Product structured data
3
+ *
4
+ * Builds type-safe Product schema for SEO rich results.
5
+ * Requirements: 13.3
6
+ */
7
+
8
+ import type { Product, WithContext, Offer } from "schema-dts";
9
+
10
+ /**
11
+ * Product data input interface
12
+ * Matches the ProductFragment from GraphQL
13
+ */
14
+ interface ProductData {
15
+ id: string;
16
+ title: string;
17
+ handle: string;
18
+ description?: string | null;
19
+ descriptionHtml?: string | null;
20
+ featuredImage?: {
21
+ url: string;
22
+ altText?: string | null;
23
+ width?: number | null;
24
+ height?: number | null;
25
+ } | null;
26
+ images?: Array<{
27
+ url: string;
28
+ altText?: string | null;
29
+ }> | null;
30
+ variants?: Array<{
31
+ id: string;
32
+ sku?: string | null;
33
+ title?: string | null;
34
+ price: {
35
+ amount: string;
36
+ currencyCode: string;
37
+ };
38
+ compareAtPrice?: {
39
+ amount: string;
40
+ currencyCode: string;
41
+ } | null;
42
+ availableForSale?: boolean;
43
+ quantityAvailable?: number | null;
44
+ }> | null;
45
+ brand?: {
46
+ name: string;
47
+ } | null;
48
+ vendor?: string | null;
49
+ seo?: {
50
+ title?: string | null;
51
+ description?: string | null;
52
+ } | null;
53
+ updatedAt?: string | null;
54
+ createdAt?: string | null;
55
+ }
56
+
57
+ interface BuildProductJsonLdOptions {
58
+ /** Base URL for the site (e.g., https://example.com) */
59
+ siteUrl: string;
60
+ /** Organization/shop name */
61
+ organizationName?: string;
62
+ }
63
+
64
+ /**
65
+ * Build JSON-LD Product schema from product data
66
+ *
67
+ * @param product - Product data from GraphQL
68
+ * @param options - Configuration options
69
+ * @returns Schema.org Product with context
70
+ */
71
+ export function buildProductJsonLd(
72
+ product: ProductData,
73
+ options: BuildProductJsonLdOptions
74
+ ): WithContext<Product> {
75
+ const { siteUrl, organizationName } = options;
76
+ const productUrl = `${siteUrl}/products/${product.handle}`;
77
+
78
+ // Get the first variant for pricing info
79
+ const firstVariant = product.variants?.[0];
80
+
81
+ // Build offer(s) from variants
82
+ const offers: Offer[] = (product.variants || []).slice(0, 10).map((variant) => ({
83
+ "@type": "Offer",
84
+ url: productUrl,
85
+ priceCurrency: variant.price.currencyCode,
86
+ price: variant.price.amount,
87
+ availability: variant.availableForSale
88
+ ? "https://schema.org/InStock"
89
+ : "https://schema.org/OutOfStock",
90
+ ...(variant.sku && { sku: variant.sku }),
91
+ ...(variant.title && variant.title !== "Default Title" && { name: variant.title }),
92
+ }));
93
+
94
+ // Build image array
95
+ const images = [
96
+ product.featuredImage?.url,
97
+ ...(product.images?.map((img) => img.url) || []),
98
+ ].filter((url): url is string => Boolean(url));
99
+
100
+ // Build the Product schema
101
+ const schema: WithContext<Product> = {
102
+ "@context": "https://schema.org",
103
+ "@type": "Product",
104
+ name: product.title,
105
+ url: productUrl,
106
+ ...(product.description && { description: product.description }),
107
+ ...(images.length > 0 && { image: images }),
108
+ ...(firstVariant?.sku && { sku: firstVariant.sku }),
109
+ ...(product.brand?.name && {
110
+ brand: {
111
+ "@type": "Brand",
112
+ name: product.brand.name,
113
+ },
114
+ }),
115
+ ...(product.vendor && !product.brand?.name && {
116
+ brand: {
117
+ "@type": "Brand",
118
+ name: product.vendor,
119
+ },
120
+ }),
121
+ ...(offers.length > 0 && {
122
+ offers: offers.length === 1 ? offers[0] : offers,
123
+ }),
124
+ };
125
+
126
+ return schema;
127
+ }
128
+
129
+ /**
130
+ * Build JSON-LD BreadcrumbList schema
131
+ */
132
+ export function buildBreadcrumbJsonLd(
133
+ items: Array<{ name: string; url: string }>,
134
+ siteUrl: string
135
+ ) {
136
+ return {
137
+ "@context": "https://schema.org" as const,
138
+ "@type": "BreadcrumbList" as const,
139
+ itemListElement: items.map((item, index) => ({
140
+ "@type": "ListItem" as const,
141
+ position: index + 1,
142
+ name: item.name,
143
+ item: item.url.startsWith("http") ? item.url : `${siteUrl}${item.url}`,
144
+ })),
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Build JSON-LD Organization schema
150
+ */
151
+ export function buildOrganizationJsonLd(options: {
152
+ name: string;
153
+ siteUrl: string;
154
+ logo?: string;
155
+ description?: string;
156
+ }) {
157
+ return {
158
+ "@context": "https://schema.org" as const,
159
+ "@type": "Organization" as const,
160
+ name: options.name,
161
+ url: options.siteUrl,
162
+ ...(options.logo && { logo: options.logo }),
163
+ ...(options.description && { description: options.description }),
164
+ };
165
+ }
166
+
167
+ export default buildProductJsonLd;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shipping Components
3
+ *
4
+ * Components for shipping method selection during checkout.
5
+ * Uses the GraphQL availableShippingMethods query.
6
+ */
7
+
8
+ export {
9
+ ShippingMethodSelector,
10
+ type ShippingMethodSelectorProps,
11
+ type AvailableShippingMethod,
12
+ type ShippingCarrier,
13
+ type DeliveryEstimate,
14
+ type FreeShippingProgress,
15
+ type Money,
16
+ } from "./shipping-method-selector";
@@ -0,0 +1,337 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { Truck, Package, Clock, Check, AlertCircle } from "lucide-react";
5
+ import { cn } from "@/lib/utils";
6
+ import { Spinner } from "@/components/ui/spinner";
7
+
8
+ /**
9
+ * Types matching GraphQL schema
10
+ */
11
+ export interface ShippingCarrier {
12
+ id?: string;
13
+ name: string;
14
+ logoUrl?: string;
15
+ serviceCode?: string;
16
+ }
17
+
18
+ export interface DeliveryEstimate {
19
+ minDays?: number;
20
+ maxDays?: number;
21
+ description?: string;
22
+ }
23
+
24
+ export interface Money {
25
+ amount: string;
26
+ currencyCode: string;
27
+ }
28
+
29
+ export interface FreeShippingProgress {
30
+ qualifies: boolean;
31
+ currentAmount?: Money;
32
+ threshold?: Money;
33
+ remaining?: Money;
34
+ progressPercent?: number;
35
+ message?: string;
36
+ }
37
+
38
+ export interface AvailableShippingMethod {
39
+ id: string;
40
+ name: string;
41
+ description?: string;
42
+ carrier?: ShippingCarrier;
43
+ price: Money;
44
+ isFree: boolean;
45
+ estimatedDelivery?: DeliveryEstimate;
46
+ freeShippingProgress?: FreeShippingProgress;
47
+ sortOrder: number;
48
+ }
49
+
50
+ export interface ShippingMethodSelectorProps {
51
+ methods: AvailableShippingMethod[];
52
+ selectedMethodId?: string;
53
+ onSelect: (method: AvailableShippingMethod) => void;
54
+ freeShippingProgress?: FreeShippingProgress;
55
+ isLoading?: boolean;
56
+ error?: string;
57
+ currencyCode?: string;
58
+ className?: string;
59
+ }
60
+
61
+ /**
62
+ * ShippingMethodSelector - Select shipping method during checkout
63
+ *
64
+ * Features:
65
+ * - Displays available shipping methods with carrier info
66
+ * - Shows delivery estimates
67
+ * - Free shipping progress indicator
68
+ * - Supports carrier logos
69
+ */
70
+ export function ShippingMethodSelector({
71
+ methods,
72
+ selectedMethodId,
73
+ onSelect,
74
+ freeShippingProgress,
75
+ isLoading,
76
+ error,
77
+ currencyCode = "PLN",
78
+ className,
79
+ }: ShippingMethodSelectorProps) {
80
+ const formatPrice = (money: Money) => {
81
+ const amount = parseFloat(money.amount);
82
+ return new Intl.NumberFormat("pl-PL", {
83
+ style: "currency",
84
+ currency: money.currencyCode || currencyCode,
85
+ }).format(amount);
86
+ };
87
+
88
+ if (isLoading) {
89
+ return (
90
+ <div className={cn("space-y-4", className)}>
91
+ <div className="flex items-center gap-2">
92
+ <Truck className="h-5 w-5 text-muted-foreground" />
93
+ <h3 className="text-sm font-medium text-foreground">Shipping Method</h3>
94
+ </div>
95
+ <div className="flex items-center justify-center py-8">
96
+ <Spinner className="h-6 w-6" />
97
+ <span className="ml-2 text-sm text-muted-foreground">
98
+ Loading shipping options...
99
+ </span>
100
+ </div>
101
+ </div>
102
+ );
103
+ }
104
+
105
+ if (error) {
106
+ return (
107
+ <div className={cn("space-y-4", className)}>
108
+ <div className="flex items-center gap-2">
109
+ <Truck className="h-5 w-5 text-muted-foreground" />
110
+ <h3 className="text-sm font-medium text-foreground">Shipping Method</h3>
111
+ </div>
112
+ <div className="rounded-lg border border-destructive/50 bg-destructive/10 p-4">
113
+ <div className="flex items-center gap-2">
114
+ <AlertCircle className="h-5 w-5 text-destructive" />
115
+ <p className="text-sm text-destructive">{error}</p>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ );
120
+ }
121
+
122
+ if (methods.length === 0) {
123
+ return (
124
+ <div className={cn("space-y-4", className)}>
125
+ <div className="flex items-center gap-2">
126
+ <Truck className="h-5 w-5 text-muted-foreground" />
127
+ <h3 className="text-sm font-medium text-foreground">Shipping Method</h3>
128
+ </div>
129
+ <div className="rounded-lg border border-border bg-muted/50 p-4">
130
+ <p className="text-sm text-muted-foreground text-center">
131
+ No shipping methods available for this address
132
+ </p>
133
+ </div>
134
+ </div>
135
+ );
136
+ }
137
+
138
+ return (
139
+ <div className={cn("space-y-4", className)}>
140
+ {/* Header */}
141
+ <div className="flex items-center gap-2">
142
+ <Truck className="h-5 w-5 text-muted-foreground" />
143
+ <h3 className="text-sm font-medium text-foreground">Shipping Method</h3>
144
+ </div>
145
+
146
+ {/* Free Shipping Progress Banner */}
147
+ {freeShippingProgress && !freeShippingProgress.qualifies && (
148
+ <FreeShippingBanner progress={freeShippingProgress} />
149
+ )}
150
+
151
+ {/* Shipping Methods List */}
152
+ <div className="space-y-2">
153
+ {methods.map((method) => (
154
+ <ShippingMethodCard
155
+ key={method.id}
156
+ method={method}
157
+ isSelected={selectedMethodId === method.id}
158
+ onSelect={() => onSelect(method)}
159
+ formatPrice={formatPrice}
160
+ />
161
+ ))}
162
+ </div>
163
+
164
+ {/* Free Shipping Achieved */}
165
+ {freeShippingProgress?.qualifies && (
166
+ <div className="rounded-lg border border-green-500/50 bg-green-500/10 p-3">
167
+ <div className="flex items-center gap-2">
168
+ <Check className="h-4 w-4 text-green-600" />
169
+ <p className="text-sm font-medium text-green-700">
170
+ {freeShippingProgress.message || "You qualify for free shipping!"}
171
+ </p>
172
+ </div>
173
+ </div>
174
+ )}
175
+ </div>
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Individual shipping method card
181
+ */
182
+ function ShippingMethodCard({
183
+ method,
184
+ isSelected,
185
+ onSelect,
186
+ formatPrice,
187
+ }: {
188
+ method: AvailableShippingMethod;
189
+ isSelected: boolean;
190
+ onSelect: () => void;
191
+ formatPrice: (money: Money) => string;
192
+ }) {
193
+ return (
194
+ <button
195
+ type="button"
196
+ onClick={onSelect}
197
+ className={cn(
198
+ "w-full rounded-lg border p-4 text-left transition-all",
199
+ isSelected
200
+ ? "border-primary bg-primary/5 ring-1 ring-primary"
201
+ : "border-border hover:border-primary/50"
202
+ )}
203
+ >
204
+ <div className="flex items-start justify-between gap-4">
205
+ {/* Left: Method Info */}
206
+ <div className="flex items-start gap-3 flex-1">
207
+ {/* Carrier Logo or Icon */}
208
+ {method.carrier?.logoUrl ? (
209
+ <img
210
+ src={method.carrier.logoUrl}
211
+ alt={method.carrier.name}
212
+ className="h-10 w-10 rounded object-contain"
213
+ />
214
+ ) : (
215
+ <div className="flex h-10 w-10 items-center justify-center rounded bg-muted">
216
+ <Package className="h-5 w-5 text-muted-foreground" />
217
+ </div>
218
+ )}
219
+
220
+ <div className="flex-1 min-w-0">
221
+ {/* Method Name */}
222
+ <div className="flex items-center gap-2">
223
+ <p className="text-sm font-medium text-foreground">
224
+ {method.name}
225
+ </p>
226
+ {method.isFree && (
227
+ <span className="rounded bg-green-500/10 px-1.5 py-0.5 text-xs font-medium text-green-700">
228
+ FREE
229
+ </span>
230
+ )}
231
+ </div>
232
+
233
+ {/* Carrier Name (if different from method name) */}
234
+ {method.carrier?.name && method.carrier.name !== method.name && (
235
+ <p className="text-xs text-muted-foreground">
236
+ via {method.carrier.name}
237
+ </p>
238
+ )}
239
+
240
+ {/* Description */}
241
+ {method.description && (
242
+ <p className="mt-1 text-xs text-muted-foreground line-clamp-2">
243
+ {method.description}
244
+ </p>
245
+ )}
246
+
247
+ {/* Delivery Estimate */}
248
+ {method.estimatedDelivery && (
249
+ <div className="mt-2 flex items-center gap-1.5">
250
+ <Clock className="h-3.5 w-3.5 text-muted-foreground" />
251
+ <span className="text-xs text-muted-foreground">
252
+ {method.estimatedDelivery.description}
253
+ </span>
254
+ </div>
255
+ )}
256
+ </div>
257
+ </div>
258
+
259
+ {/* Right: Price and Selection */}
260
+ <div className="flex flex-col items-end">
261
+ <p
262
+ className={cn(
263
+ "text-sm font-semibold",
264
+ method.isFree ? "text-green-600" : "text-foreground"
265
+ )}
266
+ >
267
+ {method.isFree ? "FREE" : formatPrice(method.price)}
268
+ </p>
269
+
270
+ {/* Selection Indicator */}
271
+ <div
272
+ className={cn(
273
+ "mt-2 h-5 w-5 rounded-full border-2 flex items-center justify-center transition-colors",
274
+ isSelected
275
+ ? "border-primary bg-primary"
276
+ : "border-muted-foreground/30"
277
+ )}
278
+ >
279
+ {isSelected && <Check className="h-3 w-3 text-primary-foreground" />}
280
+ </div>
281
+ </div>
282
+ </div>
283
+
284
+ {/* Method-specific free shipping progress */}
285
+ {method.freeShippingProgress &&
286
+ !method.freeShippingProgress.qualifies &&
287
+ method.freeShippingProgress.progressPercent !== undefined && (
288
+ <div className="mt-3 pt-3 border-t border-border">
289
+ <div className="flex items-center justify-between text-xs mb-1.5">
290
+ <span className="text-muted-foreground">
291
+ {method.freeShippingProgress.message}
292
+ </span>
293
+ <span className="font-medium text-foreground">
294
+ {Math.round(method.freeShippingProgress.progressPercent)}%
295
+ </span>
296
+ </div>
297
+ <div className="h-1.5 rounded-full bg-muted overflow-hidden">
298
+ <div
299
+ className="h-full rounded-full bg-primary transition-all"
300
+ style={{
301
+ width: `${method.freeShippingProgress.progressPercent}%`,
302
+ }}
303
+ />
304
+ </div>
305
+ </div>
306
+ )}
307
+ </button>
308
+ );
309
+ }
310
+
311
+ /**
312
+ * Free shipping progress banner
313
+ */
314
+ function FreeShippingBanner({ progress }: { progress: FreeShippingProgress }) {
315
+ if (!progress.remaining || !progress.threshold) return null;
316
+
317
+ return (
318
+ <div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-3">
319
+ <div className="flex items-center justify-between mb-2">
320
+ <p className="text-sm text-amber-700">
321
+ {progress.message}
322
+ </p>
323
+ <span className="text-xs font-medium text-amber-700">
324
+ {progress.progressPercent?.toFixed(0)}%
325
+ </span>
326
+ </div>
327
+ <div className="h-2 rounded-full bg-amber-200 overflow-hidden">
328
+ <div
329
+ className="h-full rounded-full bg-amber-500 transition-all"
330
+ style={{ width: `${progress.progressPercent || 0}%` }}
331
+ />
332
+ </div>
333
+ </div>
334
+ );
335
+ }
336
+
337
+ export default ShippingMethodSelector;