@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,125 @@
1
+ /**
2
+ * API Route: Set Authentication Token
3
+ *
4
+ * Sets the customer access token in an httpOnly cookie.
5
+ * This provides security by preventing client-side JavaScript from accessing the token.
6
+ *
7
+ * Security Features:
8
+ * 1. httpOnly cookie - Cannot be accessed via JavaScript (XSS protection)
9
+ * 2. Secure flag - Only sent over HTTPS in production
10
+ * 3. SameSite=Lax - CSRF protection
11
+ * 4. Origin validation - Only accepts requests from same origin
12
+ * 5. Content-Type validation - Only accepts JSON
13
+ *
14
+ * @see lib/auth/cookies.ts - Cookie configuration
15
+ * @see hooks/use-auth.ts - Client-side usage
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * // Client-side usage (via setAuthToken helper)
20
+ * await setAuthToken(customerAccessToken.accessToken);
21
+ * ```
22
+ */
23
+
24
+ import { NextRequest, NextResponse } from "next/server";
25
+ import { AUTH_COOKIE_CONFIG } from "@/lib/auth/cookies";
26
+
27
+ /**
28
+ * Request body schema
29
+ */
30
+ interface SetTokenRequest {
31
+ token: string;
32
+ }
33
+
34
+ /**
35
+ * POST /api/auth/set-token
36
+ *
37
+ * Sets the authentication token in an httpOnly cookie.
38
+ *
39
+ * @param request - Next.js request object
40
+ * @returns Response with Set-Cookie header
41
+ */
42
+ export async function POST(request: NextRequest) {
43
+ try {
44
+ // 1. CSRF Protection: Validate origin
45
+ const origin = request.headers.get("origin");
46
+ const host = request.headers.get("host");
47
+
48
+ // Only allow requests from same origin
49
+ if (origin && !origin.includes(host || "")) {
50
+ return NextResponse.json(
51
+ { error: "Invalid origin" },
52
+ { status: 403 }
53
+ );
54
+ }
55
+
56
+ // 2. Validate Content-Type
57
+ const contentType = request.headers.get("content-type");
58
+ if (!contentType?.includes("application/json")) {
59
+ return NextResponse.json(
60
+ { error: "Content-Type must be application/json" },
61
+ { status: 400 }
62
+ );
63
+ }
64
+
65
+ // 3. Parse and validate request body
66
+ let body: SetTokenRequest;
67
+ try {
68
+ body = await request.json();
69
+ } catch {
70
+ return NextResponse.json(
71
+ { error: "Invalid JSON body" },
72
+ { status: 400 }
73
+ );
74
+ }
75
+
76
+ const { token } = body;
77
+
78
+ if (!token || typeof token !== "string" || token.trim() === "") {
79
+ return NextResponse.json(
80
+ { error: "Token is required and must be a non-empty string" },
81
+ { status: 400 }
82
+ );
83
+ }
84
+
85
+ // 4. Create response with Set-Cookie header
86
+ const response = NextResponse.json(
87
+ { success: true, message: "Token set successfully" },
88
+ { status: 200 }
89
+ );
90
+
91
+ // 5. Set httpOnly cookie
92
+ response.cookies.set({
93
+ name: AUTH_COOKIE_CONFIG.name,
94
+ value: token,
95
+ maxAge: AUTH_COOKIE_CONFIG.maxAge,
96
+ path: AUTH_COOKIE_CONFIG.path,
97
+ sameSite: AUTH_COOKIE_CONFIG.sameSite,
98
+ secure: AUTH_COOKIE_CONFIG.secure,
99
+ httpOnly: AUTH_COOKIE_CONFIG.httpOnly,
100
+ });
101
+
102
+ return response;
103
+ } catch (error) {
104
+ console.error("Error setting auth token:", error);
105
+ return NextResponse.json(
106
+ { error: "Internal server error" },
107
+ { status: 500 }
108
+ );
109
+ }
110
+ }
111
+
112
+ /**
113
+ * OPTIONS /api/auth/set-token
114
+ *
115
+ * Handle preflight requests for CORS.
116
+ */
117
+ export async function OPTIONS() {
118
+ return new NextResponse(null, {
119
+ status: 204,
120
+ headers: {
121
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
122
+ "Access-Control-Allow-Headers": "Content-Type",
123
+ },
124
+ });
125
+ }
@@ -0,0 +1,131 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import Link from "next/link";
5
+ import { useMutation } from "@tanstack/react-query";
6
+ import { getGraphQLClient } from "@/lib/graphql/client";
7
+ import { CustomerPasswordRecoverDocument } from "@/generated/graphql";
8
+ import { Button } from "@/components/ui/button";
9
+ import { Input } from "@/components/ui/input";
10
+ import { ArrowLeft } from "lucide-react";
11
+
12
+ export default function ForgotPasswordPage() {
13
+ const [email, setEmail] = useState("");
14
+ const [isSubmitted, setIsSubmitted] = useState(false);
15
+ const [error, setError] = useState("");
16
+
17
+ const client = getGraphQLClient();
18
+
19
+ const recoverMutation = useMutation({
20
+ mutationFn: async (email: string) => {
21
+ return client.request(CustomerPasswordRecoverDocument, { email });
22
+ },
23
+ onSuccess: () => {
24
+ setIsSubmitted(true);
25
+ },
26
+ onError: () => {
27
+ setError("An error occurred. Please try again.");
28
+ },
29
+ });
30
+
31
+ const handleSubmit = async (e: React.FormEvent) => {
32
+ e.preventDefault();
33
+ setError("");
34
+ recoverMutation.mutate(email);
35
+ };
36
+
37
+ if (isSubmitted) {
38
+ return (
39
+ <div className="container mx-auto px-4 py-16">
40
+ <div className="mx-auto max-w-md text-center">
41
+ <div className="mb-8">
42
+ <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-100">
43
+ <svg
44
+ className="h-8 w-8 text-green-600"
45
+ fill="none"
46
+ viewBox="0 0 24 24"
47
+ stroke="currentColor"
48
+ >
49
+ <path
50
+ strokeLinecap="round"
51
+ strokeLinejoin="round"
52
+ strokeWidth={2}
53
+ d="M5 13l4 4L19 7"
54
+ />
55
+ </svg>
56
+ </div>
57
+ <h1 className="text-3xl font-bold text-foreground">Check Your Email</h1>
58
+ <p className="mt-2 text-muted-foreground">
59
+ We've sent password reset instructions to {email}
60
+ </p>
61
+ </div>
62
+
63
+ <div className="space-y-4">
64
+ <p className="text-sm text-muted-foreground">
65
+ Didn't receive the email? Check your spam folder or try again.
66
+ </p>
67
+ <Button variant="outline" onClick={() => setIsSubmitted(false)}>
68
+ Try Another Email
69
+ </Button>
70
+ <div>
71
+ <Link
72
+ href="/auth/login"
73
+ className="text-sm text-primary hover:underline"
74
+ >
75
+ Back to Sign In
76
+ </Link>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <div className="container mx-auto px-4 py-16">
86
+ <div className="mx-auto max-w-md">
87
+ <Link
88
+ href="/auth/login"
89
+ className="mb-8 inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground"
90
+ >
91
+ <ArrowLeft className="h-4 w-4" />
92
+ Back to Sign In
93
+ </Link>
94
+
95
+ <div className="mb-8">
96
+ <h1 className="text-3xl font-bold text-foreground">Reset Password</h1>
97
+ <p className="mt-2 text-muted-foreground">
98
+ Enter your email address and we'll send you instructions to reset your password.
99
+ </p>
100
+ </div>
101
+
102
+ <form onSubmit={handleSubmit} className="space-y-6">
103
+ {error && (
104
+ <div className="rounded-lg border border-destructive bg-destructive/10 p-4 text-sm text-destructive">
105
+ {error}
106
+ </div>
107
+ )}
108
+
109
+ <div className="space-y-2">
110
+ <label htmlFor="email" className="text-sm font-medium text-foreground">
111
+ Email
112
+ </label>
113
+ <Input
114
+ id="email"
115
+ type="email"
116
+ value={email}
117
+ onChange={(e) => setEmail(e.target.value)}
118
+ placeholder="you@example.com"
119
+ required
120
+ autoComplete="email"
121
+ />
122
+ </div>
123
+
124
+ <Button type="submit" className="w-full" disabled={recoverMutation.isPending}>
125
+ {recoverMutation.isPending ? "Sending..." : "Send Reset Instructions"}
126
+ </Button>
127
+ </form>
128
+ </div>
129
+ </div>
130
+ );
131
+ }
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import { Suspense } from "react";
4
+ import { LoginForm } from "@/components/auth/login-form";
5
+ import { Spinner } from "@/components/ui/spinner";
6
+
7
+ export default function LoginPage() {
8
+ return (
9
+ <div className="container mx-auto px-4 py-16">
10
+ <div className="mx-auto max-w-md">
11
+ <div className="mb-8 text-center">
12
+ <h1 className="text-3xl font-bold text-foreground">Sign In</h1>
13
+ <p className="mt-2 text-muted-foreground">
14
+ Sign in to your account to continue
15
+ </p>
16
+ </div>
17
+
18
+ <Suspense fallback={<Spinner />}>
19
+ <LoginForm />
20
+ </Suspense>
21
+ </div>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import { RegisterForm } from "@/components/auth/register-form";
4
+
5
+ export default function RegisterPage() {
6
+ return (
7
+ <div className="container mx-auto px-4 py-16">
8
+ <div className="mx-auto max-w-md">
9
+ <div className="mb-8 text-center">
10
+ <h1 className="text-3xl font-bold text-foreground">Create Account</h1>
11
+ <p className="mt-2 text-muted-foreground">
12
+ Sign up to start shopping
13
+ </p>
14
+ </div>
15
+
16
+ <RegisterForm />
17
+ </div>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Blog Post Detail Page
3
+ *
4
+ * Displays full blog post content with author info and related posts.
5
+ */
6
+
7
+ import { Metadata } from 'next';
8
+ import Image from 'next/image';
9
+ import Link from 'next/link';
10
+ import { notFound } from 'next/navigation';
11
+ import { Calendar, Clock, Eye, User, ChevronLeft, Share2 } from 'lucide-react';
12
+ import { Breadcrumbs } from '@/components/layout/breadcrumbs';
13
+ import { Badge } from '@/components/ui/badge';
14
+ import { Button } from '@/components/ui/button';
15
+ import { Card, CardContent } from '@/components/ui/card';
16
+ import { Separator } from '@/components/ui/separator';
17
+ import { BlogCard } from '@/components/blog/blog-card';
18
+ import { BlogSidebar } from '@/components/blog/blog-sidebar';
19
+
20
+ // Mock data - replace with actual GraphQL fetch
21
+ const mockPost = {
22
+ id: '1',
23
+ title: 'Jak wybrać idealny produkt dla siebie',
24
+ slug: 'jak-wybrac-idealny-produkt',
25
+ excerpt: 'Praktyczny poradnik, który pomoże Ci podjąć najlepszą decyzję zakupową.',
26
+ content: `
27
+ <h2>Wprowadzenie</h2>
28
+ <p>Wybór odpowiedniego produktu może być trudny, szczególnie gdy rynek oferuje tak wiele opcji. W tym artykule przedstawimy praktyczne wskazówki, które pomogą Ci podjąć najlepszą decyzję.</p>
29
+
30
+ <h2>Krok 1: Określ swoje potrzeby</h2>
31
+ <p>Zanim zaczniesz przeglądać oferty, zastanów się, czego naprawdę potrzebujesz. Sporządź listę funkcji, które są dla Ciebie niezbędne.</p>
32
+
33
+ <h2>Krok 2: Ustal budżet</h2>
34
+ <p>Określenie górnego limitu wydatków pomoże Ci zawęzić wybór i uniknąć impulsywnych zakupów.</p>
35
+
36
+ <h2>Krok 3: Przeczytaj opinie</h2>
37
+ <p>Opinie innych klientów to cenne źródło informacji. Zwróć uwagę zarówno na pozytywne, jak i negatywne recenzje.</p>
38
+
39
+ <h2>Podsumowanie</h2>
40
+ <p>Świadomy wybór produktu wymaga czasu i researchu, ale dzięki temu unikniesz rozczarowania i będziesz zadowolony z zakupu.</p>
41
+ `,
42
+ contentType: 'html',
43
+ featuredImage: null,
44
+ author: {
45
+ id: '1',
46
+ name: 'Jan Kowalski',
47
+ bio: 'Ekspert ds. produktów z 10-letnim doświadczeniem w branży.',
48
+ avatar: null,
49
+ },
50
+ category: {
51
+ id: '1',
52
+ name: 'Poradniki',
53
+ slug: 'poradniki',
54
+ },
55
+ tags: [
56
+ { id: '1', name: 'Tips', slug: 'tips', postCount: 5 },
57
+ { id: '2', name: 'Tutorial', slug: 'tutorial', postCount: 3 },
58
+ ],
59
+ status: 'PUBLISHED',
60
+ publishedAt: '2024-01-15T10:00:00Z',
61
+ readingTime: 5,
62
+ viewCount: 120,
63
+ commentCount: 8,
64
+ allowComments: true,
65
+ isFeatured: true,
66
+ seo: {
67
+ title: 'Jak wybrać idealny produkt - Poradnik',
68
+ description: 'Praktyczny poradnik zakupowy. Dowiedz się, jak wybrać najlepszy produkt.',
69
+ },
70
+ createdAt: '2024-01-10T10:00:00Z',
71
+ updatedAt: '2024-01-15T10:00:00Z',
72
+ };
73
+
74
+ const mockRelatedPosts = [
75
+ {
76
+ id: '2',
77
+ title: 'Trendy na rok 2024',
78
+ slug: 'trendy-2024',
79
+ excerpt: 'Odkryj, co będzie modne w nadchodzącym roku.',
80
+ featuredImage: null,
81
+ author: {
82
+ id: '1',
83
+ name: 'Jan Kowalski',
84
+ avatar: null,
85
+ },
86
+ category: {
87
+ id: '2',
88
+ name: 'Trendy',
89
+ slug: 'trendy',
90
+ },
91
+ publishedAt: '2024-01-10T10:00:00Z',
92
+ readingTime: 8,
93
+ viewCount: 250,
94
+ isFeatured: false,
95
+ },
96
+ ];
97
+
98
+ interface BlogPostPageProps {
99
+ params: Promise<{ slug: string }>;
100
+ }
101
+
102
+ export async function generateMetadata({
103
+ params,
104
+ }: BlogPostPageProps): Promise<Metadata> {
105
+ const { slug } = await params;
106
+ // In production, fetch post data here
107
+ const post = mockPost;
108
+
109
+ if (!post) {
110
+ return {
111
+ title: 'Nie znaleziono wpisu',
112
+ };
113
+ }
114
+
115
+ return {
116
+ title: post.seo?.title || post.title,
117
+ description: post.seo?.description || post.excerpt,
118
+ };
119
+ }
120
+
121
+ export default async function BlogPostPage({ params }: BlogPostPageProps) {
122
+ const { slug } = await params;
123
+
124
+ // In production, fetch post data here
125
+ const post = mockPost;
126
+ const relatedPosts = mockRelatedPosts;
127
+
128
+ if (!post || post.slug !== slug) {
129
+ notFound();
130
+ }
131
+
132
+ const formatDate = (dateString: string) => {
133
+ return new Intl.DateTimeFormat('pl-PL', {
134
+ day: 'numeric',
135
+ month: 'long',
136
+ year: 'numeric',
137
+ }).format(new Date(dateString));
138
+ };
139
+
140
+ return (
141
+ <article className="container py-8">
142
+ <Breadcrumbs
143
+ items={[
144
+ { label: 'Strona główna', href: '/' },
145
+ { label: 'Blog', href: '/blog' },
146
+ ...(post.category
147
+ ? [{ label: post.category.name, href: `/blog/category/${post.category.slug}` }]
148
+ : []),
149
+ { label: post.title },
150
+ ]}
151
+ />
152
+
153
+ {/* Back Link */}
154
+ <Link
155
+ href="/blog"
156
+ className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground mt-4 mb-6"
157
+ >
158
+ <ChevronLeft className="h-4 w-4 mr-1" />
159
+ Wróć do bloga
160
+ </Link>
161
+
162
+ <div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
163
+ {/* Main Content */}
164
+ <div className="lg:col-span-3">
165
+ {/* Header */}
166
+ <header className="mb-8">
167
+ {post.category && (
168
+ <Link
169
+ href={`/blog/category/${post.category.slug}`}
170
+ className="inline-block mb-4"
171
+ >
172
+ <Badge variant="secondary">{post.category.name}</Badge>
173
+ </Link>
174
+ )}
175
+
176
+ <h1 className="text-4xl font-bold tracking-tight mb-4">
177
+ {post.title}
178
+ </h1>
179
+
180
+ {post.excerpt && (
181
+ <p className="text-xl text-muted-foreground mb-6">{post.excerpt}</p>
182
+ )}
183
+
184
+ {/* Meta */}
185
+ <div className="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
186
+ <div className="flex items-center gap-2">
187
+ {post.author.avatar ? (
188
+ <Image
189
+ src={post.author.avatar.url}
190
+ alt={post.author.name}
191
+ width={32}
192
+ height={32}
193
+ className="rounded-full"
194
+ />
195
+ ) : (
196
+ <div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center">
197
+ <User className="h-4 w-4" />
198
+ </div>
199
+ )}
200
+ <span className="font-medium text-foreground">
201
+ {post.author.name}
202
+ </span>
203
+ </div>
204
+ {post.publishedAt && (
205
+ <div className="flex items-center gap-1">
206
+ <Calendar className="h-4 w-4" />
207
+ <span>{formatDate(post.publishedAt)}</span>
208
+ </div>
209
+ )}
210
+ <div className="flex items-center gap-1">
211
+ <Clock className="h-4 w-4" />
212
+ <span>{post.readingTime} min czytania</span>
213
+ </div>
214
+ <div className="flex items-center gap-1">
215
+ <Eye className="h-4 w-4" />
216
+ <span>{post.viewCount} wyświetleń</span>
217
+ </div>
218
+ </div>
219
+ </header>
220
+
221
+ {/* Featured Image */}
222
+ {post.featuredImage && (
223
+ <div className="relative aspect-[16/9] mb-8 rounded-lg overflow-hidden bg-muted">
224
+ <Image
225
+ src={post.featuredImage.url}
226
+ alt={post.featuredImage.altText || post.title}
227
+ fill
228
+ className="object-cover"
229
+ priority
230
+ />
231
+ </div>
232
+ )}
233
+
234
+ {/* Content */}
235
+ <div
236
+ className="prose prose-lg max-w-none dark:prose-invert mb-8"
237
+ dangerouslySetInnerHTML={{ __html: post.content }}
238
+ />
239
+
240
+ {/* Tags */}
241
+ {post.tags.length > 0 && (
242
+ <div className="flex flex-wrap gap-2 mb-8">
243
+ {post.tags.map((tag) => (
244
+ <Link key={tag.id} href={`/blog/tag/${tag.slug}`}>
245
+ <Badge variant="outline" className="hover:bg-muted">
246
+ #{tag.name}
247
+ </Badge>
248
+ </Link>
249
+ ))}
250
+ </div>
251
+ )}
252
+
253
+ <Separator className="my-8" />
254
+
255
+ {/* Author Box */}
256
+ <Card className="mb-8">
257
+ <CardContent className="p-6">
258
+ <div className="flex items-start gap-4">
259
+ {post.author.avatar ? (
260
+ <Image
261
+ src={post.author.avatar.url}
262
+ alt={post.author.name}
263
+ width={64}
264
+ height={64}
265
+ className="rounded-full"
266
+ />
267
+ ) : (
268
+ <div className="w-16 h-16 rounded-full bg-muted flex items-center justify-center flex-shrink-0">
269
+ <User className="h-8 w-8 text-muted-foreground" />
270
+ </div>
271
+ )}
272
+ <div>
273
+ <h3 className="font-semibold">O autorze</h3>
274
+ <p className="font-medium text-lg">{post.author.name}</p>
275
+ {post.author.bio && (
276
+ <p className="text-muted-foreground mt-2">{post.author.bio}</p>
277
+ )}
278
+ </div>
279
+ </div>
280
+ </CardContent>
281
+ </Card>
282
+
283
+ {/* Related Posts */}
284
+ {relatedPosts.length > 0 && (
285
+ <section>
286
+ <h2 className="text-2xl font-bold mb-6">Powiązane artykuły</h2>
287
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
288
+ {relatedPosts.map((relatedPost) => (
289
+ <BlogCard key={relatedPost.id} post={relatedPost} />
290
+ ))}
291
+ </div>
292
+ </section>
293
+ )}
294
+ </div>
295
+
296
+ {/* Sidebar */}
297
+ <div className="lg:col-span-1">
298
+ <div className="sticky top-24">
299
+ {/* Share */}
300
+ <Card className="mb-6">
301
+ <CardContent className="p-4">
302
+ <h3 className="font-semibold mb-3 flex items-center gap-2">
303
+ <Share2 className="h-4 w-4" />
304
+ Udostępnij
305
+ </h3>
306
+ <div className="flex gap-2">
307
+ <Button variant="outline" size="sm" className="flex-1">
308
+ Facebook
309
+ </Button>
310
+ <Button variant="outline" size="sm" className="flex-1">
311
+ Twitter
312
+ </Button>
313
+ </div>
314
+ </CardContent>
315
+ </Card>
316
+
317
+ <BlogSidebar recentPosts={relatedPosts.slice(0, 3)} />
318
+ </div>
319
+ </div>
320
+ </div>
321
+ </article>
322
+ );
323
+ }