@doswiftly/cli 0.1.17 → 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 (213) 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 +43 -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-minimal/wrangler.toml +4 -0
  22. package/templates/storefront-nextjs/README.md +16 -12
  23. package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
  24. package/templates/storefront-nextjs/app/account/page.tsx +2 -2
  25. package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
  26. package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
  27. package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
  28. package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
  29. package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
  30. package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
  31. package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
  32. package/templates/storefront-nextjs/app/page.tsx +1 -1
  33. package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
  34. package/templates/storefront-nextjs/app/products/page.tsx +2 -2
  35. package/templates/storefront-nextjs/app/search/page.tsx +1 -1
  36. package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
  37. package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
  38. package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
  39. package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
  40. package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
  41. package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
  42. package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
  43. package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
  44. package/templates/storefront-nextjs/components/providers.tsx +1 -1
  45. package/templates/storefront-nextjs/lib/currency.tsx +3 -3
  46. package/templates/storefront-nextjs/lib/format.ts +1 -1
  47. package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
  48. package/templates/storefront-nextjs/package.dev.json +1 -1
  49. package/templates/storefront-nextjs/package.json +1 -1
  50. package/templates/storefront-nextjs/package.json.template +1 -1
  51. package/templates/storefront-nextjs/wrangler.toml +4 -0
  52. package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
  53. package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
  54. package/templates/storefront-nextjs-shadcn/CLAUDE.md +148 -35
  55. package/templates/storefront-nextjs-shadcn/README.md +29 -162
  56. package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +98 -91
  57. package/templates/storefront-nextjs-shadcn/app/account/error.tsx +43 -0
  58. package/templates/storefront-nextjs-shadcn/app/account/loading.tsx +19 -0
  59. package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +53 -162
  60. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/loading.tsx +60 -0
  61. package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +36 -47
  62. package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +46 -29
  63. package/templates/storefront-nextjs-shadcn/app/account/page.tsx +8 -5
  64. package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +108 -71
  65. package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
  66. package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
  67. package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +10 -5
  68. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/loading.tsx +17 -0
  69. package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +43 -2
  70. package/templates/storefront-nextjs-shadcn/app/blog/loading.tsx +19 -0
  71. package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +2 -1
  72. package/templates/storefront-nextjs-shadcn/app/cart/loading.tsx +26 -0
  73. package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +6 -3
  74. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/category-products-client.tsx +56 -0
  75. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/loading.tsx +32 -0
  76. package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +76 -59
  77. package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +8 -4
  78. package/templates/storefront-nextjs-shadcn/app/checkout/error.tsx +43 -0
  79. package/templates/storefront-nextjs-shadcn/app/checkout/loading.tsx +31 -0
  80. package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +116 -79
  81. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/loading.tsx +19 -0
  82. package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +1 -1
  83. package/templates/storefront-nextjs-shadcn/app/collections/loading.tsx +18 -0
  84. package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +7 -4
  85. package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
  86. package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
  87. package/templates/storefront-nextjs-shadcn/app/layout.tsx +46 -11
  88. package/templates/storefront-nextjs-shadcn/app/products/[slug]/error.tsx +43 -0
  89. package/templates/storefront-nextjs-shadcn/app/products/[slug]/loading.tsx +29 -0
  90. package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +6 -6
  91. package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +15 -61
  92. package/templates/storefront-nextjs-shadcn/app/products/loading.tsx +32 -0
  93. package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +405 -151
  94. package/templates/storefront-nextjs-shadcn/app/search/loading.tsx +18 -0
  95. package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +8 -5
  96. package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
  97. package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
  98. package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +3 -1
  99. package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +26 -24
  100. package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
  101. package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +9 -9
  102. package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +11 -37
  103. package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +37 -23
  104. package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +4 -3
  105. package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +8 -5
  106. package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +1 -1
  107. package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
  108. package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +1 -1
  109. package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +22 -7
  110. package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +2 -2
  111. package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +1 -1
  112. package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +2 -2
  113. package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
  114. package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +1 -1
  115. package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +3 -3
  116. package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
  117. package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +2 -2
  118. package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +2 -1
  119. package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
  120. package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +2 -12
  121. package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
  122. package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
  123. package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +4 -4
  124. package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
  125. package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +2 -2
  126. package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +33 -23
  127. package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
  128. package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
  129. package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +10 -19
  130. package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
  131. package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
  132. package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
  133. package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +3 -1
  134. package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +69 -0
  135. package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +84 -0
  136. package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +138 -0
  137. package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
  138. package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
  139. package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +3 -31
  140. package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
  141. package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +176 -123
  142. package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
  143. package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +2 -2
  144. package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
  145. package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
  146. package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +19 -7
  147. package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
  148. package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
  149. package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
  150. package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +1 -7
  151. package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
  152. package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
  153. package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
  154. package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +30 -0
  155. package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
  156. package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
  157. package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +3 -2
  158. package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
  159. package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
  160. package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
  161. package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
  162. package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
  163. package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
  164. package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
  165. package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +3 -1
  166. package/templates/storefront-nextjs-shadcn/generated/graphql.ts +12779 -0
  167. package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
  168. package/templates/storefront-nextjs-shadcn/hooks/index.ts +2 -0
  169. package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
  170. package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
  171. package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +51 -19
  172. package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +13 -9
  173. package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
  174. package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
  175. package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +32 -0
  176. package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
  177. package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +687 -632
  178. package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +86 -0
  179. package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +131 -182
  180. package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
  181. package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
  182. package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
  183. package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
  184. package/templates/storefront-nextjs-shadcn/package.json +12 -13
  185. package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
  186. package/templates/storefront-nextjs-shadcn/proxy.ts +3 -4
  187. package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +41 -39
  188. package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
  189. package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
  190. package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
  191. package/templates/storefront-nextjs-shadcn/wrangler.toml +4 -0
  192. package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
  193. package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
  194. package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
  195. package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
  196. package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
  197. package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
  198. package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
  199. package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
  200. package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
  201. package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
  202. package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
  203. package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
  204. package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
  205. package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
  206. package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
  207. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
  208. package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
  209. package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
  210. package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
  211. package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
  212. package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
  213. package/templates/storefront-nextjs-shadcn/stores/currency-store.ts +0 -103
