@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.
- package/README.md +23 -323
- package/dist/commands/check.js +1 -1
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +43 -20
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +4 -4
- package/dist/commands/sdk.js +5 -5
- package/dist/commands/sdk.js.map +1 -1
- package/dist/commands/template.js +4 -4
- package/dist/commands/template.js.map +1 -1
- package/dist/commands/types.js +5 -5
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/verify.js +2 -2
- package/dist/commands/verify.js.map +1 -1
- package/dist/lib/package-manager.d.ts +1 -1
- package/dist/lib/package-manager.js +1 -1
- package/package.json +1 -1
- package/templates/storefront-minimal/wrangler.toml +4 -0
- package/templates/storefront-nextjs/README.md +16 -12
- package/templates/storefront-nextjs/app/account/orders/page.tsx +2 -2
- package/templates/storefront-nextjs/app/account/page.tsx +2 -2
- package/templates/storefront-nextjs/app/auth/login/page.tsx +1 -1
- package/templates/storefront-nextjs/app/auth/register/page.tsx +1 -1
- package/templates/storefront-nextjs/app/cart/page.tsx +1 -1
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +2 -2
- package/templates/storefront-nextjs/app/categories/page.tsx +1 -1
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs/app/collections/page.tsx +1 -1
- package/templates/storefront-nextjs/app/page.tsx +1 -1
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-nextjs/app/products/page.tsx +2 -2
- package/templates/storefront-nextjs/app/search/page.tsx +1 -1
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +1 -1
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +1 -1
- package/templates/storefront-nextjs/components/providers.tsx +1 -1
- package/templates/storefront-nextjs/lib/currency.tsx +3 -3
- package/templates/storefront-nextjs/lib/format.ts +1 -1
- package/templates/storefront-nextjs/lib/graphql-queries.ts +3 -3
- package/templates/storefront-nextjs/package.dev.json +1 -1
- package/templates/storefront-nextjs/package.json +1 -1
- package/templates/storefront-nextjs/package.json.template +1 -1
- package/templates/storefront-nextjs/wrangler.toml +4 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/deploy.yml +47 -0
- package/templates/storefront-nextjs-shadcn/.github/workflows/preview.yml +47 -0
- package/templates/storefront-nextjs-shadcn/CLAUDE.md +148 -35
- package/templates/storefront-nextjs-shadcn/README.md +29 -162
- package/templates/storefront-nextjs-shadcn/app/account/addresses/page.tsx +98 -91
- package/templates/storefront-nextjs-shadcn/app/account/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/account/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/account/loyalty/page.tsx +53 -162
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/loading.tsx +60 -0
- package/templates/storefront-nextjs-shadcn/app/account/orders/[id]/page.tsx +36 -47
- package/templates/storefront-nextjs-shadcn/app/account/orders/page.tsx +46 -29
- package/templates/storefront-nextjs-shadcn/app/account/page.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/app/account/settings/page.tsx +108 -71
- package/templates/storefront-nextjs-shadcn/app/api/auth/clear-token/route.ts +2 -86
- package/templates/storefront-nextjs-shadcn/app/api/auth/set-token/route.ts +2 -124
- package/templates/storefront-nextjs-shadcn/app/auth/forgot-password/page.tsx +10 -5
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/loading.tsx +17 -0
- package/templates/storefront-nextjs-shadcn/app/blog/[slug]/page.tsx +43 -2
- package/templates/storefront-nextjs-shadcn/app/blog/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/brands/page.tsx +2 -1
- package/templates/storefront-nextjs-shadcn/app/cart/loading.tsx +26 -0
- package/templates/storefront-nextjs-shadcn/app/cart/page.tsx +6 -3
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/category-products-client.tsx +56 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/categories/[slug]/page.tsx +76 -59
- package/templates/storefront-nextjs-shadcn/app/categories/page.tsx +8 -4
- package/templates/storefront-nextjs-shadcn/app/checkout/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/loading.tsx +31 -0
- package/templates/storefront-nextjs-shadcn/app/checkout/page.tsx +116 -79
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/loading.tsx +19 -0
- package/templates/storefront-nextjs-shadcn/app/collections/[handle]/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/collections/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/collections/page.tsx +7 -4
- package/templates/storefront-nextjs-shadcn/app/global-error.tsx +117 -0
- package/templates/storefront-nextjs-shadcn/app/globals.css +8 -0
- package/templates/storefront-nextjs-shadcn/app/layout.tsx +46 -11
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/error.tsx +43 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/loading.tsx +29 -0
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/page.tsx +6 -6
- package/templates/storefront-nextjs-shadcn/app/products/[slug]/product-client.tsx +15 -61
- package/templates/storefront-nextjs-shadcn/app/products/loading.tsx +32 -0
- package/templates/storefront-nextjs-shadcn/app/products/products-client.tsx +405 -151
- package/templates/storefront-nextjs-shadcn/app/search/loading.tsx +18 -0
- package/templates/storefront-nextjs-shadcn/app/wishlist/page.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/codegen.ts +48 -31
- package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +26 -24
- package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +36 -0
- package/templates/storefront-nextjs-shadcn/components/auth/account-menu.tsx +9 -9
- package/templates/storefront-nextjs-shadcn/components/auth/login-form.tsx +11 -37
- package/templates/storefront-nextjs-shadcn/components/auth/register-form.tsx +37 -23
- package/templates/storefront-nextjs-shadcn/components/cart/cart-drawer.tsx +4 -3
- package/templates/storefront-nextjs-shadcn/components/cart/cart-icon.tsx +8 -5
- package/templates/storefront-nextjs-shadcn/components/cart/cart-item.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +53 -0
- package/templates/storefront-nextjs-shadcn/components/cart/cart-summary.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/cart/shipping-estimator.tsx +22 -7
- package/templates/storefront-nextjs-shadcn/components/commerce/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/commerce/product-actions.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/commerce/search-input.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/common/price-display.tsx +35 -11
- package/templates/storefront-nextjs-shadcn/components/discount/discount-breakdown.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +3 -3
- package/templates/storefront-nextjs-shadcn/components/filters/range-slider-filter.tsx +5 -5
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/home/category-grid.tsx +2 -1
- package/templates/storefront-nextjs-shadcn/components/home/collection-card.fragment.graphql +21 -0
- package/templates/storefront-nextjs-shadcn/components/home/featured-collections.tsx +2 -12
- package/templates/storefront-nextjs-shadcn/components/home/index.ts +0 -1
- package/templates/storefront-nextjs-shadcn/components/hydrated.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/layout/breadcrumbs.tsx +4 -4
- package/templates/storefront-nextjs-shadcn/components/layout/category-node.fragment.graphql +22 -0
- package/templates/storefront-nextjs-shadcn/components/layout/currency-selector.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/layout/header.tsx +33 -23
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-balance.tsx +2 -11
- package/templates/storefront-nextjs-shadcn/components/loyalty/points-history.tsx +8 -25
- package/templates/storefront-nextjs-shadcn/components/loyalty/referral-section.tsx +10 -19
- package/templates/storefront-nextjs-shadcn/components/loyalty/rewards-catalog.tsx +17 -41
- package/templates/storefront-nextjs-shadcn/components/loyalty/tier-progress.tsx +2 -29
- package/templates/storefront-nextjs-shadcn/components/order/index.ts +6 -1
- package/templates/storefront-nextjs-shadcn/components/product/b2b-price-display.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/components/product/filter-active-pills.tsx +69 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-mobile-sheet.tsx +84 -0
- package/templates/storefront-nextjs-shadcn/components/product/filter-price-range.tsx +138 -0
- package/templates/storefront-nextjs-shadcn/components/product/index.ts +9 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-card.fragment.graphql +49 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +3 -31
- package/templates/storefront-nextjs-shadcn/components/product/product-detail.fragment.graphql +52 -0
- package/templates/storefront-nextjs-shadcn/components/product/product-filters.tsx +176 -123
- package/templates/storefront-nextjs-shadcn/components/product/product-grid.tsx +3 -5
- package/templates/storefront-nextjs-shadcn/components/product/product-image.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-price.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-reviews.tsx +5 -4
- package/templates/storefront-nextjs-shadcn/components/product/product-sort.tsx +19 -7
- package/templates/storefront-nextjs-shadcn/components/product/product-variant-selector.tsx +8 -23
- package/templates/storefront-nextjs-shadcn/components/product/product-variant.fragment.graphql +51 -0
- package/templates/storefront-nextjs-shadcn/components/product/review-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/product/review-form.tsx +1 -7
- package/templates/storefront-nextjs-shadcn/components/product/savings-display.tsx +17 -2
- package/templates/storefront-nextjs-shadcn/components/product/similar-products.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/providers/index.ts +1 -1
- package/templates/storefront-nextjs-shadcn/components/providers/stores-provider.tsx +30 -0
- package/templates/storefront-nextjs-shadcn/components/providers/theme-provider.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/returns/index.ts +2 -2
- package/templates/storefront-nextjs-shadcn/components/returns/return-request-form.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/search/search-results.tsx +3 -2
- package/templates/storefront-nextjs-shadcn/components/ui/form.tsx +174 -0
- package/templates/storefront-nextjs-shadcn/components/ui/index.ts +30 -2
- package/templates/storefront-nextjs-shadcn/components/ui/progress.tsx +40 -0
- package/templates/storefront-nextjs-shadcn/components/ui/sheet.tsx +107 -0
- package/templates/storefront-nextjs-shadcn/components/ui/slider.tsx +33 -0
- package/templates/storefront-nextjs-shadcn/components/ui/textarea.tsx +24 -0
- package/templates/storefront-nextjs-shadcn/components/wishlist/wishlist-icon.tsx +3 -1
- package/templates/storefront-nextjs-shadcn/generated/graphql.ts +12779 -0
- package/templates/storefront-nextjs-shadcn/graphql/custom.example.graphql +159 -0
- package/templates/storefront-nextjs-shadcn/hooks/index.ts +2 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +42 -0
- package/templates/storefront-nextjs-shadcn/hooks/use-auth.ts +17 -295
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +51 -19
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +13 -9
- package/templates/storefront-nextjs-shadcn/lib/auth/routes.ts +4 -17
- package/templates/storefront-nextjs-shadcn/lib/graphql/client.ts +22 -99
- package/templates/storefront-nextjs-shadcn/lib/graphql/config.ts +32 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/fragments.ts +34 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +687 -632
- package/templates/storefront-nextjs-shadcn/lib/graphql/query-keys.ts +86 -0
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +131 -182
- package/templates/storefront-nextjs-shadcn/lib/graphql/types.ts +62 -0
- package/templates/storefront-nextjs-shadcn/lib/theme/theme-config.ts +0 -17
- package/templates/storefront-nextjs-shadcn/next-env.d.ts +6 -0
- package/templates/storefront-nextjs-shadcn/package.dev.json +1 -3
- package/templates/storefront-nextjs-shadcn/package.json +12 -13
- package/templates/storefront-nextjs-shadcn/package.json.template +6 -7
- package/templates/storefront-nextjs-shadcn/proxy.ts +3 -4
- package/templates/storefront-nextjs-shadcn/stores/cart-store.ts +41 -39
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +64 -75
- package/templates/storefront-nextjs-shadcn/stores/wishlist-store.ts +178 -177
- package/templates/storefront-nextjs-shadcn/tsconfig.json +23 -5
- package/templates/storefront-nextjs-shadcn/wrangler.toml +4 -0
- package/templates/storefront-nextjs-shadcn/CART_INTEGRATION.md +0 -282
- package/templates/storefront-nextjs-shadcn/GRAPHQL_DOCUMENT_NAMES.md +0 -190
- package/templates/storefront-nextjs-shadcn/GRAPHQL_ERROR_HANDLING.md +0 -263
- package/templates/storefront-nextjs-shadcn/GRAPHQL_FIXES_SUMMARY.md +0 -135
- package/templates/storefront-nextjs-shadcn/GRAPHQL_INTEGRATION_COMPLETE.md +0 -142
- package/templates/storefront-nextjs-shadcn/INTEGRATION_CHECKLIST.md +0 -448
- package/templates/storefront-nextjs-shadcn/PRODUCT_DETAIL_PAGE_IMPLEMENTATION.md +0 -307
- package/templates/storefront-nextjs-shadcn/THEME_CUSTOMIZATION.md +0 -245
- package/templates/storefront-nextjs-shadcn/components/providers/currency-provider.tsx +0 -103
- package/templates/storefront-nextjs-shadcn/graphql/collections.example.ts +0 -168
- package/templates/storefront-nextjs-shadcn/graphql/products.example.ts +0 -160
- package/templates/storefront-nextjs-shadcn/lib/auth/cookies.ts +0 -220
- package/templates/storefront-nextjs-shadcn/lib/config.ts +0 -46
- package/templates/storefront-nextjs-shadcn/lib/currency/IMPLEMENTATION_SUMMARY.md +0 -254
- package/templates/storefront-nextjs-shadcn/lib/currency/README.md +0 -464
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.test.ts +0 -328
- package/templates/storefront-nextjs-shadcn/lib/currency/cookie-manager.ts +0 -295
- package/templates/storefront-nextjs-shadcn/lib/currency/index.ts +0 -27
- package/templates/storefront-nextjs-shadcn/lib/format.ts +0 -226
- package/templates/storefront-nextjs-shadcn/lib/hooks.ts +0 -30
- package/templates/storefront-nextjs-shadcn/stores/auth-store.ts +0 -66
- 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 {
|
|
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
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 "
|
|
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.
|
|
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 "
|
|
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 "
|
|
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
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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:
|
|
105
|
-
setError(e.message
|
|
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<
|
|
183
|
-
fn:
|
|
182
|
+
function debounce<TArgs extends unknown[]>(
|
|
183
|
+
fn: (...args: TArgs) => void,
|
|
184
184
|
delay: number
|
|
185
|
-
):
|
|
185
|
+
): (...args: TArgs) => void {
|
|
186
186
|
let timeoutId: NodeJS.Timeout;
|
|
187
|
-
return (
|
|
187
|
+
return (...args: TArgs) => {
|
|
188
188
|
clearTimeout(timeoutId);
|
|
189
189
|
timeoutId = setTimeout(() => fn(...args), delay);
|
|
190
|
-
}
|
|
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:
|
|
148
|
-
setError(err.message
|
|
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:
|
|
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:
|
|
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
|
|
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 '
|
|
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 "
|
|
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
|
-
//
|
|
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
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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:
|
|
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:
|
|
16
|
+
transactions: LoyaltyTransaction[];
|
|
38
17
|
className?: string;
|
|
39
18
|
}
|
|
40
19
|
|
|
41
|
-
const transactionConfig: Record<
|
|
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
|
|