@doswiftly/cli 0.1.18 → 0.1.19

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 (210) 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.map +1 -1
  5. package/dist/commands/deploy.js +39 -20
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/doctor.js +3 -3
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/init.js +4 -4
  10. package/dist/commands/sdk.js +5 -5
  11. package/dist/commands/sdk.js.map +1 -1
  12. package/dist/commands/template.js +4 -4
  13. package/dist/commands/template.js.map +1 -1
  14. package/dist/commands/types.js +5 -5
  15. package/dist/commands/types.js.map +1 -1
  16. package/dist/commands/verify.js +2 -2
  17. package/dist/commands/verify.js.map +1 -1
  18. package/dist/lib/package-manager.d.ts +1 -1
  19. package/dist/lib/package-manager.js +1 -1
  20. package/package.json +1 -1
  21. package/templates/storefront-nextjs/README.md +16 -12
  22. package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
  23. package/templates/storefront-nextjs/app/account/page.tsx +2 -2
  24. package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
  25. package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
  26. package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
  27. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
  28. package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
  29. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
  30. package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
  31. package/templates/storefront-nextjs/app/page.tsx +1 -1
  32. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
  33. package/templates/storefront-nextjs/app/products/page.tsx +2 -2
  34. package/templates/storefront-nextjs/app/search/page.tsx +1 -1
  35. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
  36. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
  37. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
  38. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
  39. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
  40. package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
  41. package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
  42. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
  43. package/templates/storefront-nextjs/components/providers.tsx +1 -1
  44. package/templates/storefront-nextjs/lib/currency.tsx +3 -3
  45. package/templates/storefront-nextjs/lib/format.ts +1 -1
  46. package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
  47. package/templates/storefront-nextjs/package.dev.json +1 -1
  48. package/templates/storefront-nextjs/package.json +1 -1
  49. package/templates/storefront-nextjs/package.json.template +1 -1
  50. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
  51. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
  52. package/templates/storefront-nextjs-shadcn/CLAUDE.md +148 -35
  53. package/templates/storefront-nextjs-shadcn/README.md +29 -162
  54. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +98 -91
  55. package/templates/storefront-nextjs-shadcn/app/account/error.tsx +43 -0
  56. package/templates/storefront-nextjs-shadcn/app/account/loading.tsx +19 -0
  57. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +53 -162
  58. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/loading.tsx +60 -0
  59. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +36 -47
  60. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +46 -29
  61. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +8 -5
  62. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +108 -71
  63. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
  64. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
  65. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +10 -5
  66. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/loading.tsx +17 -0
  67. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +43 -2
  68. package/templates/storefront-nextjs-shadcn/app/blog/loading.tsx +19 -0
  69. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +2 -1
  70. package/templates/storefront-nextjs-shadcn/app/cart/loading.tsx +26 -0
  71. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +6 -3
  72. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/category-products-client.tsx +56 -0
  73. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/loading.tsx +32 -0
  74. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +76 -59
  75. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +8 -4
  76. package/templates/storefront-nextjs-shadcn/app/checkout/error.tsx +43 -0
  77. package/templates/storefront-nextjs-shadcn/app/checkout/loading.tsx +31 -0
  78. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +116 -79
  79. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/loading.tsx +19 -0
  80. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +1 -1
  81. package/templates/storefront-nextjs-shadcn/app/collections/loading.tsx +18 -0
  82. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +7 -4
  83. package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
  84. package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
  85. package/templates/storefront-nextjs-shadcn/app/layout.tsx +46 -11
  86. package/templates/storefront-nextjs-shadcn/app/products/[slug]/error.tsx +43 -0
  87. package/templates/storefront-nextjs-shadcn/app/products/[slug]/loading.tsx +29 -0
  88. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +6 -6
  89. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +15 -61
  90. package/templates/storefront-nextjs-shadcn/app/products/loading.tsx +32 -0
  91. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +405 -151
  92. package/templates/storefront-nextjs-shadcn/app/search/loading.tsx +18 -0
  93. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +8 -5
  94. package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
  95. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
  96. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +3 -1
  97. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +26 -24
  98. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
  99. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +9 -9
  100. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +11 -37
  101. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +37 -23
  102. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +4 -3
  103. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +8 -5
  104. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +1 -1
  105. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
  106. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +1 -1
  107. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +22 -7
  108. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +2 -2
  109. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +1 -1
  110. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +2 -2
  111. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
  112. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +1 -1
  113. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +3 -3
  114. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
  115. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +2 -2
  116. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +2 -1
  117. package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
  118. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +2 -12
  119. package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
  120. package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
  121. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +4 -4
  122. package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
  123. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +2 -2
  124. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +33 -23
  125. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
  126. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
  127. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +10 -19
  128. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
  129. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
  130. package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
  131. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +3 -1
  132. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +69 -0
  133. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +84 -0
  134. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +138 -0
  135. package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
  136. package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
  137. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +3 -31
  138. package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
  139. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +176 -123
  140. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
  141. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +2 -2
  142. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
  143. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
  144. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +19 -7
  145. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
  146. package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
  147. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
  148. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +1 -7
  149. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
  150. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
  151. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
  152. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +30 -0
  153. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
  154. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
  155. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +3 -2
  156. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
  157. package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
  158. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
  159. package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
  160. package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
  161. package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
  162. package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
  163. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +3 -1
  164. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +12779 -0
  165. package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
  166. package/templates/storefront-nextjs-shadcn/hooks/index.ts +2 -0
  167. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
  168. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
  169. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +51 -19
  170. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +13 -9
  171. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
  172. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
  173. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +32 -0
  174. package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
  175. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +687 -632
  176. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +86 -0
  177. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +131 -182
  178. package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
  179. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
  180. package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
  181. package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
  182. package/templates/storefront-nextjs-shadcn/package.json +12 -13
  183. package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
  184. package/templates/storefront-nextjs-shadcn/proxy.ts +3 -4
  185. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +41 -39
  186. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
  187. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
  188. package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
  189. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
  190. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
  191. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
  192. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
  193. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
  194. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
  195. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
  196. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
  197. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
  198. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
  199. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
  200. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
  201. package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
  202. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
  203. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
  204. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
  205. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
  206. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
  207. package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
  208. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
  209. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
  210. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
