@doswiftly/cli 0.1.18 → 0.1.20

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 (277) hide show
  1. package/README.md +23 -323
  2. package/dist/commands/check.js +1 -1
  3. package/dist/commands/check.js.map +1 -1
  4. package/dist/commands/deploy.d.ts +20 -0
  5. package/dist/commands/deploy.d.ts.map +1 -1
  6. package/dist/commands/deploy.js +249 -17
  7. package/dist/commands/deploy.js.map +1 -1
  8. package/dist/commands/doctor.js +3 -3
  9. package/dist/commands/doctor.js.map +1 -1
  10. package/dist/commands/init.js +4 -4
  11. package/dist/commands/sdk.js +5 -5
  12. package/dist/commands/sdk.js.map +1 -1
  13. package/dist/commands/template.js +4 -4
  14. package/dist/commands/template.js.map +1 -1
  15. package/dist/commands/types.js +5 -5
  16. package/dist/commands/types.js.map +1 -1
  17. package/dist/commands/verify.js +2 -2
  18. package/dist/commands/verify.js.map +1 -1
  19. package/dist/lib/package-manager.d.ts +1 -1
  20. package/dist/lib/package-manager.js +1 -1
  21. package/package.json +4 -4
  22. package/templates/storefront-minimal/.github/workflows/build-template.yml +10 -0
  23. package/templates/storefront-minimal/wrangler.toml +11 -0
  24. package/templates/storefront-nextjs/.github/workflows/build-template.yml +10 -0
  25. package/templates/storefront-nextjs/README.md +16 -12
  26. package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
  27. package/templates/storefront-nextjs/app/account/page.tsx +2 -2
  28. package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
  29. package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
  30. package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
  31. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
  32. package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
  33. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
  34. package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
  35. package/templates/storefront-nextjs/app/page.tsx +1 -1
  36. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
  37. package/templates/storefront-nextjs/app/products/page.tsx +2 -2
  38. package/templates/storefront-nextjs/app/search/page.tsx +1 -1
  39. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
  40. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
  41. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
  42. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
  43. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
  44. package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
  45. package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
  46. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
  47. package/templates/storefront-nextjs/components/providers.tsx +1 -1
  48. package/templates/storefront-nextjs/lib/currency.tsx +3 -3
  49. package/templates/storefront-nextjs/lib/format.ts +1 -1
  50. package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
  51. package/templates/storefront-nextjs/package.dev.json +1 -1
  52. package/templates/storefront-nextjs/package.json +1 -1
  53. package/templates/storefront-nextjs/package.json.template +1 -1
  54. package/templates/storefront-nextjs/wrangler.toml +11 -0
  55. package/templates/storefront-nextjs-shadcn/.github/workflows/build-template.yml +10 -0
  56. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
  57. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
  58. package/templates/storefront-nextjs-shadcn/CLAUDE.md +172 -35
  59. package/templates/storefront-nextjs-shadcn/README.md +29 -162
  60. package/templates/storefront-nextjs-shadcn/app/{about → [locale]/about}/page.tsx +17 -14
  61. package/templates/storefront-nextjs-shadcn/app/[locale]/account/addresses/page.tsx +226 -0
  62. package/templates/storefront-nextjs-shadcn/app/[locale]/account/error.tsx +46 -0
  63. package/templates/storefront-nextjs-shadcn/app/[locale]/account/loading.tsx +19 -0
  64. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/loyalty/page.tsx +89 -193
  65. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/loading.tsx +60 -0
  66. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/page.tsx +119 -0
  67. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/orders/[id]/tracking/page.tsx +27 -25
  68. package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/page.tsx +101 -0
  69. package/templates/storefront-nextjs-shadcn/app/{account → [locale]/account}/page.tsx +9 -7
  70. package/templates/storefront-nextjs-shadcn/app/[locale]/account/settings/page.tsx +208 -0
  71. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/forgot-password/page.tsx +24 -17
  72. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/login/page.tsx +5 -2
  73. package/templates/storefront-nextjs-shadcn/app/{auth → [locale]/auth}/register/page.tsx +5 -2
  74. package/templates/storefront-nextjs-shadcn/app/[locale]/blog/[slug]/loading.tsx +17 -0
  75. package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/[slug]/page.tsx +44 -3
  76. package/templates/storefront-nextjs-shadcn/app/[locale]/blog/loading.tsx +19 -0
  77. package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/page.tsx +2 -1
  78. package/templates/storefront-nextjs-shadcn/app/[locale]/cart/loading.tsx +26 -0
  79. package/templates/storefront-nextjs-shadcn/app/{cart → [locale]/cart}/page.tsx +20 -13
  80. package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/category-products-client.tsx +58 -0
  81. package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/loading.tsx +32 -0
  82. package/templates/storefront-nextjs-shadcn/app/[locale]/categories/[slug]/page.tsx +95 -0
  83. package/templates/storefront-nextjs-shadcn/app/{categories → [locale]/categories}/page.tsx +21 -12
  84. package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/error.tsx +43 -0
  85. package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/loading.tsx +31 -0
  86. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/page.tsx +334 -253
  87. package/templates/storefront-nextjs-shadcn/app/{checkout → [locale]/checkout}/success/[orderId]/page.tsx +36 -34
  88. package/templates/storefront-nextjs-shadcn/app/[locale]/collections/[handle]/loading.tsx +19 -0
  89. package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/[handle]/page.tsx +6 -4
  90. package/templates/storefront-nextjs-shadcn/app/[locale]/collections/loading.tsx +18 -0
  91. package/templates/storefront-nextjs-shadcn/app/{collections → [locale]/collections}/page.tsx +20 -12
  92. package/templates/storefront-nextjs-shadcn/app/{contact → [locale]/contact}/page.tsx +24 -21
  93. package/templates/storefront-nextjs-shadcn/app/{error.tsx → [locale]/error.tsx} +13 -8
  94. package/templates/storefront-nextjs-shadcn/app/[locale]/layout.tsx +92 -0
  95. package/templates/storefront-nextjs-shadcn/app/{not-found.tsx → [locale]/not-found.tsx} +13 -18
  96. package/templates/storefront-nextjs-shadcn/app/{page.tsx → [locale]/page.tsx} +8 -4
  97. package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/error.tsx +43 -0
  98. package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/loading.tsx +29 -0
  99. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/page.tsx +17 -14
  100. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/[slug]/product-client.tsx +18 -62
  101. package/templates/storefront-nextjs-shadcn/app/[locale]/products/loading.tsx +32 -0
  102. package/templates/storefront-nextjs-shadcn/app/{products → [locale]/products}/page.tsx +6 -3
  103. package/templates/storefront-nextjs-shadcn/app/[locale]/products/products-client.tsx +450 -0
  104. package/templates/storefront-nextjs-shadcn/app/[locale]/search/loading.tsx +18 -0
  105. package/templates/storefront-nextjs-shadcn/app/{wishlist → [locale]/wishlist}/page.tsx +27 -28
  106. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
  107. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
  108. package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
  109. package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
  110. package/templates/storefront-nextjs-shadcn/app/layout.tsx +8 -35
  111. package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
  112. package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +25 -20
  113. package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +11 -10
  114. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
  115. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +17 -13
  116. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +42 -30
  117. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
  118. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +18 -16
  119. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +37 -58
  120. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +85 -66
  121. package/templates/storefront-nextjs-shadcn/components/blog/blog-card.tsx +1 -1
  122. package/templates/storefront-nextjs-shadcn/components/blog/blog-sidebar.tsx +1 -1
  123. package/templates/storefront-nextjs-shadcn/components/brand/brand-card.tsx +1 -1
  124. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +10 -6
  125. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +9 -6
  126. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +8 -6
  127. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
  128. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +10 -8
  129. package/templates/storefront-nextjs-shadcn/components/cart/promo-code-input.tsx +8 -5
  130. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +38 -20
  131. package/templates/storefront-nextjs-shadcn/components/checkout/payment-method-card.tsx +15 -25
  132. package/templates/storefront-nextjs-shadcn/components/checkout/payment-step.tsx +10 -8
  133. package/templates/storefront-nextjs-shadcn/components/checkout/tax-breakdown.tsx +9 -6
  134. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +7 -5
  135. package/templates/storefront-nextjs-shadcn/components/commerce/pagination.tsx +8 -5
  136. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +6 -4
  137. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +10 -9
  138. package/templates/storefront-nextjs-shadcn/components/common/category-card.tsx +1 -1
  139. package/templates/storefront-nextjs-shadcn/components/common/collection-card.tsx +1 -1
  140. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
  141. package/templates/storefront-nextjs-shadcn/components/common/social-share.tsx +9 -6
  142. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +22 -12
  143. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +18 -15
  144. package/templates/storefront-nextjs-shadcn/components/error/error-boundary.tsx +53 -28
  145. package/templates/storefront-nextjs-shadcn/components/filters/dynamic-attribute-filters.tsx +7 -5
  146. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
  147. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-balance.tsx +19 -15
  148. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +13 -10
  149. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +10 -6
  150. package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
  151. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +3 -13
  152. package/templates/storefront-nextjs-shadcn/components/home/featured-products.tsx +12 -8
  153. package/templates/storefront-nextjs-shadcn/components/home/hero-section.tsx +13 -8
  154. package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
  155. package/templates/storefront-nextjs-shadcn/components/home/newsletter-signup.tsx +10 -8
  156. package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
  157. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +41 -16
  158. package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
  159. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +7 -4
  160. package/templates/storefront-nextjs-shadcn/components/layout/footer.tsx +24 -23
  161. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +52 -34
  162. package/templates/storefront-nextjs-shadcn/components/layout/language-switcher.tsx +54 -0
  163. package/templates/storefront-nextjs-shadcn/components/layout/mobile-menu.tsx +33 -30
  164. package/templates/storefront-nextjs-shadcn/components/layout/navigation.tsx +27 -24
  165. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
  166. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
  167. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +32 -42
  168. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
  169. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
  170. package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
  171. package/templates/storefront-nextjs-shadcn/components/product/add-to-cart-button.tsx +6 -14
  172. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +4 -2
  173. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +72 -0
  174. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +87 -0
  175. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +140 -0
  176. package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
  177. package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
  178. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +11 -37
  179. package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
  180. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +179 -124
  181. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
  182. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +3 -7
  183. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
  184. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
  185. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +44 -19
  186. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
  187. package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
  188. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
  189. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +26 -34
  190. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
  191. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
  192. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
  193. package/templates/storefront-nextjs-shadcn/components/providers/language-sync-provider.tsx +27 -0
  194. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +63 -0
  195. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
  196. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
  197. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +59 -72
  198. package/templates/storefront-nextjs-shadcn/components/search/search-bar.tsx +7 -4
  199. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
  200. package/templates/storefront-nextjs-shadcn/components/shipping/shipping-method-selector.tsx +12 -9
  201. package/templates/storefront-nextjs-shadcn/components/ui/empty-state.tsx +23 -12
  202. package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
  203. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
  204. package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
  205. package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
  206. package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
  207. package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
  208. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-button.tsx +7 -4
  209. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +4 -2
  210. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-item.tsx +2 -10
  211. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +13387 -0
  212. package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
  213. package/templates/storefront-nextjs-shadcn/hooks/index.ts +3 -0
  214. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
  215. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
  216. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +34 -229
  217. package/templates/storefront-nextjs-shadcn/hooks/use-cart-di.ts +67 -0
  218. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +16 -12
  219. package/templates/storefront-nextjs-shadcn/i18n/navigation.ts +12 -0
  220. package/templates/storefront-nextjs-shadcn/i18n/request.ts +17 -0
  221. package/templates/storefront-nextjs-shadcn/i18n/routing.ts +17 -0
  222. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
  223. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
  224. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +33 -0
  225. package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
  226. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +720 -632
  227. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +88 -0
  228. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +132 -182
  229. package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
  230. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
  231. package/templates/storefront-nextjs-shadcn/messages/en.json +869 -0
  232. package/templates/storefront-nextjs-shadcn/messages/pl.json +869 -0
  233. package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
  234. package/templates/storefront-nextjs-shadcn/next.config.ts +6 -5
  235. package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
  236. package/templates/storefront-nextjs-shadcn/package.json +14 -14
  237. package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
  238. package/templates/storefront-nextjs-shadcn/proxy.ts +115 -47
  239. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +24 -56
  240. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
  241. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
  242. package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
  243. package/templates/storefront-nextjs-shadcn/wrangler.toml +11 -0
  244. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
  245. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
  246. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
  247. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
  248. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
  249. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
  250. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
  251. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
  252. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +0 -215
  253. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +0 -128
  254. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +0 -80
  255. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +0 -171
  256. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +0 -78
  257. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +0 -192
  258. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
  259. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
  260. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
  261. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
  262. package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
  263. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
  264. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
  265. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
  266. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
  267. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
  268. package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
  269. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
  270. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
  271. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
  272. /package/templates/storefront-nextjs-shadcn/app/{blog → [locale]/blog}/page.tsx +0 -0
  273. /package/templates/storefront-nextjs-shadcn/app/{brands → [locale]/brands}/[slug]/page.tsx +0 -0
  274. /package/templates/storefront-nextjs-shadcn/app/{returns → [locale]/returns}/page.tsx +0 -0
  275. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/page.tsx +0 -0
  276. /package/templates/storefront-nextjs-shadcn/app/{search → [locale]/search}/search-client.tsx +0 -0
  277. /package/templates/storefront-nextjs-shadcn/app/{shipping → [locale]/shipping}/page.tsx +0 -0
