@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,245 @@
1
+ "use client";
2
+
3
+ /**
4
+ * DiscountBreakdown - Component to display applied discounts
5
+ *
6
+ * Features:
7
+ * - Shows each applied discount with code and amount
8
+ * - Calculates and displays total savings
9
+ * - Supports multiple stacked discounts
10
+ * - Remove discount option
11
+ *
12
+ * Requirements: 25.6
13
+ */
14
+
15
+ import { cn } from "@/lib/utils";
16
+ import { Tag, X, Percent, Truck, Gift } from "lucide-react";
17
+ import { Button } from "@/components/ui/button";
18
+ import { formatPrice } from "@/lib/format";
19
+
20
+ export interface AppliedDiscount {
21
+ id: string;
22
+ code: string;
23
+ type: "PERCENTAGE" | "FIXED_AMOUNT" | "FREE_SHIPPING" | "BUY_X_GET_Y";
24
+ value?: number; // Percentage value or fixed amount
25
+ allocatedAmount: {
26
+ amount: string;
27
+ currencyCode: string;
28
+ };
29
+ targetType?: "ORDER" | "LINE_ITEM" | "SHIPPING";
30
+ description?: string;
31
+ }
32
+
33
+ export interface DiscountBreakdownProps {
34
+ /** List of applied discounts */
35
+ discounts: AppliedDiscount[];
36
+ /** Called when user removes a discount */
37
+ onRemove?: (discountId: string, code: string) => void | Promise<void>;
38
+ /** Whether remove is in progress */
39
+ isRemoving?: boolean;
40
+ /** Show total savings */
41
+ showTotal?: boolean;
42
+ /** Currency code for total calculation */
43
+ currencyCode?: string;
44
+ /** Compact mode for smaller spaces */
45
+ compact?: boolean;
46
+ className?: string;
47
+ }
48
+
49
+ /**
50
+ * Get icon for discount type
51
+ */
52
+ function getDiscountIcon(type: AppliedDiscount["type"]) {
53
+ switch (type) {
54
+ case "PERCENTAGE":
55
+ return Percent;
56
+ case "FREE_SHIPPING":
57
+ return Truck;
58
+ case "BUY_X_GET_Y":
59
+ return Gift;
60
+ default:
61
+ return Tag;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get label for discount type
67
+ */
68
+ function getDiscountTypeLabel(discount: AppliedDiscount): string {
69
+ switch (discount.type) {
70
+ case "PERCENTAGE":
71
+ return discount.value ? `${discount.value}% zniżki` : "Rabat procentowy";
72
+ case "FIXED_AMOUNT":
73
+ return "Rabat kwotowy";
74
+ case "FREE_SHIPPING":
75
+ return "Darmowa dostawa";
76
+ case "BUY_X_GET_Y":
77
+ return "Kup X, dostań Y";
78
+ default:
79
+ return "Rabat";
80
+ }
81
+ }
82
+
83
+ /**
84
+ * DiscountBreakdown - Display applied discounts with breakdown
85
+ *
86
+ * Shows all applied discount codes with their savings amounts
87
+ * and optionally a total savings summary.
88
+ */
89
+ export function DiscountBreakdown({
90
+ discounts,
91
+ onRemove,
92
+ isRemoving = false,
93
+ showTotal = true,
94
+ currencyCode = "PLN",
95
+ compact = false,
96
+ className,
97
+ }: DiscountBreakdownProps) {
98
+ if (discounts.length === 0) {
99
+ return null;
100
+ }
101
+
102
+ // Calculate total savings
103
+ const totalSavings = discounts.reduce((sum, discount) => {
104
+ return sum + parseFloat(discount.allocatedAmount.amount);
105
+ }, 0);
106
+
107
+ return (
108
+ <div className={cn("space-y-2", className)}>
109
+ {/* Individual discounts */}
110
+ {discounts.map((discount) => {
111
+ const Icon = getDiscountIcon(discount.type);
112
+ const isNegative = parseFloat(discount.allocatedAmount.amount) > 0;
113
+
114
+ return (
115
+ <div
116
+ key={discount.id}
117
+ className={cn(
118
+ "flex items-center justify-between rounded-md border bg-green-50/50 p-2 dark:bg-green-950/20",
119
+ compact && "px-2 py-1"
120
+ )}
121
+ >
122
+ <div className="flex items-center gap-2 min-w-0">
123
+ <Icon className={cn("h-4 w-4 text-green-600 shrink-0", compact && "h-3 w-3")} />
124
+ <div className="min-w-0">
125
+ <div className="flex items-center gap-2">
126
+ <span
127
+ className={cn(
128
+ "font-medium text-green-700 dark:text-green-400 truncate",
129
+ compact && "text-sm"
130
+ )}
131
+ >
132
+ {discount.code}
133
+ </span>
134
+ {!compact && (
135
+ <span className="text-xs text-muted-foreground">
136
+ {getDiscountTypeLabel(discount)}
137
+ </span>
138
+ )}
139
+ </div>
140
+ {discount.description && !compact && (
141
+ <p className="text-xs text-muted-foreground truncate">
142
+ {discount.description}
143
+ </p>
144
+ )}
145
+ </div>
146
+ </div>
147
+
148
+ <div className="flex items-center gap-2 shrink-0">
149
+ <span
150
+ className={cn(
151
+ "font-medium text-green-700 dark:text-green-400",
152
+ compact && "text-sm"
153
+ )}
154
+ >
155
+ -{formatPrice(discount.allocatedAmount)}
156
+ </span>
157
+ {onRemove && (
158
+ <Button
159
+ type="button"
160
+ variant="ghost"
161
+ size="icon"
162
+ className={cn("h-6 w-6 text-muted-foreground hover:text-destructive", compact && "h-5 w-5")}
163
+ onClick={() => onRemove(discount.id, discount.code)}
164
+ disabled={isRemoving}
165
+ >
166
+ <X className={cn("h-3 w-3", compact && "h-2.5 w-2.5")} />
167
+ </Button>
168
+ )}
169
+ </div>
170
+ </div>
171
+ );
172
+ })}
173
+
174
+ {/* Total savings */}
175
+ {showTotal && discounts.length > 1 && (
176
+ <div
177
+ className={cn(
178
+ "flex items-center justify-between border-t pt-2",
179
+ compact && "pt-1"
180
+ )}
181
+ >
182
+ <span className={cn("font-medium", compact && "text-sm")}>
183
+ Łączne oszczędności
184
+ </span>
185
+ <span
186
+ className={cn(
187
+ "font-semibold text-green-700 dark:text-green-400",
188
+ compact && "text-sm"
189
+ )}
190
+ >
191
+ -{formatPrice({ amount: totalSavings.toFixed(2), currencyCode })}
192
+ </span>
193
+ </div>
194
+ )}
195
+ </div>
196
+ );
197
+ }
198
+
199
+ /**
200
+ * DiscountSummaryLine - Single line discount display for order summary
201
+ */
202
+ export interface DiscountSummaryLineProps {
203
+ label?: string;
204
+ amount: {
205
+ amount: string;
206
+ currencyCode: string;
207
+ };
208
+ code?: string;
209
+ onRemove?: () => void;
210
+ className?: string;
211
+ }
212
+
213
+ export function DiscountSummaryLine({
214
+ label = "Rabat",
215
+ amount,
216
+ code,
217
+ onRemove,
218
+ className,
219
+ }: DiscountSummaryLineProps) {
220
+ return (
221
+ <div className={cn("flex items-center justify-between text-sm", className)}>
222
+ <div className="flex items-center gap-1.5">
223
+ <Tag className="h-3.5 w-3.5 text-green-600" />
224
+ <span className="text-muted-foreground">
225
+ {label}
226
+ {code && <span className="ml-1 font-medium text-foreground">({code})</span>}
227
+ </span>
228
+ </div>
229
+ <div className="flex items-center gap-1">
230
+ <span className="text-green-600 font-medium">-{formatPrice(amount)}</span>
231
+ {onRemove && (
232
+ <button
233
+ type="button"
234
+ onClick={onRemove}
235
+ className="ml-1 text-muted-foreground hover:text-destructive"
236
+ >
237
+ <X className="h-3 w-3" />
238
+ </button>
239
+ )}
240
+ </div>
241
+ </div>
242
+ );
243
+ }
244
+
245
+ export default DiscountBreakdown;
@@ -0,0 +1,246 @@
1
+ "use client";
2
+
3
+ /**
4
+ * DiscountCodeInput - Input component for entering and validating discount codes
5
+ *
6
+ * Features:
7
+ * - Code validation on blur or button click
8
+ * - Shows validation result (savings preview or error)
9
+ * - Applies code only after successful validation
10
+ * - Loading state during validation
11
+ *
12
+ * Requirements: 27.1, 27.3, 27.4
13
+ */
14
+
15
+ import { useState, useCallback } from "react";
16
+ import { cn } from "@/lib/utils";
17
+ import { Tag, Loader2, Check, X, AlertCircle } from "lucide-react";
18
+ import { Input } from "@/components/ui/input";
19
+ import { Button } from "@/components/ui/button";
20
+ import { Alert, AlertDescription } from "@/components/ui/alert";
21
+ import { formatPrice } from "@/lib/format";
22
+
23
+ export interface DiscountValidationResult {
24
+ valid: boolean;
25
+ code?: string;
26
+ discountType?: "PERCENTAGE" | "FIXED_AMOUNT" | "FREE_SHIPPING";
27
+ discountValue?: number;
28
+ estimatedSavings?: {
29
+ amount: string;
30
+ currencyCode: string;
31
+ };
32
+ minimumOrderAmount?: {
33
+ amount: string;
34
+ currencyCode: string;
35
+ };
36
+ errorMessage?: string;
37
+ errorCode?: string;
38
+ }
39
+
40
+ export interface DiscountCodeInputProps {
41
+ /** Called when user wants to apply a validated code */
42
+ onApply: (code: string) => void | Promise<void>;
43
+ /** Optional: Validate code before applying (returns validation result) */
44
+ onValidate?: (code: string) => Promise<DiscountValidationResult>;
45
+ /** Already applied discount codes */
46
+ appliedCodes?: string[];
47
+ /** Whether the apply mutation is loading */
48
+ isApplying?: boolean;
49
+ /** Placeholder text */
50
+ placeholder?: string;
51
+ /** Disable input */
52
+ disabled?: boolean;
53
+ className?: string;
54
+ }
55
+
56
+ /**
57
+ * DiscountCodeInput - Discount code entry with validation
58
+ *
59
+ * Validates discount codes before applying them to provide
60
+ * immediate feedback on savings or errors.
61
+ */
62
+ export function DiscountCodeInput({
63
+ onApply,
64
+ onValidate,
65
+ appliedCodes = [],
66
+ isApplying = false,
67
+ placeholder = "Wpisz kod rabatowy",
68
+ disabled = false,
69
+ className,
70
+ }: DiscountCodeInputProps) {
71
+ const [code, setCode] = useState("");
72
+ const [isValidating, setIsValidating] = useState(false);
73
+ const [validationResult, setValidationResult] = useState<DiscountValidationResult | null>(null);
74
+ const [error, setError] = useState<string | null>(null);
75
+
76
+ const isLoading = isValidating || isApplying;
77
+
78
+ const handleValidate = useCallback(async () => {
79
+ const trimmedCode = code.trim().toUpperCase();
80
+
81
+ if (!trimmedCode) {
82
+ setError("Wpisz kod rabatowy");
83
+ return;
84
+ }
85
+
86
+ // Check if already applied
87
+ if (appliedCodes.includes(trimmedCode)) {
88
+ setError("Ten kod jest już zastosowany");
89
+ return;
90
+ }
91
+
92
+ setError(null);
93
+ setValidationResult(null);
94
+
95
+ if (onValidate) {
96
+ setIsValidating(true);
97
+ try {
98
+ const result = await onValidate(trimmedCode);
99
+ setValidationResult(result);
100
+
101
+ if (!result.valid) {
102
+ setError(result.errorMessage || "Kod jest nieprawidłowy");
103
+ }
104
+ } catch (e: any) {
105
+ setError(e.message || "Nie udało się zweryfikować kodu");
106
+ } finally {
107
+ setIsValidating(false);
108
+ }
109
+ } else {
110
+ // No validation function, assume valid
111
+ setValidationResult({ valid: true, code: trimmedCode });
112
+ }
113
+ }, [code, appliedCodes, onValidate]);
114
+
115
+ const handleApply = useCallback(async () => {
116
+ const trimmedCode = code.trim().toUpperCase();
117
+
118
+ // If not yet validated, validate first
119
+ if (!validationResult?.valid) {
120
+ await handleValidate();
121
+ return;
122
+ }
123
+
124
+ if (validationResult.valid) {
125
+ await onApply(trimmedCode);
126
+ // Reset state after successful apply
127
+ setCode("");
128
+ setValidationResult(null);
129
+ setError(null);
130
+ }
131
+ }, [code, validationResult, onApply, handleValidate]);
132
+
133
+ const handleClear = () => {
134
+ setCode("");
135
+ setValidationResult(null);
136
+ setError(null);
137
+ };
138
+
139
+ const handleKeyDown = (e: React.KeyboardEvent) => {
140
+ if (e.key === "Enter") {
141
+ e.preventDefault();
142
+ handleApply();
143
+ }
144
+ };
145
+
146
+ // Format discount value for display
147
+ const getDiscountPreview = () => {
148
+ if (!validationResult?.valid) return null;
149
+
150
+ if (validationResult.estimatedSavings) {
151
+ return `Oszczędzisz ${formatPrice(validationResult.estimatedSavings)}`;
152
+ }
153
+
154
+ if (validationResult.discountType === "PERCENTAGE" && validationResult.discountValue) {
155
+ return `${validationResult.discountValue}% zniżki`;
156
+ }
157
+
158
+ if (validationResult.discountType === "FREE_SHIPPING") {
159
+ return "Darmowa dostawa";
160
+ }
161
+
162
+ return "Kod jest prawidłowy";
163
+ };
164
+
165
+ return (
166
+ <div className={cn("space-y-3", className)}>
167
+ {/* Input row */}
168
+ <div className="flex gap-2">
169
+ <div className="relative flex-1">
170
+ <Tag className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
171
+ <Input
172
+ type="text"
173
+ value={code}
174
+ onChange={(e) => {
175
+ setCode(e.target.value);
176
+ setValidationResult(null);
177
+ setError(null);
178
+ }}
179
+ onBlur={handleValidate}
180
+ onKeyDown={handleKeyDown}
181
+ placeholder={placeholder}
182
+ disabled={disabled || isLoading}
183
+ className={cn(
184
+ "pl-9 pr-8 uppercase",
185
+ validationResult?.valid && "border-green-500 focus-visible:ring-green-500",
186
+ error && "border-destructive focus-visible:ring-destructive"
187
+ )}
188
+ />
189
+ {code && !isLoading && (
190
+ <button
191
+ type="button"
192
+ onClick={handleClear}
193
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
194
+ >
195
+ <X className="h-4 w-4" />
196
+ </button>
197
+ )}
198
+ </div>
199
+ <Button
200
+ type="button"
201
+ onClick={handleApply}
202
+ disabled={disabled || isLoading || !code.trim()}
203
+ variant={validationResult?.valid ? "default" : "secondary"}
204
+ >
205
+ {isLoading ? (
206
+ <Loader2 className="h-4 w-4 animate-spin" />
207
+ ) : validationResult?.valid ? (
208
+ <>
209
+ <Check className="mr-2 h-4 w-4" />
210
+ Zastosuj
211
+ </>
212
+ ) : (
213
+ "Zastosuj"
214
+ )}
215
+ </Button>
216
+ </div>
217
+
218
+ {/* Validation result - success preview */}
219
+ {validationResult?.valid && (
220
+ <Alert className="border-green-500 bg-green-50 dark:bg-green-950/20">
221
+ <Check className="h-4 w-4 text-green-600" />
222
+ <AlertDescription className="text-green-700 dark:text-green-400">
223
+ {getDiscountPreview()}
224
+ </AlertDescription>
225
+ </Alert>
226
+ )}
227
+
228
+ {/* Error message */}
229
+ {error && (
230
+ <Alert variant="destructive">
231
+ <AlertCircle className="h-4 w-4" />
232
+ <AlertDescription>{error}</AlertDescription>
233
+ </Alert>
234
+ )}
235
+
236
+ {/* Minimum order info */}
237
+ {validationResult?.minimumOrderAmount && (
238
+ <p className="text-xs text-muted-foreground">
239
+ Minimalna kwota zamówienia: {formatPrice(validationResult.minimumOrderAmount)}
240
+ </p>
241
+ )}
242
+ </div>
243
+ );
244
+ }
245
+
246
+ export default DiscountCodeInput;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Discount Components
3
+ *
4
+ * Components for discount code entry, validation, and display.
5
+ */
6
+
7
+ export {
8
+ DiscountCodeInput,
9
+ type DiscountCodeInputProps,
10
+ type DiscountValidationResult,
11
+ } from "./discount-code-input";
12
+
13
+ export {
14
+ DiscountBreakdown,
15
+ DiscountSummaryLine,
16
+ type DiscountBreakdownProps,
17
+ type DiscountSummaryLineProps,
18
+ type AppliedDiscount,
19
+ } from "./discount-breakdown";
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * ErrorBoundary - React error boundary with fallback UI
5
+ *
6
+ * Catches JavaScript errors in child components and displays a fallback UI.
7
+ * Includes a retry button to reset the error state.
8
+ *
9
+ * Requirements: 11.1
10
+ */
11
+
12
+ import { Component, type ReactNode } from 'react';
13
+ import { AlertTriangle, RefreshCcw } from 'lucide-react';
14
+ import { Button } from '@/components/ui/button';
15
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
16
+
17
+ interface ErrorBoundaryProps {
18
+ /** Child components to render */
19
+ children: ReactNode;
20
+ /** Optional fallback component */
21
+ fallback?: ReactNode;
22
+ /** Callback when error is caught */
23
+ onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
24
+ /** Custom error title */
25
+ title?: string;
26
+ /** Custom error description */
27
+ description?: string;
28
+ }
29
+
30
+ interface ErrorBoundaryState {
31
+ hasError: boolean;
32
+ error: Error | null;
33
+ }
34
+
35
+ /**
36
+ * ErrorBoundary - Catches and displays errors gracefully
37
+ *
38
+ * Usage:
39
+ * ```tsx
40
+ * <ErrorBoundary>
41
+ * <MyComponent />
42
+ * </ErrorBoundary>
43
+ * ```
44
+ */
45
+ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
46
+ constructor(props: ErrorBoundaryProps) {
47
+ super(props);
48
+ this.state = { hasError: false, error: null };
49
+ }
50
+
51
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
52
+ return { hasError: true, error };
53
+ }
54
+
55
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
56
+ // Log error details in development
57
+ if (process.env.NODE_ENV === 'development') {
58
+ console.error('[ErrorBoundary] Caught error:', error);
59
+ console.error('[ErrorBoundary] Component stack:', errorInfo.componentStack);
60
+ }
61
+
62
+ // Call optional error callback
63
+ this.props.onError?.(error, errorInfo);
64
+ }
65
+
66
+ handleReset = (): void => {
67
+ this.setState({ hasError: false, error: null });
68
+ };
69
+
70
+ render(): ReactNode {
71
+ if (this.state.hasError) {
72
+ // Custom fallback provided
73
+ if (this.props.fallback) {
74
+ return this.props.fallback;
75
+ }
76
+
77
+ // Default fallback UI
78
+ return (
79
+ <div className="flex min-h-[400px] items-center justify-center p-4">
80
+ <Card className="w-full max-w-md">
81
+ <CardHeader className="text-center">
82
+ <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10">
83
+ <AlertTriangle className="h-6 w-6 text-destructive" />
84
+ </div>
85
+ <CardTitle className="text-xl">
86
+ {this.props.title || 'Coś poszło nie tak'}
87
+ </CardTitle>
88
+ <CardDescription>
89
+ {this.props.description || 'Wystąpił nieoczekiwany błąd. Spróbuj odświeżyć stronę.'}
90
+ </CardDescription>
91
+ </CardHeader>
92
+ <CardContent className="flex flex-col gap-3">
93
+ {/* Show error message in development */}
94
+ {process.env.NODE_ENV === 'development' && this.state.error && (
95
+ <div className="rounded-md bg-muted p-3 text-xs">
96
+ <p className="font-medium text-destructive">{this.state.error.message}</p>
97
+ </div>
98
+ )}
99
+ <Button onClick={this.handleReset} className="w-full">
100
+ <RefreshCcw className="mr-2 h-4 w-4" />
101
+ Spróbuj ponownie
102
+ </Button>
103
+ </CardContent>
104
+ </Card>
105
+ </div>
106
+ );
107
+ }
108
+
109
+ return this.props.children;
110
+ }
111
+ }
112
+
113
+ export default ErrorBoundary;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error Components
3
+ *
4
+ * Components for error handling and display.
5
+ */
6
+
7
+ export { ErrorBoundary } from './error-boundary';