@@ -19,18 +19,10 @@ import { Input } from '@/components/ui/input';
19
19
  import { Badge } from '@/components/ui/badge';
20
20
  import { cn } from '@/lib/utils';
21
21
  import { toast } from 'sonner';
22
-
23
- interface ReferralStats {
24
- totalReferred: number;
25
- completedReferrals: number;
26
- pendingReferrals: number;
27
- totalPointsEarned: number;
28
- }
22
+ import type { ReferralStats } from '@/lib/graphql/fragments';
29
23
 
30
24
  interface ReferralSectionProps {
31
- referralCode: string;
32
- shareUrl: string;
33
- stats: ReferralStats;
25
+ referralStats: ReferralStats;
34
26
  pointsName?: string;
35
27
  referralPoints?: number;
36
28
  bonusPoints?: number;
@@ -38,14 +30,13 @@ interface ReferralSectionProps {
38
30
  }
39
31
 
40
32
  export function ReferralSection({
41
- referralCode,
42
- shareUrl,
43
- stats,
33
+ referralStats,
44
34
  pointsName = 'punktów',
45
35
  referralPoints = 0,
46
36
  bonusPoints = 0,
47
37
  className,
48
38
  }: ReferralSectionProps) {
39
+ const { referralCode, shareUrl } = referralStats;
49
40
  const [copied, setCopied] = useState<'code' | 'url' | null>(null);
50
41
 
51
42
  const formatNumber = (num: number) => {
@@ -199,7 +190,7 @@ export function ReferralSection({
199
190
  <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
200
191
  <div className="p-4 bg-muted/50 rounded-lg text-center">
201
192
  <div className="text-2xl font-bold text-primary">
202
- {formatNumber(stats.totalReferred)}
193
+ {formatNumber(referralStats.totalReferred)}
203
194
  </div>
204
195
  <div className="text-sm text-muted-foreground">Zaproszonych</div>
205
196
  </div>
@@ -207,7 +198,7 @@ export function ReferralSection({
207
198
  <div className="p-4 bg-muted/50 rounded-lg text-center">
208
199
  <div className="flex items-center justify-center gap-1">
209
200
  <div className="text-2xl font-bold text-green-600">
210
- {formatNumber(stats.completedReferrals)}
201
+ {formatNumber(referralStats.completedReferrals)}
211
202
  </div>
212
203
  <Check className="h-5 w-5 text-green-600" />
213
204
  </div>
@@ -217,7 +208,7 @@ export function ReferralSection({
217
208
  <div className="p-4 bg-muted/50 rounded-lg text-center">
218
209
  <div className="flex items-center justify-center gap-1">
219
210
  <div className="text-2xl font-bold text-amber-600">
220
- {formatNumber(stats.pendingReferrals)}
211
+ {formatNumber(referralStats.pendingReferrals)}
221
212
  </div>
222
213
  <Clock className="h-5 w-5 text-amber-600" />
223
214
  </div>
@@ -226,7 +217,7 @@ export function ReferralSection({
226
217
 
227
218
  <div className="p-4 bg-muted/50 rounded-lg text-center">
228
219
  <div className="text-2xl font-bold text-primary">
229
- {formatNumber(stats.totalPointsEarned)}
220
+ {formatNumber(referralStats.totalPointsEarned)}
230
221
  </div>
231
222
  <div className="text-sm text-muted-foreground">
232
223
  Zdobytych {pointsName}
@@ -234,11 +225,11 @@ export function ReferralSection({
234
225
  </div>
235
226
  </div>
236
227
 
237
- {stats.pendingReferrals > 0 && (
228
+ {referralStats.pendingReferrals > 0 && (
238
229
  <div className="mt-4 p-3 bg-amber-50 dark:bg-amber-950/30 rounded-lg border border-amber-200 dark:border-amber-800">
239
230
  <p className="text-sm text-amber-800 dark:text-amber-200">
240
231
  <Clock className="h-4 w-4 inline mr-1" />
241
- Masz {stats.pendingReferrals} oczekujących poleceń. Punkty zostaną
232
+ Masz {referralStats.pendingReferrals} oczekujących poleceń. Punkty zostaną
242
233
  przyznane po dokonaniu pierwszego zakupu przez poleconą osobę.
243
234
  </p>
244
235
  </div>
@@ -15,41 +15,17 @@ import { Badge } from '@/components/ui/badge';
15
15
  import { cn } from '@/lib/utils';
16
16
  import { TierBadge } from './tier-badge';
17
17
  import { toast } from 'sonner';
18
-
19
- type TierType = 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINUM' | 'DIAMOND';
20
-
21
- interface Reward {
22
- id: string;
23
- name: string;
24
- slug: string;
25
- type: string;
26
- pointsCost: number;
27
- discountPercent?: number;
28
- discountAmount?: {
29
- amount: string;
30
- currencyCode: string;
31
- };
32
- description?: string;
33
- image?: {
34
- url: string;
35
- altText?: string | null;
36
- } | null;
37
- available: boolean;
38
- tierRequired?: TierType | null;
39
- remainingRedemptions?: number;
40
- }
41
-
42
- interface RedeemResult {
43
- discountCode?: string | null;
44
- productDiscountCode?: string | null;
45
- giftCardCode?: string | null;
46
- }
18
+ import type { LoyaltyReward, LoyaltyTier } from '@/lib/graphql/fragments';
47
19
 
48
20
  interface RewardsCatalogProps {
49
- rewards: Reward[];
21
+ rewards: LoyaltyReward[];
50
22
  currentPoints: number;
51
- currentTier?: TierType | null;
52
- onRedeem?: (rewardId: string) => Promise<RedeemResult>;
23
+ currentTier?: LoyaltyTier['type'] | null;
24
+ onRedeem?: (rewardId: string) => Promise<{
25
+ discountCode?: string | null;
26
+ productDiscountCode?: string | null;
27
+ giftCardCode?: string | null;
28
+ }>;
53
29
  className?: string;
54
30
  }
55
31
 
@@ -73,13 +49,13 @@ export function RewardsCatalog({
73
49
  }).format(parseFloat(amount));
74
50
  };
75
51
 
76
- const tierOrder: TierType[] = ['BRONZE', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND'];
77
- const canAccessTier = (requiredTier?: TierType | null) => {
78
- if (!requiredTier || !currentTier) return true;
79
- return tierOrder.indexOf(currentTier) >= tierOrder.indexOf(requiredTier);
52
+ const tierOrder: string[] = ['BRONZE', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND'];
53
+ const canAccessTier = (requiredTierType?: string | null) => {
54
+ if (!requiredTierType || !currentTier) return true;
55
+ return tierOrder.indexOf(currentTier) >= tierOrder.indexOf(requiredTierType);
80
56
  };
81
57
 
82
- const handleRedeem = async (reward: Reward) => {
58
+ const handleRedeem = async (reward: LoyaltyReward) => {
83
59
  if (!onRedeem) return;
84
60
 
85
61
  setRedeemingId(reward.id);
@@ -125,7 +101,7 @@ export function RewardsCatalog({
125
101
  <div className={cn('grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4', className)}>
126
102
  {rewards.map((reward) => {
127
103
  const canAfford = currentPoints >= reward.pointsCost;
128
- const hasTierAccess = canAccessTier(reward.tierRequired);
104
+ const hasTierAccess = canAccessTier(reward.tierRequired?.type);
129
105
  const canRedeem = reward.available && canAfford && hasTierAccess;
130
106
 
131
107
  return (
@@ -144,8 +120,8 @@ export function RewardsCatalog({
144
120
  <CardContent className="p-4">
145
121
  <div className="flex items-start justify-between gap-2 mb-2">
146
122
  <h3 className="font-semibold">{reward.name}</h3>
147
- {reward.tierRequired && (
148
- <TierBadge tier={reward.tierRequired} size="sm" />
123
+ {reward.tierRequired?.type && (
124
+ <TierBadge tier={reward.tierRequired.type} size="sm" />
149
125
  )}
150
126
  </div>
151
127
 
@@ -183,7 +159,7 @@ export function RewardsCatalog({
183
159
  {!hasTierAccess ? (
184
160
  <Button variant="outline" className="w-full" disabled>
185
161
  <Lock className="h-4 w-4 mr-2" />
186
- Wymaga {reward.tierRequired}
162
+ Wymaga {reward.tierRequired?.name ?? reward.tierRequired?.type}
187
163
  </Button>
188
164
  ) : !canAfford ? (
189
165
  <Button variant="outline" className="w-full" disabled>
@@ -11,37 +11,10 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
11
11
  import { Progress } from '@/components/ui/progress';
12
12
  import { TierBadge } from './tier-badge';
13
13
  import { cn } from '@/lib/utils';
14
-
15
- type TierType = 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINUM' | 'DIAMOND';
16
-
17
- interface CustomBenefit {
18
- name: string;
19
- description?: string | null;
20
- icon?: string | null;
21
- }
22
-
23
- interface Tier {
24
- id: string;
25
- name: string;
26
- type?: TierType | null;
27
- minPoints: number;
28
- pointsMultiplier: number;
29
- customBenefits?: CustomBenefit[];
30
- }
31
-
32
- interface TierProgressData {
33
- currentTier: Tier;
34
- nextTier?: Tier;
35
- pointsToNextTier: number;
36
- progressPercent: number;
37
- spendToNextTier?: {
38
- amount: string;
39
- currencyCode: string;
40
- };
41
- }
14
+ import type { TierProgress as TierProgressType } from '@/lib/graphql/fragments';
42
15
 
43
16
  interface TierProgressProps {
44
- progress: TierProgressData;
17
+ progress: TierProgressType;
45
18
  className?: string;
46
19
  }
47
20
 
@@ -7,5 +7,10 @@
7
7
  export * from "./order-tracking";
8
8
  export * from "./shipment-card";
9
9
  export * from "./tracking-timeline";
10
- export * from "./tracking-status";
10
+ export {
11
+ TrackingStatus,
12
+ CompactTrackingStatus,
13
+ type TrackingStatusProps,
14
+ type CompactTrackingStatusProps,
15
+ } from "./tracking-status";
11
16
  export * from "./delivery-estimate";
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { cn } from "@/lib/utils";
4
+ import { CURRENCY_LOCALES } from "@doswiftly/storefront-sdk";
4
5
  import { Tag, LogIn, TrendingDown } from "lucide-react";
5
6
  import Link from "next/link";
6
7
 
@@ -67,7 +68,8 @@ export function B2BPriceDisplayComponent({
67
68
 
68
69
  const formatPrice = (money: Money) => {
69
70
  const amount = parseFloat(money.amount);
70
- return new Intl.NumberFormat("pl-PL", {
71
+ const locale = CURRENCY_LOCALES[money.currencyCode] || "en-US";
72
+ return new Intl.NumberFormat(locale, {
71
73
  style: "currency",
72
74
  currency: money.currencyCode,
73
75
  }).format(amount);
@@ -0,0 +1,69 @@
1
+ "use client";
2
+
3
+ import { X } from "lucide-react";
4
+ import { Badge } from "@/components/ui/badge";
5
+ import { Button } from "@/components/ui/button";
6
+
7
+ export interface ActivePill {
8
+ filterId: string;
9
+ label: string;
10
+ value: string;
11
+ displayValue: string;
12
+ }
13
+
14
+ interface FilterActivePillsProps {
15
+ pills: ActivePill[];
16
+ onRemove: (filterId: string, value: string) => void;
17
+ onClearAll: () => void;
18
+ }
19
+
20
+ /**
21
+ * FilterActivePills - Shows active filters as removable badges
22
+ *
23
+ * Pattern from Saleor Storefront:
24
+ * - Each pill shows "Label: Value" with X button
25
+ * - "Clear all" link at the end
26
+ * - Horizontally scrollable on mobile
27
+ */
28
+ export function FilterActivePills({
29
+ pills,
30
+ onRemove,
31
+ onClearAll,
32
+ }: FilterActivePillsProps) {
33
+ if (pills.length === 0) return null;
34
+
35
+ return (
36
+ <div
37
+ className="scrollbar-hide flex items-center gap-2 overflow-x-auto py-1"
38
+ role="region"
39
+ aria-label="Active filters"
40
+ >
41
+ {pills.map((pill) => (
42
+ <Badge
43
+ key={`${pill.filterId}-${pill.value}`}
44
+ variant="secondary"
45
+ className="shrink-0 gap-1.5 pr-1.5"
46
+ >
47
+ <span className="text-xs text-muted-foreground">{pill.label}:</span>
48
+ {pill.displayValue}
49
+ <button
50
+ type="button"
51
+ onClick={() => onRemove(pill.filterId, pill.value)}
52
+ className="ml-0.5 rounded-full p-0.5 transition-colors hover:bg-background/50"
53
+ aria-label={`Remove ${pill.displayValue} filter`}
54
+ >
55
+ <X className="h-3 w-3" />
56
+ </button>
57
+ </Badge>
58
+ ))}
59
+ <Button
60
+ variant="ghost"
61
+ size="sm"
62
+ className="h-6 shrink-0 px-2 text-xs text-muted-foreground"
63
+ onClick={onClearAll}
64
+ >
65
+ Clear all
66
+ </Button>
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { SlidersHorizontal } from "lucide-react";
5
+ import { Button } from "@/components/ui/button";
6
+ import { Badge } from "@/components/ui/badge";
7
+ import {
8
+ Sheet,
9
+ SheetTrigger,
10
+ SheetContent,
11
+ SheetHeader,
12
+ SheetTitle,
13
+ SheetDescription,
14
+ } from "@/components/ui/sheet";
15
+ import { ProductFilters, type ProductFiltersProps } from "./product-filters";
16
+
17
+ interface FilterMobileSheetProps extends ProductFiltersProps {
18
+ activeFilterCount: number;
19
+ totalProducts?: number;
20
+ }
21
+
22
+ /**
23
+ * FilterMobileSheet - Mobile filter panel (slides from left)
24
+ *
25
+ * Pattern from Saleor Storefront:
26
+ * - Trigger button visible only on mobile (lg:hidden)
27
+ * - Sheet slides from left with full filter panel
28
+ * - Footer shows "Clear all" when filters active
29
+ * - Auto-closes after clearing
30
+ */
31
+ export function FilterMobileSheet({
32
+ activeFilterCount,
33
+ totalProducts,
34
+ ...filterProps
35
+ }: FilterMobileSheetProps) {
36
+ const [open, setOpen] = useState(false);
37
+
38
+ return (
39
+ <Sheet open={open} onOpenChange={setOpen}>
40
+ <SheetTrigger asChild>
41
+ <Button
42
+ variant="outline"
43
+ size="sm"
44
+ className="shrink-0 lg:hidden"
45
+ >
46
+ <SlidersHorizontal className="mr-2 h-4 w-4" />
47
+ Filters
48
+ {activeFilterCount > 0 && (
49
+ <Badge variant="secondary" className="ml-2 h-5 px-1.5 py-0 text-xs">
50
+ {activeFilterCount}
51
+ </Badge>
52
+ )}
53
+ </Button>
54
+ </SheetTrigger>
55
+ <SheetContent side="left" className="flex w-[300px] flex-col overflow-hidden">
56
+ <SheetHeader>
57
+ <SheetTitle>Filters</SheetTitle>
58
+ <SheetDescription>
59
+ {totalProducts !== undefined && (
60
+ <span>{totalProducts} products</span>
61
+ )}
62
+ </SheetDescription>
63
+ </SheetHeader>
64
+ <div className="flex-1 overflow-y-auto px-4 pb-4">
65
+ <ProductFilters {...filterProps} />
66
+ </div>
67
+ {activeFilterCount > 0 && filterProps.onClearAll && (
68
+ <div className="border-t border-border p-4">
69
+ <Button
70
+ variant="outline"
71
+ className="w-full"
72
+ onClick={() => {
73
+ filterProps.onClearAll?.();
74
+ setOpen(false);
75
+ }}
76
+ >
77
+ Clear all filters ({activeFilterCount})
78
+ </Button>
79
+ </div>
80
+ )}
81
+ </SheetContent>
82
+ </Sheet>
83
+ );
84
+ }
@@ -0,0 +1,138 @@
1
+ "use client";
2
+
3
+ import { useState, useCallback, useEffect, useRef } from "react";
4
+ import { Slider } from "@/components/ui/slider";
5
+ import { Input } from "@/components/ui/input";
6
+
7
+ interface FilterPriceRangeProps {
8
+ min: number;
9
+ max: number;
10
+ currentMin?: number;
11
+ currentMax?: number;
12
+ currency?: string;
13
+ onChange: (min: number | undefined, max: number | undefined) => void;
14
+ }
15
+
16
+ /**
17
+ * FilterPriceRange - Dual-thumb range slider with number inputs
18
+ *
19
+ * - Slider commits on thumb release (onValueCommit) — natural debounce
20
+ * - Input fields commit on blur — no Apply button needed
21
+ * - Shows formatted currency values
22
+ */
23
+ export function FilterPriceRange({
24
+ min,
25
+ max,
26
+ currentMin,
27
+ currentMax,
28
+ currency = "PLN",
29
+ onChange,
30
+ }: FilterPriceRangeProps) {
31
+ const [localMin, setLocalMin] = useState<number>(currentMin ?? min);
32
+ const [localMax, setLocalMax] = useState<number>(currentMax ?? max);
33
+ const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
34
+
35
+ // Sync local state when props change (e.g., URL navigation)
36
+ useEffect(() => {
37
+ setLocalMin(currentMin ?? min);
38
+ setLocalMax(currentMax ?? max);
39
+ }, [currentMin, currentMax, min, max]);
40
+
41
+ const commitChange = useCallback(
42
+ (newMin: number, newMax: number) => {
43
+ const effectiveMin = newMin <= min ? undefined : newMin;
44
+ const effectiveMax = newMax >= max ? undefined : newMax;
45
+ onChange(effectiveMin, effectiveMax);
46
+ },
47
+ [min, max, onChange]
48
+ );
49
+
50
+ const handleSliderChange = useCallback(
51
+ (values: number[]) => {
52
+ setLocalMin(values[0]);
53
+ setLocalMax(values[1]);
54
+ },
55
+ []
56
+ );
57
+
58
+ const handleSliderCommit = useCallback(
59
+ (values: number[]) => {
60
+ commitChange(values[0], values[1]);
61
+ },
62
+ [commitChange]
63
+ );
64
+
65
+ const handleInputBlur = useCallback(() => {
66
+ // Clamp values
67
+ const clampedMin = Math.max(min, Math.min(localMin, localMax));
68
+ const clampedMax = Math.min(max, Math.max(localMin, localMax));
69
+ setLocalMin(clampedMin);
70
+ setLocalMax(clampedMax);
71
+
72
+ if (debounceRef.current) clearTimeout(debounceRef.current);
73
+ debounceRef.current = setTimeout(() => {
74
+ commitChange(clampedMin, clampedMax);
75
+ }, 150);
76
+ }, [localMin, localMax, min, max, commitChange]);
77
+
78
+ // Cleanup timeout
79
+ useEffect(() => {
80
+ return () => {
81
+ if (debounceRef.current) clearTimeout(debounceRef.current);
82
+ };
83
+ }, []);
84
+
85
+ const step = max - min > 1000 ? 10 : max - min > 100 ? 1 : 0.01;
86
+
87
+ return (
88
+ <div className="space-y-4 px-1">
89
+ {/* Slider */}
90
+ <Slider
91
+ value={[localMin, localMax]}
92
+ min={min}
93
+ max={max}
94
+ step={step}
95
+ onValueChange={handleSliderChange}
96
+ onValueCommit={handleSliderCommit}
97
+ aria-label="Price range"
98
+ />
99
+
100
+ {/* Input fields */}
101
+ <div className="flex items-center gap-2">
102
+ <div className="relative flex-1">
103
+ <Input
104
+ type="number"
105
+ value={localMin}
106
+ onChange={(e) => setLocalMin(parseFloat(e.target.value) || min)}
107
+ onBlur={handleInputBlur}
108
+ min={min}
109
+ max={max}
110
+ step={step}
111
+ className="pr-12 text-sm"
112
+ aria-label="Minimum price"
113
+ />
114
+ <span className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">
115
+ {currency}
116
+ </span>
117
+ </div>
118
+ <span className="text-muted-foreground">—</span>
119
+ <div className="relative flex-1">
120
+ <Input
121
+ type="number"
122
+ value={localMax}
123
+ onChange={(e) => setLocalMax(parseFloat(e.target.value) || max)}
124
+ onBlur={handleInputBlur}
125
+ min={min}
126
+ max={max}
127
+ step={step}
128
+ className="pr-12 text-sm"
129
+ aria-label="Maximum price"
130
+ />
131
+ <span className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">
132
+ {currency}
133
+ </span>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ );
138
+ }
@@ -1,5 +1,5 @@
1
1
  export { ProductCard } from './product-card';
2
- export type { ProductCardProps, ProductCardProduct } from './product-card';
2
+ export type { ProductCardProps } from './product-card';
3
3
 
4
4
  export { ProductGrid } from './product-grid';
5
5
  export type { ProductGridProps } from './product-grid';
@@ -16,8 +16,15 @@ export type { ProductFiltersProps, FilterGroup, FilterOption } from './product-f
16
16
  export { ProductSort } from './product-sort';
17
17
  export type { ProductSortProps, SortOption } from './product-sort';
18
18
 
19
+ export { FilterActivePills } from './filter-active-pills';
20
+ export type { ActivePill } from './filter-active-pills';
21
+
22
+ export { FilterPriceRange } from './filter-price-range';
23
+
24
+ export { FilterMobileSheet } from './filter-mobile-sheet';
25
+
19
26
  export { ProductVariantSelector } from './product-variant-selector';
20
- export type { ProductVariantSelectorProps, ProductVariant, VariantOption } from './product-variant-selector';
27
+ export type { ProductVariantSelectorProps } from './product-variant-selector';
21
28
 
22
29
  export { ProductGallery } from './product-gallery';
23
30
  export type { ProductGalleryProps } from './product-gallery';
@@ -0,0 +1,49 @@
1
+ # Fragment for ProductCard component.
2
+ #
3
+ # Defines the minimal product data needed for listing views:
4
+ # grids, search results, collection products, recommendations.
5
+ #
6
+ # Usage in components:
7
+ # import type { ProductCardFieldsFragment } from '@/generated/graphql';
8
+ # interface Props { product: ProductCardFieldsFragment }
9
+ #
10
+ # Usage in custom queries:
11
+ # query FeaturedProducts {
12
+ # products(first: 8) {
13
+ # edges { node { ...ProductCardFields } }
14
+ # }
15
+ # }
16
+
17
+ fragment ProductCardFields on Product {
18
+ id
19
+ handle
20
+ title
21
+ vendor
22
+ productType
23
+ availableForSale
24
+ averageRating
25
+ reviewCount
26
+ tags
27
+ featuredImage {
28
+ url
29
+ altText
30
+ width
31
+ height
32
+ }
33
+ priceRange {
34
+ minVariantPrice {
35
+ amount
36
+ currencyCode
37
+ }
38
+ maxVariantPrice {
39
+ amount
40
+ currencyCode
41
+ }
42
+ }
43
+ compareAtPriceRange {
44
+ minVariantPrice {
45
+ amount
46
+ currencyCode
47
+ }
48
+ }
49
+ }
@@ -6,38 +6,10 @@ import { ProductImage } from "./product-image";
6
6
  import { ProductPrice } from "./product-price";
7
7
  import { Badge } from "@/components/ui/badge";
8
8
  import { cn } from "@/lib/utils";
9
-
10
- export interface ProductCardProduct {
11
- id: string;
12
- handle: string;
13
- title: string;
14
- featuredImage?: {
15
- url: string;
16
- altText?: string | null;
17
- } | null;
18
- priceRange: {
19
- minVariantPrice: {
20
- amount: string;
21
- currencyCode: string;
22
- };
23
- maxVariantPrice: {
24
- amount: string;
25
- currencyCode: string;
26
- };
27
- };
28
- compareAtPriceRange?: {
29
- minVariantPrice: {
30
- amount: string;
31
- currencyCode: string;
32
- };
33
- } | null;
34
- availableForSale: boolean;
35
- tags?: string[];
36
- type?: string;
37
- }
9
+ import type { ProductCardFields } from "@/lib/graphql/fragments";
38
10
 
39
11
  export interface ProductCardProps {
40
- product: ProductCardProduct;
12
+ product: ProductCardFields;
41
13
  className?: string;
42
14
  priority?: boolean;
43
15
  showBadges?: boolean;
@@ -72,7 +44,7 @@ export function ProductCard({
72
44
  );
73
45
 
74
46
  const isOutOfStock = !product.availableForSale;
75
- const isGiftCard = product.type === "GIFT_CARD";
47
+ const isGiftCard = product.productType === "GIFT_CARD";
76
48
 
77
49
  return (
78
50
  <Link