@@ -1,15 +1,24 @@
1
1
  "use client";
2
2
 
3
- import { useState, useMemo } from "react";
4
- import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion";
5
- import { Input } from "@/components/ui/input";
3
+ import { useMemo } from "react";
4
+ import { useTranslations } from "next-intl";
5
+ import { Checkbox } from "@/components/ui/checkbox";
6
+ import { Label } from "@/components/ui/label";
7
+ import {
8
+ Accordion,
9
+ AccordionItem,
10
+ AccordionTrigger,
11
+ AccordionContent,
12
+ } from "@/components/ui/accordion";
6
13
  import { Button } from "@/components/ui/button";
14
+ import { FilterPriceRange } from "./filter-price-range";
7
15
  import { cn } from "@/lib/utils";
8
16
 
9
17
  export interface FilterOption {
10
18
  label: string;
11
19
  value: string;
12
20
  count?: number;
21
+ colorHex?: string;
13
22
  }
14
23
 
15
24
  export interface FilterGroup {
@@ -19,6 +28,7 @@ export interface FilterGroup {
19
28
  options?: FilterOption[];
20
29
  min?: number;
21
30
  max?: number;
31
+ currency?: string;
22
32
  }
23
33
 
24
34
  export interface ProductFiltersProps {
@@ -30,20 +40,14 @@ export interface ProductFiltersProps {
30
40
  }
31
41
 
32
42
  /**
33
- * ProductFilters - Filter products by price, category, attributes
34
- *
35
- * @example
36
- * ```tsx
37
- * const [filters, setFilters] = useState({});
38
- *
39
- * <ProductFilters
40
- * filters={filterGroups}
41
- * selectedFilters={filters}
42
- * onFilterChange={(id, values) => {
43
- * setFilters({ ...filters, [id]: values });
44
- * }}
45
- * />
46
- * ```
43
+ * ProductFilters - Accordion-based filter panel
44
+ *
45
+ * Supports:
46
+ * - Checkbox groups (categories, attributes) with Radix Checkbox
47
+ * - Color swatches with visual selection
48
+ * - Price range with dual-thumb Radix Slider
49
+ * - Filter count badges in accordion triggers
50
+ * - Clear all button
47
51
  */
48
52
  export function ProductFilters({
49
53
  filters,
@@ -52,11 +56,7 @@ export function ProductFilters({
52
56
  onClearAll,
53
57
  className,
54
58
  }: ProductFiltersProps) {
55
- const [priceRange, setPriceRange] = useState<{ min: string; max: string }>({
56
- min: "",
57
- max: "",
58
- });
59
-
59
+ const t = useTranslations("product");
60
60
  const handleCheckboxChange = (filterId: string, value: string) => {
61
61
  const current = selectedFilters[filterId] || [];
62
62
  const updated = current.includes(value)
@@ -65,20 +65,31 @@ export function ProductFilters({
65
65
  onFilterChange(filterId, updated);
66
66
  };
67
67
 
68
- const handlePriceRangeApply = (filterId: string, min: number, max: number) => {
69
- onFilterChange(filterId, [`${min}-${max}`]);
68
+ const handlePriceRangeChange = (
69
+ filterId: string,
70
+ min: number | undefined,
71
+ max: number | undefined
72
+ ) => {
73
+ if (min === undefined && max === undefined) {
74
+ onFilterChange(filterId, []);
75
+ } else {
76
+ onFilterChange(filterId, [`${min ?? 0}-${max ?? 999999}`]);
77
+ }
70
78
  };
71
79
 
72
80
  const hasActiveFilters = Object.values(selectedFilters).some(
73
81
  (values) => values.length > 0
74
82
  );
75
83
 
76
- // Memoize default open filter IDs to avoid re-creating array on every render
77
- const defaultOpenFilters = useMemo(() => filters.map((f) => f.id), [filters]);
84
+ const defaultOpenFilters = useMemo(
85
+ () => filters.slice(0, 5).map((f) => f.id),
86
+ [filters]
87
+ );
88
+
89
+ if (filters.length === 0) return null;
78
90
 
79
91
  return (
80
92
  <div className={cn("space-y-4", className)}>
81
- {/* Clear all button */}
82
93
  {hasActiveFilters && onClearAll && (
83
94
  <Button
84
95
  variant="outline"
@@ -86,11 +97,10 @@ export function ProductFilters({
86
97
  onClick={onClearAll}
87
98
  className="w-full"
88
99
  >
89
- Clear all filters
100
+ {t("clearAllFilters")}
90
101
  </Button>
91
102
  )}
92
103
 
93
- {/* Filter groups */}
94
104
  <Accordion type="multiple" defaultValue={defaultOpenFilters}>
95
105
  {filters.map((filter) => (
96
106
  <AccordionItem key={filter.id} value={filter.id}>
@@ -107,106 +117,44 @@ export function ProductFilters({
107
117
  <AccordionContent value={filter.id}>
108
118
  {/* Checkbox filters */}
109
119
  {filter.type === "checkbox" && filter.options && (
110
- <div className="space-y-2">
111
- {filter.options.map((option) => (
112
- <label
113
- key={option.value}
114
- className="flex items-center gap-2 cursor-pointer"
115
- >
116
- <input
117
- type="checkbox"
118
- checked={selectedFilters[filter.id]?.includes(
119
- option.value
120
- )}
121
- onChange={() =>
122
- handleCheckboxChange(filter.id, option.value)
123
- }
124
- className="h-4 w-4 rounded border-border text-primary focus:ring-2 focus:ring-ring focus:ring-offset-2"
125
- />
126
- <span className="flex-1 text-sm text-foreground">
127
- {option.label}
128
- </span>
129
- {option.count !== undefined && (
130
- <span className="text-xs text-muted-foreground">
131
- ({option.count})
132
- </span>
133
- )}
134
- </label>
135
- ))}
136
- </div>
120
+ <CheckboxFilterGroup
121
+ filterId={filter.id}
122
+ options={filter.options}
123
+ selected={selectedFilters[filter.id] || []}
124
+ onToggle={handleCheckboxChange}
125
+ />
137
126
  )}
138
127
 
139
- {/* Price range filter */}
140
- {filter.type === "range" && (
141
- <div className="space-y-3">
142
- <div className="flex items-center gap-2">
143
- <Input
144
- type="number"
145
- placeholder="Min"
146
- value={priceRange.min}
147
- onChange={(e) =>
148
- setPriceRange({ ...priceRange, min: e.target.value })
149
- }
150
- min={filter.min}
151
- max={filter.max}
152
- className="w-full"
153
- />
154
- <span className="text-muted-foreground">–</span>
155
- <Input
156
- type="number"
157
- placeholder="Max"
158
- value={priceRange.max}
159
- onChange={(e) =>
160
- setPriceRange({ ...priceRange, max: e.target.value })
161
- }
162
- min={filter.min}
163
- max={filter.max}
164
- className="w-full"
165
- />
166
- </div>
167
- <Button
168
- size="sm"
169
- onClick={() =>
170
- handlePriceRangeApply(
171
- filter.id,
172
- parseFloat(priceRange.min) || filter.min || 0,
173
- parseFloat(priceRange.max) || filter.max || 999999
174
- )
128
+ {/* Price / numeric range filter */}
129
+ {filter.type === "range" &&
130
+ filter.min != null &&
131
+ filter.max != null && (
132
+ <FilterPriceRange
133
+ min={filter.min}
134
+ max={filter.max}
135
+ currentMin={parseRangeValue(
136
+ selectedFilters[filter.id],
137
+ "min"
138
+ )}
139
+ currentMax={parseRangeValue(
140
+ selectedFilters[filter.id],
141
+ "max"
142
+ )}
143
+ currency={filter.currency}
144
+ onChange={(min, max) =>
145
+ handlePriceRangeChange(filter.id, min, max)
175
146
  }
176
- className="w-full"
177
- >
178
- Apply
179
- </Button>
180
- </div>
181
- )}
147
+ />
148
+ )}
182
149
 
183
- {/* Color filters */}
150
+ {/* Color swatch filters */}
184
151
  {filter.type === "color" && filter.options && (
185
- <div className="flex flex-wrap gap-2">
186
- {filter.options.map((option) => {
187
- const isSelected = selectedFilters[filter.id]?.includes(
188
- option.value
189
- );
190
- return (
191
- <button
192
- key={option.value}
193
- type="button"
194
- onClick={() =>
195
- handleCheckboxChange(filter.id, option.value)
196
- }
197
- className={cn(
198
- "h-8 w-8 rounded-full border-2 transition-all",
199
- isSelected
200
- ? "border-primary ring-2 ring-ring ring-offset-2"
201
- : "border-border hover:border-primary"
202
- )}
203
- style={{ backgroundColor: option.value }}
204
- title={option.label}
205
- aria-label={option.label}
206
- />
207
- );
208
- })}
209
- </div>
152
+ <ColorSwatchGroup
153
+ filterId={filter.id}
154
+ options={filter.options}
155
+ selected={selectedFilters[filter.id] || []}
156
+ onToggle={handleCheckboxChange}
157
+ />
210
158
  )}
211
159
  </AccordionContent>
212
160
  </AccordionItem>
@@ -215,3 +163,110 @@ export function ProductFilters({
215
163
  </div>
216
164
  );
217
165
  }
166
+
167
+ // ============================================================================
168
+ // SUB-COMPONENTS
169
+ // ============================================================================
170
+
171
+ function CheckboxFilterGroup({
172
+ filterId,
173
+ options,
174
+ selected,
175
+ onToggle,
176
+ }: {
177
+ filterId: string;
178
+ options: FilterOption[];
179
+ selected: string[];
180
+ onToggle: (filterId: string, value: string) => void;
181
+ }) {
182
+ return (
183
+ <div className="space-y-2">
184
+ {options.map((option) => {
185
+ const isChecked = selected.includes(option.value);
186
+ const checkboxId = `${filterId}-${option.value}`;
187
+
188
+ return (
189
+ <div key={option.value} className="flex items-center gap-2">
190
+ <Checkbox
191
+ id={checkboxId}
192
+ checked={isChecked}
193
+ onCheckedChange={() => onToggle(filterId, option.value)}
194
+ />
195
+ <Label
196
+ htmlFor={checkboxId}
197
+ className="flex flex-1 cursor-pointer items-center justify-between text-sm"
198
+ >
199
+ <span>{option.label}</span>
200
+ {option.count !== undefined && (
201
+ <span className="text-xs text-muted-foreground">
202
+ ({option.count})
203
+ </span>
204
+ )}
205
+ </Label>
206
+ </div>
207
+ );
208
+ })}
209
+ </div>
210
+ );
211
+ }
212
+
213
+ function ColorSwatchGroup({
214
+ filterId,
215
+ options,
216
+ selected,
217
+ onToggle,
218
+ }: {
219
+ filterId: string;
220
+ options: FilterOption[];
221
+ selected: string[];
222
+ onToggle: (filterId: string, value: string) => void;
223
+ }) {
224
+ return (
225
+ <div className="flex flex-wrap gap-3">
226
+ {options.map((option) => {
227
+ const isSelected = selected.includes(option.value);
228
+ const bgColor = option.colorHex || option.value;
229
+
230
+ return (
231
+ <button
232
+ key={option.value}
233
+ type="button"
234
+ onClick={() => onToggle(filterId, option.value)}
235
+ className="group flex flex-col items-center gap-1"
236
+ title={option.label}
237
+ aria-label={`${option.label}${option.count !== undefined ? ` (${option.count})` : ""}`}
238
+ aria-pressed={isSelected}
239
+ >
240
+ <span
241
+ className={cn(
242
+ "block h-8 w-8 rounded-full border-2 transition-all",
243
+ isSelected
244
+ ? "border-primary ring-2 ring-ring ring-offset-2"
245
+ : "border-border group-hover:border-primary"
246
+ )}
247
+ style={{ backgroundColor: bgColor }}
248
+ />
249
+ <span className="text-[10px] text-muted-foreground">
250
+ {option.label}
251
+ {option.count !== undefined && ` (${option.count})`}
252
+ </span>
253
+ </button>
254
+ );
255
+ })}
256
+ </div>
257
+ );
258
+ }
259
+
260
+ // ============================================================================
261
+ // HELPERS
262
+ // ============================================================================
263
+
264
+ function parseRangeValue(
265
+ values: string[] | undefined,
266
+ part: "min" | "max"
267
+ ): number | undefined {
268
+ if (!values || values.length === 0) return undefined;
269
+ const [minStr, maxStr] = values[0].split("-");
270
+ const val = parseFloat(part === "min" ? minStr : maxStr);
271
+ return isNaN(val) ? undefined : val;
272
+ }
@@ -1,14 +1,12 @@
1
1
  "use client";
2
2
 
3
- import { ProductCard, type ProductCardProduct } from "./product-card";
3
+ import { ProductCard } from "./product-card";
4
4
  import { EmptyProducts } from "@/components/ui/empty-state";
5
5
  import { cn } from "@/lib/utils";
6
-
7
- // Re-export for external use
8
- export type { ProductCardProduct };
6
+ import type { ProductCardFields } from "@/lib/graphql/fragments";
9
7
 
10
8
  export interface ProductGridProps {
11
- products: ProductCardProduct[];
9
+ products: ProductCardFields[];
12
10
  className?: string;
13
11
  columns?: 2 | 3 | 4 | 5;
14
12
  gap?: "sm" | "md" | "lg";
@@ -3,13 +3,9 @@
3
3
  import { useState } from "react";
4
4
  import Image from "next/image";
5
5
  import { cn } from "@/lib/utils";
6
+ import type { ImageData } from "@doswiftly/storefront-sdk";
6
7
 
7
- export interface ProductImageData {
8
- url: string;
9
- altText?: string | null;
10
- width?: number;
11
- height?: number;
12
- }
8
+ export type ProductImageData = ImageData;
13
9
 
14
10
  export interface ProductImageProps {
15
11
  image?: ProductImageData | null;
@@ -115,7 +111,7 @@ export function ProductImage({
115
111
 
116
112
  const imageAlt = alt || image.altText || "Product image";
117
113
 
118
- // Common image props
114
+ // Global loaderFile (next.config.ts) handles imgproxy resize — no per-component loader needed
119
115
  const commonProps = {
120
116
  src: image.url,
121
117
  alt: imageAlt,
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { cn } from "@/lib/utils";
4
- import { formatPrice } from "@/lib/format";
5
- import type { PriceMoney } from "@/lib/format";
4
+ import { formatPrice } from "@doswiftly/storefront-sdk";
5
+ import type { PriceMoney } from "@doswiftly/storefront-sdk";
6
6
 
7
7
  export interface ProductPriceMoney {
8
8
  amount: string;
@@ -199,10 +199,11 @@ export function ProductReviews({
199
199
  icon={<MessageSquare className="h-12 w-12" />}
200
200
  title="Brak opinii"
201
201
  description="Bądź pierwszą osobą, która podzieli się opinią o tym produkcie."
202
- action={onSubmitReview ? {
203
- label: 'Napisz opinię',
204
- onClick: () => setShowForm(true),
205
- } : undefined}
202
+ action={onSubmitReview ? (
203
+ <Button onClick={() => setShowForm(true)}>
204
+ Napisz opinię
205
+ </Button>
206
+ ) : undefined}
206
207
  />
207
208
  ) : (
208
209
  <div className="space-y-4">
@@ -1,6 +1,14 @@
1
1
  "use client";
2
2
 
3
- import { Select } from "@/components/ui/select";
3
+ import { useMemo } from "react";
4
+ import { useTranslations } from "next-intl";
5
+ import {
6
+ Select,
7
+ SelectContent,
8
+ SelectItem,
9
+ SelectTrigger,
10
+ SelectValue,
11
+ } from "@/components/ui/select";
4
12
 
5
13
  // Sort options that match backend expectations
6
14
  // Backend should normalize these to ProductSortKeys + reverse flag
@@ -20,39 +28,56 @@ export interface ProductSortProps {
20
28
  className?: string;
21
29
  }
22
30
 
23
- const sortOptions: { value: SortOption; label: string }[] = [
24
- { value: "relevance", label: "Featured" },
25
- { value: "best-selling", label: "Best Selling" },
26
- { value: "price-low-to-high", label: "Price: Low to High" },
27
- { value: "price-high-to-low", label: "Price: High to Low" },
28
- { value: "title-asc", label: "Name: A to Z" },
29
- { value: "title-desc", label: "Name: Z to A" },
30
- { value: "created-desc", label: "Newest First" },
31
- { value: "created-asc", label: "Oldest First" },
31
+ const SORT_OPTION_KEYS: { value: SortOption; labelKey: string }[] = [
32
+ { value: "relevance", labelKey: "sortOptions.relevance" },
33
+ { value: "best-selling", labelKey: "sortOptions.bestSelling" },
34
+ { value: "price-low-to-high", labelKey: "sortOptions.priceLowToHigh" },
35
+ { value: "price-high-to-low", labelKey: "sortOptions.priceHighToLow" },
36
+ { value: "title-asc", labelKey: "sortOptions.titleAsc" },
37
+ { value: "title-desc", labelKey: "sortOptions.titleDesc" },
38
+ { value: "created-desc", labelKey: "sortOptions.newestFirst" },
39
+ { value: "created-asc", labelKey: "sortOptions.oldestFirst" },
32
40
  ];
33
41
 
34
42
  /**
35
43
  * ProductSort - Sorting dropdown for product listings
36
- *
44
+ *
37
45
  * @example
38
46
  * ```tsx
39
47
  * const [sort, setSort] = useState<SortOption>("featured");
40
- *
41
- * <ProductSort
48
+ *
49
+ * <ProductSort
42
50
  * value={sort}
43
51
  * onChange={setSort}
44
52
  * />
45
53
  * ```
46
54
  */
47
55
  export function ProductSort({ value, onChange, className }: ProductSortProps) {
56
+ const t = useTranslations("product");
57
+
58
+ const sortOptions = useMemo(
59
+ () =>
60
+ SORT_OPTION_KEYS.map((opt) => ({
61
+ value: opt.value,
62
+ label: t(opt.labelKey),
63
+ })),
64
+ [t]
65
+ );
66
+
48
67
  return (
49
68
  <div className={className}>
50
- <Select
51
- value={value}
52
- onChange={(e) => onChange(e.target.value as SortOption)}
53
- label="Sort by"
54
- options={sortOptions}
55
- />
69
+ <Select value={value} onValueChange={(val) => onChange(val as SortOption)}>
70
+ <SelectTrigger>
71
+ <SelectValue placeholder={t("sortBy")} />
72
+ </SelectTrigger>
73
+ <SelectContent>
74
+ {sortOptions.map((option) => (
75
+ <SelectItem key={option.value} value={option.value}>
76
+ {option.label}
77
+ </SelectItem>
78
+ ))}
79
+ </SelectContent>
80
+ </Select>
56
81
  </div>
57
82
  );
58
83
  }
@@ -2,36 +2,21 @@
2
2
 
3
3
  import { useState, useEffect } from "react";
4
4
  import { cn } from "@/lib/utils";
5
-
6
- export interface VariantOption {
7
- name: string;
8
- value: string;
9
- }
10
-
11
- export interface ProductVariant {
12
- id: string;
13
- title: string;
14
- available: boolean;
15
- selectedOptions: VariantOption[];
16
- price: {
17
- amount: string;
18
- currencyCode: string;
19
- };
20
- }
5
+ import type { ProductVariantFields } from "@/lib/graphql/fragments";
21
6
 
22
7
  export interface ProductVariantSelectorProps {
23
- variants: ProductVariant[];
8
+ variants: ProductVariantFields[];
24
9
  selectedVariantId?: string;
25
- onVariantChange: (variant: ProductVariant) => void;
10
+ onVariantChange: (variant: ProductVariantFields) => void;
26
11
  className?: string;
27
12
  }
28
13
 
29
14
  /**
30
15
  * ProductVariantSelector - Select product options (size, color, etc.)
31
- *
16
+ *
32
17
  * Extracts unique options from variants and allows selection.
33
18
  * Automatically finds matching variant when options change.
34
- *
19
+ *
35
20
  * @example
36
21
  * ```tsx
37
22
  * <ProductVariantSelector
@@ -78,12 +63,12 @@ export function ProductVariantSelector({
78
63
  ...acc,
79
64
  [opt.name]: opt.value,
80
65
  }),
81
- {}
66
+ {} as Record<string, string>
82
67
  );
83
68
  });
84
69
 
85
70
  // Find variant matching current selection
86
- const findMatchingVariant = (opts: Record<string, string>): ProductVariant | undefined => {
71
+ const findMatchingVariant = (opts: Record<string, string>): ProductVariantFields | undefined => {
87
72
  return variants.find((variant) =>
88
73
  variant.selectedOptions.every((opt) => opts[opt.name] === opt.value)
89
74
  );
@@ -114,7 +99,7 @@ export function ProductVariantSelector({
114
99
  if (variant) {
115
100
  const opts = variant.selectedOptions.reduce(
116
101
  (acc, opt) => ({ ...acc, [opt.name]: opt.value }),
117
- {}
102
+ {} as Record<string, string>
118
103
  );
119
104
  setSelectedOptions(opts);
120
105
  }
@@ -0,0 +1,51 @@
1
+ # Fragment for ProductVariant data.
2
+ #
3
+ # Used within ProductDetailFields and any component that renders
4
+ # variant selectors (size, color), pricing, and availability.
5
+ #
6
+ # Usage in components:
7
+ # import type { ProductVariantFieldsFragment } from '@/generated/graphql';
8
+ # interface Props { variant: ProductVariantFieldsFragment }
9
+
10
+ fragment ProductVariantFields on ProductVariant {
11
+ id
12
+ title
13
+ sku
14
+ available
15
+ quantityAvailable
16
+ barcode
17
+ weight
18
+ position
19
+ price {
20
+ amount
21
+ currencyCode
22
+ baseAmount
23
+ baseCurrencyCode
24
+ isConverted
25
+ }
26
+ originalPrice {
27
+ amount
28
+ currencyCode
29
+ }
30
+ compareAtPrice {
31
+ amount
32
+ currencyCode
33
+ baseAmount
34
+ baseCurrencyCode
35
+ isConverted
36
+ }
37
+ originalCompareAtPrice {
38
+ amount
39
+ currencyCode
40
+ }
41
+ image {
42
+ url
43
+ altText
44
+ width
45
+ height
46
+ }
47
+ selectedOptions {
48
+ name
49
+ value
50
+ }
51
+ }
@@ -110,7 +110,7 @@ export function ReviewCard({ review, onVoteHelpful, className }: ReviewCardProps
110
110
  <p className="text-foreground whitespace-pre-line">{displayContent}</p>
111
111
  {shouldTruncate && (
112
112
  <Button
113
- variant="link"
113
+ variant="ghost"
114
114
  size="sm"
115
115
  className="p-0 h-auto mt-1"
116
116
  onClick={() => setExpanded(!expanded)}