UNPKG

@godaddy/react

Version:

The `createCheckoutSession` function creates a new checkout session with GoDaddy's commerce API.

1,281 lines (1,268 loc) 63.1 kB
import { D as CheckoutType, E as PaymentMethodRenderer, F as Select, H as Button, I as SelectContent, J as Form, L as SelectItem, M as Skeleton, N as TrackingProvider, O as PaymentMethodType, Q as useIsPaymentDisabled, R as SelectTrigger, T as Target, U as buttonVariants, V as Input, X as FormField, Y as FormControl, Z as FormItem, a as redirectToSuccessUrl, at as useTheme, i as checkoutContext, it as useVariables, j as CheckoutSection, k as PaymentProvider, l as ConditionalExpressProviders, n as LayoutSections, nt as queryClient, o as useCheckoutContext, r as baseCheckoutSchema, rt as useGoDaddyContext, t as Checkout, tt as GoDaddyProvider, v as DraftOrderTotals, w as useFormatCurrency, x as DraftOrderLineItems, z as SelectValue } from "./checkout-CCruxHvk.js"; import { C as getSku, T as getSkuGroups, a as addCartLineItem, d as createCartOrder, g as getCartOrder, p as deleteCartLineItem, t as cn, w as getSkuGroup } from "./utils-DWBfAHfx.js"; import { ArrowLeft, ArrowRight, ChevronLeftIcon, ChevronRight, ChevronRightIcon, Loader2, Minus, MoreHorizontalIcon, Plus, Search, ShoppingBag, ShoppingCart, X } from "lucide-react"; import * as React$1 from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { cva } from "class-variance-authority"; import * as SheetPrimitive from "@radix-ui/react-dialog"; import useEmblaCarousel from "embla-carousel-react"; //#region src/components/checkout/express-checkout/express-checkout.tsx function DraftOrderExpressCheckoutButtons() { const { session } = useCheckoutContext(); const availableExpressButtons = React.useMemo(() => { if (!session?.paymentMethods) return []; return Object.entries(session.paymentMethods).filter(([, method]) => method && Array.isArray(method.checkoutTypes) && method.checkoutTypes.includes("express")).map(([provider]) => provider); }, [session?.paymentMethods]).map((provider) => { const processor = session?.paymentMethods?.[provider]?.processor; return /* @__PURE__ */ jsx(PaymentMethodRenderer, { type: "button", method: provider, provider: processor }, `express-${provider}`); }).filter(Boolean); if (availableExpressButtons.length === 0) return null; return /* @__PURE__ */ jsxs(CheckoutSection, { style: { gridArea: "express-checkout" }, children: [ /* @__PURE__ */ jsx(Target, { id: "checkout.form.express-checkout.before" }), /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: availableExpressButtons }), /* @__PURE__ */ jsx(Target, { id: "checkout.form.express-checkout.after" }) ] }); } function DraftOrderExpressCheckout(props) { const { session, enableTracking = false, stripeConfig, godaddyPaymentsConfig, squareConfig, paypalConfig } = props; useTheme(session?.appearance?.theme); useVariables(session?.appearance?.variables || props?.appearance?.variables); const [isConfirmingCheckout, setIsConfirmingCheckout] = React.useState(false); const [checkoutErrors, setCheckoutErrors] = React.useState(void 0); const contextValue = { elements: props?.appearance?.elements, targets: props?.targets, session, stripeConfig, godaddyPaymentsConfig, squareConfig, paypalConfig, isConfirmingCheckout, setIsConfirmingCheckout, checkoutErrors, setCheckoutErrors }; if (!React.useMemo(() => { if (!session?.paymentMethods) return false; return Object.values(session.paymentMethods).some((method) => method && Array.isArray(method.checkoutTypes) && method.checkoutTypes.includes("express")); }, [session?.paymentMethods])) return null; return /* @__PURE__ */ jsx(TrackingProvider, { session, trackingEnabled: enableTracking && !!session?.id, children: /* @__PURE__ */ jsx(checkoutContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(CheckoutSection, { children: /* @__PURE__ */ jsx(ConditionalExpressProviders, { children: /* @__PURE__ */ jsx(DraftOrderExpressCheckoutButtons, {}) }) }) }) }); } //#endregion //#region src/components/storefront/cart-line-items.tsx function CartLineItems({ ...props }) { return /* @__PURE__ */ jsx(DraftOrderLineItems, { ...props }); } //#endregion //#region src/components/storefront/cart-totals.tsx function CartTotals({ ...props }) { return /* @__PURE__ */ jsx(DraftOrderTotals, { ...props, enableDiscounts: false }); } //#endregion //#region src/components/ui/sheet.tsx const Sheet = SheetPrimitive.Root; const SheetTrigger = SheetPrimitive.Trigger; const SheetClose = SheetPrimitive.Close; const SheetPortal = SheetPrimitive.Portal; const SheetOverlay = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, { className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props, ref })); SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; const sheetVariants = cva("fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", { variants: { side: { top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm" } }, defaultVariants: { side: "right" } }); const SheetContent = React$1.forwardRef(({ side = "right", className, children,...props }, ref) => { const { t } = useGoDaddyContext(); return /* @__PURE__ */ jsxs(SheetPortal, { children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, { ref, className: cn(sheetVariants({ side }), className), ...props, children: [/* @__PURE__ */ jsxs(SheetPrimitive.Close, { className: "cursor-pointer absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary", children: [/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", { className: "sr-only", children: t.ui.accessibility.close })] }), children] })] }); }); SheetContent.displayName = SheetPrimitive.Content.displayName; const SheetHeader = ({ className,...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props }); SheetHeader.displayName = "SheetHeader"; const SheetFooter = ({ className,...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props }); SheetFooter.displayName = "SheetFooter"; const SheetTitle = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, { ref, className: cn("text-lg font-semibold text-foreground", className), ...props })); SheetTitle.displayName = SheetPrimitive.Title.displayName; const SheetDescription = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props })); SheetDescription.displayName = SheetPrimitive.Description.displayName; //#endregion //#region src/lib/cart-storage.ts const CART_ORDER_ID_KEY = "godaddy_cart_order_id"; const CART_CREATED_AT_KEY = "godaddy_cart_created_at"; const CART_TTL = 720 * 60 * 60 * 1e3; /** * Get the cart order ID from localStorage. * Returns null if the cart doesn't exist or has expired. */ function getCartOrderId() { if (typeof window === "undefined") return null; const orderId = localStorage.getItem(CART_ORDER_ID_KEY); const createdAt = localStorage.getItem(CART_CREATED_AT_KEY); if (!orderId) return null; if (createdAt) { if (Date.now() - parseInt(createdAt, 10) > CART_TTL) { clearCartOrderId(); return null; } } return orderId; } /** * Save the cart order ID to localStorage with a timestamp. */ function setCartOrderId(orderId) { if (typeof window === "undefined") return; localStorage.setItem(CART_ORDER_ID_KEY, orderId); localStorage.setItem(CART_CREATED_AT_KEY, Date.now().toString()); } /** * Remove the cart order ID and timestamp from localStorage. */ function clearCartOrderId() { if (typeof window === "undefined") return; localStorage.removeItem(CART_ORDER_ID_KEY); localStorage.removeItem(CART_CREATED_AT_KEY); } //#endregion //#region src/components/storefront/cart.tsx function Cart({ open, onOpenChange, onCheckout, isCheckingOut = false }) { const context = useGoDaddyContext(); const queryClient$1 = useQueryClient(); const cartOrderId = getCartOrderId(); const { data: cartData, isLoading, error } = useQuery({ queryKey: ["cart-order", cartOrderId], queryFn: () => getCartOrder(cartOrderId, context.storeId, context.clientId, context?.apiHost), enabled: !!cartOrderId && !!context.storeId && !!context.clientId }); const deleteMutation = useMutation({ mutationFn: (lineItemId) => deleteCartLineItem({ id: lineItemId, orderId: cartOrderId }, context.storeId, context.clientId, context?.apiHost), onSuccess: () => { queryClient$1.invalidateQueries({ queryKey: ["cart-order", cartOrderId] }); } }); const handleRemoveFromCart = (itemId) => { deleteMutation.mutate(itemId); }; const order = cartData?.orderById; const { t } = useGoDaddyContext(); const items = order?.lineItems?.map((item) => ({ id: item.id, name: item.name || t.storefront.product, image: item.details?.productAssetUrl || "", quantity: item.quantity || 0, originalPrice: (item.totals?.subTotal?.value || 0) / (item.quantity || 1), price: (item.totals?.subTotal?.value || 0) / (item.quantity || 1), selectedOptions: item?.details?.selectedOptions?.map((option) => ({ attribute: option.attribute || "", values: option.values || [] })) || [], addons: item.details?.selectedAddons?.map((addon) => ({ attribute: addon.attribute || "", sku: addon.sku || "", values: addon.values?.map((value) => ({ costAdjustment: value.costAdjustment ? { currencyCode: value.costAdjustment.currencyCode ?? void 0, value: value.costAdjustment.value ?? void 0 } : void 0, name: value.name ?? void 0 })) })) })) || []; const itemCount = items.reduce((sum, item) => sum + item.quantity, 0); const currencyCode = order?.totals?.total?.currencyCode || "USD"; const subtotal = order?.totals?.subTotal?.value || 0; const shipping = order?.totals?.shippingTotal?.value || 0; const taxes = order?.totals?.taxTotal?.value || 0; const totals = { subtotal, discount: order?.totals?.discountTotal?.value || 0, shipping, currencyCode, itemCount, total: order?.totals?.total?.value || 0, tip: 0, taxes, enableDiscounts: false, enableTaxes: true, isTaxLoading: false }; return /* @__PURE__ */ jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs(SheetContent, { className: "w-full sm:max-w-lg overflow-y-auto", children: [/* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: t.storefront.shoppingCart }) }), /* @__PURE__ */ jsxs("div", { className: "mt-8 space-y-6", children: [ isLoading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Loader2, { className: "h-8 w-8 animate-spin text-muted-foreground" }) }), !isLoading && error && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [/* @__PURE__ */ jsxs("p", { className: "text-destructive mb-2", children: [ t.storefront.failedToLoadCart, " ", error instanceof Error ? error.message : String(error) ] }), /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => queryClient$1.invalidateQueries({ queryKey: ["cart-order", cartOrderId] }), children: t.storefront.retry })] }), !isLoading && !error && (!cartOrderId || items.length === 0) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [ /* @__PURE__ */ jsx(ShoppingCart, { className: "h-16 w-16 text-muted-foreground mb-4" }), /* @__PURE__ */ jsx("p", { className: "text-lg font-medium text-foreground mb-1", children: t.storefront.yourCartIsEmpty }), /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t.storefront.addItemsToGetStarted }) ] }), !isLoading && !error && cartOrderId && items.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(CartLineItems, { items, currencyCode, inputInMinorUnits: true, onRemoveFromCart: handleRemoveFromCart, isRemovingFromCart: deleteMutation.isPending, removingItemId: deleteMutation.variables }), /* @__PURE__ */ jsx(CartTotals, { ...totals, inputInMinorUnits: true, enableTaxes: false, enableShipping: false }), onCheckout ? /* @__PURE__ */ jsx(SheetFooter, { children: /* @__PURE__ */ jsx(Button, { className: "w-full", size: "lg", onClick: () => onCheckout(cartOrderId), disabled: isCheckingOut, children: isCheckingOut ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : t.storefront.checkout }) }) : null ] }) ] })] }) }); } //#endregion //#region src/components/storefront/hooks/use-add-to-cart.ts function useAddToCart(options) { const context = useGoDaddyContext(); const queryClient$1 = useQueryClient(); const createCartMutation = useMutation({ mutationFn: () => createCartOrder({ context: { storeId: context?.storeId || "", channelId: context?.channelId || "" }, totals: { subTotal: { value: 0, currencyCode: "USD" }, shippingTotal: { value: 0, currencyCode: "USD" }, discountTotal: { value: 0, currencyCode: "USD" }, feeTotal: { value: 0, currencyCode: "USD" }, taxTotal: { value: 0, currencyCode: "USD" }, total: { value: 0, currencyCode: "USD" } } }, context.storeId, context.clientId, context?.apiHost), onSuccess: (data) => { if (data.addDraftOrder?.id) setCartOrderId(data.addDraftOrder.id); } }); const addLineItemMutation = useMutation({ mutationFn: ({ orderId, input }) => addCartLineItem({ orderId, skuId: input.skuId, name: input.name, quantity: input.quantity, fulfillmentMode: "NONE", status: "DRAFT", details: { productAssetUrl: input.productAssetUrl || void 0 } }, context.storeId, context.clientId, context?.apiHost), onSuccess: () => { queryClient$1.invalidateQueries({ queryKey: ["cart-order"] }); options?.onSuccess?.(); }, onError: (error) => { options?.onError?.(error); } }); const addToCart = async (input) => { if (!context.storeId || !context.clientId) { const error = /* @__PURE__ */ new Error("Store ID and Client ID are required"); options?.onError?.(error); return; } let cartOrderId = getCartOrderId(); if (!cartOrderId) { cartOrderId = (await createCartMutation.mutateAsync()).addDraftOrder?.id || null; if (!cartOrderId) { const error = /* @__PURE__ */ new Error("Failed to create cart"); options?.onError?.(error); return; } } await addLineItemMutation.mutateAsync({ orderId: cartOrderId, input }); }; return { addToCart, isLoading: createCartMutation.isPending || addLineItemMutation.isPending, isCreatingCart: createCartMutation.isPending, isAddingItem: addLineItemMutation.isPending }; } //#endregion //#region src/components/ui/badge.tsx const badgeVariants = cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", accent: "border-transparent bg-accent text-accent-foreground hover:bg-accent/80", outline: "text-foreground" } }, defaultVariants: { variant: "default" } }); function Badge({ className, variant,...props }) { return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props }); } //#endregion //#region src/components/ui/card.tsx const Card = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("rounded-lg border bg-card text-card-foreground shadow-sm", className), ...props })); Card.displayName = "Card"; const CardHeader = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })); CardHeader.displayName = "CardHeader"; const CardTitle = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })); CardTitle.displayName = "CardTitle"; const CardDescription = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props })); CardDescription.displayName = "CardDescription"; const CardContent = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props })); CardContent.displayName = "CardContent"; const CardFooter = React$1.forwardRef(({ className,...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex items-center p-6 pt-0", className), ...props })); CardFooter.displayName = "CardFooter"; //#endregion //#region src/components/ui/link.tsx /** * Default Link implementation that falls back to a regular anchor tag * if no custom Link component is provided via GoDaddyProvider */ const DefaultLink = React.forwardRef(({ href, children,...props }, ref) => { return /* @__PURE__ */ jsx("a", { ref, href, ...props, children }); }); DefaultLink.displayName = "DefaultLink"; /** * RouterLink component that uses the Link component from GoDaddyProvider context * or falls back to a default anchor implementation */ const RouterLink = React.forwardRef((props, ref) => { const { Link } = useGoDaddyContext(); return /* @__PURE__ */ jsx(Link || DefaultLink, { ref, ...props }); }); RouterLink.displayName = "RouterLink"; //#endregion //#region src/components/storefront/product-card.tsx function ProductCard({ product: productProp, productId, storeId: storeIdProp, clientId: clientIdProp, href: hrefProp, getProductHref, onAddToCartSuccess, onAddToCartError }) { const context = useGoDaddyContext(); const { t } = context; const formatCurrency = useFormatCurrency(); const storeId = storeIdProp || context.storeId; const clientId = clientIdProp || context.clientId; const { data: fetchedProductData, isLoading, error } = useQuery({ queryKey: [ "sku-group", productId, storeId, clientId ], queryFn: () => getSkuGroup({ id: productId }, storeId, clientId, context.apiHost), enabled: !!productId && !!storeId && !!clientId && !productProp }); const { addToCart, isLoading: isAddingToCart } = useAddToCart({ onSuccess: onAddToCartSuccess, onError: onAddToCartError }); const product = productProp || fetchedProductData?.skuGroup; const resolvedProductId = product?.id || productId; const href = hrefProp || (getProductHref && resolvedProductId ? getProductHref(resolvedProductId) : void 0); if (isLoading && !product) return /* @__PURE__ */ jsxs(Card, { className: "overflow-hidden border-border flex flex-col h-full", children: [/* @__PURE__ */ jsx("div", { className: "aspect-square overflow-hidden bg-muted", children: /* @__PURE__ */ jsx(Skeleton, { className: "w-full h-full" }) }), /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-2 flex flex-col flex-1", children: [ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-3/4" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-2/3" }), /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 mt-auto", children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-20" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-24" })] }) ] })] }); if (error && !product) { const message = error instanceof Error ? error.message : String(error); return /* @__PURE__ */ jsx(Card, { className: "overflow-hidden border-border flex flex-col h-full", children: /* @__PURE__ */ jsxs("div", { className: "p-4 text-sm text-destructive", children: [ t.storefront.errorLoadingProducts, " ", message ] }) }); } const title = product?.label || product?.name || t.storefront.product; const description = product?.description || ""; const priceMin = product?.priceRange?.min || 0; const priceMax = product?.priceRange?.max || priceMin; const compareAtMin = product?.compareAtPriceRange?.min; const isOnSale = compareAtMin && compareAtMin > priceMin; const isPriceRange = priceMin !== priceMax; const imageUrl = product?.mediaObjects?.edges?.find((edge) => edge?.node?.type === "IMAGE")?.node?.url; const firstSku = product?.skus?.edges?.[0]?.node; const skuId = firstSku?.id || product?.id || ""; const isFirstSkuInStock = ((firstSku?.inventoryCounts?.edges || []).find((edge) => edge?.node?.type === "AVAILABLE")?.node?.quantity || 0) > 0; const hasMultipleSkus = (product?.skus?.edges?.length || 0) > 1; const handleAddToCart = async (e) => { e.preventDefault(); e.stopPropagation(); if (!skuId) return; await addToCart({ skuId, name: title, quantity: 1, productAssetUrl: imageUrl || void 0 }); }; const getActionButton = () => { if (!isFirstSkuInStock) { if (hasMultipleSkus && href) return /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "secondary", className: "gap-1", children: [/* @__PURE__ */ jsx("span", { children: t.storefront.viewDetails }), /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })] }); return /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled: true, children: t.storefront.outOfStock }); } return /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleAddToCart, className: "gap-2", disabled: isAddingToCart, children: [isAddingToCart ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(ShoppingBag, { className: "h-4 w-4" }), isAddingToCart ? t.storefront.adding : t.storefront.addToCart] }); }; const cardContent = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", { className: "aspect-square overflow-hidden bg-muted relative", children: [isOnSale && /* @__PURE__ */ jsx(Badge, { variant: "accent", className: "absolute top-3 right-3 z-10 font-semibold", children: t.storefront.sale }), imageUrl ? /* @__PURE__ */ jsx("img", { src: imageUrl, alt: title, className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-muted-foreground", children: t.storefront.noImage })] }), /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-2 flex flex-col flex-1", children: [ /* @__PURE__ */ jsx("h3", { className: "font-medium text-foreground group-hover:text-primary transition-colors", children: title }), /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground line-clamp-2", children: description }), /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 mt-auto", children: [/* @__PURE__ */ jsx("span", { className: "text-md font-semibold text-foreground", children: isPriceRange ? `${formatCurrency({ amount: priceMin, currencyCode: "USD", inputInMinorUnits: true })} - ${formatCurrency({ amount: priceMax, currencyCode: "USD", inputInMinorUnits: true })}` : formatCurrency({ amount: priceMin, currencyCode: "USD", inputInMinorUnits: true }) }), getActionButton()] }) ] })] }); if (href) return /* @__PURE__ */ jsx(RouterLink, { href, className: "block", children: /* @__PURE__ */ jsx(Card, { className: "group overflow-hidden border-border hover:shadow-lg transition-shadow duration-300 flex flex-col h-full", children: cardContent }) }); return /* @__PURE__ */ jsx(Card, { className: "group overflow-hidden border-border hover:shadow-lg transition-shadow duration-300 flex flex-col", children: cardContent }); } //#endregion //#region src/components/ui/carousel.tsx const CarouselContext = React$1.createContext(null); function useCarousel() { const context = React$1.useContext(CarouselContext); if (!context) throw new Error("useCarousel must be used within a <Carousel />"); return context; } function Carousel({ orientation = "horizontal", opts, setApi, plugins, className, children,...props }) { const [carouselRef, api] = useEmblaCarousel({ ...opts, axis: orientation === "horizontal" ? "x" : "y" }, plugins); const [canScrollPrev, setCanScrollPrev] = React$1.useState(false); const [canScrollNext, setCanScrollNext] = React$1.useState(false); const onSelect = React$1.useCallback((carouselApi) => { if (!carouselApi) return; setCanScrollPrev(carouselApi.canScrollPrev()); setCanScrollNext(carouselApi.canScrollNext()); }, []); const scrollPrev = React$1.useCallback(() => { api?.scrollPrev(); }, [api]); const scrollNext = React$1.useCallback(() => { api?.scrollNext(); }, [api]); const handleKeyDown = React$1.useCallback((event) => { if (event.key === "ArrowLeft") { event.preventDefault(); scrollPrev(); } else if (event.key === "ArrowRight") { event.preventDefault(); scrollNext(); } }, [scrollPrev, scrollNext]); React$1.useEffect(() => { if (!api || !setApi) return; setApi(api); }, [api, setApi]); React$1.useEffect(() => { if (!api) return; onSelect(api); api.on("reInit", onSelect); api.on("select", onSelect); return () => { api?.off("select", onSelect); }; }, [api, onSelect]); return /* @__PURE__ */ jsx(CarouselContext.Provider, { value: { carouselRef, api, opts, orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"), scrollPrev, scrollNext, canScrollPrev, canScrollNext }, children: /* @__PURE__ */ jsx("div", { onKeyDownCapture: handleKeyDown, className: cn("relative", className), role: "region", "aria-roledescription": "carousel", "data-slot": "carousel", ...props, children }) }); } function CarouselContent({ className,...props }) { const { carouselRef, orientation } = useCarousel(); return /* @__PURE__ */ jsx("div", { ref: carouselRef, className: "overflow-hidden", "data-slot": "carousel-content", children: /* @__PURE__ */ jsx("div", { className: cn("flex", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", className), ...props }) }); } function CarouselItem({ className,...props }) { const { orientation } = useCarousel(); return /* @__PURE__ */ jsx("div", { role: "group", "aria-roledescription": "slide", "data-slot": "carousel-item", className: cn("min-w-0 shrink-0 grow-0 basis-full", orientation === "horizontal" ? "pl-4" : "pt-4", className), ...props }); } function CarouselPrevious({ className, variant = "outline", size = "icon",...props }) { const { orientation, scrollPrev, canScrollPrev } = useCarousel(); return /* @__PURE__ */ jsxs(Button, { "data-slot": "carousel-previous", variant, size, className: cn("absolute size-8 rounded-full", orientation === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90", className), disabled: !canScrollPrev, onClick: scrollPrev, ...props, children: [/* @__PURE__ */ jsx(ArrowLeft, {}), /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Previous slide" })] }); } function CarouselNext({ className, variant = "outline", size = "icon",...props }) { const { orientation, scrollNext, canScrollNext } = useCarousel(); return /* @__PURE__ */ jsxs(Button, { "data-slot": "carousel-next", variant, size, className: cn("absolute size-8 rounded-full", orientation === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", className), disabled: !canScrollNext, onClick: scrollNext, ...props, children: [/* @__PURE__ */ jsx(ArrowRight, {}), /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Next slide" })] }); } //#endregion //#region src/components/storefront/product-details.tsx /** * TODO: Product Details Enhancements * * 1. Variant SKU Management * - [ ] Fetch individual variant SKUs based on selected attribute combinations * - [ ] Query SKU-level data when attributes change (size, color, etc.) * - [ ] Update product price and images when variant changes * - [ ] Handle variant-specific media objects * * 2. Inventory Management * - [ ] Check real-time inventory levels for the selected variant * - [ ] Display available quantity or low stock warnings * - [ ] Integrate inventory API calls * - [ ] Cache inventory data with appropriate TTL * * 3. Out of Stock UI * - [ ] Add "Out of Stock" badge when inventory is depleted * - [ ] Disable "Add to Cart" button for out-of-stock items * - [ ] Show "Notify When Available" option for out-of-stock products * - [ ] Display estimated restock date if available * * 4. Unavailable Variant UI * - [ ] Disable attribute options that result in unavailable variants * - [ ] Show strikethrough or greyed-out style for unavailable options * - [ ] Display tooltip explaining why option is unavailable * - [ ] Prevent selection of invalid attribute combinations * - [ ] Show nearest available alternative when variant is unavailable */ function ProductDetailsSkeleton() { return /* @__PURE__ */ jsxs("div", { className: "grid md:grid-cols-2 gap-8 p-4", children: [/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [/* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(Card, { className: "overflow-hidden aspect-square bg-muted border-border", children: /* @__PURE__ */ jsx(Skeleton, { className: "w-full h-full" }) }) }), /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-2", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "aspect-square w-full rounded-md border border-input overflow-hidden", children: /* @__PURE__ */ jsx(Skeleton, { className: "w-full h-full" }) }, i)) })] }), /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [ /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-3/4 mb-4" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-1/3 mb-2" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-1/4" }) ] }), /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-border pt-4", children: [ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }) ] }), /* @__PURE__ */ jsx(Skeleton, { className: "h-11 w-full rounded-md" }), /* @__PURE__ */ jsxs("div", { className: "border-t border-border pt-4 space-y-2", children: [/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" })] }), /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-40" })] })] }) ] })] }); } function ProductDetails({ productId, storeId: storeIdProp, clientId: clientIdProp, onAddToCartSuccess, onAddToCartError }) { const context = useGoDaddyContext(); const { t } = context; const formatCurrency = useFormatCurrency(); const storeId = storeIdProp || context.storeId; const clientId = clientIdProp || context.clientId; const [quantity, setQuantity] = useState(1); const [carouselApi, setCarouselApi] = useState(); const [thumbnailApi, setThumbnailApi] = useState(); const [currentImageIndex, setCurrentImageIndex] = useState(0); const [variantParams, setVariantParamsState] = useState(() => { if (typeof window === "undefined") return {}; const params = new URLSearchParams(window.location.search); const result = {}; params.forEach((value, key) => { result[key] = value; }); return result; }); const { addToCart, isLoading: isAddingToCart } = useAddToCart({ onSuccess: () => { setQuantity(1); onAddToCartSuccess?.(); }, onError: onAddToCartError }); const setVariantParams = useCallback((updates) => { const params = new URLSearchParams(window.location.search); Object.entries(updates).forEach(([key, value]) => { if (value) params.set(key, value); else params.delete(key); }); const newUrl = `${window.location.pathname}?${params.toString()}`; window.history.replaceState({}, "", newUrl); setVariantParamsState((prev) => { const next = { ...prev }; Object.entries(updates).forEach(([key, value]) => { if (value) next[key] = value; else delete next[key]; }); return next; }); }, []); useEffect(() => { const handlePopState = () => { const params = new URLSearchParams(window.location.search); const result = {}; params.forEach((value, key) => { result[key] = value; }); setVariantParamsState(result); }; window.addEventListener("popstate", handlePopState); return () => window.removeEventListener("popstate", handlePopState); }, []); const selectedAttributes = useMemo(() => { const attrs = {}; Object.entries(variantParams).forEach(([key, value]) => { if (value) attrs[key] = value; }); return attrs; }, [variantParams]); const selectedAttributeValues = useMemo(() => { return Object.values(selectedAttributes).filter(Boolean); }, [selectedAttributes]); const { data, isLoading, error } = useQuery({ queryKey: [ "sku-group", storeId, clientId, productId, ...selectedAttributeValues.sort() ], queryFn: () => getSkuGroup({ id: productId, attributeValues: selectedAttributeValues, ...!selectedAttributeValues.length ? { first: 2 } : {} }, storeId, clientId, context?.apiHost), enabled: !!storeId && !!clientId && !!productId, placeholderData: (previousData) => previousData }); const attributesEdges = data?.skuGroup?.attributes?.edges || []; const attributes = useMemo(() => { return attributesEdges.map((edge) => { const attributeNode = edge?.node; const valuesEdges = attributeNode?.values?.edges || []; return { id: attributeNode?.id || "", name: attributeNode?.name || "", label: attributeNode?.label || attributeNode?.name || "", values: valuesEdges.map((valueEdge) => { const valueNode = valueEdge?.node; return { id: valueNode?.id || "", name: valueNode?.name || "", label: valueNode?.label || valueNode?.name || "" }; }) }; }); }, [attributesEdges]); const matchedSkus = data?.skuGroup?.skus?.edges || []; const matchedSkuId = matchedSkus.length === 1 ? matchedSkus[0]?.node?.id : null; const { data: individualSkuData, isLoading: isSkuLoading } = useQuery({ queryKey: [ "individual-sku", storeId, clientId, matchedSkuId ], queryFn: () => getSku({ id: matchedSkuId }, storeId, clientId, context?.apiHost), enabled: !!storeId && !!clientId && !!matchedSkuId }); const selectedSku = individualSkuData?.sku; useEffect(() => { if (!carouselApi) return; const onSelect = () => { const index = carouselApi.selectedScrollSnap(); setCurrentImageIndex(index); if (thumbnailApi) thumbnailApi.scrollTo(index); }; onSelect(); carouselApi.on("select", onSelect); return () => { carouselApi.off("select", onSelect); }; }, [carouselApi, thumbnailApi]); if (isLoading) return /* @__PURE__ */ jsx(ProductDetailsSkeleton, {}); if (error) return /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center text-destructive", children: [ t.storefront.errorLoadingProduct, " ", error.message ] }) }); const product = data?.skuGroup; if (!product) return /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsx("div", { className: "text-center text-muted-foreground", children: t.storefront.productNotFound }) }); const title = product?.label || product?.name || t.storefront.product; const description = product?.description || ""; const htmlDescription = product?.htmlDescription || ""; const skuPrice = selectedSku?.prices?.edges?.[0]?.node; const priceMin = skuPrice?.value?.value ?? product?.priceRange?.min ?? 0; const priceMax = selectedSku ? priceMin : product?.priceRange?.max ?? priceMin; const compareAtMin = skuPrice?.compareAtValue?.value ?? product?.compareAtPriceRange?.min; const compareAtMax = selectedSku ? compareAtMin : product?.compareAtPriceRange?.max; const isOnSale = compareAtMin && compareAtMin > priceMin; const isPriceRange = priceMin !== priceMax; const isCompareAtPriceRange = compareAtMin && compareAtMax && compareAtMin !== compareAtMax; const skuMediaObjects = selectedSku?.mediaObjects?.edges || []; const productMediaObjects = product?.mediaObjects?.edges || []; const images = (skuMediaObjects.length > 0 ? skuMediaObjects : productMediaObjects).filter((edge) => edge?.node?.type === "IMAGE").map((edge) => edge?.node?.url).filter(Boolean); const handleAttributeChange = (attributeName, valueName) => { setVariantParams({ [attributeName]: valueName }); }; const handleQuantityChange = (change) => { setQuantity((prev) => Math.max(1, prev + change)); }; const handleThumbnailClick = (index) => { carouselApi?.scrollTo(index); thumbnailApi?.scrollTo(index); }; const isOutOfStock = selectedSku ? (() => { return (selectedSku.inventoryCounts?.edges?.find((edge) => edge?.node?.type === "AVAILABLE")?.node?.quantity ?? 0) === 0; })() : false; const canAddToCart = !isOutOfStock && Boolean(selectedSku); const handleAddToCart = async () => { if (!canAddToCart) return; await addToCart({ skuId: selectedSku?.id || product?.skus?.edges?.[0]?.node?.id || "", name: title, quantity, productAssetUrl: images[0] || void 0 }); }; return /* @__PURE__ */ jsxs("div", { className: "grid md:grid-cols-2 gap-8 p-4", children: [/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [/* @__PURE__ */ jsxs("div", { className: "relative", children: [isOnSale && /* @__PURE__ */ jsx(Badge, { variant: "accent", className: "absolute top-4 right-4 z-10 font-semibold", children: t.storefront.sale }), /* @__PURE__ */ jsxs(Carousel, { setApi: setCarouselApi, opts: { align: "center", loop: true }, className: "w-full", children: [/* @__PURE__ */ jsx(CarouselContent, { children: images.length > 0 ? images.map((image, index) => /* @__PURE__ */ jsx(CarouselItem, { children: /* @__PURE__ */ jsx(Card, { className: "overflow-hidden aspect-square bg-muted border-border", children: /* @__PURE__ */ jsx("img", { src: image, alt: `${title} - ${index + 1}`, className: "w-full h-full object-cover" }) }) }, index)) : /* @__PURE__ */ jsx(CarouselItem, { children: /* @__PURE__ */ jsx(Card, { className: "overflow-hidden aspect-square bg-muted", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-muted-foreground", children: t.storefront.noImageAvailable }) }) }) }), images.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CarouselPrevious, { className: "left-4" }), /* @__PURE__ */ jsx(CarouselNext, { className: "right-4" })] })] })] }), images.length > 1 && /* @__PURE__ */ jsx(Fragment, { children: images.length <= 4 ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-2", children: images.map((image, index) => /* @__PURE__ */ jsx(Button, { variant: "outline", className: `p-0 h-auto aspect-square overflow-hidden ${currentImageIndex === index ? "ring-2 ring-primary ring-offset-2" : "opacity-60 hover:opacity-100"}`, onClick: () => handleThumbnailClick(index), children: /* @__PURE__ */ jsx("img", { src: image, alt: `${title} - thumbnail ${index + 1}`, className: "w-full h-full object-cover" }) }, index)) }) : /* @__PURE__ */ jsxs(Carousel, { setApi: setThumbnailApi, opts: { align: "start", slidesToScroll: 1, containScroll: "keepSnaps" }, className: "w-full", children: [ /* @__PURE__ */ jsx(CarouselContent, { className: "-ml-2", children: images.map((image, index) => /* @__PURE__ */ jsx(CarouselItem, { className: "pl-2 basis-1/4", children: /* @__PURE__ */ jsx("div", { className: "p-1", children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: `p-0 h-auto aspect-square overflow-hidden w-full ${currentImageIndex === index ? "ring-2 ring-primary ring-offset-2" : "opacity-60 hover:opacity-100"}`, onClick: () => handleThumbnailClick(index), children: /* @__PURE__ */ jsx("img", { src: image, alt: `${title} - thumbnail ${index + 1}`, className: "w-full h-full object-cover" }) }) }) }, index)) }), /* @__PURE__ */ jsx(CarouselPrevious, { className: "-left-4" }), /* @__PURE__ */ jsx(CarouselNext, { className: "-right-4" }) ] }) })] }), /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", { className: "text-3xl font-bold text-foreground mb-2", children: title }), /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-3 mb-4", children: [/* @__PURE__ */ jsx("span", { className: "text-2xl font-bold text-foreground", children: isPriceRange ? `${formatCurrency({ amount: priceMin, currencyCode: "USD", inputInMinorUnits: true })} - ${formatCurrency({ amount: priceMax, currencyCode: "USD", inputInMinorUnits: true })}` : formatCurrency({ amount: priceMin, currencyCode: "USD", inputInMinorUnits: true }) }), isOnSale && compareAtMin && /* @__PURE__ */ jsx("span", { className: "text-lg text-muted-foreground line-through", children: isCompareAtPriceRange ? `${formatCurrency({ amount: compareAtMin, currencyCode: "USD", inputInMinorUnits: true })} - ${formatCurrency({ amount: compareAtMax, currencyCode: "USD", inputInMinorUnits: true })}` : formatCurrency({ amount: compareAtMin, currencyCode: "USD", inputInMinorUnits: true }) })] })] }), htmlDescription || description ? /* @__PURE__ */ jsx("div", { children: htmlDescription ? /* @__PURE__ */ jsx("div", { className: "text-muted-foreground prose prose-sm max-w-none", dangerouslySetInnerHTML: { __html: htmlDescription } }) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }) }) : null, attributes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [attributes.map((attribute) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground mb-2 block", children: attribute.label }), /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: attribute.values.map((value) => /* @__PURE__ */ jsx(Button, { variant: selectedAttributes[attribute.name] === value.name ? "default" : "outline", size: "sm", onClick: () => handleAttributeChange(attribute.name, value.name), className: "min-w-[60px]", children: value.label }, value.id)) })] }, attribute.id)), selectedAttributeValues.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [ isSkuLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [/* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-muted-foreground border-t-transparent" }), t.storefront.loadingVariantDetails] }), !isSkuLoading && matchedSkus.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-destructive", children: t.storefront.combinationNotAvailable }), !isSkuLoading && matchedSkus.length > 1 && /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground", children: [ matchedSkus.length, " ", t.storefront.variantsMatch ] }) ] })] }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground mb-2 block", children: t.storefront.quantity }), /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => handleQuantityChange(-1), disabled: quantity <= 1, children: /* @__PURE__ */ jsx(Minus, { className: "h-4 w-4" }) }), /* @__PURE__ */ jsx("span", { className: "text-lg font-medium min-w-[40px] text-center", children: quantity }), /* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => handleQuantityChange(1), children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) }) ] })] }), /* @__PURE__ */ jsx(Button, { size: "lg", className: "w-full gap-2", onClick: handleAddToCart, disabled: !canAddToCart || isAddingToCart, children: isAddingToCart ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin" }), t.storefront.addingToCart] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ShoppingCart, { className: "h-5 w-5" }), isOutOfStock ? t.storefront.outOfStock : t.storefront.addToCart] }) }), /* @__PURE__ */ jsxs("div", { className: "border-t border-border pt-4 space-y-2", children: [ product?.type && /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-sm", children: [/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t.storefront.productType }), /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: product.type })] }), product?.id && /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-sm", children: [/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t.storefront.productId }), /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-foreground", children: product.id })] }), selectedSku && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-sm", children: [/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t.storefront.selectedSku }), /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-foreground", children: selectedSku.code })] }), selectedSku.inventoryCounts?.edges && selectedSku.inventoryCounts.edges.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-sm", children: [/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t.storefront.stockStatus }), /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: (() => { const availableCount = selectedSku.inventoryCounts.edges.find((edge) => edge?.node?.type === "AVAILABLE")?.node?.quantity ?? 0; if (availableCount === 0) return t.storefront.outOfStock; if (availableCount < 10) return `${t.storefront.lowStock} (${availabl