@kasuvia/sdk 0.0.8 → 0.0.9
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 +42 -15
- package/dist/AddressAutocomplete-CjcqPElD.d.mts +46 -0
- package/dist/AddressAutocomplete-CvJHuRkf.d.ts +46 -0
- package/dist/components/index.d.mts +497 -0
- package/dist/components/index.d.ts +497 -0
- package/dist/components/index.js +4217 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +4194 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/server.d.mts +26 -0
- package/dist/config/server.d.ts +26 -0
- package/dist/config/server.js +38 -0
- package/dist/config/server.js.map +1 -0
- package/dist/config/server.mjs +13 -0
- package/dist/config/server.mjs.map +1 -0
- package/dist/hooks/index.d.mts +393 -160
- package/dist/hooks/index.d.ts +393 -160
- package/dist/hooks/index.js +2792 -2137
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +2734 -2121
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index-CNW_LINy.d.mts +578 -0
- package/dist/index-CNW_LINy.d.ts +578 -0
- package/dist/{index-C1chUA3P.d.mts → index-F7N6UItS.d.mts} +1 -1
- package/dist/{index-C1chUA3P.d.ts → index-F7N6UItS.d.ts} +1 -1
- package/dist/index-NkI10J_E.d.mts +40 -0
- package/dist/index-NkI10J_E.d.ts +40 -0
- package/dist/index.d.mts +20437 -103
- package/dist/index.d.ts +20437 -103
- package/dist/index.js +2111 -1802
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2006 -1786
- package/dist/index.mjs.map +1 -1
- package/dist/media.service-Cx7dQjzX.d.mts +1393 -0
- package/dist/media.service-mrBFJ2UQ.d.ts +1393 -0
- package/dist/services/index.d.mts +908 -2
- package/dist/services/index.d.ts +908 -2
- package/dist/services/index.js +1492 -1686
- package/dist/services/index.js.map +1 -1
- package/dist/services/index.mjs +1431 -1670
- package/dist/services/index.mjs.map +1 -1
- package/package.json +46 -6
- package/src/components/bespoke/BespokeAvailabilityBadge.tsx +32 -0
- package/src/components/bespoke/BespokeProgressTracker.tsx +58 -0
- package/src/components/bespoke/index.ts +3 -0
- package/src/components/booking/SlotPicker.tsx +97 -0
- package/src/components/booking/index.ts +1 -0
- package/src/components/cart/CartItem.tsx +81 -0
- package/src/components/cart/CartSidebar.tsx +115 -0
- package/src/components/cart/CartTypeMismatchBanner.tsx +55 -0
- package/src/components/cart/index.ts +3 -0
- package/src/components/index.ts +8 -0
- package/src/components/orders/DeliverableReviewer.tsx +139 -0
- package/src/components/orders/index.ts +1 -0
- package/src/components/product/ProductCard.tsx +350 -0
- package/src/components/product/ProductGrid.tsx +84 -0
- package/src/components/product/index.ts +2 -0
- package/src/components/shared/AddressAutocomplete.tsx +212 -0
- package/src/components/shared/AnalyticsScripts.tsx +56 -0
- package/src/components/shared/CookieConsentBanner.tsx +98 -0
- package/src/components/shared/ImageEditorModal.tsx +1066 -0
- package/src/components/shared/ImageViewerModal.tsx +598 -0
- package/src/components/shared/LucideReactIcon.tsx +52 -0
- package/src/components/shared/MapPinDrop.tsx +101 -0
- package/src/components/shared/MultiSelectPicker.tsx +193 -0
- package/src/components/shared/OfflineBanner.tsx +46 -0
- package/src/components/shared/SearchableInput.tsx +153 -0
- package/src/components/shared/SearchablePopover.tsx +166 -0
- package/src/components/shared/SmartImage.tsx +311 -0
- package/src/components/shared/TruncatedValue.tsx +27 -0
- package/src/components/shared/UnsavedChangesGuard.tsx +138 -0
- package/src/components/shared/index.ts +15 -0
- package/src/components/skeletons/CardSkeleton.tsx +26 -0
- package/src/components/skeletons/CartItemSkeleton.tsx +26 -0
- package/src/components/skeletons/OrderItemSkeleton.tsx +26 -0
- package/src/components/skeletons/ProductCardSkeleton.tsx +26 -0
- package/src/components/skeletons/TableSkeleton.tsx +42 -0
- package/src/components/skeletons/index.ts +5 -0
- package/src/config/index.ts +22 -4
- package/src/config/server.ts +45 -0
- package/src/context/sdk-context.tsx +5 -2
- package/src/generated/auth-openapi.ts +2935 -0
- package/src/generated/cart-openapi.ts +652 -0
- package/src/generated/cms-openapi.ts +1668 -0
- package/src/generated/crm-openapi.ts +986 -0
- package/src/generated/graphql.ts +126 -0
- package/src/generated/index.ts +11 -0
- package/src/generated/media-openapi.ts +613 -0
- package/src/generated/notification-openapi.ts +862 -0
- package/src/generated/payment-openapi.ts +2713 -0
- package/src/generated/store-openapi.ts +9510 -0
- package/src/graphql/operations/collections.graphql +32 -0
- package/src/graphql/operations/products.graphql +197 -0
- package/src/graphql/operations/wishlist.graphql +25 -0
- package/src/hooks/index.ts +9 -2
- package/src/hooks/use-auth.ts +163 -69
- package/src/hooks/use-bespoke-availability.ts +39 -22
- package/src/hooks/use-business-config.ts +51 -0
- package/src/hooks/use-cart.ts +310 -238
- package/src/hooks/use-cms.ts +107 -127
- package/src/hooks/use-collections.ts +35 -55
- package/src/hooks/use-consultations.ts +9 -136
- package/src/hooks/use-countries.ts +19 -28
- package/src/hooks/use-currencies.ts +8 -20
- package/src/hooks/use-customer-address.ts +64 -0
- package/src/hooks/use-feedback.ts +34 -0
- package/src/hooks/use-languages.ts +26 -39
- package/src/hooks/use-media-upload.ts +23 -0
- package/src/hooks/use-notification-stream.ts +88 -0
- package/src/hooks/use-notifications.ts +163 -0
- package/src/hooks/use-order-stream.ts +29 -29
- package/src/hooks/use-orders.ts +91 -73
- package/src/hooks/use-payment.ts +116 -84
- package/src/hooks/use-products.ts +65 -269
- package/src/hooks/use-push-notifications.ts +122 -0
- package/src/hooks/use-reviews.ts +175 -0
- package/src/hooks/use-service-booking.ts +109 -132
- package/src/hooks/use-states.ts +16 -30
- package/src/hooks/use-wishlist.ts +44 -69
- package/src/index.ts +15 -11
- package/src/services/auth.service.ts +422 -132
- package/src/services/bespoke.service.ts +69 -10
- package/src/services/cart.service.ts +155 -186
- package/src/services/cms.service.ts +41 -50
- package/src/services/collection.service.ts +25 -63
- package/src/services/config.service.ts +10 -21
- package/src/services/consultation.service.ts +36 -93
- package/src/services/feedback.service.ts +76 -0
- package/src/services/index.ts +7 -1
- package/src/services/legal.service.ts +62 -0
- package/src/services/logistics.service.ts +235 -53
- package/src/services/media.service.ts +115 -0
- package/src/services/notifications.service.ts +204 -20
- package/src/services/order.service.ts +498 -127
- package/src/services/payment.service.ts +149 -100
- package/src/services/product.service.ts +157 -814
- package/src/services/review.service.ts +211 -0
- package/src/services/service-booking.service.ts +198 -99
- package/src/services/wishlist.service.ts +27 -74
- package/src/types/auth/index.ts +161 -18
- package/src/types/business/index.ts +9 -1
- package/src/types/cms/index.ts +91 -6
- package/src/types/cms/legal.ts +18 -0
- package/src/types/consultations/index.ts +60 -68
- package/src/types/index.ts +5 -3
- package/src/types/media/index.ts +35 -3
- package/src/types/payments/index.ts +80 -16
- package/src/types/reviews/index.ts +116 -0
- package/src/types/services/index.ts +113 -38
- package/src/types/store/checkout.ts +53 -0
- package/src/types/store/index.ts +273 -52
- package/src/types/store/invoice.ts +31 -0
- package/src/types/store/shipping.ts +84 -3
- package/src/types/ui/index.ts +1 -1
- package/src/utils/checkout.ts +19 -3
- package/src/utils/cookieConsent.ts +34 -0
- package/src/utils/format.ts +408 -0
- package/src/utils/geolocation.ts +134 -0
- package/src/utils/graphql-url.ts +47 -0
- package/src/utils/guest-session.ts +59 -0
- package/src/utils/http-client.ts +147 -0
- package/src/utils/index.ts +11 -4
- package/src/utils/status-colors.ts +79 -0
- package/src/utils/token-refresh.ts +115 -0
- package/src/utils/validators.ts +13 -0
- package/src/utils/youtube.ts +18 -0
- package/dist/index-C5YO9gTl.d.mts +0 -507
- package/dist/index-C5YO9gTl.d.ts +0 -507
- package/dist/index-CefG7_xq.d.mts +0 -793
- package/dist/index-DeHCf3zI.d.ts +0 -793
- package/src/graphql/collections.graphql.ts +0 -48
- package/src/graphql/index.ts +0 -7
- package/src/graphql/products.graphql.ts +0 -860
- package/src/graphql/wishlist.graphql.ts +0 -34
- package/src/hooks/use-consultation-stream.ts +0 -142
- package/src/types/bespoke/index.ts +0 -39
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/index.ts","../../src/components/skeletons/TableSkeleton.tsx","../../src/components/skeletons/CardSkeleton.tsx","../../src/components/skeletons/CartItemSkeleton.tsx","../../src/components/skeletons/OrderItemSkeleton.tsx","../../src/components/skeletons/ProductCardSkeleton.tsx","../../src/components/cart/CartItem.tsx","../../src/hooks/use-cart.ts","../../src/utils/token-refresh.ts","../../src/utils/http-client.ts","../../src/services/cart.service.ts","../../src/utils/logger.ts","../../src/context/sdk-context.tsx","../../src/components/cart/CartTypeMismatchBanner.tsx","../../src/utils/format.ts","../../src/components/cart/CartSidebar.tsx","../../src/components/product/ProductCard.tsx","../../src/components/shared/SmartImage.tsx","../../src/components/product/ProductGrid.tsx","../../src/utils/graphql-url.ts","../../src/generated/graphql.ts","../../src/services/product.service.ts","../../src/utils/cache.ts","../../src/components/booking/SlotPicker.tsx","../../src/hooks/use-service-booking.ts","../../src/services/service-booking.service.ts","../../src/hooks/use-bespoke-availability.ts","../../src/services/bespoke.service.ts","../../src/components/bespoke/BespokeAvailabilityBadge.tsx","../../src/components/bespoke/BespokeProgressTracker.tsx","../../src/components/orders/DeliverableReviewer.tsx","../../src/components/shared/TruncatedValue.tsx","../../src/components/shared/AddressAutocomplete.tsx","../../src/services/logistics.service.ts","../../src/components/shared/OfflineBanner.tsx","../../src/components/shared/MapPinDrop.tsx","../../src/components/shared/SearchableInput.tsx","../../src/components/shared/SearchablePopover.tsx","../../src/components/shared/LucideReactIcon.tsx","../../src/components/shared/UnsavedChangesGuard.tsx","../../src/components/shared/MultiSelectPicker.tsx","../../src/components/shared/ImageEditorModal.tsx","../../src/components/shared/ImageViewerModal.tsx","../../src/components/shared/AnalyticsScripts.tsx","../../src/utils/cookieConsent.ts","../../src/components/shared/CookieConsentBanner.tsx"],"sourcesContent":["export * from './skeletons'\nexport * from './cart'\nexport * from './product'\nexport * from './booking'\nexport * from './bespoke'\nexport * from './orders'\nexport * from './shared'\n\n","import React from 'react'\n\nexport interface TableSkeletonProps {\n rows?: number\n columns?: number\n className?: string\n rowClassName?: string\n}\n\nexport function TableSkeleton({\n rows = 5,\n columns = 4,\n className = \"w-full border-collapse\",\n rowClassName = \"border-b\"\n}: TableSkeletonProps) {\n return (\n <div className=\"w-full overflow-x-auto\">\n <table className={className}>\n <thead>\n <tr className={rowClassName}>\n {Array.from({ length: columns }).map((_, c) => (\n <th key={c} className=\"p-3 text-left\">\n <div className=\"h-4 bg-muted rounded animate-pulse w-20\" />\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {Array.from({ length: rows }).map((_, r) => (\n <tr key={r} className={rowClassName}>\n {Array.from({ length: columns }).map((_, c) => (\n <td key={c} className=\"p-3\">\n <div className=\"h-4 bg-muted rounded animate-pulse w-full max-w-[120px]\" />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n","import React from 'react'\n\nexport interface CardSkeletonProps {\n count?: number\n containerClassName?: string\n cardClassName?: string\n}\n\nexport function CardSkeleton({\n count = 4,\n containerClassName = \"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6\",\n cardClassName = \"rounded-xl border p-4 space-y-3 animate-pulse bg-card\"\n}: CardSkeletonProps) {\n return (\n <div className={containerClassName}>\n {Array.from({ length: count }).map((_, i) => (\n <div key={i} className={cardClassName}>\n <div className=\"w-full aspect-square bg-muted rounded-lg\" />\n <div className=\"h-4 bg-muted rounded w-3/4\" />\n <div className=\"h-4 bg-muted rounded w-1/2\" />\n <div className=\"h-8 bg-muted rounded w-full mt-2\" />\n </div>\n ))}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface CartItemSkeletonProps {\n count?: number\n className?: string\n}\n\nexport function CartItemSkeleton({\n count = 3,\n className = \"space-y-4\"\n}: CartItemSkeletonProps) {\n return (\n <div className={className}>\n {Array.from({ length: count }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-4 p-3 border-b animate-pulse\">\n <div className=\"w-16 h-16 bg-muted rounded-md shrink-0\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 bg-muted rounded w-2/3\" />\n <div className=\"h-3 bg-muted rounded w-1/3\" />\n </div>\n <div className=\"h-6 w-16 bg-muted rounded shrink-0\" />\n </div>\n ))}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface OrderItemSkeletonProps {\n count?: number\n className?: string\n}\n\nexport function OrderItemSkeleton({\n count = 3,\n className = \"space-y-4\"\n}: OrderItemSkeletonProps) {\n return (\n <div className={className}>\n {Array.from({ length: count }).map((_, i) => (\n <div key={i} className=\"p-4 border rounded-lg animate-pulse space-y-3\">\n <div className=\"flex justify-between items-center\">\n <div className=\"h-4 bg-muted rounded w-1/3\" />\n <div className=\"h-5 w-20 bg-muted rounded-full\" />\n </div>\n <div className=\"h-3 bg-muted rounded w-1/4\" />\n <div className=\"h-4 bg-muted rounded w-1/2\" />\n </div>\n ))}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface ProductCardSkeletonProps {\n className?: string\n imageClassName?: string\n}\n\nexport function ProductCardSkeleton({\n className = \"flex flex-col h-full rounded-xl border bg-card overflow-hidden shadow-sm animate-pulse\",\n imageClassName = \"w-full aspect-square bg-muted\",\n}: ProductCardSkeletonProps) {\n return (\n <div className={className}>\n <div className={imageClassName} />\n <div className=\"p-4 flex flex-col flex-1 space-y-3\">\n <div className=\"h-5 bg-muted rounded w-3/4\" />\n <div className=\"h-3 bg-muted rounded w-full\" />\n <div className=\"h-3 bg-muted rounded w-2/3\" />\n <div className=\"flex items-center justify-between pt-2 mt-auto\">\n <div className=\"h-6 bg-muted rounded w-20\" />\n </div>\n <div className=\"w-full h-10 bg-muted rounded-lg mt-auto\" />\n </div>\n </div>\n )\n}\n","\"use client\";\nimport React from 'react'\nimport type { CartItem as CartItemType } from '../../types/store'\n\nexport interface CartItemProps {\n item: CartItemType\n onUpdateQty: (itemId: string, qty: number) => void\n onRemove: (itemId: string) => void\n formatPriceFn?: (price: number) => string\n className?: string\n imageClassName?: string\n titleClassName?: string\n priceClassName?: string\n quantityGroupClassName?: string\n buttonClassName?: string\n removeButtonClassName?: string\n}\n\nexport function CartItem({\n item,\n onUpdateQty,\n onRemove,\n formatPriceFn,\n className = \"flex items-center gap-4 py-3 border-b border-gray-100 dark:border-gray-800\",\n imageClassName = \"w-16 h-16 object-cover rounded-md bg-gray-100 dark:bg-gray-800 shrink-0\",\n titleClassName = \"text-sm font-medium text-gray-900 dark:text-gray-100 line-clamp-1\",\n priceClassName = \"text-sm font-semibold text-gray-900 dark:text-gray-100\",\n quantityGroupClassName = \"flex items-center border rounded-md border-gray-200 dark:border-gray-700\",\n buttonClassName = \"px-2 py-1 text-xs hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors disabled:opacity-50\",\n removeButtonClassName = \"text-xs text-red-600 hover:text-red-700 dark:text-red-400 transition-colors ml-auto\",\n}: CartItemProps) {\n return (\n <div className={className}>\n {item.image_url ? (\n <img src={item.image_url} alt={item.product_name} className={imageClassName} />\n ) : (\n <div className={imageClassName} />\n )}\n <div className=\"flex-1 min-w-0 space-y-1\">\n <h4 className={titleClassName}>{item.product_name}</h4>\n {item.variant_attributes && (\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">\n {typeof item.variant_attributes === 'string'\n ? item.variant_attributes\n : Object.entries(item.variant_attributes).map(([k, v]) => `${k}: ${v}`).join(', ')}\n </p>\n )}\n <div className={priceClassName}>\n {formatPriceFn ? formatPriceFn(item.unit_price * item.quantity) : `${(item as { currency?: string }).currency || ''}${item.unit_price * item.quantity}`}\n </div>\n </div>\n <div className=\"flex flex-col items-end gap-2 shrink-0\">\n <div className={quantityGroupClassName}>\n <button\n type=\"button\"\n onClick={() => onUpdateQty(item.id, Math.max(1, item.quantity - 1))}\n disabled={item.quantity <= 1}\n className={buttonClassName}\n >\n -\n </button>\n <span className=\"px-2 text-xs font-medium\">{item.quantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQty(item.id, item.quantity + 1)}\n className={buttonClassName}\n >\n +\n </button>\n </div>\n <button\n type=\"button\"\n onClick={() => onRemove(item.id)}\n className={removeButtonClassName}\n >\n Remove\n </button>\n </div>\n </div>\n )\n}\n","/**\n * Cart Hook & Context Provider\n *\n * React Query hooks for cart operations with unified drawer state sharing.\n * All cart operations route directly to kasuvia-cart-management via the SDK\n * service functions — never through framework-coupled proxy routes.\n *\n * - Authenticated users: use Authorization Bearer token\n * - Guest users: use guestSessionId (X-Guest-Session header)\n *\n * config and accessToken are optional — if omitted they are read from SdkContext.\n */\nimport React, { createContext, useContext, useState, type ReactNode } from 'react'\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport {\n getCart,\n getGuestCart,\n addToCart,\n addToGuestCart,\n updateCartItem,\n updateGuestCartItemQuantity,\n removeFromCart,\n removeFromGuestCart,\n clearCart,\n clearGuestCart,\n mergeGuestCartIfPresent,\n} from '../services/cart.service'\nimport { logger } from '../utils/logger'\nimport type { CartItem, CartResponse, Product } from '../types/store'\nimport type { SdkConfig } from '../config'\nimport { useSdkContext, resolveHookArgs } from '../context/sdk-context'\n\nexport interface UseCartReturn {\n cart: CartResponse | undefined\n items: CartItem[]\n count: number\n isOpen: boolean\n setOpen: (open: boolean) => void\n isLoading: boolean\n error: Error | null\n addToCart: (item: {\n product_id: string\n product_slug?: string\n product_name?: string\n image?: string\n variant_id?: string\n variant_attributes?: string\n unit_price?: number\n quantity: number\n custom_measurements?: string\n is_bespoke?: boolean\n }) => void\n updateCartItem: (variables: { itemId: string; quantity: number }) => void\n removeFromCart: (itemId: string) => void\n clearCart: () => void\n clear: () => void\n mergeGuestCart: (guestSessionId: string) => void\n addItem: (product: Product, variantId?: string, quantity?: number, customMeasurements?: Record<string, string>) => void\n /** Rejects with the real backend error on failure — the optimistic UI update it made is already rolled back by then; catch this to show why. */\n updateQty: (itemId: string, quantity: number) => Promise<CartResponse>\n /** Rejects with the real backend error on failure — the optimistic UI update it made is already rolled back by then; catch this to show why. */\n removeItem: (itemId: string) => Promise<void>\n subtotal: number\n productTypeMismatch: ProductTypeMismatchDetail | null\n handleClearCartAndAdd: () => void\n handleKeepCurrentCart: () => void\n}\n\n// ── React Context for Shared Drawer State ─────────────────────────────────────\n\nexport interface ProductTypeMismatchDetail {\n message: string\n existing_type: string\n new_type: string\n}\n\nexport interface CartContextValue {\n isOpen: boolean\n setOpen: (open: boolean) => void\n // REAL BUG FIXED (2026-09-24): this used to be a plain useState() local to\n // every individual useCart() call - since each component invoking\n // useCart() gets its own separate hook instance, a mismatch set by\n // ProductDetailClient's addToCartMutation could never be seen by\n // CartClient's own \"keep cart or clear it\" dialog (a different component,\n // a different useCart() instance, a different local state). Lifted into\n // this same shared context isOpen/setOpen already use, for the same\n // reason: real cross-component cart state belongs here, not in a hook-\n // local useState.\n productTypeMismatch: ProductTypeMismatchDetail | null\n setProductTypeMismatch: (detail: ProductTypeMismatchDetail | null) => void\n}\n\nexport const CartCtx = React.createContext ? React.createContext<CartContextValue | null>(null) : null\n\nexport function CartProvider({ children }: { children: ReactNode }) {\n const [isOpen, setIsOpen] = useState(false)\n const [productTypeMismatch, setProductTypeMismatch] = useState<ProductTypeMismatchDetail | null>(null)\n if (!CartCtx) return children\n return React.createElement(CartCtx.Provider, { value: { isOpen, setOpen: setIsOpen, productTypeMismatch, setProductTypeMismatch } }, children)\n}\n\n// ── Hook ──────────────────────────────────────────────────────────────────────\n\nexport function useCart(\n config?: SdkConfig,\n accessToken?: string | null,\n guestSessionId?: string | null\n): UseCartReturn {\n const ctx = useSdkContext();\n const resolved = resolveHookArgs(config, accessToken, ctx);\n const resolvedConfig = resolved.config;\n const resolvedToken = resolved.accessToken;\n // Falls back to SdkContext's guestSessionId (set by SdkProvider) when not\n // passed explicitly — matches config/accessToken's own resolution pattern,\n // so every real call site (none of which ever passed this explicitly) picks\n // it up automatically. See utils/guest-session.ts for why this matters:\n // without it, anonymous add-to-cart has never worked at all.\n guestSessionId = guestSessionId !== undefined ? guestSessionId : (ctx?.guestSessionId ?? null)\n const queryClient = useQueryClient()\n const context = CartCtx ? useContext(CartCtx) : null\n\n const isOpen = context ? context.isOpen : false\n const setOpen = context ? context.setOpen : () => {}\n // See CartContextValue's own doc comment: this must be context-shared\n // (not a local useState) so the \"keep cart or clear it\" dialog on the\n // cart page can actually see a mismatch that happened while adding from\n // a product page - a different component, a different useCart() call.\n const productTypeMismatch = context ? context.productTypeMismatch : null\n const setProductTypeMismatch = context ? context.setProductTypeMismatch : () => {}\n\n // Single source of truth for this identity's cart query key — every\n // mutation's optimistic-update/rollback logic below targets this same key,\n // so it must never be computed two different ways.\n const cartQueryKey = ['cart', resolvedToken ?? guestSessionId ?? 'anon']\n\n // ── Cart Query ─────────────────────────────────────────────────────────────\n // Authenticated → getCart(token)\n // Guest → getGuestCart(guestSessionId)\n // No identity → return empty cart (do not throw)\n const cartQuery = useQuery({\n queryKey: cartQueryKey,\n queryFn: async (): Promise<CartResponse> => {\n if (resolvedToken) {\n return getCart(resolvedConfig, resolvedToken)\n }\n if (guestSessionId) {\n return getGuestCart(resolvedConfig, guestSessionId)\n }\n // No identity — return an empty cart shape; caller can render an empty state.\n return { items: [], subtotal: 0, total: 0, item_count: 0, product_type: undefined }\n },\n enabled: !!(resolvedToken || guestSessionId),\n })\n\n // ── Add to Cart Mutation ──────────────────────────────────────────────────\n const addToCartMutation = useMutation({\n mutationFn: async (item: Parameters<typeof addToCart>[2]) => {\n if (resolvedToken) {\n return addToCart(resolvedConfig, resolvedToken, item)\n }\n if (guestSessionId) {\n return addToGuestCart(resolvedConfig, guestSessionId, item)\n }\n throw new Error('Cannot add to cart: no access token or guest session ID provided.')\n },\n onSuccess: () => {\n setProductTypeMismatch(null)\n queryClient.invalidateQueries({ queryKey: ['cart'] })\n },\n // REAL BUG FIXED (2026-09-24), live-reported (\"mixed products between\n // physical to digital\"): this read `error.status`/`error.data` - but\n // apiRequest throws a real `ApiError` (utils/http-client.ts), whose\n // fields are `.status`/`.body`/`.message`, not `.data` - `err.data` was\n // always `undefined`, so this branch could never fire even when the\n // backend genuinely sent a 409. Separately, kasuvia-cart-management's\n // error body is a huma ErrorModel (`{detail: \"plain string\"}`), never a\n // nested `{detail: {code, ...}}` object - this service's own\n // TranslateServiceError doc comment already documents that convention\n // (a bracketed \"[CODE] ...\" prefix in the message string, matching\n // every other cart error). Parses that same convention here instead of\n // a response shape the backend never produces.\n onError: (error: unknown) => {\n const err = error as { status?: number; message?: string }\n if (err.status !== 409 || !err.message?.startsWith('[CART_PRODUCT_TYPE_MISMATCH]')) return\n const match = err.message.match(/contains (\\w+) items\\. Add (\\w+) items/i)\n setProductTypeMismatch({\n message: err.message.replace(/^\\[CART_PRODUCT_TYPE_MISMATCH\\]\\s*/, ''),\n existing_type: match?.[1] ?? 'unknown',\n new_type: match?.[2] ?? 'unknown',\n })\n },\n })\n\n // Recomputes subtotal/total/item_count for a locally-mutated items array —\n // mirrors kasuvia-cart-management's own BuildResponse exactly (Total ===\n // Subtotal at the cart stage; delivery/tax are only added at checkout).\n // Deliberately does NOT recompute `product_type` (DeriveProductType is\n // real backend logic this shouldn't reproduce client-side) - onSettled's\n // invalidateQueries reconciles that within one round-trip regardless.\n function recomputeCartTotals(previous: CartResponse, items: CartItem[]): CartResponse {\n const subtotal = items.reduce((sum, i) => sum + i.unit_price * i.quantity, 0)\n const item_count = items.reduce((sum, i) => sum + i.quantity, 0)\n return { ...previous, items, subtotal, total: subtotal, item_count }\n }\n\n // ── Update Cart Item Mutation ─────────────────────────────────────────────\n // REAL GAP FIXED (2026-09-23), owner-requested: add-to-cart already felt\n // instant (the caller shows its own success toast without waiting for the\n // mutation to settle), but remove/update-quantity only updated the visible\n // list once the server round-trip finished and the query refetched -\n // sluggish by comparison. Both now write the predicted result straight\n // into the query cache in onMutate (matching kasuvia-cart-management's own\n // upsertLine merge/removal semantics), roll back to the exact pre-mutation\n // snapshot in onError, and always reconcile with the server's real state\n // in onSettled - the cache can never end up out of sync with the backend,\n // even if two of these fire concurrently.\n const updateCartItemMutation = useMutation({\n mutationFn: async ({ itemId, quantity }: { itemId: string; quantity: number }) => {\n if (resolvedToken) {\n return updateCartItem(resolvedConfig, resolvedToken, itemId, quantity)\n }\n if (guestSessionId) {\n return updateGuestCartItemQuantity(resolvedConfig, guestSessionId, itemId, quantity)\n }\n throw new Error('Cannot update cart: no access token or guest session ID provided.')\n },\n onMutate: async ({ itemId, quantity }) => {\n await queryClient.cancelQueries({ queryKey: cartQueryKey })\n const previous = queryClient.getQueryData<CartResponse>(cartQueryKey)\n if (previous) {\n // Matches the real backend's own \"quantity<=0 removes the line\"\n // contract (UpdateItemQuantity's doc comment in cart_service.go).\n const items = quantity > 0\n ? previous.items.map((i) => i.id === itemId ? { ...i, quantity } : i)\n : previous.items.filter((i) => i.id !== itemId)\n queryClient.setQueryData<CartResponse>(cartQueryKey, recomputeCartTotals(previous, items))\n }\n return { previous }\n },\n onError: (error, _variables, context) => {\n if (context?.previous) {\n queryClient.setQueryData(cartQueryKey, context.previous)\n }\n logger.error('[useCart] Failed to update item quantity, rolled back:', error)\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['cart'] })\n },\n })\n\n // ── Remove from Cart Mutation ─────────────────────────────────────────────\n // Same optimistic/rollback pattern as updateCartItemMutation above.\n const removeFromCartMutation = useMutation({\n mutationFn: async (itemId: string) => {\n if (resolvedToken) {\n return removeFromCart(resolvedConfig, resolvedToken, itemId)\n }\n if (guestSessionId) {\n return removeFromGuestCart(resolvedConfig, guestSessionId, itemId)\n }\n throw new Error('Cannot remove from cart: no access token or guest session ID provided.')\n },\n onMutate: async (itemId) => {\n await queryClient.cancelQueries({ queryKey: cartQueryKey })\n const previous = queryClient.getQueryData<CartResponse>(cartQueryKey)\n if (previous) {\n const items = previous.items.filter((i) => i.id !== itemId)\n queryClient.setQueryData<CartResponse>(cartQueryKey, recomputeCartTotals(previous, items))\n }\n return { previous }\n },\n onError: (error, _itemId, context) => {\n if (context?.previous) {\n queryClient.setQueryData(cartQueryKey, context.previous)\n }\n logger.error('[useCart] Failed to remove item, rolled back:', error)\n },\n onSettled: () => {\n queryClient.invalidateQueries({ queryKey: ['cart'] })\n },\n })\n\n // ── Clear Cart Mutation ───────────────────────────────────────────────────\n const clearCartMutation = useMutation({\n mutationFn: async () => {\n if (resolvedToken) {\n return clearCart(resolvedConfig, resolvedToken)\n }\n if (guestSessionId) {\n return clearGuestCart(resolvedConfig, guestSessionId)\n }\n throw new Error('Cannot clear cart: no access token or guest session ID provided.')\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['cart'] })\n },\n })\n\n // ── Merge Guest Cart Mutation ─────────────────────────────────────────────\n const mergeGuestCartMutation = useMutation({\n mutationFn: (sessionId: string) => {\n if (!resolvedToken) {\n logger.warn('[useCart] mergeGuestCart called without an access token — skipping merge.')\n return Promise.resolve()\n }\n return mergeGuestCartIfPresent(resolvedConfig, resolvedToken, sessionId)\n },\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['cart'] })\n },\n })\n\n // ── Convenience: add by Product object ───────────────────────────────────\n // NOTE: product_slug, product_name, image_url, unit_price, is_bespoke are\n // intentionally NOT forwarded to the backend — kasuvia-cart-management\n // resolves them server-side. They're extracted from the Product here only\n // for any local optimistic-UI the caller may implement; the backend response\n // is the authoritative cart state.\n const addItem = (product: Product, variantId?: string, quantity: number = 1, customMeasurements?: Record<string, string>) => {\n return addToCartMutation.mutate({\n product_id: product.id,\n variant_id: variantId,\n quantity,\n custom_measurements: customMeasurements ? JSON.stringify(customMeasurements) : undefined,\n // Below are accepted by the mutation signature for UI convenience but stripped before the fetch:\n product_slug: product.slug || \"\",\n product_name: product.name,\n image_url: product.images[0] || undefined,\n })\n }\n\n const items = cartQuery.data?.items ?? []\n const subtotal = cartQuery.data?.subtotal ?? 0\n const count = items.reduce((total: number, item: CartItem) => total + item.quantity, 0)\n\n // mutateAsync (not mutate) so a caller's `await updateQty(...)`/`await\n // removeItem(...)` actually rejects on failure - both already have callers\n // using `await` + try/catch expecting this (CartClient.tsx/CartDrawer.tsx),\n // but `.mutate()` returns void, so those awaits previously resolved\n // instantly regardless of outcome and never caught a real failure. The\n // optimistic cache update above already applies/rolls back independently\n // of whether the caller awaits this at all.\n const updateQty = (itemId: string, quantity: number) => {\n return updateCartItemMutation.mutateAsync({ itemId, quantity })\n }\n\n const removeItem = (itemId: string) => {\n return removeFromCartMutation.mutateAsync(itemId)\n }\n\n const handleClearCartAndAdd = () => {\n clearCartMutation.mutate()\n setProductTypeMismatch(null)\n }\n\n const handleKeepCurrentCart = () => {\n setProductTypeMismatch(null)\n }\n\n return {\n cart: cartQuery.data,\n items,\n count,\n isOpen,\n setOpen,\n isLoading: cartQuery.isLoading,\n error: cartQuery.error as Error | null,\n addToCart: addToCartMutation.mutate,\n updateCartItem: updateCartItemMutation.mutate,\n removeFromCart: removeFromCartMutation.mutate,\n clearCart: clearCartMutation.mutate,\n clear: clearCartMutation.mutate,\n mergeGuestCart: mergeGuestCartMutation.mutate,\n addItem,\n updateQty,\n removeItem,\n subtotal,\n productTypeMismatch,\n handleClearCartAndAdd,\n handleKeepCurrentCart,\n }\n}\n","/**\n * Silent-refresh registry for apiRequest's automatic 401 retry.\n *\n * REAL GAP FIXED (2026-09-28), live-reported (\"authentication in\n * kasuvia-storefront is terrible, sometimes it gets cut off while using\n * the app\"): AuthProvider only ever called /api/auth/refresh once, in a\n * useEffect on mount (restoreSession()) — every one of apiRequest's ~100+\n * real call sites had zero awareness of token expiry at all. The moment a\n * customer's access token expired mid-session (JWT_ACCESS_TOKEN_LIFETIME_\n * MINUTES defaults to 30 on kasuvia-auth — see common/utils/tokens.py),\n * every subsequent authenticated call 401'd and stayed broken for the rest\n * of that browser tab, with no recovery short of a full page reload\n * (which re-runs restoreSession() exactly once more, then the cycle\n * repeats). This is a fundamentally different failure mode than \"signed\n * out on refresh\" (investigated exhaustively earlier this session and\n * found structurally sound) - it only shows up while actively using the\n * app between page loads, matching the user's own description exactly.\n *\n * Compared directly against kasuvia/ (the merchant dashboard)'s own proven\n * architecture (lib/api/interceptors.ts's withAuthInterceptors): every\n * axios instance there has a response interceptor that, on any 401 from a\n * non-refresh endpoint, transparently calls the refresh endpoint once\n * (coalescing concurrent 401s into a single in-flight refresh via a\n * pending-request queue - never a stampede of parallel refresh calls),\n * updates the in-memory access token, and retries the original request\n * with the new token - completely invisible to the calling code and the\n * user. kasuvia/ can do this because its access token lives in a plain,\n * synchronously-readable/writable module-level variable\n * (lib/api/auth/axios.ts's tokenStore), not React state - exactly what\n * makes an imperative, outside-the-render-cycle interceptor possible.\n *\n * This module ports that same pattern into kasuvia-sdk's fetch-based\n * apiRequest, which every one of the storefront's ~100+ authenticated SDK\n * calls already funnels through - fixing it once here reaches every call\n * site with zero changes to any of them. A consumer (AuthProvider) calls\n * registerRefreshHandler() once with a function that knows how to obtain a\n * fresh token (POST /api/auth/refresh, in the storefront's case) and keeps\n * its own React state in sync; apiRequest below calls it automatically\n * whenever a request carrying an Authorization header gets a real 401,\n * with the same in-flight-coalescing guarantee kasuvia/'s interceptor has.\n * If no handler is registered (e.g. a server-side call, or a consumer that\n * never opts in), behavior is completely unchanged - a 401 throws\n * immediately, exactly as before this module existed.\n *\n * REAL BUG FOUND AND FIXED while live-verifying this exact mechanism\n * (2026-09-28): kasuvia-sdk's tsup config builds each package entry point\n * (`.` / `./services` / `./hooks` / `./components`) as a fully independent\n * bundle with no shared chunk - confirmed by grepping the built output,\n * every one of the 3 real dist files contains its OWN separate copy of this\n * file's code. A plain module-level `let refreshHandler` therefore gave\n * each entry point its own invisible-to-each-other copy of this state:\n * registering via `@kasuvia/sdk` (the root entry, what AuthProvider\n * imports from) silently did nothing for any authenticated call made\n * through `@kasuvia/sdk/hooks` (a different physical bundle, and the one\n * real hooks like useWalletBalance/useCart actually route through) - the\n * exact majority-of-real-traffic case this whole fix exists for. Storing\n * the registry on `globalThis` under a fixed key instead makes it a true\n * process/tab-wide singleton no matter how many separate bundle copies of\n * this module end up loaded side by side in the same browser tab - the\n * standard fix for this exact \"multiple bundled copies of one logical\n * module\" bug class, without needing to change the package's build/\n * splitting setup (out of scope for an auth fix, wider blast radius).\n */\n\nexport type RefreshHandler = () => Promise<string | null>\n\ninterface RefreshRegistry {\n handler: RefreshHandler | null\n inFlight: Promise<string | null> | null\n}\n\nconst GLOBAL_KEY = '__kasuviaSdkTokenRefreshRegistry__'\n\nfunction getRegistry(): RefreshRegistry {\n const g = globalThis as unknown as Record<string, RefreshRegistry | undefined>\n if (!g[GLOBAL_KEY]) {\n g[GLOBAL_KEY] = { handler: null, inFlight: null }\n }\n return g[GLOBAL_KEY]!\n}\n\n/**\n * Registers the function apiRequest calls to obtain a fresh access token\n * after a 401. Call this once, e.g. in AuthProvider's top-level mount\n * effect - not tied to login state, since the handler itself is stateless\n * (it always attempts a real refresh call and returns null on failure,\n * which is exactly correct behavior whether or not a customer happens to be\n * logged in right now). `null` is accepted for a consumer that genuinely\n * unmounts its provider (e.g. route-level teardown) and wants to stop\n * apiRequest from calling a handler whose closure is about to go stale.\n */\nexport function registerRefreshHandler(handler: RefreshHandler | null): void {\n getRegistry().handler = handler\n}\n\n/**\n * Runs the registered refresh handler, coalescing concurrent callers into\n * the single in-flight attempt (mirrors withAuthInterceptors' `_isRefreshing`\n * / `_pendingQueue` exactly - several requests failing with a 401 at once\n * must never each independently call the refresh endpoint).\n */\nexport async function refreshAccessToken(): Promise<string | null> {\n const registry = getRegistry()\n if (!registry.handler) return null\n if (registry.inFlight) return registry.inFlight\n\n registry.inFlight = registry.handler().finally(() => {\n registry.inFlight = null\n })\n return registry.inFlight\n}\n\nexport function hasRefreshHandler(): boolean {\n return getRegistry().handler !== null\n}\n","import type { SdkConfig } from '../config'\nimport { refreshAccessToken, hasRefreshHandler } from './token-refresh'\n\n/**\n * Thrown by apiRequest on any non-2xx response. Carries the real HTTP status\n * and parsed body so a caller that needs to branch on status (e.g. a 409\n * \"confirmation required\" two-step flow) can catch this instead of the\n * generic client special-casing every such flow.\n */\nexport class ApiError extends Error {\n status: number\n body: unknown\n constructor(message: string, status: number, body: unknown) {\n super(message)\n this.name = 'ApiError'\n this.status = status\n this.body = body\n }\n}\n\n/**\n * Extract the real, specific error message from a Kasuvia backend's error\n * body, whichever of the two real shapes this platform's services return:\n *\n * - FastAPI/Pydantic (store-management, cart-management, cms, crm-go,\n * notification-go, payment-go): `{\"detail\": \"plain string\"}` for a\n * manually-raised HTTPException, or `{\"detail\": [{\"loc\": [...], \"msg\":\n * \"...\", \"type\": \"...\"}]}` for an automatic Pydantic validation failure.\n * - Django REST Framework (kasuvia-auth): `{\"detail\": \"plain string\"}` for\n * AuthenticationFailed/NotFound/PermissionDenied-style exceptions, or a\n * bare validation-error dict with no top-level `detail` at all -\n * `{\"non_field_errors\": [...]}` or `{\"email\": [...]}\"` - when a\n * serializer's own `is_valid(raise_exception=True)` fires.\n *\n * Previously two separately-named, independently-adopted copies of this\n * logic existed (`extractHttpErrorDetail`, `extractAuthErrorMessage`) - only\n * 7 of 18 SDK service files used either one at all. Merged into one function\n * every call site now goes through via apiRequest, so error quality no\n * longer depends on which service function happens to have remembered to\n * check for it.\n */\nexport function extractApiErrorMessage(body: unknown, fallback: string): string {\n if (typeof body !== 'object' || body === null) return fallback\n const record = body as Record<string, unknown>\n\n if (typeof record.detail === 'string') return record.detail\n\n if (Array.isArray(record.detail) && record.detail.length > 0) {\n const first = record.detail[0] as Record<string, unknown> | undefined\n if (first && typeof first.msg === 'string') {\n const loc = first.loc\n const field = Array.isArray(loc) ? loc[loc.length - 1] : undefined\n return typeof field === 'string' || typeof field === 'number' ? `${field}: ${first.msg}` : first.msg\n }\n }\n\n const firstStringOrArrayItem = (value: unknown): string | undefined => {\n if (typeof value === 'string') return value\n if (Array.isArray(value) && typeof value[0] === 'string') return value[0]\n return undefined\n }\n\n const nonFieldMessage = firstStringOrArrayItem(record.non_field_errors)\n if (nonFieldMessage) return nonFieldMessage\n\n for (const [key, value] of Object.entries(record)) {\n if (key === 'detail' || key === 'non_field_errors') continue\n const message = firstStringOrArrayItem(value)\n if (message) return message\n }\n\n return fallback\n}\n\n/** `{'X-Storefront-Key': ..., 'X-Business-ID': ...}` - every storefront-scoped call. */\nexport function storefrontHeaders(config: SdkConfig): Record<string, string> {\n return {\n 'X-Storefront-Key': config.storefrontApiKey,\n 'X-Business-ID': config.businessId,\n }\n}\n\n/** storefrontHeaders plus `Authorization: Bearer <token>` - every authenticated customer call. */\nexport function authHeaders(config: SdkConfig, accessToken: string): Record<string, string> {\n return {\n ...storefrontHeaders(config),\n Authorization: `Bearer ${accessToken}`,\n }\n}\n\nexport const JSON_HEADERS: Record<string, string> = { 'Content-Type': 'application/json' }\n\nexport interface ApiRequestOptions extends RequestInit {\n /** Used when the response is a non-2xx status whose body carries nothing usable. */\n fallbackErrorMessage?: string\n}\n\n/**\n * The one real HTTP call path every REST SDK service function should use.\n * Replaces each function independently doing fetch + response.ok check +\n * error-body parsing (100 call sites across 18 files, only 7 of which\n * consistently surfaced the real backend error before this existed).\n *\n * A 204/empty body resolves to `undefined` rather than throwing on the\n * `response.json()` call - real for logout and a few other no-content\n * responses on this platform.\n *\n * Silent-refresh-and-retry on 401 (see token-refresh.ts's own doc comment\n * for the full real-world bug this fixes and the kasuvia/ pattern it\n * ports): only triggers for a request that actually carried an\n * `Authorization: Bearer` header AND has a refresh handler registered -\n * a storefront-key-only public call's 401 means something else entirely\n * (wrong key/business) and must never trigger a token refresh attempt.\n * Retries at most once per call, mirroring withAuthInterceptors' own\n * `_retry` guard - a fresh token that still 401s is a genuine, final\n * failure, not something to loop on.\n */\nexport async function apiRequest<T>(url: string, options: ApiRequestOptions = {}): Promise<T> {\n const { fallbackErrorMessage = 'Request failed', ...init } = options\n const headers = new Headers(init.headers)\n const hadBearerToken = headers.get('Authorization')?.startsWith('Bearer ') ?? false\n\n const response = await fetch(url, { ...init, headers })\n\n if (response.status === 401 && hadBearerToken && hasRefreshHandler()) {\n const freshToken = await refreshAccessToken()\n if (freshToken) {\n const retryHeaders = new Headers(headers)\n retryHeaders.set('Authorization', `Bearer ${freshToken}`)\n const retryResponse = await fetch(url, { ...init, headers: retryHeaders })\n if (retryResponse.ok) {\n const retryText = await retryResponse.text()\n return (retryText ? JSON.parse(retryText) : undefined) as T\n }\n const retryBody = await retryResponse.json().catch(() => ({}))\n throw new ApiError(extractApiErrorMessage(retryBody, fallbackErrorMessage), retryResponse.status, retryBody)\n }\n }\n\n if (!response.ok) {\n const body = await response.json().catch(() => ({}))\n throw new ApiError(extractApiErrorMessage(body, fallbackErrorMessage), response.status, body)\n }\n\n const text = await response.text()\n return (text ? JSON.parse(text) : undefined) as T\n}\n","/**\n * Cart Service\n *\n * Handles cart operations for both guest and authenticated users via\n * kasuvia-cart-management (Go/Huma). All cart operations target the\n * cart-management service, which is the single source of truth for cart state.\n *\n * KEY CONSTRAINT — backend-resolved fields:\n * kasuvia-cart-management resolves product_name, product_slug, image_url,\n * variant_attributes, unit_price, is_bespoke, and product_type entirely\n * server-side from kasuvia-store-management at add/merge time. These MUST NOT\n * be sent in request payloads — only product_id, variant_id, quantity, and\n * custom_measurements are accepted by the backend.\n *\n * Stateless — all configuration passed as parameters.\n */\nimport type { SdkConfig } from '../config'\nimport { ProductTypeEnum, type CartItem, type CartResponse, type ProductType } from '../types/store'\nimport { apiRequest, storefrontHeaders, authHeaders, JSON_HEADERS } from '../utils/http-client'\n\n// REAL BUG FIXED (2026-09-21): every fetch() in this file targeted\n// `config.backendUrls.store` (kasuvia-store-management) even though this\n// file's own header comment already documented that cart moved to\n// kasuvia-cart-management (a separate Go/Huma service, strangler-fig\n// migration). kasuvia-store-management's own router.py explicitly says\n// \"Cart is NOT registered here anymore\" — every one of these calls 404s\n// against `store`. Fixed by routing through `backendUrls.cart` instead,\n// which callers must now provide (kasuvia-cart-management, same\n// `/api/v1/cart/...` route shape it was ported from — only the base URL\n// changes).\n\nfunction parseItems(items: CartItem[]): CartItem[] {\n return items.map((item) => ({\n ...item,\n variant_attributes:\n item.variant_attributes && typeof item.variant_attributes === \"string\"\n ? JSON.parse(item.variant_attributes)\n : item.variant_attributes,\n }));\n}\n\nfunction guestHeaders(config: SdkConfig, guestSessionId: string, extra?: Record<string, string>): Record<string, string> {\n return { ...extra, ...storefrontHeaders(config), 'X-Guest-Session': guestSessionId }\n}\n\n/**\n * Fetch guest cart from backend (Redis-backed).\n */\nexport async function getGuestCart(config: SdkConfig, guestSessionId: string): Promise<CartResponse> {\n if (!guestSessionId) throw new Error('Guest session ID is required to fetch guest cart');\n\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/`, {\n headers: guestHeaders(config, guestSessionId),\n fallbackErrorMessage: 'Failed to fetch guest cart',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Add item to guest cart (Redis-backed).\n * Only product_id, variant_id, quantity, and custom_measurements are sent —\n * all other fields are resolved server-side by kasuvia-cart-management.\n */\nexport async function addToGuestCart(\n config: SdkConfig,\n guestSessionId: string,\n item: {\n product_id: string\n variant_id?: string | null\n quantity: number\n custom_measurements?: string | null\n }\n): Promise<CartResponse> {\n if (!guestSessionId) throw new Error('Guest session ID is required to add to guest cart');\n\n const payload = {\n product_id: item.product_id,\n variant_id: item.variant_id ?? undefined,\n quantity: item.quantity,\n custom_measurements: item.custom_measurements ?? undefined,\n }\n\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/`, {\n method: 'POST',\n headers: guestHeaders(config, guestSessionId, JSON_HEADERS),\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to add to guest cart',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Update quantity of a guest cart item.\n */\nexport async function updateGuestCartItemQuantity(\n config: SdkConfig,\n guestSessionId: string,\n itemId: string,\n quantity: number\n): Promise<CartResponse> {\n if (!guestSessionId) throw new Error('Guest session ID is required to update guest cart item');\n\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/${itemId}`, {\n method: 'PATCH',\n headers: guestHeaders(config, guestSessionId, JSON_HEADERS),\n body: JSON.stringify({ quantity }),\n fallbackErrorMessage: 'Failed to update guest cart item',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Remove an item from the guest cart.\n *\n * REAL BUG FIXED (2026-09-22), live-reproduced: this used to type its\n * result as `CartResponse` and read `data.items` off it, but the real\n * backend's `DELETE /cart/{item_id}` returns `204 No Content` with a\n * genuinely empty body (confirmed live: `HTTP_STATUS:204 BODY_LENGTH:0`) -\n * `apiRequest` resolves an empty body to `undefined`, so `data.items`\n * threw `TypeError: Cannot read properties of undefined (reading 'items')`\n * on every real delete. The item really was removed server-side (the\n * DELETE request itself succeeded), but the thrown error sent\n * `removeFromCartMutation` down `onError` instead of `onSuccess`, so\n * `queryClient.invalidateQueries` never ran - a customer's cart kept\n * showing the \"removed\" item until something else (a full page reload)\n * happened to trigger a fresh, uncached fetch. Matches `clearGuestCart`'s\n * existing `Promise<void>` shape, which was already correct for this same\n * \"backend returns no body\" pattern.\n */\nexport async function removeFromGuestCart(\n config: SdkConfig,\n guestSessionId: string,\n itemId: string\n): Promise<void> {\n if (!guestSessionId) throw new Error('Guest session ID is required to remove from guest cart');\n\n await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {\n method: 'DELETE',\n headers: guestHeaders(config, guestSessionId),\n fallbackErrorMessage: 'Failed to remove item from guest cart',\n })\n}\n\n/**\n * Clear the entire guest cart.\n */\nexport async function clearGuestCart(config: SdkConfig, guestSessionId: string): Promise<void> {\n if (!guestSessionId) throw new Error('Guest session ID is required to clear guest cart');\n\n await apiRequest(`${config.backendUrls.cart}/cart/clear`, {\n method: 'DELETE',\n headers: guestHeaders(config, guestSessionId),\n fallbackErrorMessage: 'Failed to clear guest cart',\n })\n}\n\n/**\n * Fetch authenticated user's cart from backend.\n */\nexport async function getCart(config: SdkConfig, accessToken?: string | null): Promise<CartResponse> {\n if (!accessToken) {\n throw new Error('Access token is required to fetch authenticated cart');\n }\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/`, {\n headers: authHeaders(config, accessToken),\n fallbackErrorMessage: 'Failed to fetch cart',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Add item to authenticated user's cart.\n * Only product_id, variant_id, quantity, and custom_measurements are sent —\n * all other fields are resolved server-side by kasuvia-cart-management.\n */\nexport async function addToCart(\n config: SdkConfig,\n accessToken: string,\n item: {\n product_id: string\n variant_id?: string | null\n quantity: number\n custom_measurements?: string | null\n // These fields are accepted from the caller for UI convenience but are\n // NOT forwarded to the backend — kasuvia-cart-management resolves them\n // server-side. Declared here so callers don't need to strip them.\n product_slug?: string\n product_name?: string\n image_url?: string\n variant_attributes?: string\n unit_price?: number\n is_bespoke?: boolean\n }\n): Promise<CartResponse> {\n // Only send the fields the backend accepts — strip everything else.\n const payload = {\n product_id: item.product_id,\n variant_id: item.variant_id ?? undefined,\n quantity: item.quantity,\n custom_measurements: item.custom_measurements ?? undefined,\n }\n\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to add to cart',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Update item quantity in authenticated user's cart.\n */\nexport async function updateCartItem(\n config: SdkConfig,\n accessToken: string,\n itemId: string,\n quantity: number\n): Promise<CartResponse> {\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/${itemId}`, {\n method: 'PATCH',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify({ quantity }),\n fallbackErrorMessage: 'Failed to update cart item',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Remove item from authenticated user's cart.\n * Same real bug/fix as removeFromGuestCart above - see its doc comment.\n */\nexport async function removeFromCart(\n config: SdkConfig,\n accessToken: string,\n itemId: string\n): Promise<void> {\n await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {\n method: 'DELETE',\n headers: authHeaders(config, accessToken),\n fallbackErrorMessage: 'Failed to remove cart item',\n })\n}\n\n/**\n * Clear authenticated user's cart.\n */\nexport async function clearCart(config: SdkConfig, accessToken: string): Promise<void> {\n await apiRequest(`${config.backendUrls.cart}/cart/clear`, {\n method: 'DELETE',\n headers: authHeaders(config, accessToken),\n fallbackErrorMessage: 'Failed to clear cart',\n })\n}\n\n/**\n * Merge guest cart items into the authenticated customer's cart.\n *\n * The backend (POST /api/v1/cart/merge) accepts only:\n * { items: [{ product_id, variant_id?, quantity, custom_measurements? }] }\n * Product name, image, price, type are all re-resolved server-side.\n *\n * Sends X-Guest-Session so the backend can atomically delete the guest\n * cart (best-effort) after merging.\n */\nexport async function mergeGuestCart(\n config: SdkConfig,\n accessToken: string,\n guestSessionId: string,\n guestItems: Pick<CartItem, 'product_id' | 'variant_id' | 'quantity' | 'custom_measurements'>[]\n): Promise<CartResponse> {\n const payloadItems = guestItems.map((item) => ({\n product_id: item.product_id,\n variant_id: item.variant_id ?? undefined,\n quantity: item.quantity,\n custom_measurements: item.custom_measurements ?? undefined,\n }))\n\n const headers: Record<string, string> = { ...JSON_HEADERS, ...authHeaders(config, accessToken) }\n if (guestSessionId) {\n headers[\"X-Guest-Session\"] = guestSessionId\n }\n\n const data = await apiRequest<CartResponse>(`${config.backendUrls.cart}/cart/merge`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ items: payloadItems }),\n fallbackErrorMessage: 'Failed to merge guest cart',\n })\n return { ...data, items: parseItems(data.items || []) }\n}\n\n/**\n * Convenience wrapper: fetch guest cart and merge if it has items (called post-login).\n */\nexport async function mergeGuestCartIfPresent(\n config: SdkConfig,\n accessToken: string,\n guestSessionId: string\n): Promise<void> {\n const guestCart = await getGuestCart(config, guestSessionId)\n if (!guestCart.items.length) return\n await mergeGuestCart(config, accessToken, guestSessionId, guestCart.items)\n}\n\n/**\n * Helper to get a guest cart's product type (for type-mismatch checks before adding an item).\n */\nexport function getGuestCartProductType(items?: CartItem[]): ProductType | null {\n if (!items || !items.length) return null;\n const types = new Set(items.map(i => i.product_type).filter(Boolean));\n if (types.size === 0) return null;\n if (types.size === 1) return ([...types][0] as ProductType) ?? null;\n return ProductTypeEnum.PHYSICAL;\n}\n","/**\n * Logger Utility\n * \n * Environment-gated logger that suppresses output in production.\n */\n\nlet isDevelopment = true;\n\n/**\n * Set the environment mode for logging\n */\nexport function setLoggerEnvironmentMode(development: boolean): void {\n isDevelopment = development;\n}\n\nexport const logger = {\n log: (...args: unknown[]) => {\n if (isDevelopment) {\n console.log(...args)\n }\n },\n\n warn: (...args: unknown[]) => {\n if (isDevelopment) {\n console.warn(...args)\n }\n },\n\n /**\n * Logs a warning to the console in development only.\n * Alias for warn() for Python logging compatibility.\n * In production, this is a no-op.\n */\n warning: (...args: unknown[]) => {\n if (isDevelopment) {\n console.warn(...args)\n }\n },\n\n error: (...args: unknown[]) => {\n if (isDevelopment) {\n console.error(...args)\n }\n },\n\n info: (...args: unknown[]) => {\n if (isDevelopment) {\n console.info(...args)\n }\n },\n\n createScope: (scope: string) => ({\n log: (...args: unknown[]) => {\n if (isDevelopment) {\n console.log(`[${scope}]`, ...args)\n }\n },\n warn: (...args: unknown[]) => {\n if (isDevelopment) {\n console.warn(`[${scope}]`, ...args)\n }\n },\n warning: (...args: unknown[]) => {\n if (isDevelopment) {\n console.warn(`[${scope}]`, ...args)\n }\n },\n error: (...args: unknown[]) => {\n if (isDevelopment) {\n console.error(`[${scope}]`, ...args)\n }\n },\n info: (...args: unknown[]) => {\n if (isDevelopment) {\n console.info(`[${scope}]`, ...args)\n }\n },\n }),\n}\n","'use client';\n\n/**\n * SdkContext\n *\n * Provides SdkConfig and the current access token to all SDK hooks\n * in the component tree, so individual hooks do not require explicit\n * config/accessToken parameters when called from client components.\n *\n * Usage in storefront:\n * // In your AuthProvider or root layout:\n * <SdkProvider config={sdkConfig} accessToken={accessToken}>\n * {children}\n * </SdkProvider>\n *\n * // In any client component:\n * const { items, addItem } = useCart() // no args needed\n */\n\nimport React, { createContext, useContext, type ReactNode } from 'react';\nimport type { SdkConfig } from '../config';\n\nexport interface SdkContextValue {\n config: SdkConfig;\n accessToken: string | null;\n /** Current browser's guest cart session id (see utils/guest-session.ts) — null when unauthenticated context hasn't resolved one yet, or when the caller is authenticated and doesn't need one. */\n guestSessionId?: string | null;\n}\n\nconst SdkContext = createContext<SdkContextValue | null>(null);\n\nexport interface SdkProviderProps {\n config: SdkConfig;\n accessToken: string | null;\n guestSessionId?: string | null;\n children: ReactNode;\n}\n\nexport function SdkProvider({ config, accessToken, guestSessionId, children }: SdkProviderProps) {\n return (\n <SdkContext.Provider value={{ config, accessToken, guestSessionId }}>\n {children}\n </SdkContext.Provider>\n );\n}\n\n/**\n * Returns the SdkContext value, or null if no SdkProvider is present.\n * SDK hooks use this internally to fall back to context when explicit\n * parameters are not provided.\n */\nexport function useSdkContext(): SdkContextValue | null {\n return useContext(SdkContext);\n}\n\n/**\n * Resolves config and accessToken from explicit arguments or SdkContext.\n * Throws if neither source is available.\n */\nexport function resolveHookArgs(\n explicitConfig: SdkConfig | undefined,\n explicitToken: string | null | undefined,\n ctx: SdkContextValue | null\n): { config: SdkConfig; accessToken: string | null } {\n const config = explicitConfig ?? ctx?.config;\n if (!config) {\n throw new Error(\n '[kasuvia-sdk] No SdkConfig found. Either pass config explicitly or wrap your app with <SdkProvider config={...}>.'\n );\n }\n const accessToken = explicitToken !== undefined ? explicitToken : (ctx?.accessToken ?? null);\n return { config, accessToken };\n}\n","\"use client\";\nimport React from 'react'\n\nexport interface CartTypeMismatchBannerProps {\n mismatch: {\n message: string\n existing_type: string\n new_type: string\n }\n onClearAndAdd: () => void\n onKeepCurrent: () => void\n className?: string\n titleClassName?: string\n messageClassName?: string\n buttonGroupClassName?: string\n confirmButtonClassName?: string\n cancelButtonClassName?: string\n}\n\nexport function CartTypeMismatchBanner({\n mismatch,\n onClearAndAdd,\n onKeepCurrent,\n className = \"p-4 bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 rounded-lg space-y-3\",\n titleClassName = \"text-sm font-semibold text-amber-800 dark:text-amber-200\",\n messageClassName = \"text-xs text-amber-700 dark:text-amber-300\",\n buttonGroupClassName = \"flex items-center gap-2 pt-1\",\n confirmButtonClassName = \"px-3 py-1.5 text-xs font-medium bg-amber-600 hover:bg-amber-700 text-white rounded transition-colors\",\n cancelButtonClassName = \"px-3 py-1.5 text-xs font-medium border border-amber-300 dark:border-amber-700 text-amber-800 dark:text-amber-200 rounded hover:bg-amber-100 dark:hover:bg-amber-900/50 transition-colors\",\n}: CartTypeMismatchBannerProps) {\n return (\n <div className={className}>\n <div className={titleClassName}>Cart Product Type Conflict</div>\n <p className={messageClassName}>\n {mismatch.message || `Your cart currently contains ${mismatch.existing_type} items. You cannot mix ${mismatch.existing_type} and ${mismatch.new_type} items in the same order.`}\n </p>\n <div className={buttonGroupClassName}>\n <button\n type=\"button\"\n onClick={onClearAndAdd}\n className={confirmButtonClassName}\n >\n Clear Cart & Add New Item\n </button>\n <button\n type=\"button\"\n onClick={onKeepCurrent}\n className={cancelButtonClassName}\n >\n Keep Current Cart\n </button>\n </div>\n </div>\n )\n}\n","/**\n * format.ts\n * Comprehensive number formatting utility with multiple format styles.\n * Migrated from kasuvia-storefront to kasuvia-sdk for shared use.\n */\n\nexport type FormatStyle =\n | \"compact\" // 1000000 → \"1M\"\n | \"compact-lower\" // 1000000 → \"1m\"\n | \"compact-full\" // 1000000 → \"1 Million\"\n | \"compact-full-lower\" // 1000000 → \"1 million\"\n | \"currency\" // 1000000 → \"$1,000,000.00\"\n | \"currency-compact\" // 1000000 → \"$1M\"\n | \"decimal\" // 1000000 → \"1,000,000\"\n | \"decimal-dot\" // 1000000 → \"1.000.000\" (European style)\n | \"decimal-space\" // 1000000 → \"1 000 000\" (French/SI style)\n | \"plain\" // 1000000 → \"1000000\"\n | \"scientific\" // 1000000 → \"1.00e+6\"\n | \"ordinal\" // 1 → \"1st\", 2 → \"2nd\", etc.\n | \"percent\" // 0.75 → \"75%\"\n | \"bytes\" // 1048576 → \"1.00 MB\"\n | \"roman\"; // 14 → \"XIV\"\n\nexport interface FormatOptions {\n style?: FormatStyle;\n /** Number of decimal places (where applicable). Default: auto */\n decimals?: number;\n /** Currency symbol for \"currency\" styles. Default: \"$\" */\n currencySymbol?: string;\n /** Locale for Intl-based formatting. Default: \"en-US\" */\n locale?: string;\n /** If true, always show + sign for positive numbers */\n showSign?: boolean;\n}\n\n// ─── Thresholds ───────────────────────────────────────────────────────────────\n\nconst COMPACT_THRESHOLDS = [\n { value: 1e12, suffixUpper: \"T\", suffixLower: \"t\", full: \"Trillion\" },\n { value: 1e9, suffixUpper: \"B\", suffixLower: \"b\", full: \"Billion\" },\n { value: 1e6, suffixUpper: \"M\", suffixLower: \"m\", full: \"Million\" },\n { value: 1e3, suffixUpper: \"K\", suffixLower: \"k\", full: \"Thousand\" },\n] as const;\n\nconst BYTE_THRESHOLDS = [\n { value: 1024 ** 5, label: \"PB\" },\n { value: 1024 ** 4, label: \"TB\" },\n { value: 1024 ** 3, label: \"GB\" },\n { value: 1024 ** 2, label: \"MB\" },\n { value: 1024 ** 1, label: \"KB\" },\n] as const;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction trimDecimals(value: number, decimals?: number): string {\n if (decimals !== undefined) return value.toFixed(decimals);\n // Auto: trim unnecessary trailing zeros\n const fixed = value.toFixed(10);\n return parseFloat(fixed).toString();\n}\n\nfunction autoDecimals(value: number, defaultDecimals: number, decimals?: number): string {\n return (decimals !== undefined ? value.toFixed(decimals) : value.toFixed(defaultDecimals))\n .replace(/\\.?0+$/, \"\");\n}\n\nfunction applySign(str: string, num: number, showSign?: boolean): string {\n if (showSign && num > 0) return `+${str}`;\n return str;\n}\n\n// ─── Ordinal ──────────────────────────────────────────────────────────────────\n\nfunction toOrdinal(n: number): string {\n const abs = Math.abs(Math.round(n));\n const mod100 = abs % 100;\n const mod10 = abs % 10;\n if (mod100 >= 11 && mod100 <= 13) return `${abs}th`;\n if (mod10 === 1) return `${abs}st`;\n if (mod10 === 2) return `${abs}nd`;\n if (mod10 === 3) return `${abs}rd`;\n return `${abs}th`;\n}\n\n// ─── Roman Numerals ───────────────────────────────────────────────────────────\n\nfunction toRoman(n: number): string {\n if (n <= 0 || n > 3999 || !Number.isInteger(n)) {\n throw new RangeError(\"Roman numerals only support integers between 1 and 3999.\");\n }\n const vals = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];\n const syms = [\"M\", \"CM\", \"D\", \"CD\", \"C\", \"XC\", \"L\", \"XL\", \"X\", \"IX\", \"V\", \"IV\", \"I\"];\n let result = \"\";\n let remaining = n;\n for (let i = 0; i < vals.length; i++) {\n while (remaining >= vals[i]) {\n result += syms[i];\n remaining -= vals[i];\n }\n }\n return result;\n}\n\n// ─── Compact ─────────────────────────────────────────────────────────────────\n\nfunction formatCompact(\n n: number,\n suffixKey: \"suffixUpper\" | \"suffixLower\" | \"full\",\n lowerFull: boolean,\n decimals?: number,\n): string {\n const abs = Math.abs(n);\n for (const tier of COMPACT_THRESHOLDS) {\n if (abs >= tier.value) {\n const divided = n / tier.value;\n const suffix = suffixKey === \"full\"\n ? (lowerFull ? tier.full.toLowerCase() : tier.full)\n : tier[suffixKey];\n const spacer = suffixKey === \"full\" ? \" \" : \"\";\n return `${autoDecimals(divided, 2, decimals)}${spacer}${suffix}`;\n }\n }\n return trimDecimals(n, decimals ?? 0);\n}\n\n// ─── Decimal ─────────────────────────────────────────────────────────────────\n\nfunction formatDecimal(n: number, separator: string, decimalChar = \".\", decimals?: number): string {\n const fixed = decimals !== undefined ? Math.abs(n).toFixed(decimals) : Math.abs(n).toFixed(0);\n const [intPart, decPart] = fixed.split(\".\");\n const grouped = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, separator);\n const sign = n < 0 ? \"-\" : \"\";\n return decPart !== undefined ? `${sign}${grouped}${decimalChar}${decPart}` : `${sign}${grouped}`;\n}\n\n// ─── Bytes ────────────────────────────────────────────────────────────────────\n\nfunction formatBytes(n: number, decimals = 2): string {\n if (n === 0) return \"0 B\";\n const abs = Math.abs(n);\n for (const tier of BYTE_THRESHOLDS) {\n if (abs >= tier.value) {\n return `${(n / tier.value).toFixed(decimals)} ${tier.label}`;\n }\n }\n return `${n.toFixed(0)} B`;\n}\n\n// ─── Main Export ──────────────────────────────────────────────────────────────\n\n/**\n * Format a number in a variety of styles.\n *\n * @example\n * formatNumber(1_000_000) // \"1M\"\n * formatNumber(1_000_000, { style: \"compact-lower\" }) // \"1m\"\n * formatNumber(1_000_000, { style: \"compact-full\" }) // \"1 Million\"\n * formatNumber(1_000_000, { style: \"currency\" }) // \"$1,000,000.00\"\n * formatNumber(1_000_000, { style: \"decimal\" }) // \"1,000,000\"\n * formatNumber(1_234_567.89, { style: \"decimal-dot\" }) // \"1.234.567\"\n * formatNumber(0.754, { style: \"percent\" }) // \"75.4%\"\n * formatNumber(1_048_576, { style: \"bytes\" }) // \"1.00 MB\"\n * formatNumber(14, { style: \"roman\" }) // \"XIV\"\n */\nexport function formatNumber(value: number, options: FormatOptions = {}): string {\n const {\n style = \"compact\",\n decimals,\n currencySymbol = \"$\",\n locale = \"en-US\",\n showSign,\n } = options;\n\n if (!isFinite(value)) return String(value); // Handle Infinity / NaN\n\n let result: string;\n\n switch (style) {\n case \"compact\":\n result = formatCompact(value, \"suffixUpper\", false, decimals);\n break;\n\n case \"compact-lower\":\n result = formatCompact(value, \"suffixLower\", false, decimals);\n break;\n\n case \"compact-full\":\n result = formatCompact(value, \"full\", false, decimals);\n break;\n\n case \"compact-full-lower\":\n result = formatCompact(value, \"full\", true, decimals);\n break;\n\n case \"currency\": {\n const d = decimals ?? 2;\n const formatted = formatDecimal(value, \",\", \".\", d);\n result = `${currencySymbol}${formatted}`;\n break;\n }\n\n case \"currency-compact\": {\n const compact = formatCompact(Math.abs(value), \"suffixUpper\", false, decimals);\n const sign = value < 0 ? \"-\" : \"\";\n result = `${sign}${currencySymbol}${compact}`;\n break;\n }\n\n case \"decimal\":\n result = formatDecimal(value, \",\", \".\", decimals);\n break;\n\n case \"decimal-dot\":\n result = formatDecimal(value, \".\", \",\", decimals);\n break;\n\n case \"decimal-space\":\n result = formatDecimal(value, \"\\u00A0\", \".\", decimals); // non-breaking space\n break;\n\n case \"plain\":\n result = decimals !== undefined ? value.toFixed(decimals) : String(value);\n break;\n\n case \"scientific\": {\n const d = decimals ?? 2;\n result = value.toExponential(d);\n break;\n }\n\n case \"ordinal\":\n return toOrdinal(value); // sign not applicable\n\n case \"percent\": {\n const d = decimals ?? 1;\n result = `${(value * 100).toFixed(d).replace(/\\.?0+$/, \"\")}%`;\n break;\n }\n\n case \"bytes\":\n result = formatBytes(value, decimals);\n break;\n\n case \"roman\":\n return toRoman(Math.round(value)); // sign not applicable\n\n default:\n result = String(value);\n }\n\n return applySign(result, value, showSign);\n}\n\n// ─── Convenience wrappers ─────────────────────────────────────────────────────\n\n/** 1000000 → \"1M\" */\nexport const compact = (n: number, decimals?: number) =>\n formatNumber(n, { style: \"compact\", decimals });\n\n/** 1000000 → \"1m\" */\nexport const compactLower = (n: number, decimals?: number) =>\n formatNumber(n, { style: \"compact-lower\", decimals });\n\n/** 1000000 → \"1 Million\" */\nexport const compactFull = (n: number, decimals?: number) =>\n formatNumber(n, { style: \"compact-full\", decimals });\n\n/** 1000000 → \"$1,000,000.00\" */\nexport const currencyFormat = (n: number, symbol = \"$\", decimals = 2) =>\n formatNumber(n, { style: \"currency\", currencySymbol: symbol, decimals });\n\n/** 1000000 → \"$1M\" */\nexport const currencyCompact = (n: number, symbol = \"$\") =>\n formatNumber(n, { style: \"currency-compact\", currencySymbol: symbol });\n\n/** 1000000 → \"1,000,000\" */\nexport const decimal = (n: number, decimals?: number) =>\n formatNumber(n, { style: \"decimal\", decimals });\n\n/** 0.75 → \"75%\" */\nexport const percent = (n: number, decimals?: number) =>\n formatNumber(n, { style: \"percent\", decimals });\n\n/** 1048576 → \"1.00 MB\" */\nexport const bytes = (n: number, decimals?: number) =>\n formatNumber(n, { style: \"bytes\", decimals });\n\n/** 14 → \"XIV\" */\nexport const roman = (n: number) => formatNumber(n, { style: \"roman\" });\n\n/** 1 → \"1st\", 2 → \"2nd\" */\nexport const ordinal = (n: number) => formatNumber(n, { style: \"ordinal\" });\n\n// ─── Currency Formatting (Multi-Currency Support) ─────────────────────────────\n\n/**\n * NOTE: Currency and language data should be fetched from KASUVIA_PAYMENT backend API\n * using the useSupportedLanguages hook. Do not hardcode currency values here.\n * Use the formatPrice, getCurrencySymbol, and formatCompactPrice functions with\n * currency/locale parameters from the API response.\n */\n\n/**\n * Format a bare \"HH:mm\" / \"HH:mm:ss\" time string (no date component) as a\n * human-readable 12-hour time, e.g. \"14:30:00\" -> \"2:30 PM\".\n *\n * Owner-flagged cleanup (2026-09-20): call sites needing this used to build\n * a `new Date(\\`1970-01-01T${time}\\`)` just to borrow Date's own formatting\n * — a hardcoded, arbitrary epoch date standing in for a value that was\n * never actually a date. Pure string/number parsing instead — no Date\n * object, no dependency on the JS engine's date-string parsing behavior for\n * a value with no real date component at all.\n */\nexport function formatTimeString(time: string): string {\n const [hoursStr, minutesStr] = time.split(\":\");\n const hours = parseInt(hoursStr, 10);\n if (isNaN(hours) || minutesStr === undefined) return time;\n const period = hours >= 12 ? \"PM\" : \"AM\";\n const displayHours = hours % 12 || 12;\n return `${displayHours}:${minutesStr} ${period}`;\n}\n\n/**\n * Format a price amount with currency symbol and proper decimal places.\n * Uses Intl.NumberFormat for accurate currency formatting.\n *\n * REAL BUG FIXED (2026-09-20): a missing/omitted `currency` used to\n * silently default to \"USD\" — displaying a real price with a fabricated,\n * likely WRONG currency (most Kasuvia businesses price in NGN or another\n * non-USD currency) instead of flagging that the caller never passed the\n * real one. Confirmed this is the actual reachable `formatPrice` (exported\n * via utils/index.ts + src/index.ts) that ~26 call sites across\n * kasuvia-storefront call — most of them WITHOUT a currency argument at\n * all, meaning every one of those was silently mislabeling real prices as\n * USD. Prefer `useAppContext().formatPrice` in storefront components — it\n * reads the actual business currency from real config; this stateless\n * function should only be used where a real currency is already in hand.\n *\n * @param amount - The amount to format (null/undefined returns '')\n * @param currency - ISO 4217 currency code (e.g., USD, NGN, EUR). Required —\n * there is no default; a missing value returns an explicit \"N/A\" marker\n * rather than guessing.\n * @param locale - Locale string for formatting (default: en-US)\n * @returns Formatted currency string\n */\nexport function formatPrice(\n amount: number | null | undefined,\n currency: string | null | undefined,\n locale: string = \"en-US\",\n): string {\n if (amount == null || isNaN(amount)) return \"\";\n if (!currency) return `N/A ${amount.toLocaleString()}`;\n const code = currency.toUpperCase();\n try {\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: code,\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n } catch {\n return `${code} ${amount.toLocaleString()}`;\n }\n}\n\n/**\n * Get the currency symbol for a given currency code.\n *\n * REAL BUG FIXED (2026-09-20): a missing `currency` used to silently\n * default to \"USD\" (same class of bug as formatPrice above) — required now,\n * no default.\n *\n * @param currency - ISO 4217 currency code\n * @param locale - Locale string (default: en-US)\n * @returns Currency symbol (e.g., $, ₦, €)\n */\nexport function getCurrencySymbol(currency: string, locale: string = \"en-US\"): string {\n const formatter = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: currency,\n });\n const parts = formatter.formatToParts(0);\n const symbolPart = parts.find(part => part.type === \"currency\");\n return symbolPart?.value || currency;\n}\n\n/**\n * Format a price in compact notation (e.g., $1M, ₦250K).\n *\n * REAL BUG FIXED (2026-09-20): a missing `currency` used to silently\n * default to \"USD\" (same class of bug as formatPrice above) — required now,\n * no default; a missing value returns an explicit \"N/A\" marker.\n *\n * @param amount - The amount to format\n * @param currency - ISO 4217 currency code (e.g., USD, NGN, EUR)\n * @param locale - Locale string for formatting (default: en-US)\n * @returns Formatted compact currency string\n */\nexport function formatCompactPrice(\n amount: number,\n currency: string | null | undefined,\n locale: string = \"en-US\",\n): string {\n if (!currency) return `N/A ${formatNumber(amount, { style: \"compact\", decimals: 0 })}`;\n const symbol = getCurrencySymbol(currency, locale);\n const compactStr = formatNumber(amount, { style: \"compact\", decimals: 0 });\n return `${symbol}${compactStr}`;\n}\n","\"use client\";\nimport React from 'react'\nimport { useCart } from '../../hooks/use-cart'\nimport { CartItem } from './CartItem'\nimport { CartTypeMismatchBanner } from './CartTypeMismatchBanner'\nimport { CartItemSkeleton } from '../skeletons/CartItemSkeleton'\nimport { formatPrice } from '../../utils/format'\n\nexport interface CartSidebarProps {\n onCheckout?: () => void\n onClose?: () => void\n formatPriceFn?: (price: number) => string\n className?: string\n headerClassName?: string\n titleClassName?: string\n bodyClassName?: string\n footerClassName?: string\n checkoutButtonClassName?: string\n}\n\nexport function CartSidebar({\n onCheckout,\n onClose,\n formatPriceFn,\n className = \"flex flex-col h-full w-full max-w-md bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-800 shadow-xl\",\n headerClassName = \"flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-800\",\n titleClassName = \"text-lg font-semibold text-gray-900 dark:text-gray-100\",\n bodyClassName = \"flex-1 overflow-y-auto p-4 space-y-4\",\n footerClassName = \"p-4 border-t border-gray-200 dark:border-gray-800 space-y-3 bg-gray-50/50 dark:bg-gray-900/50\",\n checkoutButtonClassName = \"w-full py-3 px-4 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-lg transition-colors text-center block disabled:opacity-50\",\n}: CartSidebarProps) {\n const {\n items,\n count,\n subtotal,\n isLoading,\n updateQty,\n removeItem,\n productTypeMismatch,\n handleClearCartAndAdd,\n handleKeepCurrentCart,\n setOpen,\n } = useCart()\n\n const handleClose = () => {\n setOpen(false)\n if (onClose) onClose()\n }\n\n return (\n <div className={className}>\n <div className={headerClassName}>\n <h3 className={titleClassName}>Shopping Cart ({count})</h3>\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200\"\n aria-label=\"Close cart\"\n >\n ✕\n </button>\n </div>\n\n <div className={bodyClassName}>\n {productTypeMismatch && (\n <CartTypeMismatchBanner\n mismatch={productTypeMismatch}\n onClearAndAdd={handleClearCartAndAdd}\n onKeepCurrent={handleKeepCurrentCart}\n />\n )}\n\n {isLoading ? (\n <CartItemSkeleton count={3} />\n ) : items.length === 0 ? (\n <div className=\"text-center py-12 text-gray-500 dark:text-gray-400\">\n Your cart is empty\n </div>\n ) : (\n items.map((item) => (\n <CartItem\n key={item.id}\n item={item}\n onUpdateQty={updateQty}\n onRemove={removeItem}\n formatPriceFn={formatPriceFn}\n />\n ))\n )}\n </div>\n\n {items.length > 0 && (\n <div className={footerClassName}>\n <div className=\"flex justify-between items-center text-sm font-semibold text-gray-900 dark:text-gray-100\">\n <span>Subtotal</span>\n {/* CartResponse carries no currency field — a consuming app\n MUST supply formatPriceFn (its own business-currency-aware\n formatter) for a correct display; the bare fallback is\n honest about not knowing the real currency rather than\n guessing one. */}\n <span>{formatPriceFn ? formatPriceFn(subtotal) : formatPrice(subtotal, undefined)}</span>\n </div>\n <button\n type=\"button\"\n onClick={onCheckout}\n disabled={items.length === 0}\n className={checkoutButtonClassName}\n >\n Proceed to Checkout\n </button>\n </div>\n )}\n </div>\n )\n}\n","\"use client\";\nimport React, { useState, useRef, useId } from 'react'\nimport type { Product } from '../../types/store'\nimport { useCart } from '../../hooks/use-cart'\nimport { formatPrice } from '../../utils/format'\nimport { SmartImage } from '../shared/SmartImage'\n\nexport interface ProductCardProps {\n product: Product\n onSelectProduct?: (product: Product) => void\n onWishlistToggle?: (product: Product, e: React.MouseEvent) => void\n isWishlistSaved?: boolean\n showWishlistButton?: boolean\n priority?: boolean\n className?: string\n /** Sizing/aspect-ratio classes for the image's container (e.g. \"aspect-square\").\n * Kept separate from `imageClassName` because SmartImage renders the image\n * in `fill` mode — sizing has to live on the container, not the <img>. */\n imageContainerClassName?: string\n imageClassName?: string\n titleClassName?: string\n priceClassName?: string\n buttonClassName?: string\n enableImageCarousel?: boolean\n showCarouselDots?: boolean\n showCarouselArrows?: boolean\n activeDotClassName?: string\n dotClassName?: string\n formatPriceFn?: (price: number) => string\n /** Rendered top-left over the image. Falls back to a plain product-type\n * badge when omitted — pass your own (e.g. New/Popular/On Sale badges) to\n * replace it entirely. */\n badgeSlot?: React.ReactNode\n /**\n * Built-in star-rating row (product.average_rating / rating_count — real\n * platform fields, synced from kasuvia-crm's review pipeline). On by\n * default when a product actually has a rating; a storefront with no use\n * for it, or that wants to build its own, can pass `showRating={false}`.\n */\n showRating?: boolean\n ratingClassName?: string\n /**\n * Generic composable slot rendered between the price row and the CTA\n * button — for \"and many more\" (stock urgency, shipping badges, a custom\n * promo line, anything else a specific storefront wants) without forking\n * this component. Renders nothing by default.\n */\n footerSlot?: React.ReactNode\n}\n\nfunction StarIcon({ half, gradientId }: { half?: boolean; gradientId: string }) {\n return (\n <svg viewBox=\"0 0 20 20\" className=\"w-3.5 h-3.5\" aria-hidden=\"true\">\n {half ? (\n <>\n <defs>\n <linearGradient id={gradientId}>\n <stop offset=\"50%\" stopColor=\"currentColor\" />\n <stop offset=\"50%\" stopColor=\"transparent\" stopOpacity=\"1\" />\n </linearGradient>\n </defs>\n <path\n fill={`url(#${gradientId})`}\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n d=\"M10 1.5l2.6 5.27 5.82.85-4.21 4.1.99 5.79L10 14.9l-5.2 2.73.99-5.8-4.21-4.09 5.82-.85L10 1.5z\"\n />\n </>\n ) : (\n <path\n fill=\"currentColor\"\n d=\"M10 1.5l2.6 5.27 5.82.85-4.21 4.1.99 5.79L10 14.9l-5.2 2.73.99-5.8-4.21-4.09 5.82-.85L10 1.5z\"\n />\n )}\n </svg>\n )\n}\n\n/** Renders 5 stars for a 0-5 `average_rating`, including a half star when\n * the fractional part is >= 0.25 and < 0.75 (standard rounding convention\n * for star displays — avoids a misleading full/empty jump at the midpoint).\n * `idPrefix` (from the caller's own `useId()`) keeps the half-star gradient's\n * `<linearGradient id>` unique per card instance — several cards with a\n * half star can render on the same page (a listing grid), and duplicate DOM\n * ids are invalid HTML even though browsers happen to render them right. */\nfunction StarRow({ rating, idPrefix }: { rating: number; idPrefix: string }) {\n const stars = [];\n for (let i = 1; i <= 5; i++) {\n const diff = rating - (i - 1);\n if (diff >= 0.75) stars.push(<StarIcon key={i} gradientId={`${idPrefix}-star-${i}`} />);\n else if (diff >= 0.25) stars.push(<StarIcon key={i} half gradientId={`${idPrefix}-star-${i}`} />);\n else stars.push(<svg key={i} viewBox=\"0 0 20 20\" className=\"w-3.5 h-3.5 text-muted-foreground/30\" aria-hidden=\"true\"><path fill=\"currentColor\" d=\"M10 1.5l2.6 5.27 5.82.85-4.21 4.1.99 5.79L10 14.9l-5.2 2.73.99-5.8-4.21-4.09 5.82-.85L10 1.5z\" /></svg>);\n }\n return <>{stars}</>;\n}\n\nexport function ProductCard({\n product,\n onSelectProduct,\n onWishlistToggle,\n isWishlistSaved = false,\n showWishlistButton = false,\n priority = false,\n className = \"flex flex-col h-full rounded-xl border bg-card overflow-hidden shadow-sm hover:shadow-md transition-shadow group\",\n imageContainerClassName = \"w-full aspect-square\",\n imageClassName = \"transition-transform duration-500 group-hover:scale-105\",\n titleClassName = \"text-base font-semibold text-foreground line-clamp-1 cursor-pointer hover:underline\",\n priceClassName = \"text-lg font-bold text-foreground\",\n buttonClassName = \"w-full py-2.5 px-4 bg-primary hover:bg-primary/90 text-primary-foreground text-sm font-medium rounded-lg transition-colors text-center block mt-auto\",\n enableImageCarousel = true,\n showCarouselDots = true,\n showCarouselArrows = true,\n activeDotClassName = \"bg-primary w-4 h-1.5 rounded-full transition-all\",\n dotClassName = \"bg-white/70 hover:bg-white w-1.5 h-1.5 rounded-full transition-all\",\n formatPriceFn,\n badgeSlot,\n showRating = true,\n ratingClassName = \"flex items-center gap-1 text-amber-500\",\n footerSlot,\n}: ProductCardProps) {\n const { addItem } = useCart()\n const ratingIdPrefix = useId()\n const [currentImageIndex, setCurrentImageIndex] = useState(0)\n const touchStartX = useRef<number | null>(null)\n const touchEndX = useRef<number | null>(null)\n\n const productType = product.product_type\n const serviceType = product.service_type || 'appointment'\n const isConsultationRequired = product.service_requires_consultation\n const images = product.images && product.images.length > 0 ? product.images : []\n // Swipe-to-browse (touch) and hover arrows/dots (pointer) both gate on\n // this — a single-image product never renders carousel affordances at all.\n const hasMultipleImages = images.length > 1 && enableImageCarousel\n const hasRating = showRating && product.average_rating != null && product.rating_count > 0\n\n /**\n * REAL BUG FIXED (2026-09-21), live-reproduced: for `service_type ===\n * 'recurring'` this used to fall through to `addItem(product)` —\n * silently trying to cart a SERVICE product. The real backend\n * (kasuvia-cart-management) hard-rejects every service product from the\n * cart outright (ErrServiceNotCartable), no sub-type exception — so this\n * either failed with no user-facing feedback (fire-and-forget mutation,\n * no error handling here) or, worse, looked like it might have worked.\n * Separately, `onBookAppointment`/`onRequestQuote`/`onRequestBespoke`\n * were dead props — no consumer in kasuvia-storefront ever passed them\n * (confirmed by reading its actual card wrapper), so \"Book Appointment\"/\n * \"Get a Quote\"/bespoke clicks on every product LISTING card (home page,\n * catalog, related products, wishlist) were silent no-ops even for the\n * sub-types that didn't try to cart. Every non-cartable type now routes\n * to the product detail page instead, which already has the full, real\n * flow for each one (BookingSheet, /bespoke/[slug], or an honest\n * disabled state) — a listing card was never the right place to\n * complete a booking/commission/quote flow inline anyway.\n */\n const handleCTA = () => {\n if (productType === 'physical' || productType === 'digital') {\n addItem(product)\n return\n }\n onSelectProduct?.(product)\n }\n\n const getCtaText = () => {\n if (productType === 'physical' || productType === 'digital') return 'Add to Cart'\n if (productType === 'bespoke') return 'Start Commission'\n if (productType === 'service') {\n if (isConsultationRequired || serviceType === 'project') return 'Get a Quote'\n if (serviceType === 'appointment') return 'Book Appointment'\n return 'View Details'\n }\n return 'View Details'\n }\n\n const handleNextImage = (e?: React.MouseEvent) => {\n e?.stopPropagation()\n if (!hasMultipleImages) return\n setCurrentImageIndex((prev) => (prev + 1) % images.length)\n }\n\n const handlePrevImage = (e?: React.MouseEvent) => {\n e?.stopPropagation()\n if (!hasMultipleImages) return\n setCurrentImageIndex((prev) => (prev - 1 + images.length) % images.length)\n }\n\n const handleTouchStart = (e: React.TouchEvent) => {\n if (!hasMultipleImages) return\n touchStartX.current = e.touches[0].clientX\n }\n\n const handleTouchMove = (e: React.TouchEvent) => {\n if (!hasMultipleImages) return\n touchEndX.current = e.touches[0].clientX\n }\n\n const handleTouchEnd = () => {\n if (!hasMultipleImages || touchStartX.current === null || touchEndX.current === null) return\n const distance = touchStartX.current - touchEndX.current\n const isSwipeLeft = distance > 40\n const isSwipeRight = distance < -40\n\n if (isSwipeLeft) handleNextImage()\n else if (isSwipeRight) handlePrevImage()\n\n touchStartX.current = null\n touchEndX.current = null\n }\n\n return (\n <div className={className}>\n <div\n className=\"relative overflow-hidden cursor-pointer group/image\"\n onClick={() => onSelectProduct?.(product)}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {images.length > 0 ? (\n <SmartImage\n src={images[currentImageIndex]}\n alt={`${product.name} - image ${currentImageIndex + 1}`}\n className={imageContainerClassName}\n imageClassName={imageClassName}\n priority={priority}\n sizes=\"(min-width: 1024px) 25vw, 50vw\"\n />\n ) : (\n <div className={`${imageContainerClassName} flex items-center justify-center text-muted-foreground text-sm`}>\n No Image\n </div>\n )}\n\n {badgeSlot ? (\n <div className=\"absolute top-2 left-2 z-10 flex flex-col gap-1\">\n {badgeSlot}\n </div>\n ) : (\n <span className=\"absolute top-2 right-2 px-2 py-0.5 text-xs font-semibold rounded bg-black/60 text-white capitalize z-10\">\n {productType}\n </span>\n )}\n\n {showWishlistButton && (\n <button\n type=\"button\"\n aria-label={isWishlistSaved ? \"Remove from wishlist\" : \"Save to wishlist\"}\n onClick={(e) => {\n e.stopPropagation()\n onWishlistToggle?.(product, e)\n }}\n className=\"absolute top-2 right-2 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-card/80 backdrop-blur shadow-sm hover:bg-card transition-colors\"\n >\n <svg\n className={`w-4 h-4 ${isWishlistSaved ? \"fill-red-500 text-red-500\" : \"text-muted-foreground\"}`}\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <path d=\"M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z\" />\n </svg>\n </button>\n )}\n\n {/* Carousel controls & dots — swipe-to-browse is handled by the\n touch handlers on this wrapper above regardless of these props;\n these only control the visible pointer-device affordances. */}\n {hasMultipleImages && (\n <>\n {showCarouselArrows && (\n <>\n <button\n type=\"button\"\n aria-label=\"Previous image\"\n onClick={handlePrevImage}\n className=\"absolute left-2 top-1/2 -translate-y-1/2 w-7 h-7 rounded-full bg-black/50 hover:bg-black/75 text-white flex items-center justify-center opacity-0 group-hover/image:opacity-100 transition-opacity z-10\"\n >\n ‹\n </button>\n <button\n type=\"button\"\n aria-label=\"Next image\"\n onClick={handleNextImage}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 w-7 h-7 rounded-full bg-black/50 hover:bg-black/75 text-white flex items-center justify-center opacity-0 group-hover/image:opacity-100 transition-opacity z-10\"\n >\n ›\n </button>\n </>\n )}\n\n {showCarouselDots && (\n <div className=\"absolute bottom-2 left-1/2 -translate-x-1/2 flex items-center gap-1 z-10 px-2 py-1 rounded-full bg-black/30 backdrop-blur-xs\">\n {images.map((_, idx) => (\n <button\n key={idx}\n type=\"button\"\n aria-label={`Go to image ${idx + 1}`}\n onClick={(e) => {\n e.stopPropagation()\n setCurrentImageIndex(idx)\n }}\n className={idx === currentImageIndex ? activeDotClassName : dotClassName}\n />\n ))}\n </div>\n )}\n </>\n )}\n </div>\n\n <div className=\"p-4 flex flex-col flex-1 space-y-3\">\n <h3 className={titleClassName} onClick={() => onSelectProduct?.(product)}>\n {product.name}\n </h3>\n\n {product.description && (\n <p className=\"text-xs text-muted-foreground line-clamp-2\">\n {product.description}\n </p>\n )}\n\n {hasRating && (\n <div className={ratingClassName}>\n <StarRow rating={product.average_rating!} idPrefix={ratingIdPrefix} />\n <span className=\"text-xs text-muted-foreground font-medium\">\n {product.average_rating!.toFixed(1)} ({product.rating_count})\n </span>\n </div>\n )}\n\n <div className=\"flex items-center justify-between pt-2 mt-auto\">\n <div className={priceClassName}>\n {formatPriceFn\n ? formatPriceFn(product.sale_price ?? product.base_price)\n : formatPrice(product.sale_price ?? product.base_price, product.currency)}\n </div>\n </div>\n\n {footerSlot}\n\n <button\n type=\"button\"\n onClick={handleCTA}\n className={buttonClassName}\n >\n {getCtaText()}\n </button>\n </div>\n </div>\n )\n}\n","\"use client\";\n\nimport { useState, useEffect, forwardRef, type CSSProperties } from \"react\";\nimport { motion, type HTMLMotionProps } from \"framer-motion\";\nimport Image from \"next/image\";\nimport { ImageIcon } from \"lucide-react\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype ObjectFit = \"cover\" | \"contain\" | \"fill\" | \"none\" | \"scale-down\";\n\ninterface BaseSmartImageProps {\n src: string | null | undefined;\n alt: string;\n className?: string;\n imageClassName?: string;\n priority?: boolean;\n isRelative?: boolean;\n sizes?: string;\n objectFit?: ObjectFit;\n /**\n * Optional fallback image src rendered instead of the broken-image\n * placeholder if the primary src fails to load.\n */\n fallbackSrc?: string;\n /**\n * Passed-through to the underlying Next.js <Image> to suppress\n * hydration warnings on SSR-rendered images.\n */\n suppressHydrationWarning?: boolean;\n}\n\n/**\n * SmartImageProps for the standard (non-motion) variant.\n * Adds optional `style` for container sizing (e.g. explicit width/height).\n */\nexport type SmartImageProps = BaseSmartImageProps & {\n style?: CSSProperties;\n draggable?: boolean;\n};\n\n/**\n * SmartMotionImageProps for the Framer Motion variant.\n * Extends SmartImageProps with all `motion.div` props (initial, animate, etc.)\n */\nexport type SmartMotionImageProps = BaseSmartImageProps &\n Omit<HTMLMotionProps<\"div\">, \"draggable\"> & {\n style?: CSSProperties;\n draggable?: boolean;\n };\n\n// ─── Source Validator ─────────────────────────────────────────────────────────\n\n/**\n * Strictly validates that a source is a viable image path/URL.\n * Rejects empty strings, the literal strings \"null\"/\"undefined\", and anything\n * that doesn't start with a recognised URL scheme or path prefix.\n */\nconst isValidSrc = (url: unknown): url is string => {\n if (!url || typeof url !== \"string\") return false;\n const s = url.trim();\n if (s === \"\" || s === \"null\" || s === \"undefined\") return false;\n return (\n s.startsWith(\"/\") ||\n s.startsWith(\"http://\") ||\n s.startsWith(\"https://\") ||\n s.startsWith(\"data:image/\") ||\n s.startsWith(\"blob:\")\n );\n};\n\n// ─── Shared State Hook ────────────────────────────────────────────────────────\n\n/**\n * Tracks mount, load, and error state for a given `src`.\n * Centralised here so both SmartImage and SmartMotionImage stay in sync.\n */\nfunction useImageState(src: string | null | undefined) {\n const [isMounted, setIsMounted] = useState(false);\n const [isLoading, setIsLoading] = useState(true);\n const [hasError, setHasError] = useState(false);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n // Reset when src changes so a freshly swapped-in URL doesn't inherit\n // the previous URL's stale loaded/failed state.\n useEffect(() => {\n setIsLoading(true);\n setHasError(false);\n }, [src]);\n\n const handleLoad = () => setIsLoading(false);\n const handleError = () => {\n setIsLoading(false);\n setHasError(true);\n };\n\n return { isMounted, isLoading, hasError, handleLoad, handleError };\n}\n\n// ─── Placeholder ──────────────────────────────────────────────────────────────\n\ninterface ImagePlaceholderProps {\n className: string;\n /** When true, renders a visually distinct \"failed to load\" state. */\n failed?: boolean;\n}\n\n/**\n * \"No image\" / \"Failed to load\" placeholder.\n * Uses lucide-react's ImageIcon (same dep already required by consumers).\n */\nfunction ImagePlaceholder({ className, failed }: ImagePlaceholderProps) {\n return (\n <div\n className={`relative overflow-hidden flex flex-col items-center justify-center gap-1 bg-muted/40 ${className}`}\n >\n <ImageIcon className=\"w-4 h-4 text-muted-foreground/50 shrink-0\" />\n <span className=\"text-[10px] text-muted-foreground/70 text-center leading-tight px-1\">\n {failed ? \"Failed to load\" : \"No image\"}\n </span>\n </div>\n );\n}\n\n// ─── Shimmer Skeleton ─────────────────────────────────────────────────────────\n\n/** Full-bleed sweep shimmer shown while the image is in-flight. */\nfunction ImageShimmer() {\n return (\n <div className=\"absolute inset-0 z-10 bg-muted/60 overflow-hidden\">\n <div className=\"absolute inset-0 -translate-x-full animate-shimmer bg-linear-to-r from-transparent via-white/10 to-transparent\" />\n </div>\n );\n}\n\n// ─── SmartImage ───────────────────────────────────────────────────────────────\n\n/**\n * Standard smart image component for Kasuvia storefronts and dashboards.\n *\n * Wraps Next.js `<Image fill>` with:\n * - Strict src validation (rejects null / empty / \"null\" strings)\n * - A shimmer skeleton shown while the image loads\n * - A labelled placeholder shown when there is no valid src\n * - A distinct failed-to-load placeholder (or optional `fallbackSrc`)\n * - An `objectFit` prop (defaults to \"cover\") for flexibility\n *\n * Peer dependencies: next, framer-motion, lucide-react\n */\nexport const SmartImage = forwardRef<HTMLImageElement, SmartImageProps>(function SmartImage(\n {\n src,\n alt,\n className = \"\",\n imageClassName = \"\",\n priority = false,\n isRelative = true,\n sizes = \"100vw\",\n objectFit = \"cover\",\n fallbackSrc,\n suppressHydrationWarning = false,\n style,\n draggable,\n },\n ref\n) {\n const { isMounted, isLoading, hasError, handleLoad, handleError } =\n useImageState(src);\n\n const resolvedSrc = isValidSrc(src) ? src.trim() : null;\n const fallback = isValidSrc(fallbackSrc) ? fallbackSrc!.trim() : null;\n\n if (!isMounted || (!resolvedSrc && !fallback)) {\n return <ImagePlaceholder className={className} />;\n }\n\n if (hasError) {\n if (fallback) {\n return (\n <div\n className={`${isRelative ? \"relative\" : \"\"} overflow-hidden ${className}`}\n style={style}\n >\n <Image\n ref={ref}\n src={fallback}\n alt={alt}\n fill\n unoptimized\n sizes={sizes}\n className={`object-${objectFit} ${imageClassName}`}\n draggable={draggable}\n />\n </div>\n );\n }\n return <ImagePlaceholder className={className} failed />;\n }\n\n return (\n <div\n className={`${isRelative ? \"relative\" : \"\"} overflow-hidden ${className}`}\n style={style}\n >\n {isLoading && <ImageShimmer />}\n <Image\n ref={ref}\n src={resolvedSrc!}\n alt={alt}\n fill\n priority={priority}\n sizes={sizes}\n unoptimized\n suppressHydrationWarning={suppressHydrationWarning}\n className={`object-${objectFit} ${imageClassName} transition-opacity duration-300 ${isLoading ? \"opacity-0\" : \"opacity-100\"}`}\n onLoad={handleLoad}\n onError={handleError}\n draggable={draggable}\n />\n </div>\n );\n});\n\n// ─── SmartMotionImage ─────────────────────────────────────────────────────────\n\n/**\n * Framer Motion variant of SmartImage.\n * Accepts all `motion.div` props (e.g. `initial`, `animate`, `whileHover`)\n * alongside the standard SmartImage props.\n *\n * Peer dependencies: next, framer-motion, lucide-react\n */\nexport const SmartMotionImage = forwardRef<HTMLDivElement, SmartMotionImageProps>(function SmartMotionImage(\n {\n src,\n alt,\n className = \"\",\n imageClassName = \"\",\n priority = false,\n isRelative = true,\n sizes = \"100vw\",\n objectFit = \"cover\",\n fallbackSrc,\n style,\n draggable,\n ...motionProps\n },\n ref\n) {\n const { isMounted, isLoading, hasError, handleLoad, handleError } =\n useImageState(src);\n\n const resolvedSrc = isValidSrc(src) ? src.trim() : null;\n const fallback = isValidSrc(fallbackSrc) ? fallbackSrc!.trim() : null;\n\n if (!isMounted || (!resolvedSrc && !fallback)) {\n return <ImagePlaceholder className={className} />;\n }\n\n if (hasError) {\n if (fallback) {\n return (\n <motion.div\n ref={ref}\n className={`${isRelative ? \"relative\" : \"\"} overflow-hidden ${className}`}\n style={style}\n {...motionProps}\n >\n <Image\n src={fallback}\n alt={alt}\n fill\n unoptimized\n sizes={sizes}\n className={`object-${objectFit} ${imageClassName}`}\n draggable={draggable}\n />\n </motion.div>\n );\n }\n return <ImagePlaceholder className={className} failed />;\n }\n\n return (\n <motion.div\n ref={ref}\n className={`${isRelative ? \"relative\" : \"\"} overflow-hidden ${className}`}\n style={style}\n {...motionProps}\n >\n {isLoading && <ImageShimmer />}\n <Image\n src={resolvedSrc!}\n alt={alt}\n fill\n priority={priority}\n sizes={sizes}\n unoptimized\n className={`object-${objectFit} ${imageClassName} transition-opacity duration-300 ${isLoading ? \"opacity-0\" : \"opacity-100\"}`}\n onLoad={handleLoad}\n onError={handleError}\n draggable={draggable}\n />\n </motion.div>\n );\n});\n\nexport default SmartImage;\n","\"use client\";\nimport React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport type { Product, ListParams } from '../../types/store'\nimport { getStorefrontProducts } from '../../services/product.service'\nimport { useSdkContext, resolveHookArgs } from '../../context/sdk-context'\nimport { getCacheTier } from '../../utils/cache'\nimport { ProductCard } from './ProductCard'\nimport { CardSkeleton } from '../skeletons/CardSkeleton'\n\nexport interface ProductGridProps {\n params?: ListParams\n onSelectProduct?: (product: Product) => void\n onWishlistToggle?: (product: Product, e: React.MouseEvent) => void\n isWishlistSavedFn?: (product: Product) => boolean\n showWishlistButton?: boolean\n formatPriceFn?: (price: number) => string\n className?: string\n cardClassName?: string\n}\n\nexport function ProductGrid({\n params,\n onSelectProduct,\n onWishlistToggle,\n isWishlistSavedFn,\n showWishlistButton = false,\n formatPriceFn,\n className = \"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\",\n cardClassName,\n}: ProductGridProps) {\n // REAL BUG FIXED (2026-09-21): this called `useStorefrontProducts`, which\n // fetched from a Next.js API route (`/api/products`) deleted earlier this\n // session as confirmed orphaned — this component was missed by that grep\n // since it has no consumer in kasuvia-storefront yet. Fixed to call the\n // real GraphQL service function directly, resolving config from\n // SdkContext the same way every other SDK hook does.\n const ctx = useSdkContext()\n const { config } = resolveHookArgs(undefined, undefined, ctx)\n const { data, isLoading, error } = useQuery({\n queryKey: ['products', 'grid', params],\n queryFn: () => getStorefrontProducts(config, params),\n ...getCacheTier('medium'),\n })\n\n if (isLoading) {\n return <CardSkeleton count={8} containerClassName={className} />\n }\n\n if (error) {\n return (\n <div className=\"text-center py-12 text-destructive\">\n Failed to load products. Please try again.\n </div>\n )\n }\n\n const products: Product[] = data?.items || []\n\n if (products.length === 0) {\n return (\n <div className=\"text-center py-12 text-muted-foreground\">\n No products found.\n </div>\n )\n }\n\n return (\n <div className={className}>\n {products.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n onSelectProduct={onSelectProduct}\n onWishlistToggle={onWishlistToggle}\n isWishlistSaved={isWishlistSavedFn ? isWishlistSavedFn(product) : false}\n showWishlistButton={showWishlistButton}\n formatPriceFn={formatPriceFn}\n className={cardClassName}\n />\n ))}\n </div>\n )\n}\n","import { GraphQLClient } from 'graphql-request'\nimport type { SdkConfig } from '../config'\n\n/**\n * REAL BUG FIXED (2026-09-21), live-reproduced in the browser console:\n * `graphql-request`'s `GraphQLClient` constructs a `new URL(endpoint)`\n * internally when issuing a request. Client-side SDK config deliberately\n * uses a same-origin relative path (`/api/proxy/store`) so the real backend\n * host is never exposed to the browser — but the native `URL` constructor\n * throws `TypeError: Failed to construct 'URL': Invalid URL` for a relative\n * string with no base, which is exactly what broke every client-side\n * GraphQL call (products, collections, wishlist): `Failed to fetch\n * products`/`Failed to fetch collections` in ProductsListing.tsx.\n *\n * Server-side config already uses an absolute real-backend URL, so this is\n * a no-op there — only the browser case (a relative path, `window` defined)\n * needs the current origin prepended.\n */\nexport function resolveGraphQLEndpoint(url: string): string {\n if (typeof window === 'undefined') return url\n try {\n // Absolute already (real backend URL, e.g. in a server-rendered client bundle edge case) — leave as-is.\n new URL(url)\n return url\n } catch {\n return `${window.location.origin}${url}`\n }\n}\n\n/**\n * REAL DUPLICATION FIXED (2026-09-22): this exact function (same headers,\n * same endpoint resolution) was hand-copied identically into\n * product.service.ts, collection.service.ts, and wishlist.service.ts — the\n * SDK's three GraphQL-backed services. One shared implementation now.\n */\nexport function createGraphQLClient(config: SdkConfig, accessToken?: string): GraphQLClient {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'X-Storefront-Key': config.storefrontApiKey,\n 'X-Business-ID': config.businessId,\n }\n if (accessToken) {\n headers['Authorization'] = `Bearer ${accessToken}`\n }\n return new GraphQLClient(resolveGraphQLEndpoint(`${config.backendUrls.store}/graphql`), { headers })\n}\n","/** Internal type. DO NOT USE DIRECTLY. */\ntype Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };\n/** Internal type. DO NOT USE DIRECTLY. */\nexport type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };\nimport { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\nexport type CollectionStatus =\n | 'ACTIVE'\n | 'ARCHIVED'\n | 'HIDDEN';\n\nexport type CollectionType =\n | 'MANUAL'\n | 'SMART';\n\nexport type ProductType =\n | 'BESPOKE'\n | 'DIGITAL'\n | 'PHYSICAL'\n | 'SERVICE';\n\nexport type CollectionFieldsFragment = { id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown };\n\nexport type GetStorefrontCollectionsQueryVariables = Exact<{\n page: number;\n pageSize: number;\n}>;\n\n\nexport type GetStorefrontCollectionsQuery = { storefront_collections: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> } };\n\nexport type GetStorefrontCollectionBySlugQueryVariables = Exact<{\n slug: string;\n}>;\n\n\nexport type GetStorefrontCollectionBySlugQuery = { storefront_collection_by_slug: { id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown } | null };\n\nexport type ProductFieldsFragment = { id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> };\n\nexport type GetStorefrontProductsQueryVariables = Exact<{\n page: number;\n pageSize: number;\n collectionSlug?: string | null | undefined;\n search?: string | null | undefined;\n productType?: string | null | undefined;\n}>;\n\n\nexport type GetStorefrontProductsQuery = { storefront_products: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> }> } };\n\nexport type GetStorefrontProductBySlugQueryVariables = Exact<{\n slug: string;\n}>;\n\n\nexport type GetStorefrontProductBySlugQuery = { storefront_product_by_slug: { id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> } | null };\n\nexport type GetStorefrontPopularQueryVariables = Exact<{\n page: number;\n pageSize: number;\n}>;\n\n\nexport type GetStorefrontPopularQuery = { storefront_popular: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> }> } };\n\nexport type GetStorefrontFeaturedQueryVariables = Exact<{\n page: number;\n pageSize: number;\n}>;\n\n\nexport type GetStorefrontFeaturedQuery = { storefront_featured: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> }> } };\n\nexport type GetStorefrontNewQueryVariables = Exact<{\n page: number;\n pageSize: number;\n}>;\n\n\nexport type GetStorefrontNewQuery = { storefront_new: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> }> } };\n\nexport type GetStorefrontOnSaleQueryVariables = Exact<{\n page: number;\n pageSize: number;\n}>;\n\n\nexport type GetStorefrontOnSaleQuery = { storefront_on_sale: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> }> } };\n\nexport type GetStorefrontRecommendedQueryVariables = Exact<{\n page: number;\n pageSize: number;\n excludeProductId?: string | null | undefined;\n}>;\n\n\nexport type GetStorefrontRecommendedQuery = { storefront_recommended: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, business_id: string, name: string, slug: string, description: string | null, brand: string | null, product_type: ProductType, requires_shipping: boolean, is_consumable: boolean, is_bespoke: boolean, is_physical: boolean | null, is_non_returnable: boolean, deposit_percentage: number, base_price: number, currency: string, sale_price: number | null, sale_start: unknown, sale_end: unknown, images: Array<string>, tags: Array<string>, is_published: boolean, is_featured: boolean, is_popular: boolean, is_new: boolean, is_temporary: boolean, average_rating: number | null, rating_count: number, source_channel: string, source_channel_ref_id: string | null, service_type: string | null, service_duration_minutes: number | null, service_location_type: string | null, service_coverage_radius_km: number | null, service_requires_consultation: boolean, service_max_capacity_per_slot: number | null, service_buffer_minutes: number, service_is_recurring: boolean, service_recurring_interval: string | null, service_project_based: boolean, download_limit: number | null, download_expires_at: unknown, weight_kg: number, length_cm: number | null, width_cm: number | null, height_cm: number | null, created_at: unknown, updated_at: unknown, custom_fields: Array<{ label: string, unit: string | null, required: boolean, field_type: string, options: Array<string> }>, digital_files: Array<{ id: string, file_key: string, display_name: string, sort_order: number }>, variants: Array<{ id: string, product_id: string, sku: string | null, attributes: string | null, price: number | null, images: Array<string>, weight_kg: number | null, is_active: boolean, created_at: unknown, updated_at: unknown, inventory: { id: string, product_id: string, variant_id: string | null, quantity: number, reserved: number, reorder_point: number, reorder_quantity: number, track_quantity: boolean, allow_backorder: boolean, available: number, is_in_stock: boolean, is_low_stock: boolean, updated_at: unknown } | null }>, collections: Array<{ id: string, business_id: string | null, name: string, slug: string, description: string | null, image_url: string | null, collection_type: CollectionType, rules: string | null, status: CollectionStatus, sort_order: number, created_at: unknown, updated_at: unknown }> }> } };\n\nexport type GetStorefrontWishlistQueryVariables = Exact<{\n page: number;\n pageSize: number;\n}>;\n\n\nexport type GetStorefrontWishlistQuery = { storefront_wishlist: { total: number, page: number, page_size: number, total_pages: number, items: Array<{ id: string, product_id: string, notes: string | null, created_at: unknown, product_name: string | null, product_slug: string | null, product_price: number | null, currency: string | null, product_image: string | null }> } };\n\nexport type CheckInStorefrontWishlistQueryVariables = Exact<{\n productId: string;\n}>;\n\n\nexport type CheckInStorefrontWishlistQuery = { check_in_storefront_wishlist: { in_wishlist: boolean } };\n\nexport const CollectionFieldsFragmentDoc = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"CollectionFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Collection\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]} as unknown as DocumentNode<CollectionFieldsFragment, unknown>;\nexport const ProductFieldsFragmentDoc = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<ProductFieldsFragment, unknown>;\nexport const GetStorefrontCollectionsDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontCollections\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_collections\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"CollectionFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"CollectionFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Collection\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]} as unknown as DocumentNode<GetStorefrontCollectionsQuery, GetStorefrontCollectionsQueryVariables>;\nexport const GetStorefrontCollectionBySlugDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontCollectionBySlug\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_collection_by_slug\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"CollectionFields\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"CollectionFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Collection\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]} as unknown as DocumentNode<GetStorefrontCollectionBySlugQuery, GetStorefrontCollectionBySlugQueryVariables>;\nexport const GetStorefrontProductsDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontProducts\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"collectionSlug\"}},\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"search\"}},\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"productType\"}},\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_products\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"collectionSlug\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"collectionSlug\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"search\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"search\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"productType\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"productType\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontProductsQuery, GetStorefrontProductsQueryVariables>;\nexport const GetStorefrontProductBySlugDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontProductBySlug\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_product_by_slug\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontProductBySlugQuery, GetStorefrontProductBySlugQueryVariables>;\nexport const GetStorefrontPopularDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontPopular\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_popular\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontPopularQuery, GetStorefrontPopularQueryVariables>;\nexport const GetStorefrontFeaturedDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontFeatured\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_featured\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontFeaturedQuery, GetStorefrontFeaturedQueryVariables>;\nexport const GetStorefrontNewDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontNew\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_new\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontNewQuery, GetStorefrontNewQueryVariables>;\nexport const GetStorefrontOnSaleDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontOnSale\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_on_sale\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontOnSaleQuery, GetStorefrontOnSaleQueryVariables>;\nexport const GetStorefrontRecommendedDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontRecommended\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"excludeProductId\"}},\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_recommended\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"excludeProductId\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"excludeProductId\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"FragmentSpread\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}},{\"kind\":\"FragmentDefinition\",\"name\":{\"kind\":\"Name\",\"value\":\"ProductFields\"},\"typeCondition\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Product\"}},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"brand\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"requires_shipping\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_consumable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_bespoke\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_physical\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_non_returnable\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"deposit_percentage\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"base_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_start\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sale_end\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"tags\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"custom_fields\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"label\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"unit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"required\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"field_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"options\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_published\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_featured\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_popular\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_new\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_temporary\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"average_rating\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rating_count\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"source_channel_ref_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_duration_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_location_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_coverage_radius_km\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_requires_consultation\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_max_capacity_per_slot\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_buffer_minutes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_is_recurring\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_recurring_interval\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"service_project_based\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"digital_files\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"file_key\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"display_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_limit\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"download_expires_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"length_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"width_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"height_cm\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variants\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sku\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"attributes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"images\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"weight_kg\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_active\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"inventory\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"variant_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reserved\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_point\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"reorder_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"track_quantity\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"allow_backorder\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"available\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_in_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"is_low_stock\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collections\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"business_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"description\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"image_url\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"collection_type\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"rules\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"status\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"sort_order\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"updated_at\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontRecommendedQuery, GetStorefrontRecommendedQueryVariables>;\nexport const GetStorefrontWishlistDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"GetStorefrontWishlist\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}},{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"Int\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"storefront_wishlist\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}}},{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"pageSize\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"items\"},\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_id\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"notes\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"created_at\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_name\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_slug\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_price\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"currency\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"product_image\"}}]}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"page_size\"}},{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"total_pages\"}}]}}]}}]} as unknown as DocumentNode<GetStorefrontWishlistQuery, GetStorefrontWishlistQueryVariables>;\nexport const CheckInStorefrontWishlistDocument = {\"kind\":\"Document\",\"definitions\":[{\"kind\":\"OperationDefinition\",\"operation\":\"query\",\"name\":{\"kind\":\"Name\",\"value\":\"CheckInStorefrontWishlist\"},\"variableDefinitions\":[{\"kind\":\"VariableDefinition\",\"variable\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"productId\"}},\"type\":{\"kind\":\"NonNullType\",\"type\":{\"kind\":\"NamedType\",\"name\":{\"kind\":\"Name\",\"value\":\"String\"}}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"check_in_storefront_wishlist\"},\"arguments\":[{\"kind\":\"Argument\",\"name\":{\"kind\":\"Name\",\"value\":\"productId\"},\"value\":{\"kind\":\"Variable\",\"name\":{\"kind\":\"Name\",\"value\":\"productId\"}}}],\"selectionSet\":{\"kind\":\"SelectionSet\",\"selections\":[{\"kind\":\"Field\",\"name\":{\"kind\":\"Name\",\"value\":\"in_wishlist\"}}]}}]}}]} as unknown as DocumentNode<CheckInStorefrontWishlistQuery, CheckInStorefrontWishlistQueryVariables>;","/**\n * Product Service\n *\n * Handles product operations using GraphQL.\n * Stateless - all configuration passed as parameters.\n */\nimport type { Product, PaginatedResponse, ListParams } from '../types/store'\nimport { ProductTypeEnum } from '../types/store'\nimport type { SdkConfig } from '../config'\nimport { createGraphQLClient } from '../utils/graphql-url'\nimport {\n GetStorefrontProductsDocument,\n GetStorefrontProductBySlugDocument,\n GetStorefrontPopularDocument,\n GetStorefrontFeaturedDocument,\n GetStorefrontNewDocument,\n GetStorefrontOnSaleDocument,\n GetStorefrontRecommendedDocument,\n type ProductFieldsFragment,\n type ProductType as GeneratedProductType,\n} from '../generated/graphql'\n\n/**\n * REAL BUG FIXED (2026-09-21), live-reproduced — the root cause of \"nothing\n * e-commerce-related works\": kasuvia-store-management's ProductType is a\n * Python Enum (`PHYSICAL = \"physical\"`, schema.py:16-20). Strawberry\n * GraphQL serializes an enum field using the member NAME over the wire —\n * standard, spec-correct GraphQL behavior (GraphQL enum values are\n * conventionally ALL_CAPS) — so `storefront_products { items { product_type\n * } } }` has always actually returned `\"PHYSICAL\"`/`\"DIGITAL\"`/`\"SERVICE\"`/\n * `\"BESPOKE\"`, never the lowercase value every frontend `product_type ===\n * \"physical\"` check (ProductCTA, ProductInfo, ProductTypeDetails,\n * ProductCard, CheckoutClient, getGuestCartProductType — every one of them)\n * was written assuming. Every type-aware branch silently fell through to\n * whatever its default/fallback case was, for every single product,\n * platform-wide — confirmed live: a real SERVICE product rendered a plain\n * \"Add to Cart\" button instead of any service-specific flow.\n *\n * Originally fixed with a blind `.toLowerCase()` at the single point every\n * product enters the SDK from GraphQL. Now that `product_type` is a real\n * generated union (`GeneratedProductType`, from graphql-codegen against the\n * live schema — see graphql/schema.graphql), this is an exhaustive switch\n * instead: if kasuvia-store-management ever adds a 5th ProductType member,\n * this function fails to COMPILE until it's handled here, rather than\n * silently lowercasing an unrecognised string at runtime.\n */\nfunction mapProductType(raw: GeneratedProductType): ProductTypeEnum {\n switch (raw) {\n case 'PHYSICAL':\n return ProductTypeEnum.PHYSICAL\n case 'DIGITAL':\n return ProductTypeEnum.DIGITAL\n case 'SERVICE':\n return ProductTypeEnum.SERVICE\n case 'BESPOKE':\n return ProductTypeEnum.BESPOKE\n default: {\n const _exhaustive: never = raw\n throw new Error(`Unhandled GraphQL ProductType member: ${_exhaustive}`)\n }\n }\n}\n\n/**\n * `field_type` is a plain GraphQL String (CustomField isn't a GraphQL enum),\n * but the real backend (CustomFieldSchema's `Literal[\"text\", \"choice\"]`)\n * only ever writes one of these two values — validated at save time, not\n * guessed here. An unrecognised value means the backend and this mapping\n * have drifted, which should fail loud rather than silently coerce to a\n * default that would misrender the field.\n */\nfunction mapCustomFieldType(raw: string): \"text\" | \"choice\" {\n if (raw === \"text\" || raw === \"choice\") return raw\n throw new Error(`Unrecognised custom field field_type from backend: \"${raw}\"`)\n}\n\n/**\n * Maps the raw, generated GraphQL fragment shape to the SDK's public\n * `Product` type. `satisfies Product` (not `as Product`) is deliberate: it\n * keeps this a real compile-time check against the public type rather than\n * an unchecked assertion — if `ProductFieldsFragment` and `Product` ever\n * drift (a field renamed on one side, a nullability change), this function\n * fails to compile instead of quietly shipping a mismatched shape.\n */\nfunction mapProduct(raw: ProductFieldsFragment): Product {\n return {\n ...raw,\n product_type: mapProductType(raw.product_type),\n custom_fields: raw.custom_fields.map((f) => ({\n ...f,\n unit: f.unit ?? undefined,\n field_type: mapCustomFieldType(f.field_type),\n })),\n description: raw.description ?? undefined,\n brand: raw.brand ?? undefined,\n is_physical: raw.is_physical ?? undefined,\n sale_price: raw.sale_price ?? undefined,\n sale_start: (raw.sale_start as string | null) ?? undefined,\n sale_end: (raw.sale_end as string | null) ?? undefined,\n source_channel_ref_id: raw.source_channel_ref_id ?? undefined,\n download_limit: raw.download_limit ?? undefined,\n download_expires_at: (raw.download_expires_at as string | null) ?? undefined,\n service_type: (raw.service_type as Product['service_type']) ?? undefined,\n service_duration_minutes: raw.service_duration_minutes ?? undefined,\n service_location_type: (raw.service_location_type as Product['service_location_type']) ?? undefined,\n service_coverage_radius_km: raw.service_coverage_radius_km ?? undefined,\n service_max_capacity_per_slot: raw.service_max_capacity_per_slot ?? undefined,\n service_recurring_interval: (raw.service_recurring_interval as Product['service_recurring_interval']) ?? undefined,\n length_cm: raw.length_cm ?? undefined,\n width_cm: raw.width_cm ?? undefined,\n height_cm: raw.height_cm ?? undefined,\n average_rating: raw.average_rating ?? undefined,\n created_at: raw.created_at as string,\n updated_at: raw.updated_at as string,\n variants: raw.variants.map((v) => ({\n ...v,\n sku: v.sku ?? undefined,\n attributes: v.attributes ? JSON.parse(v.attributes) : {},\n price: v.price ?? undefined,\n weight_kg: v.weight_kg ?? undefined,\n inventory: v.inventory\n ? {\n ...v.inventory,\n variant_id: v.inventory.variant_id ?? undefined,\n updated_at: v.inventory.updated_at as string,\n }\n : undefined,\n created_at: v.created_at as string,\n updated_at: v.updated_at as string,\n })),\n collections: raw.collections.map((c) => ({\n ...c,\n business_id: c.business_id ?? undefined,\n description: c.description ?? undefined,\n image_url: c.image_url ?? undefined,\n rules: c.rules ?? undefined,\n created_at: c.created_at as string,\n updated_at: c.updated_at as string,\n })),\n } satisfies Product\n}\n\nfunction mapProductConnection<T extends { items: ProductFieldsFragment[]; total: number; page: number; page_size: number; total_pages: number }>(\n connection: T\n): PaginatedResponse<Product> {\n return { ...connection, items: connection.items.map(mapProduct) }\n}\n\n/**\n * Create GraphQL client with provided config\n */\n/**\n * Fetch all active products for the storefront.\n */\nexport async function getStorefrontProducts(\n config: SdkConfig,\n params?: ListParams\n): Promise<PaginatedResponse<Product>> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontProductsDocument, {\n page: params?.page || 1,\n pageSize: params?.pageSize || 20,\n collectionSlug: params?.collectionSlug,\n search: params?.search,\n productType: params?.productType,\n })\n return mapProductConnection(data.storefront_products)\n}\n\n/**\n * Fetch a single product by slug for the storefront.\n */\nexport async function getStorefrontProductBySlug(\n config: SdkConfig,\n slug: string\n): Promise<Product> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontProductBySlugDocument, { slug })\n if (!data.storefront_product_by_slug) {\n throw new Error(`Product not found: ${slug}`)\n }\n return mapProduct(data.storefront_product_by_slug)\n}\n\n/**\n * Fetch popular products for the storefront.\n */\nexport async function getPopularProducts(\n config: SdkConfig,\n params?: ListParams\n): Promise<PaginatedResponse<Product>> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontPopularDocument, {\n page: params?.page || 1,\n pageSize: params?.pageSize || 4,\n })\n return mapProductConnection(data.storefront_popular)\n}\n\n/**\n * Fetch featured products for the storefront.\n */\nexport async function getFeaturedProducts(\n config: SdkConfig,\n params?: ListParams\n): Promise<PaginatedResponse<Product>> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontFeaturedDocument, {\n page: params?.page || 1,\n pageSize: params?.pageSize || 4,\n })\n return mapProductConnection(data.storefront_featured)\n}\n\n/**\n * Fetch new products for the storefront.\n */\nexport async function getNewProducts(\n config: SdkConfig,\n params?: ListParams\n): Promise<PaginatedResponse<Product>> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontNewDocument, {\n page: params?.page || 1,\n pageSize: params?.pageSize || 4,\n })\n return mapProductConnection(data.storefront_new)\n}\n\n/**\n * Fetch products currently on sale for the storefront.\n */\nexport async function getOnSaleProducts(\n config: SdkConfig,\n params?: ListParams\n): Promise<PaginatedResponse<Product>> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontOnSaleDocument, {\n page: params?.page || 1,\n pageSize: params?.pageSize || 4,\n })\n return mapProductConnection(data.storefront_on_sale)\n}\n\n/**\n * Fetch recommended products for the storefront.\n * Returns featured, popular, and new products.\n * Optionally excludes a specific product ID.\n */\nexport async function getRecommendedProducts(\n config: SdkConfig,\n params?: ListParams & { excludeProductId?: string }\n): Promise<PaginatedResponse<Product>> {\n const client = createGraphQLClient(config)\n const data = await client.request(GetStorefrontRecommendedDocument, {\n page: params?.page || 1,\n pageSize: params?.pageSize || 4,\n excludeProductId: params?.excludeProductId,\n })\n return mapProductConnection(data.storefront_recommended)\n}\n","/**\n * Cache Tiers Utility\n * \n * Provides cache configuration for React Query based on environment.\n */\n\nconst CACHE_TIERS = {\n none: { staleTime: 0, gcTime: 0 },\n infinity: { staleTime: Infinity, gcTime: 0 },\n low: { staleTime: 1000 * 30, gcTime: 1000 * 60 }, // 30s / 1m\n medium: { staleTime: 1000 * 60 * 5, gcTime: 1000 * 60 * 10 }, // 5m / 10m\n high: { staleTime: 1000 * 60 * 30, gcTime: 1000 * 60 * 60 }, // 30m / 1h\n} as const;\n\nexport type CacheTier = keyof typeof CACHE_TIERS;\n\nlet isDevelopment = true;\n\n/**\n * Set the environment mode for cache configuration\n */\nexport function setEnvironmentMode(development: boolean): void {\n isDevelopment = development;\n}\n\n/**\n * Get cache configuration for a given tier\n * In development, returns 'none' tier to disable caching\n */\nexport const getCacheTier = (tier: CacheTier) => {\n if (isDevelopment) return CACHE_TIERS.none;\n return CACHE_TIERS[tier];\n};\n","\"use client\";\nimport React, { useState } from 'react'\nimport { useAvailableSlots } from '../../hooks/use-service-booking'\nimport { formatTimeString } from '../../utils/format'\n\nexport interface SlotPickerProps {\n productId: string\n /** A single date (YYYY-MM-DD) — the real backend checks one day at a time. */\n date: string\n staffId?: string\n locationId?: string\n onSelectSlot: (time: string) => void\n className?: string\n slotButtonClassName?: string\n}\n\n/**\n * REAL BUG FIXED (2026-09-20): rebuilt against the real backend response\n * (GET /catalog/products/{id}/available-slots — a single `date`, not a\n * dateFrom/dateTo range; `available_slots` is a flat array of ISO time\n * strings, not slot objects with start_time/end_time/slots_remaining, none\n * of which the real endpoint returns).\n */\nexport function SlotPicker({\n productId,\n date,\n staffId,\n locationId,\n onSelectSlot,\n className = \"space-y-4\",\n slotButtonClassName = \"p-3 border rounded-lg text-sm text-center hover:border-primary focus:outline-none transition-colors\",\n}: SlotPickerProps) {\n const { data, isLoading, error } = useAvailableSlots(undefined, productId, date, { staffId, locationId })\n const [selectedTime, setSelectedTime] = useState<string | null>(null)\n\n if (isLoading) {\n return (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 animate-pulse\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-12 bg-muted rounded-lg\" />\n ))}\n </div>\n )\n }\n\n if (error || !data) {\n return (\n <div className=\"text-xs text-destructive\">\n Failed to load available time slots. Please select a different date.\n </div>\n )\n }\n\n if (!data.serviceable) {\n return (\n <div className=\"text-xs text-muted-foreground py-4 text-center\">\n {data.reason || \"This service isn't available for booking right now.\"}\n </div>\n )\n }\n\n if (data.available_slots.length === 0) {\n return (\n <div className=\"text-xs text-muted-foreground py-4 text-center\">\n No time slots available on this date.\n </div>\n )\n }\n\n return (\n <div className={className}>\n <h4 className=\"text-xs font-semibold uppercase tracking-wider text-muted-foreground\">Available Time Slots</h4>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n {data.available_slots.map((time) => {\n const isSelected = selectedTime === time\n return (\n <button\n key={time}\n type=\"button\"\n onClick={() => {\n setSelectedTime(time)\n onSelectSlot(time)\n }}\n className={`${slotButtonClassName} ${\n isSelected\n ? 'border-primary bg-primary/10 text-primary font-semibold'\n : 'border-border text-foreground'\n }`}\n >\n {formatTimeString(time)}\n </button>\n )\n })}\n </div>\n </div>\n )\n}\n","/**\n * Service Booking Hooks\n *\n * React Query hooks for service bookings.\n * config and accessToken are optional — if omitted, they are read from SdkContext.\n */\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport {\n getAvailableSlots,\n bookAppointment,\n getAppointments,\n getAppointment,\n cancelAppointment,\n rescheduleAppointment,\n disputeAppointment,\n confirmAppointmentCompletion,\n getMilestones,\n approveMilestone,\n} from '../services/service-booking.service'\nimport type { SdkConfig } from '../config'\nimport type {\n ServiceAppointment,\n ServiceAppointmentCreatePayload,\n CancelAppointmentPayload,\n RescheduleAppointmentPayload,\n DisputeAppointmentPayload,\n ConfirmAppointmentPayload,\n ApproveMilestonePayload,\n} from '../types/services'\nimport { useSdkContext, resolveHookArgs } from '../context/sdk-context'\n\nexport function useAvailableSlots(\n config: SdkConfig | undefined,\n productId: string | undefined,\n date: string | undefined,\n params?: { staffId?: string; locationId?: string; customerLatitude?: number; customerLongitude?: number }\n) {\n const ctx = useSdkContext()\n const { config: cfg } = resolveHookArgs(config, undefined, ctx)\n return useQuery({\n queryKey: [\"available-slots\", productId, date, params?.staffId, params?.locationId],\n queryFn: () => getAvailableSlots(cfg, productId!, date!, params),\n enabled: !!productId && !!date,\n })\n}\n\nexport function useBookAppointment(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: (payload: ServiceAppointmentCreatePayload) =>\n token ? bookAppointment(cfg, token, payload) : Promise.reject(new Error('No access token available')),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: [\"appointments\"] })\n },\n })\n}\n\nexport function useAppointments(\n config?: SdkConfig,\n accessToken?: string | null,\n params?: { status?: ServiceAppointment['status']; page?: number; page_size?: number }\n) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n return useQuery({\n queryKey: [\"appointments\", params?.status, params?.page, params?.page_size],\n queryFn: () => token ? getAppointments(cfg, token, params) : Promise.reject(new Error('No access token available')),\n enabled: !!token,\n })\n}\n\nexport function useAppointment(config: SdkConfig | undefined, accessToken: string | null | undefined, id: string | undefined) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n return useQuery({\n queryKey: [\"appointment\", id],\n queryFn: () => token && id ? getAppointment(cfg, token, id) : Promise.reject(new Error('Missing appointment ID or access token')),\n enabled: !!id && !!token,\n })\n}\n\nexport function useCancelAppointment(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: ({ id, payload }: { id: string; payload?: CancelAppointmentPayload }) =>\n token ? cancelAppointment(cfg, token, id, payload) : Promise.reject(new Error('No access token available')),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: [\"appointments\"] })\n },\n })\n}\n\nexport function useRescheduleAppointment(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: ({ id, payload }: { id: string; payload: RescheduleAppointmentPayload }) =>\n token ? rescheduleAppointment(cfg, token, id, payload) : Promise.reject(new Error('No access token available')),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: [\"appointments\"] })\n },\n })\n}\n\nexport function useDisputeAppointment(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: ({ id, payload }: { id: string; payload: DisputeAppointmentPayload }) =>\n token ? disputeAppointment(cfg, token, id, payload) : Promise.reject(new Error('No access token available')),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: [\"appointments\"] })\n },\n })\n}\n\nexport function useConfirmAppointmentCompletion(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: ({ id, payload }: { id: string; payload?: ConfirmAppointmentPayload }) =>\n token ? confirmAppointmentCompletion(cfg, token, id, payload) : Promise.reject(new Error('No access token available')),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: [\"appointments\"] })\n },\n })\n}\n\nexport function useMilestones(config: SdkConfig | undefined, accessToken: string | null | undefined, bookingId: string | undefined) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n return useQuery({\n queryKey: [\"milestones\", bookingId],\n queryFn: () => token && bookingId ? getMilestones(cfg, token, bookingId) : Promise.reject(new Error('Missing booking ID or access token')),\n enabled: !!bookingId && !!token,\n })\n}\n\nexport function useApproveMilestone(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: ({ bookingId, milestoneId, payload }: { bookingId: string; milestoneId: string; payload?: ApproveMilestonePayload }) =>\n token ? approveMilestone(cfg, token, bookingId, milestoneId, payload) : Promise.reject(new Error('No access token available')),\n onSuccess: (_data, variables) => {\n queryClient.invalidateQueries({ queryKey: [\"milestones\", variables.bookingId] })\n },\n })\n}\n","/**\n * Service Booking Service\n *\n * Handles service appointments (bookings) and their milestone/escrow\n * sub-resource. Stateless - all configuration passed as parameters.\n *\n * REAL BUGS FIXED (2026-09-20) — every function in this file was rewritten\n * against the real backend (kasuvia-store-management/app/api/v1/endpoints/\n * service/bookings.py, app/schemas/service.py). Before this fix:\n * - getAvailableSlots called a nonexistent route (/service/available-slots/\n * {id}) with the wrong params (date_from/date_to) — the real route is\n * GET /catalog/products/{id}/available-slots?date=..., a single date.\n * - bookAppointment POSTed to a nonexistent route (/service/appointments/)\n * — the real route is POST /service/bookings/schedule.\n * - getAppointments typed its result as a plain array, but the real\n * response is the standard Kasuvia paginated envelope\n * ({results,count,page,page_size,total_pages}) — this was a CONFIRMED\n * LIVE CRASH: DashboardClient.tsx/DashboardAppointments.tsx called\n * .length/.filter() directly on the envelope object.\n * - cancelAppointment sent DELETE /service/bookings/{id} — that route is\n * owner/staff-only (require_permission(\"services:manage\")), so a\n * customer got a 403; the real customer route is POST .../{id}/cancel.\n * It also never checked response.ok at all, silently swallowing failures.\n * - getDeliverables/approveDeliverable/requestRevision referenced a\n * \"/service/projects/{order_id}/deliverables/...\" resource that does not\n * exist anywhere in the codebase — replaced with the real milestone\n * endpoints (a different resource: escrow-style payment milestones on\n * a booking, not free-form deliverable files).\n * - No coverage existed for reschedule, dispute, or buyer-confirm — all\n * real, customer-facing routes.\n */\nimport type { SdkConfig } from '../config'\nimport type {\n ServiceAppointment,\n ServiceAppointmentCreatePayload,\n CancelAppointmentPayload,\n RescheduleAppointmentPayload,\n DisputeAppointmentPayload,\n ConfirmAppointmentPayload,\n AvailableSlotsResponse,\n ServiceMilestone,\n ApproveMilestonePayload,\n} from '../types/services'\nimport type { components } from '../generated/store-openapi'\nimport { apiRequest, storefrontHeaders, authHeaders, JSON_HEADERS } from '../utils/http-client'\n\n// ─── Codegen drift-proofs (openapi/README.md — regenerate via `pnpm run codegen:rest`) ─\n// Unlike order.service.ts, this file's hand-written types (types/services/\n// index.ts) were already verified field-for-field correct against the real\n// backend Pydantic schemas (see that file's own header comment) - full\n// replacement would only add risk for zero gain. These type-level\n// assignments instead prove the match at compile time: if the real backend\n// schema ever adds/renames/retypes a field these three interfaces declare,\n// the corresponding line below fails to compile immediately (at `pnpm run\n// codegen:rest` + `tsc` time), the same drift-detection value Workstream 1\n// already proved for the GraphQL side, without touching working code.\n// (One real drift WAS found and fixed this way: ServiceMilestone.amount\n// was typed `number` but the real field is a Decimal-as-string.)\ntype _ServiceAppointmentMatchesSchema = ServiceAppointment extends components['schemas']['ServiceBookingResponse'] ? true : never\ntype _AvailableSlotsResponseMatchesSchema = AvailableSlotsResponse extends components['schemas']['AvailableSlotsResponse'] ? true : never\ntype _ServiceMilestoneMatchesSchema = ServiceMilestone extends components['schemas']['ServiceMilestoneResponse'] ? true : never\nconst _serviceAppointmentCheck: _ServiceAppointmentMatchesSchema = true\nconst _availableSlotsCheck: _AvailableSlotsResponseMatchesSchema = true\nconst _serviceMilestoneCheck: _ServiceMilestoneMatchesSchema = true\nvoid _serviceAppointmentCheck\nvoid _availableSlotsCheck\nvoid _serviceMilestoneCheck\n\n/** Standard Kasuvia pagination envelope — matches app/core/pagination.py's PaginatedResponse exactly. */\nexport interface ServiceBookingsPage {\n results: ServiceAppointment[]\n count: number\n page: number\n page_size: number\n total_pages: number\n}\n\n/**\n * GET /catalog/products/{product_id}/available-slots — real \"what times are\n * open\" for a SERVICE product on one date. `serviceable: false` means the\n * business has no/ambiguous location config for this product (check\n * `reason`); `available_slots` is always empty in that case, not an error.\n */\nexport async function getAvailableSlots(\n config: SdkConfig,\n productId: string,\n date: string,\n params?: { staffId?: string; locationId?: string; customerLatitude?: number; customerLongitude?: number }\n): Promise<AvailableSlotsResponse> {\n const query = new URLSearchParams({ date })\n if (params?.staffId) query.set('staff_id', params.staffId)\n if (params?.locationId) query.set('location_id', params.locationId)\n if (params?.customerLatitude !== undefined) query.set('customer_latitude', String(params.customerLatitude))\n if (params?.customerLongitude !== undefined) query.set('customer_longitude', String(params.customerLongitude))\n\n return apiRequest<AvailableSlotsResponse>(\n `${config.backendUrls.store}/catalog/products/${productId}/available-slots?${query.toString()}`,\n {\n headers: storefrontHeaders(config),\n fallbackErrorMessage: 'Failed to get available slots',\n }\n )\n}\n\n/** POST /service/bookings/schedule */\nexport async function bookAppointment(\n config: SdkConfig,\n accessToken: string,\n payload: ServiceAppointmentCreatePayload\n): Promise<ServiceAppointment> {\n return apiRequest<ServiceAppointment>(`${config.backendUrls.store}/service/bookings/schedule`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to book appointment',\n })\n}\n\n/** GET /service/bookings/my — paginated. */\nexport async function getAppointments(\n config: SdkConfig,\n accessToken: string,\n params?: { status?: ServiceAppointment['status']; page?: number; page_size?: number }\n): Promise<ServiceBookingsPage> {\n const query = new URLSearchParams()\n if (params?.status) query.set('status', params.status)\n if (params?.page) query.set('page', String(params.page))\n if (params?.page_size) query.set('page_size', String(params.page_size))\n\n return apiRequest<ServiceBookingsPage>(`${config.backendUrls.store}/service/bookings/my?${query.toString()}`, {\n headers: authHeaders(config, accessToken),\n fallbackErrorMessage: 'Failed to get appointments',\n })\n}\n\n/**\n * GET /service/bookings/{id} — customer-reachable single-booking lookup,\n * used by the paginated list's detail view (PATCH .../{id} is a separate,\n * owner/staff-only route).\n *\n * REAL BUG FIXED (2026-09-22): this call shape was already correct, but the\n * backend route it targeted didn't exist at all — `ServiceBookingRepository\n * .get_by_id_and_business` was used internally by every merchant action\n * (cancel/reschedule/dispute/assign/etc.) but never exposed as a customer\n * GET route. Every real call to `AppointmentDetail.tsx` (via `useAppointment`)\n * 404'd. Fixed by adding `GET /{booking_id}` to kasuvia-store-management's\n * `service/bookings.py` (customer_id-scoped, same pattern as\n * `customer_cancel_booking` — service bookings have no guest-checkout path,\n * so no guest_email fallback is needed here unlike orders).\n */\nexport async function getAppointment(config: SdkConfig, accessToken: string, id: string): Promise<ServiceAppointment> {\n return apiRequest<ServiceAppointment>(`${config.backendUrls.store}/service/bookings/${id}`, {\n headers: authHeaders(config, accessToken),\n fallbackErrorMessage: 'Failed to get appointment',\n })\n}\n\n/** POST /service/bookings/{id}/cancel — the real customer-cancel route (DELETE .../{id} is owner/staff-only). */\nexport async function cancelAppointment(\n config: SdkConfig,\n accessToken: string,\n id: string,\n payload?: CancelAppointmentPayload\n): Promise<ServiceAppointment> {\n return apiRequest<ServiceAppointment>(`${config.backendUrls.store}/service/bookings/${id}/cancel`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload ?? {}),\n fallbackErrorMessage: 'Failed to cancel appointment',\n })\n}\n\n/** POST /service/bookings/{id}/reschedule — subject to the same cancellation_window_hours as a cancel. */\nexport async function rescheduleAppointment(\n config: SdkConfig,\n accessToken: string,\n id: string,\n payload: RescheduleAppointmentPayload\n): Promise<ServiceAppointment> {\n return apiRequest<ServiceAppointment>(`${config.backendUrls.store}/service/bookings/${id}/reschedule`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to reschedule appointment',\n })\n}\n\n/** POST /service/bookings/{id}/dispute — freezes the escrow auto-release timer; requires a real reason. */\nexport async function disputeAppointment(\n config: SdkConfig,\n accessToken: string,\n id: string,\n payload: DisputeAppointmentPayload\n): Promise<ServiceAppointment> {\n return apiRequest<ServiceAppointment>(`${config.backendUrls.store}/service/bookings/${id}/dispute`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to open dispute',\n })\n}\n\n/** POST /service/bookings/{id}/confirm — buyer confirms the service was completed. */\nexport async function confirmAppointmentCompletion(\n config: SdkConfig,\n accessToken: string,\n id: string,\n payload?: ConfirmAppointmentPayload\n): Promise<ServiceAppointment> {\n return apiRequest<ServiceAppointment>(`${config.backendUrls.store}/service/bookings/${id}/confirm`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload ?? {}),\n fallbackErrorMessage: 'Failed to confirm completion',\n })\n}\n\n/** GET /service/bookings/{id}/milestones — escrow-style payment milestones for a milestone-based booking. */\nexport async function getMilestones(config: SdkConfig, accessToken: string, bookingId: string): Promise<ServiceMilestone[]> {\n return apiRequest<ServiceMilestone[]>(`${config.backendUrls.store}/service/bookings/${bookingId}/milestones`, {\n headers: authHeaders(config, accessToken),\n fallbackErrorMessage: 'Failed to get milestones',\n })\n}\n\n/** POST /service/bookings/{id}/milestones/{milestone_id}/approve — buyer approves a submitted milestone, triggering escrow release. */\nexport async function approveMilestone(\n config: SdkConfig,\n accessToken: string,\n bookingId: string,\n milestoneId: string,\n payload?: ApproveMilestonePayload\n): Promise<ServiceMilestone> {\n return apiRequest<ServiceMilestone>(\n `${config.backendUrls.store}/service/bookings/${bookingId}/milestones/${milestoneId}/approve`,\n {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify(payload ?? {}),\n fallbackErrorMessage: 'Failed to approve milestone',\n }\n )\n}\n\n// Service object export for backward compatibility\nexport const serviceBookingService = {\n getAvailableSlots,\n bookAppointment,\n getAppointments,\n getAppointment,\n cancelAppointment,\n rescheduleAppointment,\n disputeAppointment,\n confirmAppointmentCompletion,\n getMilestones,\n approveMilestone,\n}\n","/**\n * Bespoke Availability Hook\n * \n * React Query hook for checking bespoke service availability.\n * Stateless - requires config parameter.\n */\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { checkAvailability, approveBespokeDeliverable, requestBespokeRevision } from '../services/bespoke.service'\nimport type { SdkConfig } from '../config'\nimport { useSdkContext, resolveHookArgs } from '../context/sdk-context'\n\n/**\n * Get bespoke availability for a product\n */\nexport function useBespokeAvailability(product_id: string, config?: SdkConfig) {\n const ctx = useSdkContext()\n const { config: cfg } = resolveHookArgs(config, undefined, ctx)\n return useQuery({\n queryKey: ['bespoke-availability', product_id],\n queryFn: () => checkAvailability(cfg, product_id),\n enabled: !!product_id,\n retry: false,\n })\n}\n\n/** Buyer approves a bespoke order's submitted deliverable. */\nexport function useApproveBespokeDeliverable(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: (orderId: string) =>\n token ? approveBespokeDeliverable(cfg, token, orderId) : Promise.reject(new Error('No access token available')),\n onSuccess: (_data, orderId) => {\n queryClient.invalidateQueries({ queryKey: ['order', orderId] })\n queryClient.invalidateQueries({ queryKey: ['orders'] })\n },\n })\n}\n\n/** Buyer requests a revision on a bespoke order's submitted deliverable. */\nexport function useRequestBespokeRevision(config?: SdkConfig, accessToken?: string | null) {\n const ctx = useSdkContext()\n const { config: cfg, accessToken: token } = resolveHookArgs(config, accessToken, ctx)\n const queryClient = useQueryClient()\n return useMutation({\n mutationFn: ({ orderId, notes }: { orderId: string; notes: string }) =>\n token ? requestBespokeRevision(cfg, token, orderId, notes) : Promise.reject(new Error('No access token available')),\n onSuccess: (_data, variables) => {\n queryClient.invalidateQueries({ queryKey: ['order', variables.orderId] })\n queryClient.invalidateQueries({ queryKey: ['orders'] })\n },\n })\n}\n","/**\n * Bespoke Service\n *\n * Handles bespoke product availability checks and the buyer side of the\n * bespoke order deliverable proofing flow.\n * Stateless - all configuration passed as parameters.\n */\nimport type { SdkConfig } from '../config'\nimport type { BespokeAvailability, BespokeDeliverableStatus } from '../types/consultations'\nimport type { components } from '../generated/store-openapi'\nimport { apiRequest, storefrontHeaders, authHeaders, JSON_HEADERS } from '../utils/http-client'\n\n// ─── Codegen drift-proofs (openapi/README.md — regenerate via `pnpm run codegen:rest`) ─\n// Same approach as service-booking.service.ts's own comment: these prove\n// the hand-written types still match the real backend schema at compile\n// time, without a risky full rewrite of working code. Found and fixed one\n// real gap this way: BespokeDeliverableStatus was missing\n// bespoke_revision_notes entirely, a field the real backend has always\n// returned.\ntype _BespokeAvailabilityMatchesSchema = BespokeAvailability extends components['schemas']['BespokeAvailabilityResponse'] ? true : never\ntype _BespokeDeliverableStatusMatchesSchema = BespokeDeliverableStatus extends components['schemas']['BespokeStatusResponse'] ? true : never\nconst _bespokeAvailabilityCheck: _BespokeAvailabilityMatchesSchema = true\nconst _bespokeDeliverableStatusCheck: _BespokeDeliverableStatusMatchesSchema = true\nvoid _bespokeAvailabilityCheck\nvoid _bespokeDeliverableStatusCheck\n\n/**\n * REAL BUG FIXED (2026-09-20): was calling `/bespoke/availability/{id}` —\n * missing the `/capacity` segment. Confirmed against the real router\n * (kasuvia-store-management/app/api/v1/endpoints/bespoke/capacity.py:184,\n * registered under the `/bespoke/capacity` prefix): the real route is\n * `/bespoke/capacity/availability/{id}`. Every bespoke availability check\n * was 404ing.\n */\nexport async function checkAvailability(config: SdkConfig, product_id: string): Promise<BespokeAvailability> {\n return apiRequest<BespokeAvailability>(`${config.backendUrls.store}/bespoke/capacity/availability/${product_id}`, {\n headers: storefrontHeaders(config),\n fallbackErrorMessage: 'Failed to check bespoke availability',\n })\n}\n\n/**\n * POST /orders/{order_id}/bespoke/approve — buyer approves the seller's\n * submitted deliverable, starting the escrow release clock.\n */\nexport async function approveBespokeDeliverable(\n config: SdkConfig,\n accessToken: string,\n orderId: string\n): Promise<BespokeDeliverableStatus> {\n return apiRequest<BespokeDeliverableStatus>(`${config.backendUrls.store}/orders/${orderId}/bespoke/approve`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify({}),\n fallbackErrorMessage: 'Failed to approve deliverable',\n })\n}\n\n/**\n * POST /orders/{order_id}/bespoke/request-revision — buyer asks the seller\n * to revise the submitted deliverable. Capped at 2 revisions server-side;\n * beyond that, the backend escalates to manual review rather than refusing\n * silently (see the real endpoint's own doc comment).\n */\nexport async function requestBespokeRevision(\n config: SdkConfig,\n accessToken: string,\n orderId: string,\n notes: string\n): Promise<BespokeDeliverableStatus> {\n return apiRequest<BespokeDeliverableStatus>(`${config.backendUrls.store}/orders/${orderId}/bespoke/request-revision`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },\n body: JSON.stringify({ notes }),\n fallbackErrorMessage: 'Failed to request revision',\n })\n}\n\n// Service object export for backward compatibility\nexport const bespokeService = {\n checkAvailability,\n approveBespokeDeliverable,\n requestBespokeRevision,\n}\n","import React from 'react'\nimport { useBespokeAvailability } from '../../hooks/use-bespoke-availability'\n\nexport interface BespokeAvailabilityBadgeProps {\n productId: string\n className?: string\n}\n\nexport function BespokeAvailabilityBadge({ productId, className = \"inline-flex items-center gap-2 px-3 py-1 text-xs rounded-full font-medium\" }: BespokeAvailabilityBadgeProps) {\n const { data: availability, isLoading } = useBespokeAvailability(productId)\n\n if (isLoading) {\n return <div className=\"h-5 w-28 bg-gray-200 dark:bg-gray-800 animate-pulse rounded-full\" />\n }\n\n if (!availability) return null\n\n if (availability.is_available) {\n return (\n <span className={`${className} bg-emerald-100 dark:bg-emerald-950 text-emerald-700 dark:text-emerald-300 border border-emerald-200 dark:border-emerald-800`}>\n <span className=\"w-2 h-2 rounded-full bg-emerald-500 animate-ping\" />\n {availability.available_slots} slots available this month\n </span>\n )\n }\n\n return (\n <span className={`${className} bg-amber-100 dark:bg-amber-950 text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-800`}>\n Fully booked — Next available: {availability.next_available_date ? new Date(availability.next_available_date).toLocaleDateString() : 'Next Month'}\n </span>\n )\n}\n","\"use client\";\nimport React from 'react'\n\nexport type BespokeStageKey = 'received' | 'development' | 'finished' | 'dispatched'\n\nexport interface BespokeProgressTrackerProps {\n stage: BespokeStageKey\n className?: string\n activeStepClassName?: string\n completedStepClassName?: string\n inactiveStepClassName?: string\n}\n\nexport function BespokeProgressTracker({\n stage,\n className = \"flex items-center w-full my-4\",\n activeStepClassName = \"bg-emerald-600 text-white font-bold\",\n completedStepClassName = \"bg-emerald-500 text-white\",\n inactiveStepClassName = \"border border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-800 text-gray-500\",\n}: BespokeProgressTrackerProps) {\n const stages = [\n { key: 'received', label: 'Order Received' },\n { key: 'development', label: 'In Development' },\n { key: 'finished', label: 'Finished' },\n { key: 'dispatched', label: 'Ready / Dispatched' },\n ] as const\n\n const currentIdx = stages.findIndex((s) => s.key === stage)\n\n return (\n <div className={className}>\n {stages.map((s, i) => (\n <div key={s.key} className=\"flex flex-1 flex-col items-center\">\n <div className=\"flex w-full items-center\">\n {i > 0 && (\n <div className={`h-0.5 flex-1 ${i <= currentIdx ? 'bg-emerald-500' : 'bg-gray-200 dark:bg-gray-800'}`} />\n )}\n <div className={`flex h-8 w-8 items-center justify-center rounded-full text-xs font-semibold ${\n i < currentIdx\n ? completedStepClassName\n : i === currentIdx\n ? activeStepClassName\n : inactiveStepClassName\n }`}>\n {i < currentIdx ? '✓' : i + 1}\n </div>\n {i < stages.length - 1 && (\n <div className={`h-0.5 flex-1 ${i < currentIdx ? 'bg-emerald-500' : 'bg-gray-200 dark:bg-gray-800'}`} />\n )}\n </div>\n <p className={`mt-2 text-center text-xs ${i === currentIdx ? 'font-semibold text-gray-900 dark:text-gray-100' : 'text-gray-500'}`}>\n {s.label}\n </p>\n </div>\n ))}\n </div>\n )\n}\n","\"use client\";\nimport React, { useState } from 'react'\nimport { useApproveBespokeDeliverable, useRequestBespokeRevision } from '../../hooks/use-bespoke-availability'\nimport type { Order } from '../../types/store'\nimport { logger } from '../../utils/logger'\n\nexport interface DeliverableReviewerProps {\n order: Order\n className?: string\n}\n\n/**\n * REAL BUG FIXED (2026-09-20): rebuilt entirely — this used to fetch a\n * \"/service/projects/{order_id}/deliverables/\" list that doesn't exist\n * anywhere in the backend. The real bespoke deliverable model is a SINGLE\n * proofing record per order (BespokeFulfillmentResponse, embedded as\n * `order.bespoke_fulfillment` and flattened onto the SDK's Order type as\n * `bespoke_*` fields by normaliseStorefrontOrder), not a fetchable list —\n * there is no separate GET endpoint for it at all. Approve/request-revision\n * now call the real endpoints (POST /orders/{id}/bespoke/approve,\n * .../request-revision) instead of a fictional deliverable-id-scoped route.\n */\nexport function DeliverableReviewer({ order, className = \"space-y-4 p-4 border rounded-xl bg-muted/50\" }: DeliverableReviewerProps) {\n const [revisionNotes, setRevisionNotes] = useState('')\n const [showRevisionForm, setShowRevisionForm] = useState(false)\n\n const approveMutation = useApproveBespokeDeliverable()\n const revisionMutation = useRequestBespokeRevision()\n\n if (!order.bespoke_deliverable_submitted_at) {\n return (\n <div className={className}>\n <h4 className=\"text-xs font-bold uppercase tracking-wider text-foreground\">Deliverable</h4>\n <p className=\"text-xs text-muted-foreground py-4 text-center\">\n The seller hasn't submitted a deliverable for review yet.\n </p>\n </div>\n )\n }\n\n const awaitingApproval = order.bespoke_status === 'awaiting_approval'\n\n const handleApprove = async () => {\n try {\n await approveMutation.mutateAsync(order.id)\n } catch (error) {\n logger.error(error)\n }\n }\n\n const handleRequestRevision = async () => {\n if (!revisionNotes.trim() || revisionNotes.trim().length < 10) return\n try {\n await revisionMutation.mutateAsync({ orderId: order.id, notes: revisionNotes.trim() })\n setShowRevisionForm(false)\n setRevisionNotes('')\n } catch (error) {\n logger.error(error)\n }\n }\n\n return (\n <div className={className}>\n <div className=\"flex justify-between items-center\">\n <h4 className=\"text-xs font-bold uppercase tracking-wider text-foreground\">Deliverable</h4>\n {order.bespoke_status && (\n <span className={`px-2 py-0.5 rounded text-[10px] font-semibold uppercase ${\n order.bespoke_status === 'confirmed' ? 'bg-primary/10 text-primary' :\n order.bespoke_status === 'awaiting_approval' ? 'bg-blue-500/10 text-blue-600 dark:text-blue-400' :\n 'bg-amber-500/10 text-amber-600 dark:text-amber-400'\n }`}>\n {order.bespoke_status.replace('_', ' ')}\n </span>\n )}\n </div>\n\n <div className=\"p-3 bg-card border rounded-lg space-y-2\">\n {order.bespoke_deliverable_url && (\n <a\n href={order.bespoke_deliverable_url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-xs text-primary hover:underline flex items-center gap-1\"\n >\n 📎 View Deliverable\n </a>\n )}\n\n {order.bespoke_deliverable_notes && (\n <p className=\"text-xs text-muted-foreground\">{order.bespoke_deliverable_notes}</p>\n )}\n\n {(order.bespoke_revision_count ?? 0) > 0 && (\n <p className=\"text-[10px] text-muted-foreground\">Revisions requested: {order.bespoke_revision_count}</p>\n )}\n\n {awaitingApproval && (\n <div className=\"flex items-center gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={handleApprove}\n disabled={approveMutation.isPending}\n className=\"px-3 py-1.5 bg-primary text-primary-foreground text-xs font-medium rounded hover:bg-primary/90 disabled:opacity-50\"\n >\n {approveMutation.isPending ? 'Approving…' : 'Approve Deliverable'}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowRevisionForm(!showRevisionForm)}\n className=\"px-3 py-1.5 border text-xs font-medium rounded hover:bg-muted\"\n >\n Request Revision\n </button>\n </div>\n )}\n\n {showRevisionForm && (\n <div className=\"pt-2 space-y-2\">\n <textarea\n rows={2}\n value={revisionNotes}\n onChange={(e) => setRevisionNotes(e.target.value)}\n placeholder=\"Explain what needs to change (at least 10 characters)...\"\n className=\"w-full p-2 border rounded text-xs bg-transparent\"\n />\n <button\n type=\"button\"\n onClick={handleRequestRevision}\n disabled={revisionMutation.isPending || revisionNotes.trim().length < 10}\n className=\"px-3 py-1 bg-amber-600 text-white text-xs font-medium rounded hover:bg-amber-700 disabled:opacity-50\"\n >\n {revisionMutation.isPending ? 'Submitting…' : 'Submit Revision Request'}\n </button>\n </div>\n )}\n </div>\n </div>\n )\n}\n","import type { ReactNode } from \"react\";\n\nexport interface TruncatedValueProps {\n /** The (possibly visually truncated, or compact/abbreviated) displayed value. */\n value: ReactNode;\n /** The full, untruncated/unabbreviated value shown on hover/title. */\n fullValue?: ReactNode;\n className?: string;\n triggerClassName?: string;\n}\n\n/**\n * TruncatedValue\n * Wraps a string or numeric value so the full value is accessible on hover or title.\n */\nexport function TruncatedValue({ value, fullValue, className = \"\", triggerClassName = \"\" }: TruncatedValueProps) {\n const tooltipText = typeof fullValue === \"string\" ? fullValue : typeof value === \"string\" ? value : undefined;\n\n return (\n <span\n title={tooltipText}\n className={`truncate tabular-nums cursor-default inline-block max-w-full ${triggerClassName} ${className}`.trim()}\n >\n {value}\n </span>\n );\n}\n","\"use client\";\n\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { MapPin, Loader2, Check } from \"lucide-react\";\nimport { autocompleteAddress } from \"../../services/logistics.service\";\nimport { useSdkContext, resolveHookArgs } from \"../../context/sdk-context\";\nimport type { SdkConfig } from \"../../config\";\n\nexport interface AddressSuggestion {\n place_id?: string | number;\n display_name: string;\n line1: string;\n city?: string;\n state?: string;\n postal_code?: string;\n country?: string;\n latitude?: number;\n longitude?: number;\n}\n\nexport interface AddressAutocompleteProps {\n /** The street-address line 1 text. */\n value: string;\n /** Fires on every keystroke - the field stays fully editable. */\n onChange: (line1: string) => void;\n /** Fires once when a suggestion is picked, with structured components. */\n onResolve?: (suggestion: AddressSuggestion) => void;\n /** ISO-2 country code to bias results toward (e.g. \"NG\"). */\n country?: string;\n disabled?: boolean;\n placeholder?: string;\n className?: string;\n id?: string;\n \"aria-label\"?: string;\n config?: SdkConfig;\n}\n\n/**\n * REAL BUG FIXED (2026-09-22): this called a hardcoded third-party endpoint\n * (`nominatim.openstreetmap.org`) directly from the browser — a completely\n * different data source than the rest of this platform's address/logistics\n * system, which already has a real, working backend endpoint for exactly\n * this (`GET /logistics/address/autocomplete`, `autocompleteAddress` in\n * logistics.service.ts, live-verified this session). Calling Nominatim\n * directly is also a real operational risk: OpenStreetMap's usage policy\n * rate-limits and blocks unauthenticated browser traffic at production\n * volume, and it has no idea this business is in Nigeria the way the real\n * backend's geocoding provider is tuned to be. Rebuilt to call the real\n * backend through the SDK's own service function instead.\n */\nexport function AddressAutocomplete({\n value,\n onChange,\n onResolve,\n country,\n disabled,\n placeholder = \"Start typing an address…\",\n className = \"\",\n id,\n config,\n ...rest\n}: AddressAutocompleteProps) {\n const ctx = useSdkContext();\n const generatedId = useId();\n const fieldId = id ?? generatedId;\n const [open, setOpen] = useState(false);\n const [highlight, setHighlight] = useState(-1);\n const [justPicked, setJustPicked] = useState(false);\n const [suggestions, setSuggestions] = useState<AddressSuggestion[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const wrapRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open || justPicked || disabled || !value || value.trim().length < 3) {\n setSuggestions([]);\n return;\n }\n\n let isMounted = true;\n const timer = setTimeout(async () => {\n setIsLoading(true);\n try {\n const { config: cfg } = resolveHookArgs(config, undefined, ctx);\n const { results } = await autocompleteAddress(cfg, value, country);\n if (isMounted) {\n setSuggestions(\n results.map((r) => ({\n place_id: r.place_id,\n display_name: r.display_name,\n line1: r.line1,\n city: r.city ?? undefined,\n state: r.state ?? undefined,\n postal_code: r.postal_code ?? undefined,\n country: r.country_code ?? r.country ?? undefined,\n latitude: r.latitude ?? undefined,\n longitude: r.longitude ?? undefined,\n }))\n );\n }\n } catch {\n // A real backend failure (network, 5xx) degrades to \"no suggestions\"\n // here — the field stays fully editable either way (onChange above\n // already applies on every keystroke), so this never blocks the\n // customer from typing their address manually.\n if (isMounted) setSuggestions([]);\n } finally {\n if (isMounted) setIsLoading(false);\n }\n }, 350);\n\n return () => {\n isMounted = false;\n clearTimeout(timer);\n };\n }, [value, country, open, justPicked, disabled, config, ctx]);\n\n // Close on outside click.\n useEffect(() => {\n const onDocClick = (e: MouseEvent) => {\n if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener(\"mousedown\", onDocClick);\n return () => document.removeEventListener(\"mousedown\", onDocClick);\n }, []);\n\n const pick = (s: AddressSuggestion) => {\n onChange(s.line1 || value);\n onResolve?.(s);\n setJustPicked(true);\n setOpen(false);\n setHighlight(-1);\n };\n\n const showDropdown = open && !justPicked && (isLoading || suggestions.length > 0);\n\n return (\n <div ref={wrapRef} className=\"relative w-full\">\n <div className=\"relative flex items-center\">\n <MapPin className=\"absolute left-3 w-4 h-4 text-muted-foreground pointer-events-none\" />\n <input\n id={fieldId}\n value={value}\n disabled={disabled}\n placeholder={placeholder}\n autoComplete=\"off\"\n className={`w-full h-10 pl-9 pr-9 py-2 text-sm bg-background border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50 ${className}`.trim()}\n onChange={(e) => {\n onChange(e.target.value);\n setJustPicked(false);\n setOpen(true);\n }}\n onFocus={() => { if (!justPicked) setOpen(true); }}\n onKeyDown={(e) => {\n if (!showDropdown) return;\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setHighlight((h) => Math.min(h + 1, suggestions.length - 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setHighlight((h) => Math.max(h - 1, 0));\n } else if (e.key === \"Enter\" && highlight >= 0 && suggestions[highlight]) {\n e.preventDefault();\n pick(suggestions[highlight]);\n } else if (e.key === \"Escape\") {\n setOpen(false);\n }\n }}\n {...rest}\n />\n {isLoading ? (\n <Loader2 className=\"absolute right-3 w-4 h-4 text-muted-foreground animate-spin\" />\n ) : justPicked ? (\n <Check className=\"absolute right-3 w-4 h-4 text-primary\" />\n ) : null}\n </div>\n\n {showDropdown && (\n <div className=\"absolute z-50 w-full mt-1 bg-card border rounded-lg shadow-lg max-h-60 overflow-y-auto\">\n {isLoading && suggestions.length === 0 ? (\n <div className=\"flex items-center gap-2 px-3 py-3 text-sm text-muted-foreground\">\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" /> Searching addresses…\n </div>\n ) : suggestions.length === 0 ? (\n <div className=\"px-3 py-3 text-sm text-muted-foreground\">\n No matches found. Enter address manually.\n </div>\n ) : (\n suggestions.map((s, i) => (\n <button\n key={s.place_id || `${s.display_name}-${i}`}\n type=\"button\"\n onMouseEnter={() => setHighlight(i)}\n onClick={() => pick(s)}\n className={`w-full text-left flex items-start gap-2 px-3 py-2 text-sm transition-colors ${\n i === highlight ? \"bg-muted\" : \"hover:bg-muted/50\"\n }`}\n >\n <MapPin className=\"w-3.5 h-3.5 mt-0.5 text-muted-foreground shrink-0\" />\n <span className=\"min-w-0\">\n <span className=\"block font-medium text-foreground truncate\">\n {s.line1 || s.display_name}\n </span>\n <span className=\"block text-xs text-muted-foreground truncate\">{s.display_name}</span>\n </span>\n </button>\n ))\n )}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Logistics Service\n *\n * Handles shipping rates and address validation.\n * Stateless - all configuration passed as parameters.\n */\nimport type { SdkConfig } from '../config'\nimport type { ShippingRatesResponse } from '../types/store/shipping'\nimport type { components } from '../generated/store-openapi'\nimport { apiRequest, storefrontHeaders, JSON_HEADERS } from '../utils/http-client'\n\nexport interface RateAddress {\n city: string\n state: string\n country: string\n}\n\n/**\n * REAL BUG FIXED (2026-09-20): this request shape used to be `{cart_items,\n * origin_address, destination_address, currency?, shipping_method?}` —\n * completely stale against the real backend. Confirmed against the real\n * Pydantic schema (kasuvia-store-management/app/schemas/logistics.py:121-144,\n * `ShippingRatesRequest`), which explicitly documents why: \"No origin_address\n * field — the server resolves the fulfilling StoreLocation itself... A\n * client-supplied origin would either have to be silently ignored or\n * silently trusted over the resolved location; neither is acceptable, so it\n * isn't accepted at all.\" The server also fetches cart items itself from\n * `cart_id` (server-side cart as source of truth, preventing client-side\n * cart manipulation) rather than trusting a client-submitted item list.\n * Sending the old shape meant every physical checkout's shipping-rates call\n * was missing the required `cart_id` field entirely and would always 422.\n */\nexport interface ShippingRatesRequest {\n cart_id: string\n destination_address: RateAddress\n currency: string\n shipping_method?: 'carrier' | 'self_pickup'\n /** Required when shipping_method is 'self_pickup' — see GET /logistics/pickup-locations. */\n pickup_location_id?: string\n}\n\n/**\n * Fetch shipping rates for a cart.\n */\nexport async function getShippingRates(\n config: SdkConfig,\n payload: ShippingRatesRequest\n): Promise<ShippingRatesResponse> {\n return apiRequest<ShippingRatesResponse>(`${config.backendUrls.store}/logistics/shipping/rates`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to get shipping rates',\n })\n}\n\n/**\n * Result of independently forward-geocoding the typed street address via\n * LocationIQ and cross-checking it against Terminal Africa's own (city/\n * state/country-only, street-line-blind) validation. See\n * app/services/address_trust_service.py on the backend. Only present when\n * Terminal Africa itself reported the address valid.\n */\nexport interface AddressTrustResult {\n trusted: boolean\n confidence: 'high' | 'medium' | 'low' | 'unavailable'\n reason: string | null\n resolved: {\n latitude: number\n longitude: number\n display_name: string\n city: string | null\n state: string | null\n country: string | null\n } | null\n plus_code: string | null\n}\n\n/**\n * Validate a shipping address.\n */\nexport async function validateShippingAddress(\n config: SdkConfig,\n payload: {\n first_name: string\n last_name: string\n phone: string\n address_line1: string\n address_line2?: string\n city: string\n state: string\n country: string\n postal_code?: string\n }\n): Promise<{\n valid: boolean\n message?: string\n errorType?: 'state' | 'city' | 'general'\n data?: unknown\n cities?: Array<{ name: string; city_id: string; stateCode: string; countryCode: string }>\n states?: Array<{ name: string; code: string; countryCode: string }>\n /** Street-level trust check — only present when valid is true. See AddressTrustResult. */\n address_trust?: AddressTrustResult\n}> {\n return apiRequest(`${config.backendUrls.store}/logistics/shipping-address/validate`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to validate shipping address',\n })\n}\n\nexport interface PickupLocation {\n id: string\n name: string\n description: string | null\n address: {\n line1: string\n line2: string | null\n city: string\n state: string\n country: string\n postal_code: string\n }\n coordinates: { latitude: number; longitude: number }\n /** km from the coordinates passed to listPickupLocations, or null if none were passed. */\n distance_km: number | null\n operating_hours: Record<string, string> | null\n}\n\n/**\n * List the business's active pickup locations, so the customer can choose\n * one at checkout for shipping_method=\"self_pickup\" instead of the merchant\n * assigning one blindly after the fact. Pass the customer's shipping\n * coordinates to sort nearest-first.\n */\nexport interface PickupLocationsResult {\n locations: PickupLocation[]\n}\n\nasync function fetchLocationList(\n config: SdkConfig,\n path: 'pickup-locations' | 'locations',\n coords: { latitude: number; longitude: number } | undefined,\n fallbackErrorMessage: string\n): Promise<PickupLocationsResult> {\n const params = new URLSearchParams()\n if (coords) {\n params.set('latitude', String(coords.latitude))\n params.set('longitude', String(coords.longitude))\n }\n const qs = params.toString()\n return apiRequest(`${config.backendUrls.store}/logistics/${path}${qs ? `?${qs}` : ''}`, {\n headers: storefrontHeaders(config),\n fallbackErrorMessage,\n })\n}\n\nexport async function listPickupLocations(\n config: SdkConfig,\n coords?: { latitude: number; longitude: number }\n): Promise<PickupLocationsResult> {\n return fetchLocationList(config, 'pickup-locations', coords, 'Failed to list pickup locations')\n}\n\n/**\n * List ALL of the business's active locations (not just pickup-eligible\n * ones) — the set a customer needs to choose from for an 'at_business'\n * service appointment when the business runs more than one branch. Sibling\n * of listPickupLocations, same response shape (PickupLocationsResult),\n * different backend filter (GET /logistics/locations, no\n * is_pickup_location gate).\n */\nexport async function listBusinessLocations(\n config: SdkConfig,\n coords?: { latitude: number; longitude: number }\n): Promise<PickupLocationsResult> {\n return fetchLocationList(config, 'locations', coords, 'Failed to list business locations')\n}\n\n/**\n * Reverse geocode coordinates to address components using backend LocationIQ service.\n */\nexport interface ReverseGeocodeResult {\n city: string | null\n state: string | null\n country: string | null\n country_code: string | null\n display_name: string | null\n}\n\nexport async function reverseGeocode(\n config: SdkConfig,\n payload: { latitude: number; longitude: number }\n): Promise<ReverseGeocodeResult> {\n return apiRequest(`${config.backendUrls.store}/logistics/reverse-geocode`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to reverse geocode coordinates',\n })\n}\n\n/**\n * Decode a Plus Code to coordinates.\n */\nexport interface PlusCodeDecodeResult {\n latitude: number\n longitude: number\n latitude_lo: number\n longitude_hi: number\n plus_code: string\n}\n\nexport async function decodePlusCode(\n config: SdkConfig,\n payload: { plus_code: string }\n): Promise<PlusCodeDecodeResult> {\n return apiRequest(`${config.backendUrls.store}/logistics/plus-code/decode`, {\n method: 'POST',\n headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },\n body: JSON.stringify(payload),\n fallbackErrorMessage: 'Failed to decode Plus Code',\n })\n}\n\n/**\n * As-you-type address autocomplete suggestions.\n */\nexport interface AddressSuggestionResult {\n place_id: string\n display_name: string\n line1: string\n city: string | null\n state: string | null\n postal_code: string | null\n country: string | null\n country_code: string | null\n latitude: number | null\n longitude: number | null\n}\n\nexport interface AddressAutocompleteResult {\n results: AddressSuggestionResult[]\n}\n\nexport async function autocompleteAddress(\n config: SdkConfig,\n q: string,\n country?: string\n): Promise<AddressAutocompleteResult> {\n const params = new URLSearchParams()\n params.set('q', q)\n if (country) params.set('country', country)\n\n return apiRequest(`${config.backendUrls.store}/logistics/address/autocomplete?${params.toString()}`, {\n headers: storefrontHeaders(config),\n fallbackErrorMessage: 'Failed to autocomplete address',\n })\n}\n\n// ─── Codegen drift-proofs (openapi/README.md — regenerate via `pnpm run codegen:rest`) ─\n// Same approach as the other 3 target files' own comments. `getShippingRates`\n// and `validateShippingAddress` are deliberately NOT checked here:\n// GET /logistics/shipping/rates passes through Terminal Africa's own\n// response envelope as a raw dict (no Kasuvia-owned schema exists to check\n// against - a third party's contract, not this platform's), and\n// POST /logistics/shipping-address/validate similarly has no\n// response_model= (confirmed absent, same underlying gap class as\n// pickup-locations was until this pass, but out of scope for this initial\n// sweep - flagged here rather than silently left unchecked with no\n// explanation). PickupLocationsResult/ReverseGeocodeResult/\n// PlusCodeDecodeResult/AddressAutocompleteResult all have real backend\n// schemas and are checked below - PickupLocationsResponse's\n// response_model was added to the backend as part of this exact change\n// (previously also a raw-dict gap).\n// `operating_hours` is excluded from this check: it's a schemaless\n// `Dict[str, Any]` on the backend, which openapi-typescript can only infer\n// as `Record<string, never>` (an empty object) - a known tooling\n// imprecision (same class as shipping_address/variant_details elsewhere),\n// not a real type mismatch, so it would fail this check even when correct.\ntype _PickupLocationsMatchesSchema = Omit<PickupLocationsResult['locations'][number], 'operating_hours'> extends Omit<components['schemas']['PickupLocationItem'], 'operating_hours'> ? true : never\ntype _ReverseGeocodeMatchesSchema = ReverseGeocodeResult extends components['schemas']['ReverseGeocodeResponse'] ? true : never\ntype _PlusCodeDecodeMatchesSchema = PlusCodeDecodeResult extends components['schemas']['PlusCodeDecodeResponse'] ? true : never\ntype _AddressAutocompleteMatchesSchema = AddressAutocompleteResult extends components['schemas']['AddressAutocompleteResponse'] ? true : never\nconst _pickupLocationsCheck: _PickupLocationsMatchesSchema = true\nconst _reverseGeocodeCheck: _ReverseGeocodeMatchesSchema = true\nconst _plusCodeDecodeCheck: _PlusCodeDecodeMatchesSchema = true\nconst _addressAutocompleteCheck: _AddressAutocompleteMatchesSchema = true\nvoid _pickupLocationsCheck\nvoid _reverseGeocodeCheck\nvoid _plusCodeDecodeCheck\nvoid _addressAutocompleteCheck\n\n// Service object for backward compatibility\nexport const logisticsService = {\n getShippingRates,\n validateShippingAddress,\n listPickupLocations,\n listBusinessLocations,\n reverseGeocode,\n decodePlusCode,\n autocompleteAddress,\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { WifiOff } from \"lucide-react\";\n\nexport interface OfflineBannerProps {\n message?: string;\n className?: string;\n}\n\nexport function OfflineBanner({\n message = \"You are currently offline. Check your network connection.\",\n className = \"\",\n}: OfflineBannerProps) {\n const [isOffline, setIsOffline] = useState(false);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n const handleOnline = () => setIsOffline(false);\n const handleOffline = () => setIsOffline(true);\n\n setIsOffline(!navigator.onLine);\n\n window.addEventListener(\"online\", handleOnline);\n window.addEventListener(\"offline\", handleOffline);\n\n return () => {\n window.removeEventListener(\"online\", handleOnline);\n window.removeEventListener(\"offline\", handleOffline);\n };\n }, []);\n\n if (!isOffline) return null;\n\n return (\n <div\n className={`bg-amber-500/10 border-b border-amber-500/20 px-4 py-2 text-amber-700 dark:text-amber-400 text-xs font-medium flex items-center justify-between animate-in slide-in-from-top-2 duration-300 ${className}`.trim()}\n >\n <div className=\"flex items-center gap-2\">\n <WifiOff className=\"w-4 h-4 text-amber-500 shrink-0\" />\n <span>{message}</span>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Loader2 } from \"lucide-react\";\n\nexport interface MapPinDropProps {\n initialLatitude?: number;\n initialLongitude?: number;\n onPinDrop?: (coordinates: { latitude: number; longitude: number }) => void;\n value?: { latitude: number; longitude: number } | null;\n height?: string;\n disabled?: boolean;\n zoom?: number;\n}\n\nexport function MapPinDrop({\n initialLatitude = 6.5244,\n initialLongitude = 3.3792,\n onPinDrop,\n value,\n height = \"300px\",\n disabled = false,\n}: MapPinDropProps) {\n const [isMounted, setIsMounted] = useState(false);\n const [lat, setLat] = useState<number>(value?.latitude ?? initialLatitude);\n const [lng, setLng] = useState<number>(value?.longitude ?? initialLongitude);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n if (value) {\n setLat(value.latitude);\n setLng(value.longitude);\n }\n }, [value]);\n\n if (!isMounted) {\n return (\n <div\n className=\"w-full rounded-lg bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center\"\n style={{ height }}\n >\n <Loader2 className=\"w-6 h-6 text-gray-400 animate-spin\" />\n </div>\n );\n }\n\n return (\n <div className=\"w-full rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900\">\n <div className=\"p-4 flex flex-col gap-3\">\n <div className=\"flex items-center justify-between text-xs text-gray-500\">\n <span>Coordinates Selector</span>\n <span>\n {lat.toFixed(6)}, {lng.toFixed(6)}\n </span>\n </div>\n {!disabled && (\n <div className=\"grid grid-cols-2 gap-2\">\n <div>\n <label className=\"block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1\">\n Latitude\n </label>\n <input\n type=\"number\"\n step=\"any\"\n value={lat}\n onChange={(e) => {\n const val = parseFloat(e.target.value) || 0;\n setLat(val);\n onPinDrop?.({ latitude: val, longitude: lng });\n }}\n className=\"w-full h-9 px-2 text-xs border border-gray-300 dark:border-gray-700 rounded bg-transparent\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1\">\n Longitude\n </label>\n <input\n type=\"number\"\n step=\"any\"\n value={lng}\n onChange={(e) => {\n const val = parseFloat(e.target.value) || 0;\n setLng(val);\n onPinDrop?.({ latitude: lat, longitude: val });\n }}\n className=\"w-full h-9 px-2 text-xs border border-gray-300 dark:border-gray-700 rounded bg-transparent\"\n />\n </div>\n </div>\n )}\n </div>\n <div className=\"px-3 py-2 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-200 dark:border-gray-700 text-xs text-gray-500\">\n {disabled ? \"Location view only\" : \"Adjust coordinates to fine-tune map location\"}\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useRef, useEffect, ReactNode } from \"react\";\nimport { Check, X } from \"lucide-react\";\n\nexport interface SearchableInputItem {\n value: string;\n label: string;\n description?: string;\n icon?: ReactNode;\n}\n\nexport interface SearchableInputProps {\n items: SearchableInputItem[];\n value: string;\n onSelect: (value: string) => void;\n placeholder?: string;\n disabled?: boolean;\n isLoading?: boolean;\n skeletonCount?: number;\n className?: string;\n onSearchChange?: (search: string) => void;\n debounceMs?: number;\n}\n\nexport function SearchableInput({\n items,\n value,\n onSelect,\n placeholder = \"Search...\",\n disabled = false,\n isLoading = false,\n className = \"\",\n onSearchChange,\n debounceMs = 300,\n}: SearchableInputProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState(\"\");\n const inputRef = useRef<HTMLInputElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const selectedItem = items.find((item) => item.value === value);\n\n useEffect(() => {\n if (selectedItem) {\n setSearch(selectedItem.label);\n }\n }, [selectedItem]);\n\n const handleSearchChange = (newValue: string) => {\n setSearch(newValue);\n if (debounceRef.current) clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(() => {\n onSearchChange?.(newValue);\n }, debounceMs);\n };\n\n const handleSelect = (item: SearchableInputItem) => {\n onSelect(item.value);\n setSearch(item.label);\n setIsOpen(false);\n };\n\n const handleClear = () => {\n setSearch(\"\");\n onSelect(\"\");\n inputRef.current?.focus();\n };\n\n const serverSearch = typeof onSearchChange === \"function\";\n const filteredItems = serverSearch\n ? items\n : items.filter(\n (item) =>\n item.label.toLowerCase().includes(search.toLowerCase()) ||\n item.description?.toLowerCase().includes(search.toLowerCase())\n );\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n dropdownRef.current &&\n !dropdownRef.current.contains(event.target as Node) &&\n inputRef.current &&\n !inputRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n return (\n <div className=\"relative w-full\">\n <div className=\"relative\">\n <input\n ref={inputRef}\n value={search}\n onChange={(e) => handleSearchChange(e.target.value)}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n disabled={disabled}\n className={`w-full h-10 px-3 pr-8 text-sm bg-background border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50 ${className}`.trim()}\n />\n {search && !disabled && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n\n {isOpen && (\n <div\n ref={dropdownRef}\n className=\"absolute z-50 w-full mt-1 bg-card border rounded-lg shadow-lg max-h-64 overflow-y-auto\"\n >\n {isLoading ? (\n <div className=\"p-3 text-center text-xs text-muted-foreground\">Loading options…</div>\n ) : filteredItems.length > 0 ? (\n filteredItems.map((item) => (\n <div\n key={item.value}\n onClick={() => handleSelect(item)}\n className=\"flex items-center justify-between py-2 px-3 cursor-pointer hover:bg-muted transition-colors\"\n >\n <div className=\"flex items-center gap-3 flex-1 min-w-0\">\n {item.icon}\n <div className=\"flex-1 min-w-0\">\n <div className=\"font-medium text-sm text-foreground truncate\">\n {item.label}\n </div>\n {item.description && (\n <div className=\"text-xs text-muted-foreground truncate\">{item.description}</div>\n )}\n </div>\n </div>\n {value === item.value && <Check className=\"w-4 h-4 text-primary shrink-0\" />}\n </div>\n ))\n ) : (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">No results found</div>\n )}\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState, ReactNode } from \"react\";\nimport { Check, ChevronsUpDown, Loader2 } from \"lucide-react\";\n\nexport interface SearchableItem {\n value: string;\n label: string;\n icon?: ReactNode;\n description?: string;\n}\n\nexport interface SearchablePopoverProps {\n items: SearchableItem[];\n value: string;\n onSelect: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n disabled?: boolean;\n isLoading?: boolean;\n skeletonCount?: number;\n className?: string;\n triggerClassName?: string;\n contentClassName?: string;\n width?: string;\n onSearchChange?: (search: string) => void;\n searchValue?: string;\n hasNextPage?: boolean;\n isFetchingNextPage?: boolean;\n onFetchNextPage?: () => void;\n resolvedSelectedItem?: SearchableItem;\n}\n\nexport function SearchablePopover({\n items,\n value,\n onSelect,\n placeholder = \"Select an option\",\n searchPlaceholder = \"Search...\",\n emptyMessage = \"No results found.\",\n disabled = false,\n isLoading = false,\n triggerClassName = \"\",\n contentClassName = \"\",\n onSearchChange,\n searchValue,\n hasNextPage = false,\n isFetchingNextPage = false,\n onFetchNextPage,\n resolvedSelectedItem,\n}: SearchablePopoverProps) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const popoverRef = useRef<HTMLDivElement>(null);\n\n const serverSearch = typeof onSearchChange === \"function\";\n const activeSearch = serverSearch ? (searchValue ?? \"\") : query;\n\n const selectedItem = resolvedSelectedItem ?? items.find((item) => item.value === value);\n\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n const handleSearch = (text: string) => {\n if (serverSearch) {\n onSearchChange?.(text);\n } else {\n setQuery(text);\n }\n };\n\n const filteredItems = serverSearch\n ? items\n : items.filter(\n (item) =>\n item.label.toLowerCase().includes(query.toLowerCase()) ||\n item.description?.toLowerCase().includes(query.toLowerCase())\n );\n\n return (\n <div ref={popoverRef} className=\"relative w-full\">\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => setOpen((prev) => !prev)}\n className={`w-full h-10 px-3 py-2 text-sm bg-background border border-input rounded-lg flex items-center justify-between font-medium text-foreground disabled:opacity-50 ${triggerClassName}`.trim()}\n >\n {selectedItem ? (\n <div className=\"flex items-center gap-2 truncate\">\n {selectedItem.icon}\n <span>{selectedItem.label}</span>\n </div>\n ) : (\n <span className=\"text-muted-foreground\">{placeholder}</span>\n )}\n <ChevronsUpDown className=\"ml-2 h-4 w-4 shrink-0 text-muted-foreground\" />\n </button>\n\n {open && (\n <div\n className={`absolute z-50 mt-1 w-full bg-card border rounded-lg shadow-xl overflow-hidden p-2 ${contentClassName}`.trim()}\n >\n <input\n type=\"text\"\n value={activeSearch}\n onChange={(e) => handleSearch(e.target.value)}\n placeholder={searchPlaceholder}\n className=\"w-full h-9 px-3 text-xs bg-muted border border-input rounded mb-2 focus:outline-none\"\n />\n\n <div className=\"max-h-60 overflow-y-auto space-y-1\">\n {isLoading ? (\n <div className=\"p-3 text-center text-xs text-muted-foreground flex items-center justify-center gap-2\">\n <Loader2 className=\"w-4 h-4 animate-spin\" /> Loading options…\n </div>\n ) : filteredItems.length > 0 ? (\n filteredItems.map((item) => (\n <button\n key={item.value}\n type=\"button\"\n onClick={() => {\n onSelect(item.value);\n setOpen(false);\n }}\n className=\"w-full text-left flex items-center justify-between py-2 px-3 rounded text-xs hover:bg-muted transition-colors\"\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n {item.icon}\n <div className=\"truncate\">\n <div className=\"font-semibold text-foreground\">{item.label}</div>\n {item.description && (\n <div className=\"text-[10px] text-muted-foreground\">{item.description}</div>\n )}\n </div>\n </div>\n {value === item.value && <Check className=\"h-4 w-4 text-primary shrink-0\" />}\n </button>\n ))\n ) : (\n <div className=\"p-3 text-center text-xs text-muted-foreground\">{emptyMessage}</div>\n )}\n\n {hasNextPage && (\n <button\n type=\"button\"\n onClick={() => onFetchNextPage?.()}\n disabled={isFetchingNextPage}\n className=\"w-full py-1 text-center text-[11px] text-primary font-semibold hover:underline\"\n >\n {isFetchingNextPage ? \"Loading more...\" : \"Load more...\"}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { LucideProps } from \"lucide-react\";\nimport * as LucideIcons from \"lucide-react\";\n\nconst toPascalCase = (str: string): string => {\n return str\n .replace(/[-_]+/g, \" \")\n .replace(/\\s+(.)/g, (_, c) => c.toUpperCase())\n .replace(/^(.)/, (_, c) => c.toUpperCase());\n};\n\nexport interface LucideReactIconProps extends LucideProps {\n icon: string | number | boolean | null | undefined | object;\n}\n\nexport const LucideReactIcon: React.FC<LucideReactIconProps> = ({ icon, ...props }) => {\n if (!icon || typeof icon === \"boolean\" || typeof icon === \"number\" || Array.isArray(icon)) {\n return null;\n }\n\n if (typeof icon === \"function\" || (typeof icon === \"object\" && icon !== null && (\"$$typeof\" in icon || \"render\" in icon))) {\n const Component = icon as React.ComponentType<LucideProps>;\n return <Component size={props.size ?? 24} color={props.color ?? \"currentColor\"} strokeWidth={props.strokeWidth ?? 2} {...props} />;\n }\n\n const iconObj = typeof icon === \"object\" && icon !== null ? (icon as Record<string, unknown>) : null;\n const rawName = typeof icon === \"string\" ? icon : (typeof iconObj?.name === \"string\" ? iconObj.name : typeof iconObj?.icon === \"string\" ? iconObj.icon : null);\n if (!rawName) return null;\n\n const pascalName = toPascalCase(rawName);\n const IconComponent = (LucideIcons as unknown as Record<string, React.ComponentType<LucideProps>>)[pascalName] || (LucideIcons as unknown as Record<string, React.ComponentType<LucideProps>>)[rawName];\n\n if (!IconComponent) {\n return (\n <span\n style={{\n fontSize: props.size ?? 24,\n color: props.color ?? \"currentColor\",\n }}\n className=\"flex items-center justify-center text-xs font-medium text-center break-all leading-tight\"\n >\n {rawName}\n </span>\n );\n }\n\n return <IconComponent size={props.size ?? 24} color={props.color ?? \"currentColor\"} strokeWidth={props.strokeWidth ?? 2} {...props} />;\n};\n\nexport default LucideReactIcon;\n","\"use client\";\n\nimport React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport { AlertTriangle } from \"lucide-react\";\n\nexport interface UseUnsavedChangesOptions {\n browserMessage?: string;\n dialogTitle?: string;\n dialogBody?: string;\n}\n\nexport interface UnsavedChangesDialogProps {\n open: boolean;\n onStay: () => void;\n onLeave: () => void;\n title?: string;\n body?: string;\n}\n\nexport interface UseUnsavedChangesReturn {\n isDirty: boolean;\n markDirty: () => void;\n resetDirty: () => void;\n guardedNavigate: (action: () => void) => void;\n dialogProps: UnsavedChangesDialogProps;\n}\n\nexport function useUnsavedChanges(\n options: UseUnsavedChangesOptions = {}\n): UseUnsavedChangesReturn {\n const {\n browserMessage = \"You have unsaved changes. If you leave, your work will be lost.\",\n dialogTitle = \"Unsaved Changes\",\n dialogBody = \"You have unsaved changes. If you leave now, all your progress will be lost.\",\n } = options;\n\n const [isDirty, setIsDirty] = useState(false);\n const [dialogOpen, setDialogOpen] = useState(false);\n const pendingActionRef = useRef<(() => void) | null>(null);\n\n useEffect(() => {\n if (!isDirty) return;\n const handler = (e: BeforeUnloadEvent) => {\n e.preventDefault();\n e.returnValue = browserMessage;\n return browserMessage;\n };\n window.addEventListener(\"beforeunload\", handler);\n return () => window.removeEventListener(\"beforeunload\", handler);\n }, [isDirty, browserMessage]);\n\n const markDirty = useCallback(() => setIsDirty(true), []);\n\n const resetDirty = useCallback(() => {\n setIsDirty(false);\n setDialogOpen(false);\n pendingActionRef.current = null;\n }, []);\n\n const guardedNavigate = useCallback(\n (action: () => void) => {\n if (!isDirty) {\n action();\n return;\n }\n pendingActionRef.current = action;\n setDialogOpen(true);\n },\n [isDirty]\n );\n\n const handleStay = useCallback(() => {\n setDialogOpen(false);\n pendingActionRef.current = null;\n }, []);\n\n const handleLeave = useCallback(() => {\n setDialogOpen(false);\n setIsDirty(false);\n const action = pendingActionRef.current;\n pendingActionRef.current = null;\n action?.();\n }, []);\n\n return {\n isDirty,\n markDirty,\n resetDirty,\n guardedNavigate,\n dialogProps: {\n open: dialogOpen,\n onStay: handleStay,\n onLeave: handleLeave,\n title: dialogTitle,\n body: dialogBody,\n },\n };\n}\n\nexport function UnsavedChangesDialog({\n open,\n onStay,\n onLeave,\n title = \"Unsaved Changes\",\n body = \"You have unsaved changes. If you leave now, all your progress will be lost.\",\n}: UnsavedChangesDialogProps): React.JSX.Element | null {\n if (!open) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm animate-in fade-in duration-200\">\n <div className=\"w-full max-w-md bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-2xl p-6 shadow-2xl space-y-4\">\n <div className=\"flex flex-col items-center text-center\">\n <div className=\"w-12 h-12 rounded-xl bg-amber-500/10 flex items-center justify-center mb-3 text-amber-500\">\n <AlertTriangle className=\"w-6 h-6\" />\n </div>\n <h3 className=\"text-lg font-bold text-gray-900 dark:text-gray-100\">{title}</h3>\n <p className=\"text-xs text-gray-500 dark:text-gray-400 mt-1 leading-relaxed\">{body}</p>\n </div>\n <div className=\"flex flex-col sm:flex-row gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={onStay}\n className=\"flex-1 h-10 px-4 text-xs font-semibold rounded-xl border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-50\"\n >\n Stay & Keep Editing\n </button>\n <button\n type=\"button\"\n onClick={onLeave}\n className=\"flex-1 h-10 px-4 text-xs font-semibold rounded-xl bg-red-600 text-white hover:bg-red-700\"\n >\n Leave Without Saving\n </button>\n </div>\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport { useState, useRef, useEffect } from \"react\";\nimport { X, ChevronDown, Plus, Hash } from \"lucide-react\";\n\nexport interface SelectOption {\n id: string;\n name: string;\n image_url?: string | null;\n collection_type?: string;\n [key: string]: unknown;\n}\n\nexport interface MultiSelectPickerProps {\n selected: string[];\n onChange: (items: string[]) => void;\n options?: SelectOption[];\n isLoading?: boolean;\n isFetchingNextPage?: boolean;\n hasNextPage?: boolean;\n onFetchNextPage?: () => void;\n placeholder?: string;\n onCreateOption?: (name: string) => void;\n onSearchChange?: (search: string) => void;\n searchValue?: string;\n}\n\nexport function MultiSelectPicker({\n selected,\n onChange,\n options = [],\n isLoading = false,\n isFetchingNextPage = false,\n hasNextPage = false,\n onFetchNextPage,\n placeholder = \"Search or type to add...\",\n onCreateOption,\n onSearchChange,\n searchValue,\n}: MultiSelectPickerProps) {\n const [open, setOpen] = useState(false);\n const serverSearch = typeof onSearchChange === \"function\";\n const [localSearch, setLocalSearch] = useState(\"\");\n const search = serverSearch ? (searchValue ?? \"\") : localSearch;\n const setSearch = serverSearch ? onSearchChange! : setLocalSearch;\n const inputRef = useRef<HTMLInputElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n setSearch(\"\");\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [setSearch]);\n\n const filtered = options.filter(\n (opt) =>\n (serverSearch || opt.name.toLowerCase().includes(search.toLowerCase())) &&\n !selected.includes(opt.name)\n );\n\n const showCustomOption =\n search.trim().length > 0 &&\n !options.some((opt) => opt.name.toLowerCase() === search.trim().toLowerCase()) &&\n !selected.some((s) => s.toLowerCase() === search.trim().toLowerCase());\n\n const addItem = (name: string) => {\n onChange([...selected, name]);\n setSearch(\"\");\n inputRef.current?.focus();\n };\n\n const removeItem = (name: string) => {\n onChange(selected.filter((s) => s !== name));\n };\n\n return (\n <div ref={containerRef} className=\"relative w-full\">\n <div\n onClick={() => {\n setOpen(true);\n inputRef.current?.focus();\n }}\n className={`relative flex flex-wrap items-center gap-1.5 w-full cursor-text min-h-[42px] rounded-lg px-3 py-1.5 border border-input bg-background text-foreground ${\n open ? \"ring-2 ring-ring border-ring\" : \"\"\n }`}\n >\n {selected.map((name) => (\n <span\n key={name}\n className=\"inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs font-semibold bg-muted text-foreground border\"\n >\n <span className=\"truncate max-w-[140px]\">{name}</span>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeItem(name);\n }}\n className=\"rounded p-0.5 hover:bg-accent\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n ))}\n\n <input\n ref={inputRef}\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setOpen(true);\n }}\n onFocus={() => setOpen(true)}\n placeholder={selected.length === 0 ? placeholder : \"\"}\n className=\"flex-1 min-w-[120px] bg-transparent outline-none text-xs font-medium h-6 text-foreground placeholder:text-muted-foreground\"\n />\n\n <ChevronDown\n className={`h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200 ${\n open ? \"rotate-180\" : \"\"\n }`}\n />\n </div>\n\n {open && (\n <div className=\"absolute z-50 mt-1 w-full rounded-lg border bg-card text-foreground shadow-lg overflow-hidden p-1.5\">\n <div className=\"max-h-60 overflow-y-auto space-y-1\">\n {isLoading && options.length === 0 ? (\n <div className=\"p-3 text-center text-xs text-muted-foreground\">Loading options…</div>\n ) : (\n <>\n {filtered.length === 0 && !showCustomOption && !isLoading && (\n <div className=\"p-3 text-center text-xs text-muted-foreground\">No options found</div>\n )}\n\n {filtered.map((opt) => (\n <button\n key={opt.id}\n type=\"button\"\n onClick={() => addItem(opt.name)}\n className=\"w-full text-left px-3 py-2 rounded-md flex items-center gap-2 hover:bg-muted transition-colors text-xs\"\n >\n <div className=\"h-6 w-6 shrink-0 rounded bg-muted flex items-center justify-center\">\n <Hash className=\"h-3 w-3 text-muted-foreground\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"font-semibold truncate\">{opt.name}</div>\n </div>\n <Plus className=\"h-3.5 w-3.5 text-primary shrink-0\" />\n </button>\n ))}\n\n {hasNextPage && (\n <button\n type=\"button\"\n onClick={() => onFetchNextPage?.()}\n disabled={isFetchingNextPage}\n className=\"w-full py-1.5 text-center text-xs text-primary font-semibold hover:underline\"\n >\n {isFetchingNextPage ? \"Loading more...\" : \"Load more...\"}\n </button>\n )}\n\n {showCustomOption && (\n <button\n type=\"button\"\n onClick={() => {\n if (onCreateOption) {\n onCreateOption(search.trim());\n } else {\n addItem(search.trim());\n }\n setSearch(\"\");\n }}\n className=\"w-full text-left px-3 py-2 rounded-md flex items-center gap-2 bg-primary/10 text-primary font-bold text-xs hover:bg-primary/20\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n <span>Create \"{search.trim()}\"</span>\n </button>\n )}\n </>\n )}\n </div>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport {\n useState,\n useRef,\n useCallback,\n useEffect,\n useMemo,\n} from \"react\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport {\n Camera,\n Check,\n Contrast as ContrastIcon,\n Crop,\n FlipHorizontal,\n FlipVertical,\n Image as ImageIcon,\n Info,\n Loader2,\n Maximize2,\n Move,\n RectangleHorizontal,\n RectangleVertical,\n RotateCcw,\n RotateCw,\n Sliders,\n Smartphone,\n Sparkles,\n Square,\n Sun,\n Tv,\n Upload,\n Wand2,\n X,\n ZoomIn,\n ZoomOut,\n} from \"lucide-react\";\nimport { toast } from \"sonner\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ImageEditorModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onImageSelect: (processedImage: Blob, fileName: string) => void;\n aspectRatio?: number;\n /** Max file size in bytes. Defaults to 10 MB. */\n maxFileSize?: number;\n acceptedFormats?: string[];\n maxDimension?: number;\n initialFile?: File | null;\n /** Output blob format. Defaults to \"image/jpeg\". Pass \"image/png\" for alpha. */\n outputFormat?: \"image/jpeg\" | \"image/png\";\n /** JPEG export quality 0–1. */\n outputQuality?: number;\n /** Minimum output dimensions. */\n minWidth?: number;\n minHeight?: number;\n}\n\ninterface AspectPreset {\n label: string;\n ratio: number | undefined;\n icon: React.ReactNode;\n}\n\ninterface CropBox {\n x: number; // fraction 0..1 of frame container width\n y: number;\n w: number;\n h: number;\n}\n\ntype HandleType =\n | \"nw\" | \"n\" | \"ne\"\n | \"w\" | \"e\"\n | \"sw\" | \"s\" | \"se\"\n | \"body\"\n | null;\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst HANDLE_SIZE = 14; // px\nconst DEFAULT_CROP: CropBox = { x: 0.05, y: 0.05, w: 0.9, h: 0.9 };\n\nconst ASPECT_PRESETS: AspectPreset[] = [\n { label: \"Free\", ratio: undefined, icon: <Move className=\"w-3.5 h-3.5\" /> },\n { label: \"Original\", ratio: -1, icon: <ImageIcon className=\"w-3.5 h-3.5\" /> },\n { label: \"1 : 1\", ratio: 1, icon: <Square className=\"w-3.5 h-3.5\" /> },\n { label: \"16 : 9\", ratio: 16 / 9, icon: <Tv className=\"w-3.5 h-3.5\" /> },\n { label: \"9 : 16\", ratio: 9 / 16, icon: <Smartphone className=\"w-3.5 h-3.5\" /> },\n { label: \"4 : 3\", ratio: 4 / 3, icon: <RectangleHorizontal className=\"w-3.5 h-3.5\" /> },\n { label: \"3 : 4\", ratio: 3 / 4, icon: <RectangleVertical className=\"w-3.5 h-3.5\" /> },\n { label: \"4 : 5\", ratio: 4 / 5, icon: <RectangleVertical className=\"w-3.5 h-3.5\" /> },\n { label: \"3 : 2\", ratio: 3 / 2, icon: <RectangleHorizontal className=\"w-3.5 h-3.5\" /> },\n { label: \"2 : 3\", ratio: 2 / 3, icon: <RectangleVertical className=\"w-3.5 h-3.5\" /> },\n];\n\nconst HANDLE_DEFS = [\n { key: \"nw\" as HandleType, cursor: \"nwse-resize\", style: { left: -7, top: -7 } },\n { key: \"ne\" as HandleType, cursor: \"nesw-resize\", style: { right: -7, top: -7 } },\n { key: \"sw\" as HandleType, cursor: \"nesw-resize\", style: { left: -7, bottom: -7 } },\n { key: \"se\" as HandleType, cursor: \"nwse-resize\", style: { right: -7, bottom: -7 } },\n { key: \"n\" as HandleType, cursor: \"ns-resize\", style: { left: \"calc(50% - 7px)\", top: -7, width: 14 } },\n { key: \"s\" as HandleType, cursor: \"ns-resize\", style: { left: \"calc(50% - 7px)\", bottom: -7, width: 14 } },\n { key: \"w\" as HandleType, cursor: \"ew-resize\", style: { top: \"calc(50% - 7px)\", left: -7, height: 14 } },\n { key: \"e\" as HandleType, cursor: \"ew-resize\", style: { top: \"calc(50% - 7px)\", right: -7, height: 14 } },\n] as const;\n\n// ─── Helper Components ────────────────────────────────────────────────────────\n\nfunction InlineSlider({\n value,\n onValueChange,\n min,\n max,\n step = 1,\n className,\n}: {\n value: number[];\n onValueChange: (value: number[]) => void;\n min: number;\n max: number;\n step?: number;\n className?: string;\n}) {\n return (\n <input\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={value[0]}\n onChange={(e) => onValueChange([parseFloat(e.target.value)])}\n className={`w-full h-1.5 bg-muted rounded-lg appearance-none cursor-pointer accent-primary ${className ?? \"\"}`}\n />\n );\n}\n\n// ─── Main Component ───────────────────────────────────────────────────────────\n\n/**\n * ImageEditorModal — Studio-grade full-screen image editor.\n *\n * Features:\n * - Fullscreen, theme-aware responsive modal layout\n * - File drop / camera photo capture\n * - Freehand / aspect-locked crop selection box with handles & rule-of-thirds grid\n * - Pan, zoom, rotation slider, ±90° rotate buttons, horizontal & vertical flip\n * - Brightness, contrast, saturation, blur adjustments\n * - Offscreen canvas export slicing exact crop region into JPEG/PNG Blob\n */\nexport function ImageEditorModal({\n open,\n onOpenChange,\n onImageSelect,\n aspectRatio: initialAspectRatio,\n maxFileSize = 10 * 1024 * 1024,\n acceptedFormats = [\"image/jpeg\", \"image/png\", \"image/webp\"],\n maxDimension = 4000,\n initialFile = null,\n outputFormat = \"image/jpeg\",\n outputQuality = 0.92,\n minWidth,\n minHeight,\n}: ImageEditorModalProps) {\n // ── Image source ──────────────────────────────────────────────────────────\n const [imageSrc, setImageSrc] = useState<string | null>(null);\n const [fileName, setFileName] = useState(\"\");\n const [sourceDimensions, setSourceDimensions] = useState<{ width: number; height: number } | null>(null);\n\n // ── Active tool panel ──────────────────────────────────────────────────────\n const [activeTab, setActiveTab] = useState<\"crop\" | \"adjust\">(\"crop\");\n\n // ── Transform state ────────────────────────────────────────────────────────\n const [pan, setPan] = useState({ x: 0, y: 0 });\n const [zoom, setZoom] = useState(1);\n const [rotation, setRotation] = useState(0);\n const [flipX, setFlipX] = useState(false);\n const [flipY, setFlipY] = useState(false);\n\n // ── Adjustment state ───────────────────────────────────────────────────────\n const [brightness, setBrightness] = useState(0); // -100..100\n const [contrast, setContrast] = useState(0);\n const [saturation, setSaturation] = useState(0);\n const [blur, setBlur] = useState(0); // 0..10px\n\n // ── Crop state ─────────────────────────────────────────────────────────────\n const [activePresetRatio, setActivePresetRatio] = useState<number | undefined>(initialAspectRatio);\n const [cropBox, setCropBox] = useState<CropBox>(DEFAULT_CROP);\n const [isDraggingBox, setIsDraggingBox] = useState(false);\n\n // ── UI state ───────────────────────────────────────────────────────────────\n const [isProcessing, setIsProcessing] = useState(false);\n const [showCamera, setShowCamera] = useState(false);\n const [isCameraStarting, setIsCameraStarting] = useState(false);\n const [cameraSupported] = useState(\n () => typeof navigator !== \"undefined\" && !!navigator.mediaDevices?.getUserMedia\n );\n\n // ── Refs ───────────────────────────────────────────────────────────────────\n const frameRef = useRef<HTMLDivElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const cameraStreamRef = useRef<MediaStream | null>(null);\n const loadedInitialFileRef = useRef<File | null>(null);\n\n const dragStartRef = useRef<{\n handle: HandleType;\n startBox: CropBox;\n startMousePx: { x: number; y: number };\n frameRect: DOMRect;\n } | null>(null);\n\n // ── Helpers ────────────────────────────────────────────────────────────────\n const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));\n\n const effectiveAspectRatio = useMemo(() => {\n if (activePresetRatio === -1 && sourceDimensions) {\n return sourceDimensions.width / sourceDimensions.height;\n }\n return activePresetRatio && activePresetRatio > 0 ? activePresetRatio : undefined;\n }, [activePresetRatio, sourceDimensions]);\n\n const isSourceBelowMin = useMemo(() => {\n if (!sourceDimensions) return false;\n return (\n (minWidth && sourceDimensions.width < minWidth) ||\n (minHeight && sourceDimensions.height < minHeight)\n );\n }, [sourceDimensions, minWidth, minHeight]);\n\n const filterStyle = useMemo(() => {\n const b = 100 + brightness;\n const c = 100 + contrast;\n const s = 100 + saturation;\n return `brightness(${b}%) contrast(${c}%) saturate(${s}%) blur(${blur}px)`;\n }, [brightness, contrast, saturation, blur]);\n\n // ── Reset ──────────────────────────────────────────────────────────────────\n const resetEdits = useCallback(() => {\n setPan({ x: 0, y: 0 });\n setZoom(1);\n setRotation(0);\n setFlipX(false);\n setFlipY(false);\n setBrightness(0);\n setContrast(0);\n setSaturation(0);\n setBlur(0);\n setCropBox(DEFAULT_CROP);\n setActivePresetRatio(initialAspectRatio);\n }, [initialAspectRatio]);\n\n const resetModal = useCallback(() => {\n setImageSrc(null);\n setSourceDimensions(null);\n setFileName(\"\");\n setShowCamera(false);\n resetEdits();\n if (fileInputRef.current) fileInputRef.current.value = \"\";\n }, [resetEdits]);\n\n const stopCamera = useCallback(() => {\n cameraStreamRef.current?.getTracks().forEach((t) => t.stop());\n cameraStreamRef.current = null;\n }, []);\n\n const handleClose = useCallback(() => {\n stopCamera();\n resetModal();\n onOpenChange(false);\n }, [stopCamera, resetModal, onOpenChange]);\n\n // ── Keyboard & scroll lock ─────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => { if (e.key === \"Escape\") handleClose(); };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open, handleClose]);\n\n useEffect(() => {\n document.body.style.overflow = open ? \"hidden\" : \"\";\n return () => { document.body.style.overflow = \"\"; };\n }, [open]);\n\n // ── Camera ─────────────────────────────────────────────────────────────────\n useEffect(() => {\n if (!showCamera) return;\n let cancelled = false;\n (async () => {\n setIsCameraStarting(true);\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: \"user\" }, audio: false });\n if (cancelled) { stream.getTracks().forEach((t) => t.stop()); return; }\n cameraStreamRef.current = stream;\n if (videoRef.current) {\n videoRef.current.srcObject = stream;\n await videoRef.current.play();\n }\n } catch (err) {\n if (!cancelled) {\n toast.error(\"Camera access denied\", {\n description: err instanceof Error ? err.message : \"Grant camera permission in your browser settings.\",\n });\n setShowCamera(false);\n }\n } finally {\n if (!cancelled) setIsCameraStarting(false);\n }\n })();\n return () => { cancelled = true; };\n }, [showCamera]);\n\n useEffect(() => () => { cameraStreamRef.current?.getTracks().forEach((t) => t.stop()); }, []);\n\n // ── File loading ───────────────────────────────────────────────────────────\n const loadFile = useCallback(\n (file: File) => {\n if (file.size > maxFileSize) {\n toast.error(\"File too large\", { description: `Maximum file size is ${(maxFileSize / 1024 / 1024).toFixed(1)} MB.` });\n return;\n }\n if (!acceptedFormats.includes(file.type)) {\n toast.error(\"Unsupported file format\", {\n description: `Accepted formats: ${acceptedFormats.map((f) => f.split(\"/\")[1]?.toUpperCase()).join(\", \")}`,\n });\n return;\n }\n if (file.type === \"image/svg+xml\") {\n onImageSelect(file, file.name);\n onOpenChange(false);\n toast.success(\"Image selected\");\n return;\n }\n setFileName(file.name);\n const reader = new FileReader();\n reader.onload = (e) => {\n const dataUrl = e.target?.result as string;\n const img = new Image();\n img.onload = () => {\n setSourceDimensions({ width: img.naturalWidth, height: img.naturalHeight });\n setImageSrc(dataUrl);\n setCropBox(DEFAULT_CROP);\n setPan({ x: 0, y: 0 });\n setZoom(1);\n if (\n (minWidth && img.naturalWidth < minWidth) ||\n (minHeight && img.naturalHeight < minHeight)\n ) {\n toast.info(\"Low-resolution image\", {\n description: `Source is ${img.naturalWidth}×${img.naturalHeight}px. It will be upscaled to meet the ${minWidth ?? img.naturalWidth}×${minHeight ?? img.naturalHeight}px target.`,\n });\n }\n };\n img.src = dataUrl;\n };\n reader.readAsDataURL(file);\n },\n [maxFileSize, acceptedFormats, minWidth, minHeight, onImageSelect, onOpenChange]\n );\n\n const handleFileChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) loadFile(file);\n },\n [loadFile]\n );\n\n const capturePhoto = useCallback(() => {\n const video = videoRef.current;\n if (!video || video.readyState < 2) return;\n const canvas = document.createElement(\"canvas\");\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n ctx.drawImage(video, 0, 0);\n canvas.toBlob(\n (blob) => {\n if (!blob) return;\n const file = new File([blob], `capture-${Date.now()}.jpg`, { type: \"image/jpeg\" });\n stopCamera();\n setShowCamera(false);\n loadFile(file);\n },\n \"image/jpeg\",\n 0.92\n );\n }, [loadFile, stopCamera]);\n\n useEffect(() => {\n if (open && initialFile && loadedInitialFileRef.current !== initialFile) {\n loadedInitialFileRef.current = initialFile;\n loadFile(initialFile);\n }\n if (!open) loadedInitialFileRef.current = null;\n }, [open, initialFile, loadFile]);\n\n // ── Aspect-ratio preset ────────────────────────────────────────────────────\n const applyAspectRatioToCropBox = useCallback(\n (ratio: number | undefined) => {\n if (!ratio || ratio <= 0 || !frameRef.current) return;\n const rect = frameRef.current.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const vpAR = rect.width / rect.height;\n const targetFractionAR = ratio / vpAR;\n setCropBox((cur) => {\n let { x, y, w, h } = cur;\n const cx = x + w / 2;\n const cy = y + h / 2;\n if (w / h > targetFractionAR) {\n w = h * targetFractionAR;\n } else {\n h = w / targetFractionAR;\n }\n w = clamp(w, 0.1, 1);\n h = clamp(h, 0.1, 1);\n x = clamp(cx - w / 2, 0, 1 - w);\n y = clamp(cy - h / 2, 0, 1 - h);\n return { x, y, w, h };\n });\n },\n []\n );\n\n const handleSelectPreset = useCallback(\n (preset: AspectPreset) => {\n setActivePresetRatio(preset.ratio);\n if (preset.ratio === -1 && sourceDimensions) {\n applyAspectRatioToCropBox(sourceDimensions.width / sourceDimensions.height);\n } else {\n applyAspectRatioToCropBox(preset.ratio);\n }\n },\n [sourceDimensions, applyAspectRatioToCropBox]\n );\n\n // ── Crop drag ──────────────────────────────────────────────────────────────\n const startDrag = useCallback(\n (e: React.PointerEvent, handle: HandleType) => {\n e.preventDefault();\n e.stopPropagation();\n if (!frameRef.current) return;\n\n const frameRect = frameRef.current.getBoundingClientRect();\n setIsDraggingBox(true);\n\n dragStartRef.current = {\n handle,\n startBox: { ...cropBox },\n startMousePx: { x: e.clientX, y: e.clientY },\n frameRect,\n };\n\n const onMove = (me: PointerEvent) => {\n const ref = dragStartRef.current;\n if (!ref) return;\n const { handle: h, startBox: sb, startMousePx: sm, frameRect: r } = ref;\n const dx = (me.clientX - sm.x) / r.width;\n const dy = (me.clientY - sm.y) / r.height;\n\n setCropBox(() => {\n let { x, y, w, h: _h } = { ...sb };\n\n if (h === \"body\") {\n x = clamp(x + dx, 0, 1 - w);\n y = clamp(y + dy, 0, 1 - _h);\n } else {\n let x1 = x, y1 = y, x2 = x + w, y2 = y + _h;\n if (h === \"nw\" || h === \"w\" || h === \"sw\") x1 = clamp(x + dx, 0, x2 - 0.05);\n if (h === \"ne\" || h === \"e\" || h === \"se\") x2 = clamp(x + w + dx, x1 + 0.05, 1);\n if (h === \"nw\" || h === \"n\" || h === \"ne\") y1 = clamp(y + dy, 0, y2 - 0.05);\n if (h === \"sw\" || h === \"s\" || h === \"se\") y2 = clamp(y + _h + dy, y1 + 0.05, 1);\n w = x2 - x1; _h = y2 - y1;\n\n if (effectiveAspectRatio && effectiveAspectRatio > 0) {\n const vpAR = r.width / r.height;\n const tAR = effectiveAspectRatio / vpAR;\n if (h === \"n\" || h === \"s\") { w = clamp(_h * tAR, 0.05, 1 - x1); x2 = x1 + w; }\n else { _h = clamp(w / tAR, 0.05, 1 - y1); y2 = y1 + _h; }\n }\n\n x = clamp(x1, 0, 1);\n y = clamp(y1, 0, 1);\n w = clamp(x2 - x, 0.05, 1 - x);\n _h = clamp(y2 - y, 0.05, 1 - y);\n }\n return { x, y, w, h: _h };\n });\n };\n\n const onUp = () => {\n setIsDraggingBox(false);\n dragStartRef.current = null;\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n window.removeEventListener(\"pointercancel\", onUp);\n };\n\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n window.addEventListener(\"pointercancel\", onUp);\n },\n [cropBox, effectiveAspectRatio]\n );\n\n // Pointer-down on canvas background → pan\n const handleFramePointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n if ((e.target as HTMLElement).closest(\"[data-crop-handle]\")) return;\n e.preventDefault();\n const sx = e.clientX, sy = e.clientY;\n const spx = pan.x, spy = pan.y;\n const onMove = (me: PointerEvent) => setPan({ x: spx + me.clientX - sx, y: spy + me.clientY - sy });\n const onUp = () => { window.removeEventListener(\"pointermove\", onMove); window.removeEventListener(\"pointerup\", onUp); };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n },\n [pan]\n );\n\n // ── Export / apply ─────────────────────────────────────────────────────────\n const handleApply = useCallback(async () => {\n if (!imageSrc || !canvasRef.current || !frameRef.current) return;\n setIsProcessing(true);\n try {\n const image = new Image();\n image.src = imageSrc;\n await new Promise<void>((res, rej) => { image.onload = () => res(); image.onerror = () => rej(new Error(\"Image failed to load\")); });\n\n const origW = image.naturalWidth;\n const origH = image.naturalHeight;\n const rad = (rotation * Math.PI) / 180;\n const absSin = Math.abs(Math.sin(rad));\n const absCos = Math.abs(Math.cos(rad));\n const rotW = origW * absCos + origH * absSin;\n const rotH = origW * absSin + origH * absCos;\n\n // Render rotated+flipped+filtered image to offscreen canvas\n const offscreen = document.createElement(\"canvas\");\n offscreen.width = Math.ceil(rotW);\n offscreen.height = Math.ceil(rotH);\n const offCtx = offscreen.getContext(\"2d\")!;\n offCtx.imageSmoothingEnabled = true;\n offCtx.imageSmoothingQuality = \"high\";\n offCtx.filter = filterStyle;\n offCtx.translate(rotW / 2, rotH / 2);\n offCtx.rotate(rad);\n offCtx.scale(flipX ? -1 : 1, flipY ? -1 : 1);\n offCtx.drawImage(image, -origW / 2, -origH / 2);\n\n // Map crop box fractions back to offscreen coordinates\n const vpRect = frameRef.current.getBoundingClientRect();\n const vpW = vpRect.width, vpH = vpRect.height;\n const baseScale = Math.min(vpW / rotW, vpH / rotH);\n const effectiveScale = baseScale * zoom;\n const displayedW = rotW * effectiveScale;\n const displayedH = rotH * effectiveScale;\n const marginX = (vpW - displayedW) / 2 + pan.x;\n const marginY = (vpH - displayedH) / 2 + pan.y;\n\n const cropPxX = cropBox.x * vpW, cropPxY = cropBox.y * vpH;\n const cropPxW = cropBox.w * vpW, cropPxH = cropBox.h * vpH;\n\n const sx = clamp((cropPxX - marginX) / effectiveScale, 0, rotW);\n const sy = clamp((cropPxY - marginY) / effectiveScale, 0, rotH);\n const sx2 = clamp((cropPxX + cropPxW - marginX) / effectiveScale, 0, rotW);\n const sy2 = clamp((cropPxY + cropPxH - marginY) / effectiveScale, 0, rotH);\n const sw = Math.max(1, sx2 - sx);\n const sh = Math.max(1, sy2 - sy);\n\n let outW = sw, outH = sh;\n\n const maxSide = Math.max(outW, outH);\n if (maxDimension && maxSide > maxDimension) {\n const ds = maxDimension / maxSide; outW *= ds; outH *= ds;\n }\n if (minWidth && outW < minWidth) { const us = minWidth / outW; outW = minWidth; outH *= us; }\n if (minHeight && outH < minHeight) { const us = minHeight / outH; outH = minHeight; outW *= us; }\n outW = Math.round(Math.max(1, outW));\n outH = Math.round(Math.max(1, outH));\n\n const outputCanvas = canvasRef.current;\n outputCanvas.width = outW;\n outputCanvas.height = outH;\n const outCtx = outputCanvas.getContext(\"2d\")!;\n outCtx.imageSmoothingEnabled = true;\n outCtx.imageSmoothingQuality = \"high\";\n if (outputFormat === \"image/jpeg\") { outCtx.fillStyle = \"#fff\"; outCtx.fillRect(0, 0, outW, outH); }\n outCtx.drawImage(offscreen, sx, sy, sw, sh, 0, 0, outW, outH);\n\n outputCanvas.toBlob(\n (blob) => {\n if (!blob) { toast.error(\"Failed to process image. Please try again.\"); return; }\n const ext = outputFormat === \"image/png\" ? \"png\" : \"jpg\";\n const baseName = fileName.replace(/\\.[^./]+$/, \"\") || \"edited-image\";\n onImageSelect(blob, `${baseName}.${ext}`);\n resetModal();\n onOpenChange(false);\n toast.success(\"Image updated successfully\");\n },\n outputFormat,\n outputQuality\n );\n } catch (err) {\n toast.error(\"Failed to process image\", {\n description: err instanceof Error ? err.message : \"Please try again.\",\n });\n } finally {\n setIsProcessing(false);\n }\n }, [\n imageSrc, rotation, flipX, flipY, zoom, pan, cropBox, fileName,\n filterStyle, maxDimension, minWidth, minHeight,\n outputFormat, outputQuality, onImageSelect, onOpenChange, resetModal,\n ]);\n\n // ── Early exit when not open ───────────────────────────────────────────────\n if (!open) return null;\n\n const btnBase =\n \"inline-flex items-center justify-center rounded-md text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\";\n\n // ─── Render ────────────────────────────────────────────────────────────────\n return (\n <AnimatePresence>\n {open && (\n <div className=\"fixed inset-0 z-50 select-none flex\">\n {/* Backdrop */}\n <div className=\"absolute inset-0 bg-black/70 backdrop-blur-sm\" onClick={handleClose} />\n\n {/* Editor — true fullscreen, theme-aware */}\n <motion.div\n initial={{ opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.98 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n className=\"relative z-10 flex flex-col w-full h-full bg-background text-foreground overflow-hidden\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Studio Image Editor\"\n >\n\n {/* ── Slim top bar (h-11 = 44px) ─────────────────────────────── */}\n <div className=\"flex items-center gap-2 px-3 sm:px-4 h-11 border-b border-border bg-card shrink-0\">\n\n {/* Icon + title */}\n <div className=\"flex items-center gap-2 min-w-0 flex-1\">\n <Wand2 className=\"w-4 h-4 text-primary shrink-0\" />\n <span className=\"text-sm font-semibold text-foreground leading-none truncate\">\n Studio Editor\n </span>\n {sourceDimensions && (\n <span className=\"text-[11px] text-muted-foreground hidden sm:inline truncate\">\n · {sourceDimensions.width}×{sourceDimensions.height}px\n {minWidth && minHeight ? ` (min ${minWidth}×${minHeight})` : \"\"}\n </span>\n )}\n {isSourceBelowMin && (\n <span className=\"hidden sm:inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded-full bg-amber-500/15 text-amber-600 dark:text-amber-400 border border-amber-500/30 shrink-0\">\n <Info className=\"w-3 h-3\" />\n Upscale\n </span>\n )}\n </div>\n\n {/* Tabs (only when editing) */}\n {imageSrc && (\n <div className=\"flex items-center bg-muted rounded-md p-0.5 gap-0.5 shrink-0\">\n <button\n type=\"button\"\n id=\"editor-tab-crop\"\n onClick={() => setActiveTab(\"crop\")}\n className={[\n \"flex items-center gap-1 px-2.5 py-1 rounded text-xs font-medium transition-all\",\n activeTab === \"crop\"\n ? \"bg-background text-foreground shadow-sm font-semibold\"\n : \"text-muted-foreground hover:text-foreground\",\n ].join(\" \")}\n >\n <Crop className=\"w-3 h-3 text-primary\" />\n <span>Crop</span>\n </button>\n <button\n type=\"button\"\n id=\"editor-tab-adjust\"\n onClick={() => setActiveTab(\"adjust\")}\n className={[\n \"flex items-center gap-1 px-2.5 py-1 rounded text-xs font-medium transition-all\",\n activeTab === \"adjust\"\n ? \"bg-background text-foreground shadow-sm font-semibold\"\n : \"text-muted-foreground hover:text-foreground\",\n ].join(\" \")}\n >\n <Sliders className=\"w-3 h-3 text-secondary\" />\n <span>Adjust</span>\n </button>\n </div>\n )}\n\n {/* Reset + Close */}\n <div className=\"flex items-center gap-1 shrink-0\">\n {imageSrc && (\n <button\n type=\"button\"\n onClick={resetEdits}\n className={`${btnBase} h-7 text-xs px-2.5 text-muted-foreground hover:text-foreground hover:bg-muted border border-transparent rounded-md`}\n >\n <RotateCcw className=\"w-3 h-3 sm:mr-1\" />\n <span className=\"hidden sm:inline\">Reset</span>\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleClose}\n id=\"editor-close-btn\"\n className={`${btnBase} h-7 w-7 rounded-full text-muted-foreground hover:text-foreground hover:bg-muted`}\n aria-label=\"Close editor\"\n >\n <X className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n </div>\n\n {/* ── Main body ─────────────────────────────────────────────── */}\n <div className=\"flex-1 flex flex-col overflow-hidden min-h-0\">\n {!imageSrc ? (\n /* ── Upload / Camera screen ─────────────────────────── */\n showCamera ? (\n <div className=\"flex-1 flex flex-col items-center justify-center p-6 bg-background\">\n <div className=\"w-full max-w-sm space-y-4\">\n <div className=\"relative aspect-video rounded-xl overflow-hidden border border-border bg-muted flex items-center justify-center\">\n {isCameraStarting && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-background/80 z-10\">\n <Loader2 className=\"w-6 h-6 animate-spin text-primary\" />\n </div>\n )}\n <video ref={videoRef} autoPlay playsInline muted className=\"w-full h-full object-cover\" />\n </div>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={() => { stopCamera(); setShowCamera(false); }}\n className={`${btnBase} flex-1 bg-muted border border-border text-foreground hover:bg-muted/80 h-9 px-4 rounded-lg`}\n >\n <X className=\"w-4 h-4 mr-1.5\" /> Cancel\n </button>\n <button\n type=\"button\"\n onClick={capturePhoto}\n disabled={isCameraStarting}\n id=\"camera-capture-btn\"\n className={`${btnBase} flex-1 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 rounded-lg font-medium`}\n >\n <Camera className=\"w-4 h-4 mr-1.5\" /> Capture\n </button>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"flex-1 flex flex-col items-center justify-center p-6 bg-background\">\n <div className=\"w-full max-w-sm border-2 border-dashed border-border hover:border-primary/50 transition-colors rounded-2xl p-8 text-center space-y-5 bg-card/60\">\n <div className=\"w-14 h-14 bg-primary/10 text-primary rounded-2xl flex items-center justify-center mx-auto border border-primary/20\">\n <Upload className=\"w-7 h-7\" />\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-base font-semibold text-foreground\">Select an Image</p>\n <p className=\"text-xs text-muted-foreground\">\n JPEG, PNG or WebP · up to {(maxFileSize / 1024 / 1024).toFixed(0)} MB\n </p>\n </div>\n <div className=\"flex flex-wrap items-center justify-center gap-3\">\n <button\n type=\"button\"\n id=\"editor-choose-file-btn\"\n onClick={() => fileInputRef.current?.click()}\n className={`${btnBase} bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-6 rounded-xl font-medium shadow-md`}\n >\n Choose File\n </button>\n {cameraSupported && (\n <button\n type=\"button\"\n id=\"editor-camera-btn\"\n onClick={() => setShowCamera(true)}\n className={`${btnBase} bg-card border border-border text-foreground hover:bg-muted h-9 px-5 rounded-xl font-medium`}\n >\n <Camera className=\"w-4 h-4 mr-1.5\" /> Camera\n </button>\n )}\n </div>\n <input ref={fileInputRef} type=\"file\" className=\"hidden\" accept={acceptedFormats.join(\",\")} onChange={handleFileChange} />\n </div>\n </div>\n )\n ) : (\n /* ── Editing workspace ──────────────────────────────── */\n <>\n {/* Canvas — fills ALL remaining space */}\n <div className=\"flex-1 relative bg-muted/30 overflow-hidden select-none min-h-0\">\n {/* Dot-grid backdrop */}\n <div\n className=\"absolute inset-0 pointer-events-none opacity-[0.15]\"\n style={{\n backgroundImage: \"radial-gradient(circle, hsl(var(--muted-foreground)/0.6) 1px, transparent 1px)\",\n backgroundSize: \"20px 20px\",\n }}\n />\n\n {/* Zoom pill — floating, top-left, very compact */}\n <div className=\"absolute top-2 left-2 z-30 flex items-center bg-card/90 border border-border rounded-md shadow-sm backdrop-blur-sm\">\n <button\n type=\"button\"\n id=\"editor-zoom-out\"\n onClick={() => setZoom((z) => Math.max(0.2, z - 0.15))}\n className=\"h-6 w-6 flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Zoom out\"\n >\n <ZoomOut className=\"w-3 h-3\" />\n </button>\n <span className=\"w-9 text-center font-mono text-[10px] text-foreground font-medium\">\n {Math.round(zoom * 100)}%\n </span>\n <button\n type=\"button\"\n id=\"editor-zoom-in\"\n onClick={() => setZoom((z) => Math.min(5, z + 0.15))}\n className=\"h-6 w-6 flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Zoom in\"\n >\n <ZoomIn className=\"w-3 h-3\" />\n </button>\n <div className=\"h-3 w-px bg-border mx-0.5\" />\n <button\n type=\"button\"\n id=\"editor-fit-btn\"\n onClick={() => { setZoom(1); setPan({ x: 0, y: 0 }); }}\n className=\"h-6 w-6 flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Fit to view\"\n >\n <Maximize2 className=\"w-3 h-3\" />\n </button>\n </div>\n\n {/* Draggable frame */}\n <div\n ref={frameRef}\n onPointerDown={handleFramePointerDown}\n className=\"absolute inset-3 touch-none cursor-grab active:cursor-grabbing\"\n >\n {/* Image */}\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <img\n src={imageSrc}\n alt=\"Editor preview\"\n draggable={false}\n className=\"max-w-full max-h-full object-contain pointer-events-none select-none block\"\n style={{\n transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom * (flipX ? -1 : 1)}, ${zoom * (flipY ? -1 : 1)}) rotate(${rotation}deg)`,\n filter: filterStyle,\n transition: isDraggingBox ? \"none\" : \"transform 0.1s ease-out\",\n }}\n />\n </div>\n\n {/* Crop overlay */}\n <div className=\"absolute inset-0 pointer-events-none z-10\">\n <div\n className=\"absolute\"\n style={{\n left: `${cropBox.x * 100}%`,\n top: `${cropBox.y * 100}%`,\n width: `${cropBox.w * 100}%`,\n height: `${cropBox.h * 100}%`,\n boxShadow: \"0 0 0 9999px rgba(0,0,0,0.55)\",\n }}\n >\n {/* Drag body */}\n <div\n data-crop-handle=\"body\"\n onPointerDown={(e) => startDrag(e, \"body\")}\n className=\"absolute inset-0 border-2 border-white/90 cursor-move pointer-events-auto group\"\n >\n {/* Rule-of-thirds */}\n <div className={`absolute inset-0 grid grid-cols-3 grid-rows-3 transition-opacity ${isDraggingBox ? \"opacity-50\" : \"opacity-0 group-hover:opacity-30\"}`}>\n {Array.from({ length: 9 }).map((_, i) => (\n <div key={i} className={[(i % 3 !== 2) ? \"border-r border-white/50\" : \"\", (i < 6) ? \"border-b border-white/50\" : \"\"].join(\" \")} />\n ))}\n </div>\n {/* Corner L-brackets */}\n {[\n \"absolute -top-px -left-px border-t-2 border-l-2\",\n \"absolute -top-px -right-px border-t-2 border-r-2\",\n \"absolute -bottom-px -left-px border-b-2 border-l-2\",\n \"absolute -bottom-px -right-px border-b-2 border-r-2\",\n ].map((cls, i) => (\n <div key={i} className={`${cls} w-3.5 h-3.5 border-white pointer-events-none`} />\n ))}\n </div>\n\n {/* Resize handles */}\n {HANDLE_DEFS.map(({ key, cursor, style }) => (\n <div\n key={key}\n data-crop-handle={key}\n onPointerDown={(e) => key && startDrag(e, key)}\n className=\"absolute bg-white border border-black/30 rounded-sm shadow-sm pointer-events-auto z-20 hover:scale-110 transition-transform\"\n style={{ width: HANDLE_SIZE, height: HANDLE_SIZE, cursor, ...style }}\n />\n ))}\n </div>\n </div>\n </div>\n </div>\n\n {/* ── Compact bottom controls ──────────────────────── */}\n {activeTab === \"crop\" ? (\n <div className=\"bg-card border-t border-border shrink-0\">\n {/* Row 1: Rotation + flip/rotate buttons */}\n <div className=\"flex items-center gap-2 px-3 sm:px-4 h-10 border-b border-border/50\">\n <span className=\"text-[10px] font-semibold text-muted-foreground uppercase tracking-wider shrink-0\">Rotate</span>\n <div className=\"flex-1 min-w-0\">\n <InlineSlider\n value={[rotation]}\n onValueChange={([v]) => setRotation(v)}\n min={-180}\n max={180}\n step={1}\n />\n </div>\n <span className=\"font-mono text-[11px] text-foreground w-8 text-right shrink-0\">{rotation}°</span>\n <div className=\"h-4 w-px bg-border mx-1 shrink-0\" />\n {/* −90 */}\n <button\n type=\"button\"\n id=\"editor-rotate-ccw\"\n onClick={() => setRotation((r) => r - 90)}\n title=\"Rotate −90°\"\n className=\"h-7 px-2 flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground border border-border rounded hover:bg-muted transition-colors shrink-0\"\n >\n <RotateCcw className=\"w-3 h-3\" /><span className=\"hidden sm:inline\">−90°</span>\n </button>\n {/* +90 */}\n <button\n type=\"button\"\n id=\"editor-rotate-cw\"\n onClick={() => setRotation((r) => r + 90)}\n title=\"Rotate +90°\"\n className=\"h-7 px-2 flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground border border-border rounded hover:bg-muted transition-colors shrink-0\"\n >\n <RotateCw className=\"w-3 h-3\" /><span className=\"hidden sm:inline\">+90°</span>\n </button>\n <div className=\"h-4 w-px bg-border mx-0.5 shrink-0\" />\n {/* Flip H */}\n <button\n type=\"button\"\n id=\"editor-flip-h\"\n onClick={() => setFlipX((f) => !f)}\n title=\"Flip Horizontal\"\n className={[\"h-7 w-7 flex items-center justify-center rounded border transition-colors shrink-0\", flipX ? \"bg-primary/10 text-primary border-primary/40\" : \"border-border text-muted-foreground hover:text-foreground hover:bg-muted\"].join(\" \")}\n >\n <FlipHorizontal className=\"w-3.5 h-3.5\" />\n </button>\n {/* Flip V */}\n <button\n type=\"button\"\n id=\"editor-flip-v\"\n onClick={() => setFlipY((f) => !f)}\n title=\"Flip Vertical\"\n className={[\"h-7 w-7 flex items-center justify-center rounded border transition-colors shrink-0\", flipY ? \"bg-primary/10 text-primary border-primary/40\" : \"border-border text-muted-foreground hover:text-foreground hover:bg-muted\"].join(\" \")}\n >\n <FlipVertical className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n\n {/* Row 2: Aspect ratio presets */}\n <div className=\"flex items-center gap-1.5 px-3 sm:px-4 h-9 overflow-x-auto scrollbar-hide\">\n {ASPECT_PRESETS.map((preset) => {\n const active = activePresetRatio === preset.ratio;\n return (\n <button\n key={preset.label}\n type=\"button\"\n onClick={() => handleSelectPreset(preset)}\n className={[\n \"flex items-center gap-1 px-2.5 py-1 rounded text-[11px] font-medium border shrink-0 transition-all whitespace-nowrap\",\n active\n ? \"bg-primary/10 border-primary/50 text-primary font-semibold\"\n : \"border-border text-muted-foreground hover:text-foreground hover:bg-muted\",\n ].join(\" \")}\n >\n {preset.icon}\n <span>{preset.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n ) : (\n /* Adjustments panel — compact 2-col grid */\n <div className=\"bg-card border-t border-border px-4 py-3 shrink-0\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-3 max-w-4xl mx-auto\">\n {[\n { label: \"Brightness\", icon: <Sun className=\"w-3 h-3 text-amber-500\" />, value: brightness, set: setBrightness, min: -100, max: 100, unit: \"%\" },\n { label: \"Contrast\", icon: <ContrastIcon className=\"w-3 h-3 text-blue-500\" />, value: contrast, set: setContrast, min: -100, max: 100, unit: \"%\" },\n { label: \"Saturation\", icon: <Sparkles className=\"w-3 h-3 text-violet-500\" />, value: saturation, set: setSaturation, min: -100, max: 100, unit: \"%\" },\n { label: \"Blur\", icon: <Sliders className=\"w-3 h-3 text-emerald-500\" />, value: blur, set: setBlur, min: 0, max: 10, step: 0.5, unit: \"px\" },\n ].map(({ label, icon, value, set, min, max, step = 1, unit }) => (\n <div key={label} className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-1 text-[11px] font-medium text-foreground w-20 shrink-0\">{icon}{label}</span>\n <div className=\"flex-1 min-w-0\">\n <InlineSlider value={[value]} onValueChange={([v]) => set(v)} min={min} max={max} step={step} />\n </div>\n <span className=\"font-mono text-[11px] text-muted-foreground w-9 text-right shrink-0\">{value}{unit}</span>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* ── Slim footer (h-10 = 40px) ──────────────────────── */}\n <div className=\"flex items-center justify-between gap-3 px-3 sm:px-4 h-10 border-t border-border bg-card shrink-0\">\n <button\n type=\"button\"\n id=\"editor-cancel-btn\"\n onClick={resetModal}\n className={`${btnBase} flex items-center gap-1.5 h-7 px-3 text-xs text-muted-foreground hover:text-foreground border border-border rounded hover:bg-muted transition-colors`}\n >\n <X className=\"w-3 h-3\" />\n Cancel\n </button>\n\n <button\n type=\"button\"\n id=\"editor-apply-btn\"\n onClick={handleApply}\n disabled={isProcessing}\n className={`${btnBase} flex items-center gap-1.5 h-7 px-5 text-xs font-semibold bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors disabled:opacity-60`}\n >\n {isProcessing ? (\n <Loader2 className=\"w-3 h-3 animate-spin\" />\n ) : (\n <Check className=\"w-3 h-3\" />\n )}\n {isProcessing ? \"Processing…\" : \"Apply Edits\"}\n </button>\n </div>\n </>\n )}\n </div>\n </motion.div>\n\n {/* Hidden export canvas */}\n <canvas ref={canvasRef} className=\"hidden\" aria-hidden=\"true\" />\n </div>\n )}\n </AnimatePresence>\n );\n}\n\nexport default ImageEditorModal;\n","\"use client\";\n\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { motion, AnimatePresence, useMotionValue } from \"framer-motion\";\nimport { useGesture } from \"@use-gesture/react\";\nimport {\n X,\n ChevronLeft,\n ChevronRight,\n ZoomIn,\n ZoomOut,\n RotateCw,\n Maximize,\n Minimize,\n RefreshCcw,\n} from \"lucide-react\";\nimport { logger } from \"../../utils/logger\";\nimport SmartImage from \"./SmartImage\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ImageViewerModalProps {\n isOpen: boolean;\n onClose: () => void;\n images?: string[];\n selectedImage: number;\n setSelectedImage: (index: number) => void;\n productName: string;\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * ImageViewerModal — Self-contained full-screen product image viewer.\n *\n * Designed to work standalone across any application (kasuvia-storefront, kasuvia, etc.)\n * with zero reliance on external CSS purging or Tailwind utility scanning.\n */\nexport function ImageViewerModal({\n isOpen,\n onClose,\n images = [],\n selectedImage,\n setSelectedImage,\n productName,\n}: ImageViewerModalProps) {\n // Motion Values for fluid gestures\n const scale = useMotionValue(1);\n const x = useMotionValue(0);\n const y = useMotionValue(0);\n const rotation = useMotionValue(0);\n\n const [mounted, setMounted] = useState(false);\n const [isZoomed, setIsZoomed] = useState(false);\n const [currentZoomLevel, setCurrentZoomLevel] = useState(1);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [mainImgLoaded, setMainImgLoaded] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const prevSelectedImageRef = useRef(selectedImage);\n const prevIsOpenRef = useRef(isOpen);\n\n // SSR Portal safety check\n useEffect(() => {\n setMounted(true);\n }, []);\n\n // Reset zoom & pan when image changes or modal opens\n useEffect(() => {\n if (prevSelectedImageRef.current !== selectedImage || prevIsOpenRef.current !== isOpen) {\n setIsZoomed(false);\n setCurrentZoomLevel(1);\n setMainImgLoaded(false);\n prevSelectedImageRef.current = selectedImage;\n prevIsOpenRef.current = isOpen;\n }\n scale.set(1);\n x.set(0);\n y.set(0);\n rotation.set(0);\n }, [selectedImage, isOpen, scale, x, y, rotation]);\n\n const handleNext = useCallback(() => {\n if (!images.length) return;\n setSelectedImage((selectedImage + 1) % images.length);\n }, [selectedImage, images.length, setSelectedImage]);\n\n const handlePrev = useCallback(() => {\n if (!images.length) return;\n setSelectedImage((selectedImage - 1 + images.length) % images.length);\n }, [selectedImage, images.length, setSelectedImage]);\n\n const handleZoomIn = useCallback(() => {\n const next = Math.min(scale.get() + 0.5, 4);\n scale.set(next);\n setCurrentZoomLevel(next);\n setIsZoomed(next > 1);\n }, [scale]);\n\n const handleZoomOut = useCallback(() => {\n const next = Math.max(scale.get() - 0.5, 1);\n scale.set(next);\n setCurrentZoomLevel(next);\n setIsZoomed(next > 1);\n if (next === 1) {\n x.set(0);\n y.set(0);\n }\n }, [scale, x, y]);\n\n const handleRotate = useCallback(() => {\n rotation.set(rotation.get() + 90);\n }, [rotation]);\n\n const handleReset = useCallback(() => {\n scale.set(1);\n x.set(0);\n y.set(0);\n rotation.set(0);\n setCurrentZoomLevel(1);\n setIsZoomed(false);\n }, [scale, x, y, rotation]);\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return;\n if (!document.fullscreenElement) {\n containerRef.current.requestFullscreen().catch((err: Error) => {\n logger.error(`Error attempting to enable full-screen mode: ${err.message}`);\n });\n } else {\n document.exitFullscreen().catch(() => {});\n }\n }, []);\n\n useEffect(() => {\n const handleFullscreenChange = () => setIsFullscreen(!!document.fullscreenElement);\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange);\n return () => document.removeEventListener(\"fullscreenchange\", handleFullscreenChange);\n }, []);\n\n // Keyboard navigation & body scroll lock\n useEffect(() => {\n if (!isOpen) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n if (e.key === \"ArrowLeft\") handlePrev();\n if (e.key === \"ArrowRight\") handleNext();\n if (e.key === \"+\" || e.key === \"=\") handleZoomIn();\n if (e.key === \"-\") handleZoomOut();\n };\n window.addEventListener(\"keydown\", handleKeyDown);\n document.body.style.overflow = \"hidden\";\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n document.body.style.overflow = \"\";\n };\n }, [isOpen, onClose, handleNext, handlePrev, handleZoomIn, handleZoomOut]);\n\n // Gestures for pan, pinch, swipe, double-tap\n const bind = useGesture(\n {\n onDrag: ({ down, movement: [mx], offset: [ox, oy] }) => {\n if (scale.get() > 1) {\n if (down) {\n x.set(ox);\n y.set(oy);\n }\n } else {\n if (!down) {\n if (mx < -60) handleNext();\n else if (mx > 60) handlePrev();\n }\n }\n },\n onPinch: ({ offset: [s] }) => {\n scale.set(s);\n setCurrentZoomLevel(s);\n setIsZoomed(s > 1);\n if (s <= 1) {\n x.set(0);\n y.set(0);\n }\n },\n onWheel: ({ delta: [, dy], ctrlKey }) => {\n if (ctrlKey) {\n const next = Math.min(Math.max(1, scale.get() - dy / 100), 4);\n scale.set(next);\n setCurrentZoomLevel(next);\n setIsZoomed(next > 1);\n }\n },\n onDoubleClick: ({ event }) => {\n event.stopPropagation();\n if (scale.get() > 1) {\n handleReset();\n } else {\n scale.set(2.5);\n setCurrentZoomLevel(2.5);\n setIsZoomed(true);\n }\n },\n },\n {\n drag: { from: () => [x.get(), y.get()], threshold: 10 },\n pinch: { scaleBounds: { min: 1, max: 4 }, modifierKey: null },\n }\n );\n\n if (!images.length) return null;\n\n const btnStyle: React.CSSProperties = {\n background: \"rgba(255, 255, 255, 0.1)\",\n border: \"none\",\n borderRadius: \"9999px\",\n padding: \"8px\",\n color: \"rgba(255, 255, 255, 0.9)\",\n cursor: \"pointer\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n transition: \"background-color 0.15s ease, color 0.15s ease\",\n };\n\n const navBtnStyle: React.CSSProperties = {\n position: \"absolute\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n background: \"rgba(0, 0, 0, 0.6)\",\n border: \"1px solid rgba(255, 255, 255, 0.2)\",\n borderRadius: \"9999px\",\n padding: \"14px\",\n color: \"#ffffff\",\n cursor: \"pointer\",\n backdropFilter: \"blur(8px)\",\n zIndex: 40,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: \"0 10px 25px rgba(0, 0, 0, 0.5)\",\n transition: \"transform 0.15s ease, background-color 0.15s ease\",\n };\n\n const modalContent = (\n <AnimatePresence>\n {isOpen && (\n <motion.div\n ref={containerRef}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15, ease: \"easeOut\" }}\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100vw\",\n height: \"100vh\",\n zIndex: 99999,\n backgroundColor: \"#000000\",\n display: \"flex\",\n flexDirection: \"column\",\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n }}\n className=\"fixed inset-0 z-[99999] bg-black flex flex-col select-none overflow-hidden\"\n onClick={onClose}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={`${productName} image viewer`}\n >\n {/* ── Top Header Toolbar ────────────────────────────────────────────── */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n padding: \"12px 20px\",\n zIndex: 50,\n color: \"#ffffff\",\n background: \"linear-gradient(to bottom, rgba(0,0,0,0.85), rgba(0,0,0,0))\",\n flexShrink: 0,\n width: \"100%\",\n boxSizing: \"border-box\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"12px\" }}>\n <span\n style={{\n fontSize: \"13px\",\n fontWeight: 500,\n letterSpacing: \"0.08em\",\n color: \"rgba(255, 255, 255, 0.9)\",\n backgroundColor: \"rgba(255, 255, 255, 0.12)\",\n padding: \"4px 12px\",\n borderRadius: \"9999px\",\n border: \"1px solid rgba(255, 255, 255, 0.15)\",\n }}\n >\n {selectedImage + 1} / {images.length}\n </span>\n {productName && (\n <span\n style={{\n fontSize: \"13px\",\n fontWeight: 500,\n color: \"rgba(255, 255, 255, 0.7)\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n maxWidth: \"300px\",\n }}\n className=\"hidden sm:inline\"\n >\n {productName}\n </span>\n )}\n </div>\n\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"8px\" }}>\n {currentZoomLevel > 1 && (\n <span\n style={{\n fontSize: \"11px\",\n fontFamily: \"monospace\",\n color: \"rgba(255, 255, 255, 0.7)\",\n marginRight: \"4px\",\n }}\n className=\"hidden sm:inline\"\n >\n {Math.round(currentZoomLevel * 100)}%\n </span>\n )}\n\n <button\n type=\"button\"\n onClick={handleZoomOut}\n style={btnStyle}\n title=\"Zoom Out (-)\"\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.25)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.1)\")}\n >\n <ZoomOut className=\"w-5 h-5\" style={{ width: \"20px\", height: \"20px\" }} />\n </button>\n <button\n type=\"button\"\n onClick={handleZoomIn}\n style={btnStyle}\n title=\"Zoom In (+)\"\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.25)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.1)\")}\n >\n <ZoomIn className=\"w-5 h-5\" style={{ width: \"20px\", height: \"20px\" }} />\n </button>\n <button\n type=\"button\"\n onClick={handleRotate}\n style={btnStyle}\n title=\"Rotate 90°\"\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.25)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.1)\")}\n >\n <RotateCw className=\"w-5 h-5\" style={{ width: \"20px\", height: \"20px\" }} />\n </button>\n <button\n type=\"button\"\n onClick={handleReset}\n style={btnStyle}\n title=\"Reset View\"\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.25)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.1)\")}\n >\n <RefreshCcw className=\"w-5 h-5\" style={{ width: \"20px\", height: \"20px\" }} />\n </button>\n\n <div style={{ width: \"1px\", height: \"20px\", backgroundColor: \"rgba(255,255,255,0.2)\", margin: \"0 6px\" }} />\n\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n style={btnStyle}\n title={isFullscreen ? \"Exit Fullscreen\" : \"Enter Fullscreen\"}\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.25)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(255,255,255,0.1)\")}\n >\n {isFullscreen ? (\n <Minimize className=\"w-5 h-5\" style={{ width: \"20px\", height: \"20px\" }} />\n ) : (\n <Maximize className=\"w-5 h-5\" style={{ width: \"20px\", height: \"20px\" }} />\n )}\n </button>\n\n <div style={{ width: \"1px\", height: \"20px\", backgroundColor: \"rgba(255,255,255,0.2)\", margin: \"0 6px\" }} />\n\n <button\n type=\"button\"\n onClick={onClose}\n style={{\n ...btnStyle,\n backgroundColor: \"rgba(239, 68, 68, 0.25)\",\n color: \"#f87171\",\n }}\n title=\"Close (Esc)\"\n aria-label=\"Close viewer\"\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(239, 68, 68, 0.45)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(239, 68, 68, 0.25)\")}\n >\n <X className=\"w-6 h-6\" style={{ width: \"24px\", height: \"24px\" }} />\n </button>\n </div>\n </div>\n\n {/* ── Main Display Viewport ────────────────────────────────────────── */}\n <div\n style={{\n flex: 1,\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n height: \"100%\",\n minHeight: 0,\n overflow: \"hidden\",\n touchAction: \"none\",\n }}\n {...bind()}\n onClick={(e) => e.stopPropagation()}\n >\n <motion.div\n style={{\n scale,\n rotate: rotation,\n x,\n y,\n cursor: isZoomed ? \"grab\" : \"default\",\n width: \"100%\",\n height: \"100%\",\n padding: \"16px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxSizing: \"border-box\",\n }}\n className=\"will-change-transform\"\n >\n {/* Loading indicator */}\n {!mainImgLoaded && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n zIndex: 10,\n }}\n >\n <div\n style={{\n width: \"160px\",\n height: \"160px\",\n borderRadius: \"16px\",\n backgroundColor: \"rgba(255,255,255,0.05)\",\n border: \"1px solid rgba(255,255,255,0.1)\",\n }}\n />\n </div>\n )}\n\n <motion.img\n key={images[selectedImage]}\n src={images[selectedImage] ?? undefined}\n alt={`${productName} - Image ${selectedImage + 1}`}\n style={{\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n objectFit: \"contain\",\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n display: \"block\",\n margin: \"auto\",\n opacity: mainImgLoaded ? 1 : 0,\n transition: \"opacity 0.25s ease-out\",\n boxShadow: \"0 25px 50px -12px rgba(0, 0, 0, 0.8)\",\n }}\n draggable={false}\n onLoad={() => setMainImgLoaded(true)}\n onDragStart={(e) => e.preventDefault()}\n onMouseDown={(e) => e.preventDefault()}\n />\n </motion.div>\n\n {/* ── Navigation Chevrons ────────────────────────────────────────── */}\n {images.length > 1 && (\n <>\n <button\n type=\"button\"\n aria-label=\"Previous image\"\n style={{ ...navBtnStyle, left: \"24px\" }}\n onClick={(e) => {\n e.stopPropagation();\n handlePrev();\n }}\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(0,0,0,0.85)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(0,0,0,0.6)\")}\n >\n <ChevronLeft className=\"w-8 h-8\" style={{ width: \"32px\", height: \"32px\" }} />\n </button>\n\n <button\n type=\"button\"\n aria-label=\"Next image\"\n style={{ ...navBtnStyle, right: \"24px\" }}\n onClick={(e) => {\n e.stopPropagation();\n handleNext();\n }}\n onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = \"rgba(0,0,0,0.85)\")}\n onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = \"rgba(0,0,0,0.6)\")}\n >\n <ChevronRight className=\"w-8 h-8\" style={{ width: \"32px\", height: \"32px\" }} />\n </button>\n </>\n )}\n </div>\n\n {/* ── Bottom Thumbnail Toolbar ────────────────────────────────────── */}\n {images.length > 1 && (\n <div\n style={{\n padding: \"16px 20px\",\n zIndex: 50,\n background: \"linear-gradient(to top, rgba(0, 0, 0, 0.95), rgba(0, 0, 0, 0))\",\n display: \"flex\",\n justifyContent: \"center\",\n flexShrink: 0,\n width: \"100%\",\n boxSizing: \"border-box\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n display: \"flex\",\n gap: \"12px\",\n overflowX: \"auto\",\n maxWidth: \"100%\",\n paddingBottom: \"4px\",\n }}\n >\n {images.map((img, idx) => (\n <button\n key={idx}\n type=\"button\"\n onClick={() => setSelectedImage(idx)}\n aria-label={`View image ${idx + 1}`}\n style={{\n position: \"relative\",\n width: \"60px\",\n height: \"60px\",\n borderRadius: \"10px\",\n overflow: \"hidden\",\n flexShrink: 0,\n border: selectedImage === idx ? \"2px solid #ffffff\" : \"2px solid rgba(255, 255, 255, 0.2)\",\n opacity: selectedImage === idx ? 1 : 0.5,\n cursor: \"pointer\",\n transform: selectedImage === idx ? \"scale(1.05)\" : \"scale(1)\",\n transition: \"all 0.2s ease\",\n background: \"none\",\n padding: 0,\n }}\n >\n <SmartImage\n src={img}\n alt={`Thumbnail ${idx + 1}`}\n className=\"w-full h-full object-cover\"\n />\n </button>\n ))}\n </div>\n </div>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n\n if (!mounted) return null;\n return createPortal(modalContent, document.body);\n}\n\nexport default ImageViewerModal;\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport Script from \"next/script\";\nimport { getCookieConsent, COOKIE_CONSENT_CHANGED_EVENT } from \"../../utils/cookieConsent\";\n\nexport interface AnalyticsScriptsProps {\n websiteId?: string;\n scriptUrl?: string;\n recorderUrl?: string;\n}\n\n/**\n * AnalyticsScripts — Consent-gated Umami tracker + session-replay loader.\n *\n * REAL GAP FIXED (owner-requested legal-compliance review): Umami's tracker\n * AND its session-replay/heatmap recorder used to render unconditionally the\n * moment a project had analytics provisioned — no consent check anywhere.\n * Session replay specifically is non-essential tracking under GDPR/NDPR\n * consent rules regardless of PII-masking/sample-rate mitigations already\n * built into the recorder's own config — masking reduces risk, it doesn't\n * remove the consent requirement.\n *\n * This component is the actual technical gate: it renders nothing until the\n * visitor has affirmatively accepted via CookieConsentBanner, and re-checks\n * live if they change their choice without needing a page reload.\n *\n * Peer dependencies: next (next/script)\n */\nexport function AnalyticsScripts({ websiteId, scriptUrl, recorderUrl }: AnalyticsScriptsProps) {\n const [accepted, setAccepted] = useState(false);\n\n useEffect(() => {\n const sync = () => setAccepted(getCookieConsent() === \"accepted\");\n sync();\n window.addEventListener(COOKIE_CONSENT_CHANGED_EVENT, sync);\n window.addEventListener(\"storage\", sync);\n return () => {\n window.removeEventListener(COOKIE_CONSENT_CHANGED_EVENT, sync);\n window.removeEventListener(\"storage\", sync);\n };\n }, []);\n\n if (!accepted || !websiteId) return null;\n\n return (\n <>\n {scriptUrl && (\n <Script async defer data-website-id={websiteId} src={scriptUrl} strategy=\"afterInteractive\" />\n )}\n {recorderUrl && (\n <Script defer data-website-id={websiteId} src={recorderUrl} strategy=\"afterInteractive\" />\n )}\n </>\n );\n}\n","/**\n * Cookie Consent Utilities\n *\n * Cookie consent state — read/written to localStorage only (per-browser, never\n * sent to any server). \"accepted\" | \"rejected\" | null (no choice made yet).\n *\n * localStorage is intentionally used here: this is a GDPR/NDPR UI preference\n * flag, not business data. It is never sent to the backend. The banner simply\n * reappears on the next visit if localStorage is unavailable (private browsing).\n */\n\nexport type CookieConsentValue = \"accepted\" | \"rejected\" | null;\n\nconst STORAGE_KEY = \"kasuvia_cookie_consent\";\nexport const COOKIE_CONSENT_CHANGED_EVENT = \"kasuvia:cookie-consent-changed\";\n\nexport function getCookieConsent(): CookieConsentValue {\n try {\n const value = window.localStorage.getItem(STORAGE_KEY);\n return value === \"accepted\" || value === \"rejected\" ? value : null;\n } catch {\n return null;\n }\n}\n\nexport function setCookieConsent(value: \"accepted\" | \"rejected\"): void {\n try {\n window.localStorage.setItem(STORAGE_KEY, value);\n window.dispatchEvent(new Event(COOKIE_CONSENT_CHANGED_EVENT));\n } catch {\n // localStorage unavailable (private browsing, blocked site data) — the banner\n // will just reappear next visit rather than crash; nothing else depends on this.\n }\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { getCookieConsent, setCookieConsent } from \"../../utils/cookieConsent\";\n\nexport interface CookieConsentBannerProps {\n /**\n * URL for the privacy policy page. Defaults to \"/privacy\".\n */\n privacyUrl?: string;\n /**\n * Custom className for the outer banner container.\n */\n className?: string;\n /**\n * Custom className for the \"Accept\" button.\n */\n acceptButtonClassName?: string;\n /**\n * Custom className for the \"Reject non-essential\" button.\n */\n rejectButtonClassName?: string;\n}\n\n/**\n * CookieConsentBanner — GDPR/NDPR compliant consent gate.\n *\n * Owner-requested (legal-compliance review): analytics/session-replay tracking\n * must not run before the visitor has actually chosen to allow it — see\n * AnalyticsScripts, which gates the real script tags on this same stored choice.\n *\n * Shown once, bottom banner (not a blocking modal — the standard, least-intrusive\n * pattern), with Accept and Reject weighted equally, matching GDPR/NDPR guidance\n * against making \"reject\" harder to find than \"accept\".\n *\n * Styled with utility classes by default. Pass custom classNames to restyle with\n * your storefront's theme (e.g. shadcn/Tailwind classes) without touching the SDK.\n */\nexport function CookieConsentBanner({\n privacyUrl = \"/privacy\",\n className,\n acceptButtonClassName,\n rejectButtonClassName,\n}: CookieConsentBannerProps) {\n const [visible, setVisible] = useState(false);\n\n useEffect(() => {\n setVisible(getCookieConsent() === null);\n }, []);\n\n if (!visible) return null;\n\n const choose = (value: \"accepted\" | \"rejected\") => {\n setCookieConsent(value);\n setVisible(false);\n };\n\n const bannerClass = className ??\n \"fixed inset-x-0 bottom-0 z-50 border-t border-border bg-background/95 backdrop-blur-sm p-4 shadow-[0_-4px_16px_rgba(0,0,0,0.08)]\";\n\n const rejectClass = rejectButtonClassName ??\n \"inline-flex items-center justify-center rounded-md border border-input bg-background px-3 h-9 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors\";\n\n const acceptClass = acceptButtonClassName ??\n \"inline-flex items-center justify-center rounded-md bg-primary px-3 h-9 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\";\n\n return (\n <div className={bannerClass}>\n <div className=\"mx-auto flex max-w-4xl flex-col items-center gap-3 sm:flex-row sm:justify-between\">\n <p className=\"text-center text-sm text-muted-foreground sm:text-left\">\n We use cookies to understand how you use this site and improve your experience.{\" \"}\n <a\n href={privacyUrl}\n className=\"underline underline-offset-2 hover:text-foreground\"\n >\n Learn more\n </a>\n </p>\n <div className=\"flex shrink-0 gap-2\">\n <button\n type=\"button\"\n className={rejectClass}\n onClick={() => choose(\"rejected\")}\n >\n Reject non-essential\n </button>\n <button\n type=\"button\"\n className={acceptClass}\n onClick={() => choose(\"accepted\")}\n >\n Accept\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["_","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","React","items","subtotal","context","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_jsx_runtime","SmartImage","Image","SmartMotionImage","import_jsx_runtime","import_react_query","isDevelopment","isDevelopment","import_jsx_runtime","import_react","import_react_query","import_jsx_runtime","import_react_query","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_react","import_framer_motion","import_lucide_react","import_jsx_runtime","ImageIcon","ContrastIcon","import_react","import_framer_motion","import_lucide_react","import_jsx_runtime","import_react","import_jsx_runtime","Script","import_react","import_jsx_runtime"]}
|