@@ -4,7 +4,13 @@ import { useState } from "react";
4
4
  import { Truck, MapPin } from "lucide-react";
5
5
  import { Input } from "@/components/ui/input";
6
6
  import { Button } from "@/components/ui/button";
7
- import { Select } from "@/components/ui/select";
7
+ import {
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from "@/components/ui/select";
8
14
  import { cn } from "@/lib/utils";
9
15
 
10
16
  export interface ShippingOption {
@@ -90,12 +96,21 @@ export function ShippingEstimator({
90
96
 
91
97
  {/* Form */}
92
98
  <div className="space-y-3">
93
- <Select
94
- value={country}
95
- onChange={(e) => setCountry(e.target.value)}
96
- label="Country"
97
- options={countries}
98
- />
99
+ <div>
100
+ <label className="text-sm font-medium text-foreground">Country</label>
101
+ <Select value={country} onValueChange={setCountry}>
102
+ <SelectTrigger>
103
+ <SelectValue placeholder="Select country" />
104
+ </SelectTrigger>
105
+ <SelectContent>
106
+ {countries.map((c) => (
107
+ <SelectItem key={c.value} value={c.value}>
108
+ {c.label}
109
+ </SelectItem>
110
+ ))}
111
+ </SelectContent>
112
+ </Select>
113
+ </div>
99
114
 
100
115
  <div className="flex gap-2">
101
116
  <div className="relative flex-1">
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useState, useRef, useEffect } from "react";
4
4
  import { ChevronDown, Check, Globe } from "lucide-react";
5
- import { useCurrencyStore } from "@/stores/currency-store";
5
+ import { useCurrencyStore } from "@doswiftly/storefront-sdk/react";
6
6
  import { useQueryClient } from "@tanstack/react-query";
7
7
 
8
8
  // ============================================================================
@@ -89,7 +89,7 @@ export function CurrencySelector({
89
89
  const currency = useCurrencyStore((state) => state.currency);
90
90
  const supportedCurrencies = useCurrencyStore((state) => state.supportedCurrencies);
91
91
  const setCurrency = useCurrencyStore((state) => state.setCurrency);
92
- const isHydrated = useCurrencyStore((state) => state.isHydrated);
92
+ const isHydrated = useCurrencyStore((state) => state.isLoaded);
93
93
 
94
94
  // Handle currency change
95
95
  const handleCurrencyChange = (newCurrency: string) => {
@@ -3,7 +3,7 @@
3
3
  import { useState, useMemo } from "react";
4
4
  import { VariantSelector } from "@/components/commerce/variant-selector";
5
5
  import { AddToCartButton } from "@/components/product/add-to-cart-button";
6
- import { formatPrice } from "@/lib/format";
6
+ import { formatPrice } from "@doswiftly/storefront-sdk";
7
7
 
8
8
  interface ProductVariant {
9
9
  id: string;
@@ -5,7 +5,7 @@ import { useRouter } from "next/navigation";
5
5
  import { Search, X, Loader2 } from "lucide-react";
6
6
  import { useGraphQLQuery } from "@/lib/graphql/hooks";
7
7
  import { ProductSearchDocument } from "@/generated/graphql";
8
- import { useDebouncedValue } from "@/lib/hooks";
8
+ import { useDebouncedValue } from "@doswiftly/storefront-sdk/react";
9
9
  import Link from "next/link";
10
10
 
11
11
  interface SearchInputProps {
@@ -123,7 +123,7 @@ export function SearchInput({
123
123
  </div>
124
124
  ) : hasResults ? (
125
125
  <div className="max-h-96 overflow-y-auto">
126
- {products.slice(0, 5).map((product: any) => (
126
+ {products.slice(0, 5).map((product) => (
127
127
  <Link
128
128
  key={product.id}
129
129
  href={`/products/${product.handle}`}
@@ -1,34 +1,45 @@
1
1
  "use client";
2
2
 
3
- import { useCurrencyStore } from "@/stores/currency-store";
3
+ import { useCurrencyStore } from "@doswiftly/storefront-sdk/react";
4
+ import { CURRENCY_LOCALES } from "@doswiftly/storefront-sdk";
5
+
6
+ /** Resolve locale from currency code using the centralized CURRENCY_LOCALES map */
7
+ function getLocaleForCurrency(currencyCode: string): string {
8
+ return CURRENCY_LOCALES[currencyCode] || "en-US";
9
+ }
4
10
 
5
11
  export interface PriceDisplayProps {
6
12
  amount: string | number;
7
13
  currency?: string;
8
14
  className?: string;
9
15
  showCurrency?: boolean;
10
- locale?: string;
11
16
  }
12
17
 
13
18
  /**
14
19
  * PriceDisplay - Formatted price with currency
15
- * Automatically uses the user's preferred currency from the currency store
20
+ * Automatically uses the user's preferred currency from the currency store.
21
+ * Locale is derived from currency code — no hardcoded locale.
16
22
  */
17
23
  export function PriceDisplay({
18
24
  amount,
19
25
  currency,
20
26
  className = "",
21
27
  showCurrency = true,
22
- locale = "pl-PL",
23
28
  }: PriceDisplayProps) {
24
29
  const preferredCurrency = useCurrencyStore((state) => state.currency);
30
+ const isLoaded = useCurrencyStore((state) => state.isLoaded);
25
31
  const finalCurrency = currency || preferredCurrency || "PLN";
32
+ const locale = getLocaleForCurrency(finalCurrency);
33
+
34
+ if (!isLoaded && !currency) {
35
+ return <span className={`inline-block h-4 w-16 animate-pulse rounded bg-muted ${className}`} />;
36
+ }
26
37
 
27
38
  const formatPrice = (value: string | number, curr: string) => {
28
39
  const numericValue = typeof value === "string" ? parseFloat(value) : value;
29
40
 
30
41
  if (isNaN(numericValue)) {
31
- return "";
42
+ return "\u2014";
32
43
  }
33
44
 
34
45
  if (showCurrency) {
@@ -56,7 +67,6 @@ export interface PriceRangeDisplayProps {
56
67
  maxAmount: string | number;
57
68
  currency?: string;
58
69
  className?: string;
59
- locale?: string;
60
70
  }
61
71
 
62
72
  /**
@@ -67,16 +77,21 @@ export function PriceRangeDisplay({
67
77
  maxAmount,
68
78
  currency,
69
79
  className = "",
70
- locale = "pl-PL",
71
80
  }: PriceRangeDisplayProps) {
72
81
  const preferredCurrency = useCurrencyStore((state) => state.currency);
82
+ const isLoaded = useCurrencyStore((state) => state.isLoaded);
73
83
  const finalCurrency = currency || preferredCurrency || "PLN";
84
+ const locale = getLocaleForCurrency(finalCurrency);
85
+
86
+ if (!isLoaded && !currency) {
87
+ return <span className={`inline-block h-4 w-24 animate-pulse rounded bg-muted ${className}`} />;
88
+ }
74
89
 
75
90
  const formatPrice = (value: string | number) => {
76
91
  const numericValue = typeof value === "string" ? parseFloat(value) : value;
77
92
 
78
93
  if (isNaN(numericValue)) {
79
- return "";
94
+ return "\u2014";
80
95
  }
81
96
 
82
97
  return new Intl.NumberFormat(locale, {
@@ -109,7 +124,6 @@ export interface ComparePriceDisplayProps {
109
124
  salePrice: string | number;
110
125
  currency?: string;
111
126
  className?: string;
112
- locale?: string;
113
127
  }
114
128
 
115
129
  /**
@@ -120,16 +134,26 @@ export function ComparePriceDisplay({
120
134
  salePrice,
121
135
  currency,
122
136
  className = "",
123
- locale = "pl-PL",
124
137
  }: ComparePriceDisplayProps) {
125
138
  const preferredCurrency = useCurrencyStore((state) => state.currency);
139
+ const isLoaded = useCurrencyStore((state) => state.isLoaded);
126
140
  const finalCurrency = currency || preferredCurrency || "PLN";
141
+ const locale = getLocaleForCurrency(finalCurrency);
142
+
143
+ if (!isLoaded && !currency) {
144
+ return (
145
+ <div className={`flex items-center gap-2 ${className}`}>
146
+ <span className="inline-block h-5 w-16 animate-pulse rounded bg-muted" />
147
+ <span className="inline-block h-4 w-12 animate-pulse rounded bg-muted" />
148
+ </div>
149
+ );
150
+ }
127
151
 
128
152
  const formatPrice = (value: string | number) => {
129
153
  const numericValue = typeof value === "string" ? parseFloat(value) : value;
130
154
 
131
155
  if (isNaN(numericValue)) {
132
- return "";
156
+ return "\u2014";
133
157
  }
134
158
 
135
159
  return new Intl.NumberFormat(locale, {
@@ -15,7 +15,7 @@
15
15
  import { cn } from "@/lib/utils";
16
16
  import { Tag, X, Percent, Truck, Gift } from "lucide-react";
17
17
  import { Button } from "@/components/ui/button";
18
- import { formatPrice } from "@/lib/format";
18
+ import { formatPrice } from "@doswiftly/storefront-sdk";
19
19
 
20
20
  export interface AppliedDiscount {
21
21
  id: string;
@@ -18,7 +18,7 @@ import { Tag, Loader2, Check, X, AlertCircle } from "lucide-react";
18
18
  import { Input } from "@/components/ui/input";
19
19
  import { Button } from "@/components/ui/button";
20
20
  import { Alert, AlertDescription } from "@/components/ui/alert";
21
- import { formatPrice } from "@/lib/format";
21
+ import { formatPrice } from "@doswiftly/storefront-sdk";
22
22
 
23
23
  export interface DiscountValidationResult {
24
24
  valid: boolean;
@@ -101,8 +101,8 @@ export function DiscountCodeInput({
101
101
  if (!result.valid) {
102
102
  setError(result.errorMessage || "Kod jest nieprawidłowy");
103
103
  }
104
- } catch (e: any) {
105
- setError(e.message || "Nie udało się zweryfikować kodu");
104
+ } catch (e: unknown) {
105
+ setError(e instanceof Error ? e.message : "Nie udało się zweryfikować kodu");
106
106
  } finally {
107
107
  setIsValidating(false);
108
108
  }
@@ -179,15 +179,15 @@ function formatValue(
179
179
  /**
180
180
  * Simple debounce function
181
181
  */
182
- function debounce<T extends (...args: any[]) => void>(
183
- fn: T,
182
+ function debounce<TArgs extends unknown[]>(
183
+ fn: (...args: TArgs) => void,
184
184
  delay: number
185
- ): T {
185
+ ): (...args: TArgs) => void {
186
186
  let timeoutId: NodeJS.Timeout;
187
- return ((...args: Parameters<T>) => {
187
+ return (...args: TArgs) => {
188
188
  clearTimeout(timeoutId);
189
189
  timeoutId = setTimeout(() => fn(...args), delay);
190
- }) as T;
190
+ };
191
191
  }
192
192
 
193
193
  export default RangeSliderFilter;
@@ -144,8 +144,8 @@ export function GiftCardInput({
144
144
  if (!result.valid && result.error) {
145
145
  setError(result.error.message);
146
146
  }
147
- } catch (err: any) {
148
- setError(err.message || "Failed to validate gift card");
147
+ } catch (err: unknown) {
148
+ setError(err instanceof Error ? err.message : "Failed to validate gift card");
149
149
  } finally {
150
150
  setIsValidating(false);
151
151
  }
@@ -3,6 +3,7 @@
3
3
  import Link from "next/link";
4
4
  import { cn } from "@/lib/utils";
5
5
  import { useCollections } from "@/lib/graphql/hooks";
6
+ import type { CollectionCardFields } from "@/lib/graphql/fragments";
6
7
 
7
8
  export interface CategoryGridProps {
8
9
  className?: string;
@@ -52,7 +53,7 @@ export function CategoryGrid({ className }: CategoryGridProps) {
52
53
  </div>
53
54
 
54
55
  <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-4">
55
- {collections.map((collection: any) => (
56
+ {collections.map((collection: CollectionCardFields) => (
56
57
  <Link
57
58
  key={collection.id}
58
59
  href={`/collections/${collection.handle}`}
@@ -0,0 +1,21 @@
1
+ # Fragment for CollectionCard component.
2
+ #
3
+ # Minimal collection data for listing views: homepage,
4
+ # collection grid, navigation menus.
5
+ #
6
+ # Usage in components:
7
+ # import type { CollectionCardFieldsFragment } from '@/generated/graphql';
8
+ # interface Props { collection: CollectionCardFieldsFragment }
9
+
10
+ fragment CollectionCardFields on Collection {
11
+ id
12
+ handle
13
+ title
14
+ description
15
+ image {
16
+ url
17
+ altText
18
+ width
19
+ height
20
+ }
21
+ }
@@ -4,17 +4,7 @@ import Link from "next/link";
4
4
  import { Button } from "@/components/ui/button";
5
5
  import { Card, CardContent } from "@/components/ui/card";
6
6
  import { useCollections } from "@/lib/graphql/hooks";
7
-
8
- export interface Collection {
9
- id: string;
10
- handle: string;
11
- title: string;
12
- description?: string;
13
- image?: {
14
- url: string;
15
- altText?: string | null;
16
- } | null;
17
- }
7
+ import type { CollectionCardFields } from "@/lib/graphql/fragments";
18
8
 
19
9
  export function FeaturedCollections() {
20
10
  // Fetch collections using GraphQL
@@ -61,7 +51,7 @@ export function FeaturedCollections() {
61
51
 
62
52
  {collections.length > 0 ? (
63
53
  <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
64
- {collections.map((collection: Collection) => (
54
+ {collections.map((collection: CollectionCardFields) => (
65
55
  <Link
66
56
  key={collection.id}
67
57
  href={`/collections/${collection.handle}`}
@@ -1,7 +1,6 @@
1
1
  export { HeroSection } from './hero-section';
2
2
  export { FeaturedProducts } from './featured-products';
3
3
  export { FeaturedCollections } from './featured-collections';
4
- export type { Collection } from './featured-collections';
5
4
  export { CategoryGrid } from './category-grid';
6
5
  export type { CategoryGridProps } from './category-grid';
7
6
  export { NewsletterSignup } from './newsletter-signup';
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import { useHydrated } from "@doswiftly/storefront-sdk/react";
4
+ import type { ReactNode } from "react";
5
+
6
+ interface HydratedProps {
7
+ children: ReactNode;
8
+ fallback?: ReactNode;
9
+ }
10
+
11
+ /**
12
+ * Wrapper component that only renders children after client hydration.
13
+ * Use for content that depends on browser-only state (localStorage, Zustand persist).
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Hydrated fallback={<Skeleton className="w-20 h-8" />}>
18
+ * <CartIcon />
19
+ * </Hydrated>
20
+ * ```
21
+ */
22
+ export function Hydrated({ children, fallback = null }: HydratedProps) {
23
+ return useHydrated() ? children : fallback;
24
+ }
@@ -7,7 +7,7 @@ import { cn } from "@/lib/utils";
7
7
 
8
8
  export interface BreadcrumbItem {
9
9
  label: string;
10
- href: string;
10
+ href?: string;
11
11
  }
12
12
 
13
13
  export interface BreadcrumbsProps {
@@ -68,7 +68,7 @@ export function Breadcrumbs({
68
68
  const isLast = index === breadcrumbItems.length - 1;
69
69
 
70
70
  return (
71
- <li key={item.href} className="flex items-center gap-2">
71
+ <li key={item.href || `breadcrumb-${index}`} className="flex items-center gap-2">
72
72
  {(showHome || index > 0) && (
73
73
  <ChevronRight
74
74
  className="h-4 w-4 text-muted-foreground"
@@ -76,10 +76,10 @@ export function Breadcrumbs({
76
76
  />
77
77
  )}
78
78
 
79
- {isLast ? (
79
+ {isLast || !item.href ? (
80
80
  <span
81
81
  className="font-medium text-foreground"
82
- aria-current="page"
82
+ aria-current={isLast ? "page" : undefined}
83
83
  >
84
84
  {item.label}
85
85
  </span>
@@ -0,0 +1,22 @@
1
+ # Fragment for CategoryCard / CategoryNav components.
2
+ #
3
+ # Category data for navigation, sidebar filters, breadcrumbs.
4
+ #
5
+ # Usage in components:
6
+ # import type { CategoryNodeFieldsFragment } from '@/generated/graphql';
7
+ # interface Props { category: CategoryNodeFieldsFragment }
8
+
9
+ fragment CategoryNodeFields on Category {
10
+ id
11
+ name
12
+ slug
13
+ description
14
+ productCount
15
+ level
16
+ path
17
+ sortOrder
18
+ image {
19
+ url
20
+ altText
21
+ }
22
+ }
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { useState, useRef, useEffect } from 'react';
21
- import { useCurrencyStore } from '@/stores/currency-store';
21
+ import { useCurrencyStore } from '@doswiftly/storefront-sdk/react';
22
22
  import { useQueryClient } from '@tanstack/react-query';
23
23
  import { ChevronDown, Check, Globe } from 'lucide-react';
24
24
 
@@ -154,7 +154,7 @@ export function CurrencySelector({
154
154
  const supportedCurrencies = useCurrencyStore((state) => state.supportedCurrencies);
155
155
  const setCurrency = useCurrencyStore((state) => state.setCurrency);
156
156
  const isLoaded = useCurrencyStore((state) => state.isLoaded);
157
-
157
+
158
158
  // React Query client for cache invalidation
159
159
  const queryClient = useQueryClient();
160
160
 
@@ -9,13 +9,21 @@ import { SearchInput } from "@/components/commerce/search-input";
9
9
  import { Navigation } from "@/components/layout/navigation";
10
10
  import { AccountMenu } from "@/components/auth/account-menu";
11
11
  import { ThemeSwitcher } from "@/components/layout/theme-switcher";
12
- import { useAuthStore } from "@/stores/auth-store";
12
+ import { useAuthStore, useAuthHydrated } from "@doswiftly/storefront-sdk/react";
13
+ import { useHydrated } from "@doswiftly/storefront-sdk/react";
14
+ import { useAuthSync } from "@/hooks/use-auth-sync";
13
15
 
14
16
  export function Header() {
15
17
  const [isMenuOpen, setIsMenuOpen] = useState(false);
16
-
17
- // Get authentication state from auth store
18
+
19
+ // Auto-fix cookie/store desync (e.g., localStorage cleared but cookie persists)
20
+ useAuthSync();
21
+
22
+ // Double-guard: DOM must be mounted (useHydrated) AND store must be rehydrated (useAuthHydrated from Zustand persist)
23
+ const hydrated = useHydrated();
18
24
  const { isAuthenticated, customer } = useAuthStore();
25
+ const authHydrated = useAuthHydrated();
26
+ const showAuth = hydrated && authHydrated;
19
27
  const customerName = customer
20
28
  ? `${customer.firstName || ""} ${customer.lastName || ""}`.trim() || customer.email
21
29
  : undefined;
@@ -59,32 +67,34 @@ export function Header() {
59
67
  <div className="hidden w-64 lg:block">
60
68
  <SearchInput placeholder="Search products..." />
61
69
  </div>
62
-
70
+
63
71
  {/* Currency Selector */}
64
72
  <div className="hidden md:block">
65
73
  <CurrencySelector variant="compact" />
66
74
  </div>
67
-
75
+
68
76
  {/* Theme Switcher */}
69
77
  <div className="hidden md:block">
70
78
  <ThemeSwitcher />
71
79
  </div>
72
-
73
- {/* Account Menu or Login Link */}
74
- {isAuthenticated ? (
75
- <AccountMenu customerName={customerName} />
76
- ) : (
77
- <Link
78
- href="/auth/login"
79
- className="hidden md:inline-flex items-center rounded-md px-3 py-2 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
80
- >
81
- Sign In
82
- </Link>
80
+
81
+ {/* Account Menu or Login Link — hidden until DOM + auth store hydrate */}
82
+ {showAuth && (
83
+ isAuthenticated ? (
84
+ <AccountMenu customerName={customerName} />
85
+ ) : (
86
+ <Link
87
+ href="/auth/login"
88
+ className="hidden md:inline-flex items-center rounded-md px-3 py-2 text-sm font-medium text-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
89
+ >
90
+ Sign In
91
+ </Link>
92
+ )
83
93
  )}
84
-
94
+
85
95
  {/* Cart Icon */}
86
96
  <CartIcon />
87
-
97
+
88
98
  {/* Mobile Menu Toggle */}
89
99
  <button
90
100
  className="p-2 text-foreground hover:text-primary md:hidden"
@@ -109,13 +119,13 @@ export function Header() {
109
119
  <div className="lg:hidden">
110
120
  <SearchInput placeholder="Search products..." />
111
121
  </div>
112
-
122
+
113
123
  {/* Mobile Currency Selector */}
114
124
  <div className="flex items-center justify-between">
115
125
  <span className="text-sm font-medium text-muted-foreground">Currency</span>
116
126
  <CurrencySelector variant="compact" />
117
127
  </div>
118
-
128
+
119
129
  {/* Mobile Navigation Links */}
120
130
  {navigationItems.map((item) => (
121
131
  <Link
@@ -127,9 +137,9 @@ export function Header() {
127
137
  {item.label}
128
138
  </Link>
129
139
  ))}
130
-
131
- {/* Mobile Account Link */}
132
- {!isAuthenticated && (
140
+
141
+ {/* Mobile Account Link — guarded by showAuth */}
142
+ {showAuth && !isAuthenticated && (
133
143
  <Link
134
144
  href="/auth/login"
135
145
  className="text-foreground hover:text-primary transition-colors font-medium"
@@ -10,19 +10,10 @@ import { Coins, AlertTriangle } from 'lucide-react';
10
10
  import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
11
11
  import { Badge } from '@/components/ui/badge';
12
12
  import { cn } from '@/lib/utils';
13
-
14
- interface PointsSummary {
15
- totalPoints: number;
16
- currentPoints: number;
17
- pendingPoints: number;
18
- redeemedPoints: number;
19
- expiredPoints: number;
20
- expiringPoints?: number;
21
- nextExpiryDate?: string;
22
- }
13
+ import type { LoyaltyPointsSummary } from '@/lib/graphql/fragments';
23
14
 
24
15
  interface PointsBalanceProps {
25
- points: PointsSummary;
16
+ points: LoyaltyPointsSummary;
26
17
  variant?: 'default' | 'compact';
27
18
  className?: string;
28
19
  }
@@ -10,35 +10,14 @@ import { ArrowUpCircle, ArrowDownCircle, Clock, Gift, ShoppingCart, UserPlus, St
10
10
  import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
11
11
  import { Badge } from '@/components/ui/badge';
12
12
  import { cn } from '@/lib/utils';
13
-
14
- type TransactionType =
15
- | 'EARN_PURCHASE'
16
- | 'EARN_SIGNUP'
17
- | 'EARN_REFERRAL'
18
- | 'EARN_REVIEW'
19
- | 'EARN_BIRTHDAY'
20
- | 'EARN_BONUS'
21
- | 'REDEEM'
22
- | 'EXPIRE'
23
- | 'ADJUST';
24
-
25
- interface Transaction {
26
- id: string;
27
- type: TransactionType;
28
- points: number;
29
- balanceAfter: number;
30
- orderId?: string;
31
- description?: string;
32
- expiresAt?: string;
33
- createdAt: string;
34
- }
13
+ import type { LoyaltyTransaction } from '@/lib/graphql/fragments';
35
14
 
36
15
  interface PointsHistoryProps {
37
- transactions: Transaction[];
16
+ transactions: LoyaltyTransaction[];
38
17
  className?: string;
39
18
  }
40
19
 
41
- const transactionConfig: Record<TransactionType, {
20
+ const transactionConfig: Record<string, {
42
21
  label: string;
43
22
  icon: typeof ArrowUpCircle;
44
23
  color: string;
@@ -127,7 +106,11 @@ export function PointsHistory({ transactions, className }: PointsHistoryProps) {
127
106
  <CardContent>
128
107
  <div className="space-y-4">
129
108
  {transactions.map((transaction) => {
130
- const config = transactionConfig[transaction.type];
109
+ const config = transactionConfig[transaction.type] ?? {
110
+ label: transaction.type,
111
+ icon: AlertCircle,
112
+ color: 'text-muted-foreground',
113
+ };
131
114
  const Icon = config.icon;
132
115
  const isEarning = transaction.points > 0;
133
116