@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,321 @@
1
+ "use client";
2
+
3
+ import {
4
+ Gift,
5
+ CreditCard,
6
+ Calendar,
7
+ AlertTriangle,
8
+ CheckCircle,
9
+ } from "lucide-react";
10
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
11
+ import { Badge } from "@/components/ui/badge";
12
+ import { Progress } from "@/components/ui/progress";
13
+ import { cn } from "@/lib/utils";
14
+
15
+ /**
16
+ * Gift card status
17
+ */
18
+ export type GiftCardStatus = "ACTIVE" | "USED" | "EXPIRED" | "DISABLED";
19
+
20
+ /**
21
+ * Gift card data
22
+ */
23
+ export interface GiftCardData {
24
+ id: string;
25
+ maskedCode: string;
26
+ lastCharacters: string;
27
+ status: GiftCardStatus;
28
+ initialAmount: {
29
+ amount: string;
30
+ currencyCode: string;
31
+ };
32
+ balance: {
33
+ amount: string;
34
+ currencyCode: string;
35
+ };
36
+ expiresAt?: string;
37
+ recipientName?: string;
38
+ message?: string;
39
+ }
40
+
41
+ export interface GiftCardBalanceProps {
42
+ giftCard: GiftCardData;
43
+ className?: string;
44
+ }
45
+
46
+ /**
47
+ * Format currency amount
48
+ */
49
+ function formatAmount(amount: string, currencyCode: string): string {
50
+ const num = parseFloat(amount);
51
+ return new Intl.NumberFormat("pl-PL", {
52
+ style: "currency",
53
+ currency: currencyCode,
54
+ }).format(num);
55
+ }
56
+
57
+ /**
58
+ * Format date for display
59
+ */
60
+ function formatDate(dateString: string): string {
61
+ return new Date(dateString).toLocaleDateString("pl-PL", {
62
+ year: "numeric",
63
+ month: "long",
64
+ day: "numeric",
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Get status badge configuration
70
+ */
71
+ function getStatusConfig(status: GiftCardStatus) {
72
+ switch (status) {
73
+ case "ACTIVE":
74
+ return {
75
+ label: "Active",
76
+ variant: "default" as const,
77
+ color: "text-green-600",
78
+ bgColor: "bg-green-100 dark:bg-green-900/30",
79
+ };
80
+ case "USED":
81
+ return {
82
+ label: "Fully Redeemed",
83
+ variant: "secondary" as const,
84
+ color: "text-gray-600",
85
+ bgColor: "bg-gray-100 dark:bg-gray-900/30",
86
+ };
87
+ case "EXPIRED":
88
+ return {
89
+ label: "Expired",
90
+ variant: "destructive" as const,
91
+ color: "text-red-600",
92
+ bgColor: "bg-red-100 dark:bg-red-900/30",
93
+ };
94
+ case "DISABLED":
95
+ return {
96
+ label: "Disabled",
97
+ variant: "secondary" as const,
98
+ color: "text-gray-600",
99
+ bgColor: "bg-gray-100 dark:bg-gray-900/30",
100
+ };
101
+ default:
102
+ return {
103
+ label: "Unknown",
104
+ variant: "secondary" as const,
105
+ color: "text-gray-600",
106
+ bgColor: "bg-gray-100 dark:bg-gray-900/30",
107
+ };
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Check if card is expiring soon (within 30 days)
113
+ */
114
+ function isExpiringSoon(expiresAt?: string): boolean {
115
+ if (!expiresAt) return false;
116
+ const expiryDate = new Date(expiresAt);
117
+ const now = new Date();
118
+ const daysUntilExpiry = Math.ceil(
119
+ (expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
120
+ );
121
+ return daysUntilExpiry > 0 && daysUntilExpiry <= 30;
122
+ }
123
+
124
+ /**
125
+ * GiftCardBalance - Display gift card balance and status
126
+ *
127
+ * Shows:
128
+ * - Current balance with progress bar
129
+ * - Card status (active, used, expired)
130
+ * - Expiration date with warning
131
+ * - Masked code for identification
132
+ *
133
+ * Requirements: R32.1, R32.5
134
+ */
135
+ export function GiftCardBalance({
136
+ giftCard,
137
+ className = "",
138
+ }: GiftCardBalanceProps) {
139
+ const statusConfig = getStatusConfig(giftCard.status);
140
+ const initialAmount = parseFloat(giftCard.initialAmount.amount);
141
+ const currentBalance = parseFloat(giftCard.balance.amount);
142
+ const usedPercentage =
143
+ initialAmount > 0 ? ((initialAmount - currentBalance) / initialAmount) * 100 : 0;
144
+ const expiringSoon = isExpiringSoon(giftCard.expiresAt);
145
+
146
+ return (
147
+ <Card className={cn(className)}>
148
+ <CardHeader className="pb-3">
149
+ <div className="flex items-start justify-between">
150
+ <div className="flex items-center gap-3">
151
+ <div className={cn("rounded-full p-2", statusConfig.bgColor)}>
152
+ <Gift className={cn("h-5 w-5", statusConfig.color)} />
153
+ </div>
154
+ <div>
155
+ <CardTitle className="text-lg flex items-center gap-2">
156
+ Gift Card
157
+ <Badge variant={statusConfig.variant}>{statusConfig.label}</Badge>
158
+ </CardTitle>
159
+ <p className="text-sm text-muted-foreground font-mono mt-1">
160
+ {giftCard.maskedCode}
161
+ </p>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </CardHeader>
166
+
167
+ <CardContent className="space-y-4">
168
+ {/* Balance display */}
169
+ <div className="space-y-2">
170
+ <div className="flex justify-between items-baseline">
171
+ <span className="text-sm text-muted-foreground">Available Balance</span>
172
+ <span className="text-2xl font-bold">
173
+ {formatAmount(
174
+ giftCard.balance.amount,
175
+ giftCard.balance.currencyCode
176
+ )}
177
+ </span>
178
+ </div>
179
+
180
+ {/* Progress bar */}
181
+ <Progress value={100 - usedPercentage} className="h-2" />
182
+
183
+ <div className="flex justify-between text-xs text-muted-foreground">
184
+ <span>
185
+ Used:{" "}
186
+ {formatAmount(
187
+ (initialAmount - currentBalance).toFixed(2),
188
+ giftCard.balance.currencyCode
189
+ )}
190
+ </span>
191
+ <span>
192
+ Initial:{" "}
193
+ {formatAmount(
194
+ giftCard.initialAmount.amount,
195
+ giftCard.initialAmount.currencyCode
196
+ )}
197
+ </span>
198
+ </div>
199
+ </div>
200
+
201
+ {/* Expiration warning */}
202
+ {giftCard.expiresAt && (
203
+ <div
204
+ className={cn(
205
+ "flex items-center gap-2 text-sm p-2 rounded-lg",
206
+ expiringSoon
207
+ ? "bg-yellow-50 dark:bg-yellow-900/20 text-yellow-800 dark:text-yellow-200"
208
+ : "bg-muted/50"
209
+ )}
210
+ >
211
+ {expiringSoon ? (
212
+ <AlertTriangle className="h-4 w-4" />
213
+ ) : (
214
+ <Calendar className="h-4 w-4 text-muted-foreground" />
215
+ )}
216
+ <span>
217
+ {expiringSoon ? "Expires soon: " : "Expires: "}
218
+ {formatDate(giftCard.expiresAt)}
219
+ </span>
220
+ </div>
221
+ )}
222
+
223
+ {/* No expiration */}
224
+ {!giftCard.expiresAt && giftCard.status === "ACTIVE" && (
225
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
226
+ <CheckCircle className="h-4 w-4 text-green-500" />
227
+ <span>No expiration date</span>
228
+ </div>
229
+ )}
230
+
231
+ {/* Recipient/message */}
232
+ {(giftCard.recipientName || giftCard.message) && (
233
+ <div className="pt-3 border-t border-border space-y-2">
234
+ {giftCard.recipientName && (
235
+ <p className="text-sm">
236
+ <span className="text-muted-foreground">To: </span>
237
+ <span className="font-medium">{giftCard.recipientName}</span>
238
+ </p>
239
+ )}
240
+ {giftCard.message && (
241
+ <p className="text-sm text-muted-foreground italic">
242
+ &ldquo;{giftCard.message}&rdquo;
243
+ </p>
244
+ )}
245
+ </div>
246
+ )}
247
+ </CardContent>
248
+ </Card>
249
+ );
250
+ }
251
+
252
+ /**
253
+ * GiftCardBalanceSkeleton - Loading state
254
+ */
255
+ export function GiftCardBalanceSkeleton() {
256
+ return (
257
+ <Card>
258
+ <CardHeader className="pb-3">
259
+ <div className="flex items-start gap-3">
260
+ <div className="h-10 w-10 rounded-full bg-muted animate-pulse" />
261
+ <div className="space-y-2">
262
+ <div className="h-5 w-32 bg-muted animate-pulse rounded" />
263
+ <div className="h-4 w-40 bg-muted animate-pulse rounded" />
264
+ </div>
265
+ </div>
266
+ </CardHeader>
267
+ <CardContent className="space-y-4">
268
+ <div className="space-y-2">
269
+ <div className="h-8 w-24 bg-muted animate-pulse rounded" />
270
+ <div className="h-2 w-full bg-muted animate-pulse rounded" />
271
+ <div className="flex justify-between">
272
+ <div className="h-3 w-16 bg-muted animate-pulse rounded" />
273
+ <div className="h-3 w-16 bg-muted animate-pulse rounded" />
274
+ </div>
275
+ </div>
276
+ </CardContent>
277
+ </Card>
278
+ );
279
+ }
280
+
281
+ /**
282
+ * GiftCardBalanceCompact - Compact inline balance display
283
+ */
284
+ export interface GiftCardBalanceCompactProps {
285
+ balance: {
286
+ amount: string;
287
+ currencyCode: string;
288
+ };
289
+ maskedCode: string;
290
+ status: GiftCardStatus;
291
+ className?: string;
292
+ }
293
+
294
+ export function GiftCardBalanceCompact({
295
+ balance,
296
+ maskedCode,
297
+ status,
298
+ className = "",
299
+ }: GiftCardBalanceCompactProps) {
300
+ const statusConfig = getStatusConfig(status);
301
+
302
+ return (
303
+ <div
304
+ className={cn(
305
+ "flex items-center justify-between p-3 rounded-lg border",
306
+ className
307
+ )}
308
+ >
309
+ <div className="flex items-center gap-2">
310
+ <CreditCard className="h-4 w-4 text-muted-foreground" />
311
+ <span className="font-mono text-sm">{maskedCode}</span>
312
+ <Badge variant={statusConfig.variant} className="text-xs">
313
+ {statusConfig.label}
314
+ </Badge>
315
+ </div>
316
+ <span className="font-semibold">
317
+ {formatAmount(balance.amount, balance.currencyCode)}
318
+ </span>
319
+ </div>
320
+ );
321
+ }
@@ -0,0 +1,309 @@
1
+ "use client";
2
+
3
+ import { useState, useCallback } from "react";
4
+ import { Input } from "@/components/ui/input";
5
+ import { Button } from "@/components/ui/button";
6
+ import { Badge } from "@/components/ui/badge";
7
+ import {
8
+ Gift,
9
+ Check,
10
+ X,
11
+ Loader2,
12
+ AlertCircle,
13
+ CreditCard,
14
+ } from "lucide-react";
15
+ import { cn } from "@/lib/utils";
16
+
17
+ /**
18
+ * Gift card status for display
19
+ */
20
+ type GiftCardStatus = "ACTIVE" | "USED" | "EXPIRED" | "DISABLED";
21
+
22
+ /**
23
+ * Gift card validation result
24
+ */
25
+ export interface GiftCardValidationResult {
26
+ valid: boolean;
27
+ code: string;
28
+ availableBalance?: {
29
+ amount: string;
30
+ currencyCode: string;
31
+ };
32
+ error?: {
33
+ code: string;
34
+ message: string;
35
+ };
36
+ giftCard?: {
37
+ id: string;
38
+ maskedCode: string;
39
+ lastCharacters: string;
40
+ status: GiftCardStatus;
41
+ balance: {
42
+ amount: string;
43
+ currencyCode: string;
44
+ };
45
+ expiresAt?: string;
46
+ };
47
+ }
48
+
49
+ export interface GiftCardInputProps {
50
+ onValidate: (code: string) => Promise<GiftCardValidationResult>;
51
+ onApply: (code: string, validation: GiftCardValidationResult) => void;
52
+ onRemove?: (code: string) => void;
53
+ appliedCodes?: string[];
54
+ checkoutTotal?: number;
55
+ currencyCode?: string;
56
+ disabled?: boolean;
57
+ className?: string;
58
+ }
59
+
60
+ /**
61
+ * Format currency amount for display
62
+ */
63
+ function formatAmount(amount: string, currencyCode: string): string {
64
+ const num = parseFloat(amount);
65
+ return new Intl.NumberFormat("pl-PL", {
66
+ style: "currency",
67
+ currency: currencyCode,
68
+ }).format(num);
69
+ }
70
+
71
+ /**
72
+ * GiftCardInput - Input component for gift card codes
73
+ *
74
+ * Features:
75
+ * - Real-time validation on blur or button click
76
+ * - Shows balance and status feedback
77
+ * - Supports multiple gift cards
78
+ * - Error handling with user-friendly messages
79
+ *
80
+ * Requirements: R32.1, R32.4, R32.5
81
+ */
82
+ export function GiftCardInput({
83
+ onValidate,
84
+ onApply,
85
+ onRemove,
86
+ appliedCodes = [],
87
+ checkoutTotal,
88
+ currencyCode = "PLN",
89
+ disabled = false,
90
+ className = "",
91
+ }: GiftCardInputProps) {
92
+ const [code, setCode] = useState("");
93
+ const [isValidating, setIsValidating] = useState(false);
94
+ const [validation, setValidation] = useState<GiftCardValidationResult | null>(
95
+ null
96
+ );
97
+ const [error, setError] = useState<string | null>(null);
98
+
99
+ /**
100
+ * Format code as user types (XXXX-XXXX-XXXX-XXXX)
101
+ */
102
+ const handleCodeChange = useCallback(
103
+ (e: React.ChangeEvent<HTMLInputElement>) => {
104
+ let value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, "");
105
+
106
+ // Format with dashes every 4 characters
107
+ if (value.length > 0) {
108
+ const parts = value.match(/.{1,4}/g) || [];
109
+ value = parts.join("-");
110
+ }
111
+
112
+ // Limit to 19 characters (16 + 3 dashes)
113
+ if (value.length <= 19) {
114
+ setCode(value);
115
+ setValidation(null);
116
+ setError(null);
117
+ }
118
+ },
119
+ []
120
+ );
121
+
122
+ /**
123
+ * Validate the gift card code
124
+ */
125
+ const handleValidate = useCallback(async () => {
126
+ if (!code || code.length < 4) {
127
+ setError("Please enter a valid gift card code");
128
+ return;
129
+ }
130
+
131
+ // Check if already applied
132
+ if (appliedCodes.includes(code)) {
133
+ setError("This gift card has already been applied");
134
+ return;
135
+ }
136
+
137
+ setIsValidating(true);
138
+ setError(null);
139
+
140
+ try {
141
+ const result = await onValidate(code);
142
+ setValidation(result);
143
+
144
+ if (!result.valid && result.error) {
145
+ setError(result.error.message);
146
+ }
147
+ } catch (err: any) {
148
+ setError(err.message || "Failed to validate gift card");
149
+ } finally {
150
+ setIsValidating(false);
151
+ }
152
+ }, [code, appliedCodes, onValidate]);
153
+
154
+ /**
155
+ * Apply the validated gift card
156
+ */
157
+ const handleApply = useCallback(() => {
158
+ if (validation?.valid) {
159
+ onApply(code, validation);
160
+ setCode("");
161
+ setValidation(null);
162
+ }
163
+ }, [code, validation, onApply]);
164
+
165
+ /**
166
+ * Handle Enter key press
167
+ */
168
+ const handleKeyDown = useCallback(
169
+ (e: React.KeyboardEvent) => {
170
+ if (e.key === "Enter") {
171
+ e.preventDefault();
172
+ if (validation?.valid) {
173
+ handleApply();
174
+ } else {
175
+ handleValidate();
176
+ }
177
+ }
178
+ },
179
+ [validation, handleApply, handleValidate]
180
+ );
181
+
182
+ return (
183
+ <div className={cn("space-y-3", className)}>
184
+ {/* Input row */}
185
+ <div className="flex gap-2">
186
+ <div className="relative flex-1">
187
+ <Gift className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
188
+ <Input
189
+ type="text"
190
+ placeholder="Gift card code (e.g., ABCD-EFGH-IJKL-MNOP)"
191
+ value={code}
192
+ onChange={handleCodeChange}
193
+ onKeyDown={handleKeyDown}
194
+ onBlur={() => code.length >= 16 && handleValidate()}
195
+ disabled={disabled || isValidating}
196
+ className="pl-10 font-mono tracking-wider"
197
+ />
198
+ </div>
199
+ <Button
200
+ type="button"
201
+ variant={validation?.valid ? "default" : "outline"}
202
+ onClick={validation?.valid ? handleApply : handleValidate}
203
+ disabled={disabled || isValidating || !code}
204
+ >
205
+ {isValidating ? (
206
+ <Loader2 className="h-4 w-4 animate-spin" />
207
+ ) : validation?.valid ? (
208
+ <>
209
+ <Check className="mr-2 h-4 w-4" />
210
+ Apply
211
+ </>
212
+ ) : (
213
+ "Check"
214
+ )}
215
+ </Button>
216
+ </div>
217
+
218
+ {/* Validation result */}
219
+ {validation && !error && (
220
+ <div
221
+ className={cn(
222
+ "rounded-lg p-3 text-sm",
223
+ validation.valid
224
+ ? "bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200"
225
+ : "bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200"
226
+ )}
227
+ >
228
+ {validation.valid ? (
229
+ <div className="flex items-center justify-between">
230
+ <div className="flex items-center gap-2">
231
+ <Check className="h-4 w-4" />
232
+ <span>Gift card valid!</span>
233
+ </div>
234
+ {validation.availableBalance && (
235
+ <span className="font-semibold">
236
+ Balance:{" "}
237
+ {formatAmount(
238
+ validation.availableBalance.amount,
239
+ validation.availableBalance.currencyCode
240
+ )}
241
+ </span>
242
+ )}
243
+ </div>
244
+ ) : (
245
+ <div className="flex items-center gap-2">
246
+ <X className="h-4 w-4" />
247
+ <span>{validation.error?.message || "Invalid gift card"}</span>
248
+ </div>
249
+ )}
250
+ </div>
251
+ )}
252
+
253
+ {/* Error message */}
254
+ {error && (
255
+ <div className="flex items-center gap-2 text-sm text-red-600 dark:text-red-400">
256
+ <AlertCircle className="h-4 w-4" />
257
+ <span>{error}</span>
258
+ </div>
259
+ )}
260
+
261
+ {/* Applied gift cards */}
262
+ {appliedCodes.length > 0 && (
263
+ <div className="space-y-2">
264
+ <p className="text-sm font-medium text-muted-foreground">
265
+ Applied gift cards:
266
+ </p>
267
+ <div className="flex flex-wrap gap-2">
268
+ {appliedCodes.map((appliedCode) => (
269
+ <Badge
270
+ key={appliedCode}
271
+ variant="secondary"
272
+ className="flex items-center gap-1 py-1"
273
+ >
274
+ <CreditCard className="h-3 w-3" />
275
+ <span className="font-mono text-xs">
276
+ ****{appliedCode.slice(-4)}
277
+ </span>
278
+ {onRemove && (
279
+ <button
280
+ type="button"
281
+ onClick={() => onRemove(appliedCode)}
282
+ className="ml-1 rounded-full p-0.5 hover:bg-muted"
283
+ disabled={disabled}
284
+ >
285
+ <X className="h-3 w-3" />
286
+ </button>
287
+ )}
288
+ </Badge>
289
+ ))}
290
+ </div>
291
+ </div>
292
+ )}
293
+ </div>
294
+ );
295
+ }
296
+
297
+ /**
298
+ * GiftCardInputSkeleton - Loading state
299
+ */
300
+ export function GiftCardInputSkeleton() {
301
+ return (
302
+ <div className="space-y-3">
303
+ <div className="flex gap-2">
304
+ <div className="flex-1 h-10 bg-muted animate-pulse rounded-md" />
305
+ <div className="w-20 h-10 bg-muted animate-pulse rounded-md" />
306
+ </div>
307
+ </div>
308
+ );
309
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Gift Card Components
3
+ *
4
+ * Components for handling gift card functionality in the storefront.
5
+ *
6
+ * Requirements: R32 - Gift Cards
7
+ */
8
+
9
+ export {
10
+ GiftCardInput,
11
+ GiftCardInputSkeleton,
12
+ type GiftCardInputProps,
13
+ type GiftCardValidationResult,
14
+ } from "./gift-card-input";
15
+
16
+ export {
17
+ GiftCardBalance,
18
+ GiftCardBalanceSkeleton,
19
+ GiftCardBalanceCompact,
20
+ type GiftCardBalanceProps,
21
+ type GiftCardBalanceCompactProps,
22
+ type GiftCardData,
23
+ type GiftCardStatus,
24
+ } from "./gift-card-balance";