@fluid-app/portal-sdk 0.1.256 → 0.1.257
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/dist/{MessagingScreen-F_BXHzrq.mjs → MessagingScreen-J59IMCoQ.mjs} +0 -1
- package/dist/{ProfileScreen-DKAZIqb1.mjs → ProfileScreen-BdmW4weg.mjs} +0 -1
- package/dist/{ShareablesScreen-DB_My40b.mjs → ShareablesScreen-C13NYElH.mjs} +0 -2
- package/dist/{ShopScreen-Djlrfd1J.mjs → ShopScreen-BFKwuM2U.mjs} +5 -5
- package/dist/ShopScreen-BFKwuM2U.mjs.map +1 -0
- package/dist/{ShopScreen-IiXAreNF.cjs → ShopScreen-C3PYT1v_.cjs} +5 -5
- package/dist/ShopScreen-C3PYT1v_.cjs.map +1 -0
- package/dist/{ShopScreen-DhB9FK24.mjs → ShopScreen-C_8yLQjA.mjs} +1 -3
- package/dist/{ShopScreen-D7JIowTa.cjs → ShopScreen-Cg_DoIMi.cjs} +1 -1
- package/dist/index.cjs +55 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +61 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
- package/dist/ShopScreen-Djlrfd1J.mjs.map +0 -1
- package/dist/ShopScreen-IiXAreNF.cjs.map +0 -1
|
@@ -5,7 +5,6 @@ import "./store-api-context-C1lXT_3d.mjs";
|
|
|
5
5
|
import "./api-context-3GmfqvIB.mjs";
|
|
6
6
|
import "./mysite-api-context-5-qpKHWA.mjs";
|
|
7
7
|
import "./countries-api-context-CMh13cfX.mjs";
|
|
8
|
-
import "./PortalTenantClientProvider-4ZmY6hac.mjs";
|
|
9
8
|
import "./registry-context-BSJeDI2A.mjs";
|
|
10
9
|
import "./WidgetInteractionContext-I716c8kj.mjs";
|
|
11
10
|
import "./EmbedWidget-DE1yhLR1.mjs";
|
|
@@ -5,7 +5,6 @@ import "./store-api-context-C1lXT_3d.mjs";
|
|
|
5
5
|
import "./api-context-3GmfqvIB.mjs";
|
|
6
6
|
import "./mysite-api-context-5-qpKHWA.mjs";
|
|
7
7
|
import "./countries-api-context-CMh13cfX.mjs";
|
|
8
|
-
import "./PortalTenantClientProvider-4ZmY6hac.mjs";
|
|
9
8
|
import "./registry-context-BSJeDI2A.mjs";
|
|
10
9
|
import "./WidgetInteractionContext-I716c8kj.mjs";
|
|
11
10
|
import "./EmbedWidget-DE1yhLR1.mjs";
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import "./account-api-context-BIZ_aibJ.mjs";
|
|
2
2
|
import "./store-api-context-C1lXT_3d.mjs";
|
|
3
3
|
import "./mysite-api-context-5-qpKHWA.mjs";
|
|
4
|
-
import "./PortalTenantClientProvider-4ZmY6hac.mjs";
|
|
5
4
|
import "./src-B3W228vu.mjs";
|
|
6
5
|
import "./ScreenHeaderContext-kG_zh0zW.mjs";
|
|
7
|
-
import "./AppNavigationContext-Dvc0yoZF.mjs";
|
|
8
6
|
import "./use-mysite-portal-B7N48vKo.mjs";
|
|
9
7
|
import "./dist-PbA1vxAz.mjs";
|
|
10
8
|
import "./es-XtH2D7Cc.mjs";
|
|
@@ -518,7 +518,7 @@ function ProductListing({ companyLogoUrl, renderImage, onSelectProduct, cartButt
|
|
|
518
518
|
children: /* @__PURE__ */ jsx(Button, {
|
|
519
519
|
variant: "outline",
|
|
520
520
|
size: "icon",
|
|
521
|
-
className: "h-9 w-9 shrink-0",
|
|
521
|
+
className: "border-foreground/10 shadow-input h-9 w-9 shrink-0",
|
|
522
522
|
children: /* @__PURE__ */ jsx(ArrowUpDown, { className: "h-3 w-3" })
|
|
523
523
|
})
|
|
524
524
|
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
@@ -1044,15 +1044,15 @@ function CartButton({ onCheckout }) {
|
|
|
1044
1044
|
};
|
|
1045
1045
|
}, [navigateToCheckout, onCheckout]);
|
|
1046
1046
|
return /* @__PURE__ */ jsxs(Button, {
|
|
1047
|
-
className: "bg-primary text-primary-foreground hover:bg-primary/70 relative flex items-center gap-
|
|
1047
|
+
className: "bg-primary text-primary-foreground hover:bg-primary/70 relative flex h-8 items-center gap-2 rounded-sm px-3 py-1 text-xs",
|
|
1048
1048
|
onClick: () => {
|
|
1049
1049
|
window.fluidCart?.open();
|
|
1050
1050
|
},
|
|
1051
1051
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
1052
1052
|
className: "relative",
|
|
1053
|
-
children: [/* @__PURE__ */ jsx(ShoppingCart, { className: "size-
|
|
1053
|
+
children: [/* @__PURE__ */ jsx(ShoppingCart, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
|
|
1054
1054
|
id: "fluid-cart-count",
|
|
1055
|
-
className: "bg-primary-foreground text-primary absolute -top-1 -right-2 flex h-
|
|
1055
|
+
className: "bg-primary-foreground text-primary absolute -top-1 -right-2 flex h-3.5 w-3.5 items-center justify-center rounded-full text-[8px] font-bold",
|
|
1056
1056
|
children: initialCount
|
|
1057
1057
|
})]
|
|
1058
1058
|
}), /* @__PURE__ */ jsx("span", { children: "Cart" })]
|
|
@@ -1292,4 +1292,4 @@ function ProductBreadcrumb({ productId }) {
|
|
|
1292
1292
|
//#endregion
|
|
1293
1293
|
export { shopScreenPropertySchema as n, ShopScreen as t };
|
|
1294
1294
|
|
|
1295
|
-
//# sourceMappingURL=ShopScreen-
|
|
1295
|
+
//# sourceMappingURL=ShopScreen-BFKwuM2U.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShopScreen-BFKwuM2U.mjs","names":["defaultRenderImage"],"sources":["../../../shop/core/src/translation-api-context.ts","../../../shop/ui/src/utils/media-helpers.ts","../../../shop/ui/src/components/product-card.tsx","../../../shop/ui/src/components/image-gallery.tsx","../../../shop/ui/src/components/quantity-selector.tsx","../../../shop/ui/src/components/purchase-options.tsx","../../../shop/ui/src/components/shop-app.tsx","../../../cart/ui/src/components/cart-script.tsx","../../../cart/ui/src/components/cart-widget.tsx","../../../cart/ui/src/components/cart-button.tsx","../../../cart/ui/src/components/shop-container.tsx","../../../shop/core/src/translation-dictionary.ts","../../../shop/core/src/translation-adapter.ts","../src/providers/ShopTranslationBridge.tsx","../src/screens/ShopScreen.tsx"],"sourcesContent":["import { createTranslationContext } from \"@fluid-app/i18n/translation-api-context-factory\";\nimport type { ShopDict } from \"./translation-dictionary\";\n\nconst { Provider, useTranslation } = createTranslationContext<ShopDict>(\"Shop\");\n\nexport const ShopTranslationProvider = Provider;\nexport const useShopTranslation = useTranslation;\n","const VIDEO_EXTENSIONS = [\n \".mp4\",\n \".webm\",\n \".mov\",\n \".avi\",\n \".m4v\",\n \".mkv\",\n \".ogv\",\n \".ogg\",\n \".wmv\",\n \".flv\",\n \".3gp\",\n];\n\nconst IMAGE_EXTENSIONS = [\n \".jpg\",\n \".jpeg\",\n \".png\",\n \".gif\",\n \".webp\",\n \".svg\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".tif\",\n];\n\nexport function isVideoUrl(url: string | undefined | null): boolean {\n if (!url) return false;\n const lowerUrl = url.toLowerCase();\n return VIDEO_EXTENSIONS.some((ext) => lowerUrl.includes(ext));\n}\n\nexport function isImageUrl(url: string | undefined | null): boolean {\n if (!url) return false;\n const lowerUrl = url.toLowerCase();\n return IMAGE_EXTENSIONS.some((ext) => lowerUrl.includes(ext));\n}\n\nexport function getMediaTypeFromUrl(\n url: string | undefined | null,\n): \"video\" | \"image\" | \"unknown\" {\n if (isVideoUrl(url)) return \"video\";\n if (isImageUrl(url)) return \"image\";\n return \"unknown\";\n}\n\nexport function getVideoThumbnailUrl(videoUrl: string): string {\n if (videoUrl.includes(\"ik.imagekit.io\")) {\n return `${videoUrl}/ik-thumbnail.jpg`;\n }\n return videoUrl;\n}\n\nexport function ensureImageIsFeatured<\n T extends { image_url: string; position?: number },\n>(items: T[]): T[] {\n if (items.length === 0) return items;\n\n const firstItem = items[0];\n if (!firstItem || !isVideoUrl(firstItem.image_url)) {\n return items;\n }\n\n const firstImageIndex = items.findIndex(\n (item) => !isVideoUrl(item.image_url),\n );\n\n if (firstImageIndex === -1) {\n return items;\n }\n\n const reordered = [...items];\n const [imageToMove] = reordered.splice(firstImageIndex, 1);\n if (imageToMove) {\n reordered.unshift(imageToMove);\n }\n\n return reordered;\n}\n","import type React from \"react\";\nimport { useState, type ReactNode } from \"react\";\nimport type { products, portalProducts } from \"@fluid-app/products-core\";\nimport {\n determineProductPrice,\n getProductImageUrl,\n} from \"@fluid-app/products-core\";\nimport { Card } from \"@fluid-app/ui-primitives\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\nimport { CirclePlay } from \"lucide-react\";\nimport { getVideoThumbnailUrl, isVideoUrl } from \"../utils/media-helpers\";\n\ntype LegacyProduct = (products.Product | products.ShopProduct) & {\n kind?: string;\n video_url?: string;\n};\n\nexport interface RenderImageProps {\n src: string;\n alt: string;\n fill?: boolean;\n className?: string;\n onError?: (e: React.SyntheticEvent<HTMLImageElement>) => void;\n unoptimized?: boolean;\n}\n\ntype TaggedPortalProduct = portalProducts.Product & {\n readonly __portalProduct: true;\n};\n\nexport type ProductCardProduct = LegacyProduct | TaggedPortalProduct;\n\ninterface ProductCardProps {\n product: ProductCardProduct;\n countryIso?: string;\n companyLogoUrl?: string | null;\n showShareModal?: boolean;\n setShareModalOpen?: (open: boolean) => void;\n setSelectedProduct?: (product: LegacyProduct) => void;\n renderLink?: (props: { href: string; children: ReactNode }) => ReactNode;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onClick?: () => void;\n}\n\nexport function tagPortalProduct(\n product: portalProducts.Product,\n): TaggedPortalProduct {\n return { ...product, __portalProduct: true as const };\n}\n\nfunction isPortalProduct(\n product: ProductCardProduct,\n): product is TaggedPortalProduct {\n return \"__portalProduct\" in product && product.__portalProduct === true;\n}\n\nfunction getPortalProductCoverImage(\n product: portalProducts.Product,\n): string | null {\n if (product.images && product.images.length > 0) {\n return product.images[0]?.url ?? null;\n }\n return null;\n}\n\nfunction defaultRenderImage({\n src,\n alt,\n fill,\n className,\n onError,\n}: RenderImageProps): ReactNode {\n return (\n <img\n src={src}\n alt={alt}\n className={`${fill ? \"absolute inset-0 h-full w-full\" : \"\"} ${className ?? \"\"}`}\n onError={onError}\n />\n );\n}\n\nfunction formatPortalPrice(\n price: string | undefined,\n currency: string | undefined,\n): string | null {\n if (!price) return null;\n const numericPrice = Number(price);\n if (Number.isNaN(numericPrice)) return `${currency ?? \"\"}${price}`;\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency: currency || \"USD\",\n }).format(numericPrice);\n } catch {\n return `$${price}`;\n }\n}\n\nfunction ProductCardContent({\n product,\n countryIso,\n companyLogoUrl,\n renderImage = defaultRenderImage,\n}: {\n product: ProductCardProduct;\n countryIso?: string;\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}) {\n const [isHovered, setIsHovered] = useState(false);\n const { t } = useShopTranslation();\n\n const isPortal = isPortalProduct(product);\n const coverImage = isPortal\n ? getPortalProductCoverImage(product)\n : getProductImageUrl(product as Parameters<typeof getProductImageUrl>[0]);\n const isVideo = isVideoUrl(coverImage);\n const productName = isPortal\n ? product.name || t(\"no_title\")\n : (product as LegacyProduct).title || t(\"no_title\");\n\n let repPrice: string | null | undefined = null;\n let price: string | null | undefined = null;\n if (isPortal) {\n // wholesale_price is non-null for reps, null for customers\n if (product.wholesale_price) {\n repPrice = formatPortalPrice(product.wholesale_price, product.currency);\n // Show retail as strikethrough if different from wholesale\n const retailFormatted = formatPortalPrice(\n product.price,\n product.currency,\n );\n if (retailFormatted !== repPrice) {\n price = retailFormatted;\n }\n } else {\n repPrice = formatPortalPrice(product.price, product.currency);\n }\n } else if (countryIso) {\n const prices = determineProductPrice(product, countryIso);\n repPrice = prices.repPrice;\n price = prices.price;\n }\n\n return (\n <>\n {/* Image/Video container */}\n <div\n className=\"bg-muted relative aspect-square overflow-hidden\"\n onMouseEnter={() => isVideo && setIsHovered(true)}\n onMouseLeave={() => isVideo && setIsHovered(false)}\n >\n {isVideo && isHovered ? (\n <video\n src={coverImage || \"\"}\n className=\"absolute inset-0 h-full w-full object-cover\"\n autoPlay\n muted\n loop\n playsInline\n />\n ) : (\n renderImage({\n src:\n isVideo && coverImage\n ? getVideoThumbnailUrl(coverImage)\n : coverImage ||\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\",\n alt: productName,\n fill: true,\n className:\n \"object-cover transition-transform group-hover:scale-105\",\n onError: (e) => {\n e.currentTarget.src =\n companyLogoUrl ||\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n },\n unoptimized: true,\n })\n )}\n\n {/* Video play indicator */}\n {isVideo && !isHovered && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-full bg-black/50 backdrop-blur-sm\">\n <CirclePlay className=\"h-12 w-12 text-white\" />\n </div>\n </div>\n )}\n </div>\n\n {/* Product info */}\n <div className=\"px-3 pt-2 pb-4\">\n <h3 className=\"text-foreground line-clamp-1 text-sm font-bold\">\n {productName}\n </h3>\n\n <div className=\"flex items-center gap-2\">\n {repPrice && (\n <span className=\"text-foreground text-sm font-bold\">\n {repPrice}\n </span>\n )}\n {price && (\n <span className=\"text-muted-foreground text-sm line-through\">\n {price}\n </span>\n )}\n </div>\n\n {!isPortal && countryIso && (\n <div className=\"text-muted-foreground text-sm\">\n CV{\" \"}\n {getVariantCountryValue(\n getSelectedVariant(product as LegacyProduct),\n countryIso,\n \"cv\",\n ) || \"-\"}{\" \"}\n | QV{\" \"}\n {getVariantCountryValue(\n getSelectedVariant(product as LegacyProduct),\n countryIso,\n \"qv\",\n ) || \"-\"}\n </div>\n )}\n\n {isPortal && product.wholesale_price != null && (\n <div className=\"text-muted-foreground text-sm\">\n CV {product.cv ?? \"-\"} | QV {product.qv ?? \"-\"}\n </div>\n )}\n </div>\n </>\n );\n}\n\nfunction getSelectedVariant(\n product: LegacyProduct,\n): products.Variant | products.ShopVariant | null {\n if (!product.variants || product.variants.length === 0) return null;\n\n const masterVariant = product.variants.find(\n (v: products.Variant | products.ShopVariant) => {\n return \"is_master\" in v && v.is_master;\n },\n );\n if (masterVariant) return masterVariant;\n\n return product.variants[0] || null;\n}\n\nfunction getVariantCountryValue(\n variant: products.Variant | products.ShopVariant | null,\n countryIso: string,\n field: \"cv\" | \"qv\",\n): number | null {\n if (!variant || !variant.variant_countries) return null;\n\n if (\n typeof variant.variant_countries === \"object\" &&\n !Array.isArray(variant.variant_countries)\n ) {\n const countryData = variant.variant_countries[countryIso] as\n | products.VariantCountry\n | undefined;\n return countryData?.[field] ?? null;\n }\n\n if (Array.isArray(variant.variant_countries)) {\n const countryData = variant.variant_countries.find(\n (vc: products.ShopVariantCountry) => vc.country_iso === countryIso,\n );\n return countryData?.[field] ?? null;\n }\n\n return null;\n}\n\nexport default function ProductCard({\n product,\n countryIso,\n companyLogoUrl,\n showShareModal = false,\n setShareModalOpen,\n setSelectedProduct,\n renderLink,\n renderImage,\n onClick,\n}: ProductCardProps): React.JSX.Element {\n const cardContent = (\n <ProductCardContent\n product={product}\n countryIso={countryIso}\n companyLogoUrl={companyLogoUrl}\n renderImage={renderImage}\n />\n );\n\n const cardClassName = \"overflow-hidden border-0 shadow-none pt-0 gap-0\";\n\n if (showShareModal && !isPortalProduct(product)) {\n const handleShareClick = () => {\n if (setSelectedProduct && setShareModalOpen) {\n setSelectedProduct(product);\n setShareModalOpen(true);\n }\n };\n return (\n <Card className={cardClassName}>\n <button\n onClick={handleShareClick}\n className=\"group block w-full cursor-pointer text-left\"\n >\n {cardContent}\n </button>\n </Card>\n );\n }\n\n if (onClick) {\n return (\n <Card className={cardClassName}>\n <button\n onClick={onClick}\n className=\"group block w-full cursor-pointer text-left\"\n >\n {cardContent}\n </button>\n </Card>\n );\n }\n\n const href = `/portal/shop/${product.id}`;\n\n if (renderLink) {\n return (\n <Card className={cardClassName}>\n {renderLink({ href, children: cardContent })}\n </Card>\n );\n }\n\n return (\n <Card className={cardClassName}>\n <a href={href} className=\"group block cursor-pointer\">\n {cardContent}\n </a>\n </Card>\n );\n}\n","import type React from \"react\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { isVideoUrl } from \"../utils/media-helpers\";\nimport type { RenderImageProps } from \"./product-card\";\n\ninterface ImageGalleryProps {\n images: Array<{\n id: number;\n image_url: string;\n image_path: string | null;\n position: number;\n }>;\n fallbackImageUrl: string;\n productTitle: string;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}\n\nfunction defaultRenderImage({\n src,\n alt,\n fill,\n className,\n onError,\n}: RenderImageProps): ReactNode {\n return (\n <img\n src={src}\n alt={alt}\n className={`${fill ? \"absolute inset-0 h-full w-full\" : \"\"} ${className ?? \"\"}`}\n onError={onError}\n />\n );\n}\n\nexport default function ImageGallery({\n images,\n fallbackImageUrl,\n productTitle,\n renderImage = defaultRenderImage,\n}: ImageGalleryProps): React.JSX.Element {\n const [currentImageIndex, setCurrentImageIndex] = useState(0);\n\n const hasMultipleImages = images && images.length > 0;\n const displayImages = useMemo(\n () =>\n hasMultipleImages\n ? [...images].sort((a, b) => a.position - b.position)\n : [{ id: 0, image_url: fallbackImageUrl, position: 0 }],\n [images, hasMultipleImages, fallbackImageUrl],\n );\n\n // Reset to first image when the images array changes (e.g. variant switch)\n useEffect(() => {\n setCurrentImageIndex(0);\n }, [displayImages]);\n\n const nextImage = () => {\n if (displayImages.length > 1) {\n setCurrentImageIndex((prev) => (prev + 1) % displayImages.length);\n }\n };\n\n const prevImage = () => {\n if (displayImages.length > 1) {\n setCurrentImageIndex(\n (prev) => (prev - 1 + displayImages.length) % displayImages.length,\n );\n }\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Main Image */}\n <div className=\"group relative aspect-square overflow-hidden rounded-sm bg-gray-100\">\n {isVideoUrl(displayImages[currentImageIndex]?.image_url) ? (\n <video\n key={displayImages[currentImageIndex]?.id}\n src={displayImages[currentImageIndex]?.image_url}\n className=\"absolute inset-0 h-full w-full object-cover\"\n controls\n loop\n playsInline\n />\n ) : (\n renderImage({\n src:\n displayImages[currentImageIndex]?.image_url || fallbackImageUrl,\n alt: productTitle,\n fill: true,\n className: \"object-cover\",\n onError: (e) => {\n e.currentTarget.src =\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\";\n },\n unoptimized: true,\n })\n )}\n\n {/* Navigation arrows */}\n {displayImages.length > 1 && (\n <>\n <button\n type=\"button\"\n aria-label=\"Previous image\"\n className=\"absolute top-1/2 left-3 z-10 flex h-8 w-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/40 shadow-sm transition-colors group-hover:bg-white/80 hover:bg-white\"\n onClick={prevImage}\n >\n <ChevronLeft className=\"h-5 w-5 text-black/40 transition-colors group-hover:text-black\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Next image\"\n className=\"absolute top-1/2 right-3 z-10 flex h-8 w-8 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-white/40 shadow-sm transition-colors group-hover:bg-white/80 hover:bg-white\"\n onClick={nextImage}\n >\n <ChevronRight className=\"h-5 w-5 text-black/40 transition-colors group-hover:text-black\" />\n </button>\n </>\n )}\n\n {/* Page Indicators - Bottom Center */}\n {displayImages.length > 1 && (\n <div className=\"absolute bottom-3 left-1/2 flex -translate-x-1/2 gap-3\">\n {displayImages.map((_, index) => (\n <button\n type=\"button\"\n key={index}\n className={`h-1.5 w-6 cursor-pointer rounded-lg transition-colors ${\n index === currentImageIndex\n ? \"bg-gray-800\"\n : \"bg-gray-400 hover:bg-gray-600\"\n }`}\n aria-label={`Go to image ${index + 1}`}\n onClick={() => setCurrentImageIndex(index)}\n />\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\n\ninterface QuantitySelectorProps {\n quantity: number;\n setQuantity: (quantity: number) => void;\n}\n\nexport default function QuantitySelector({\n quantity,\n setQuantity,\n}: QuantitySelectorProps): React.JSX.Element {\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center rounded-lg\">\n <Button\n variant=\"default\"\n onClick={() => setQuantity(Math.max(1, quantity - 1))}\n className=\"border-0 px-3 py-2 shadow-none\"\n >\n −\n </Button>\n <span className=\"text-foreground min-w-8 px-4 py-2 text-center text-sm font-medium\">\n {quantity}\n </span>\n <Button\n variant=\"default\"\n onClick={() => setQuantity(quantity + 1)}\n className=\"border-0 px-3 py-2 shadow-none\"\n >\n +\n </Button>\n </div>\n </div>\n );\n}\n","import type React from \"react\";\nimport { useMemo } from \"react\";\nimport type { products } from \"@fluid-app/products-core\";\nimport {\n RadioGroup,\n RadioGroupItem,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@fluid-app/ui-primitives\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\n\ninterface PurchaseOptionsProps {\n showBuyOnce: boolean;\n showSubscribe: boolean;\n isSubscribe: boolean;\n onSubscribeChange: (subscribe: boolean) => void;\n product_subscription_plans: products.ProductSubscriptionPlan[];\n selectedSubscriptionPlan?: products.ProductSubscriptionPlan;\n onSubscriptionPlanChange?: (plan: products.ProductSubscriptionPlan) => void;\n userSelectedSubscribe: boolean;\n wholesalePrice?: number;\n wholesaleSubscriptionPrice?: number;\n}\n\nexport default function PurchaseOptions({\n showBuyOnce,\n showSubscribe,\n isSubscribe,\n onSubscribeChange,\n product_subscription_plans,\n selectedSubscriptionPlan,\n onSubscriptionPlanChange,\n userSelectedSubscribe,\n wholesalePrice,\n wholesaleSubscriptionPrice,\n}: PurchaseOptionsProps): React.JSX.Element | null {\n const { t } = useShopTranslation();\n\n // Find default subscription plan or use first one\n const defaultSubscriptionPlan = useMemo(() => {\n if (!product_subscription_plans?.length) return null;\n return (\n product_subscription_plans.find((plan) => plan.default) ||\n product_subscription_plans[0]\n );\n }, [product_subscription_plans]);\n\n // Use selected plan or default to the default/first plan\n const currentSubscriptionPlan =\n selectedSubscriptionPlan || defaultSubscriptionPlan;\n\n const savingsPercentage = useMemo(() => {\n if (\n wholesalePrice === undefined ||\n wholesaleSubscriptionPrice === undefined ||\n wholesalePrice <= 0\n ) {\n return null;\n }\n\n const savings = wholesalePrice - wholesaleSubscriptionPrice;\n if (savings <= 0) return null;\n\n const pct = Math.round((savings / wholesalePrice) * 100);\n return pct > 0 ? pct : null;\n }, [wholesalePrice, wholesaleSubscriptionPrice]);\n\n // Format subscription plan display name\n const formatSubscriptionPlan = (plan: products.ProductSubscriptionPlan) => {\n const interval = plan.subscription_plan.billing_interval;\n const unit = plan.subscription_plan.billing_interval_unit;\n return `${interval} ${unit} (${plan.subscription_plan.name})`;\n };\n\n // Prepare subscription plan options for Select component\n const subscriptionPlanOptions = useMemo(() => {\n if (!product_subscription_plans?.length) return [];\n\n return product_subscription_plans.map((plan) => ({\n value: plan.subscription_plan.id.toString(),\n label: formatSubscriptionPlan(plan),\n }));\n }, [product_subscription_plans]);\n\n // Don't render if only buy once is shown and subscribe is false\n if (showBuyOnce && !showSubscribe) {\n return null;\n }\n const handleSubscriptionPlanChange = (planId: string) => {\n const selectedPlan = product_subscription_plans?.find(\n (plan) => plan.subscription_plan.id.toString() === planId,\n );\n if (selectedPlan && onSubscriptionPlanChange) {\n onSubscriptionPlanChange(selectedPlan);\n }\n };\n\n const purchaseType = isSubscribe ? \"subscribe\" : \"once\";\n\n return (\n <div className=\"mb-3 flex flex-col\">\n <RadioGroup\n value={purchaseType}\n onValueChange={(value) => onSubscribeChange(value === \"subscribe\")}\n className=\"gap-0 space-y-0\"\n >\n {showSubscribe && (\n <div\n onClick={() => onSubscribeChange(true)}\n className={`cursor-pointer rounded-t-lg p-4 text-left transition-all duration-200 ${showBuyOnce ? \"border border-b-0\" : \"rounded-b-lg\"} ${userSelectedSubscribe ? \"bg-muted\" : \"bg-background\"}`}\n >\n <div className=\"flex items-start space-x-3\">\n <RadioGroupItem\n value=\"subscribe\"\n onClick={(e) => e.stopPropagation()}\n className={`flex items-center justify-center [&_svg]:h-1.5 [&_svg]:w-1.5 [&_svg]:fill-white ${\n isSubscribe\n ? \"border-contrast bg-contrast text-foreground\"\n : \"border-border bg-background text-muted-foreground\"\n }`}\n />\n <div className=\"flex-1\">\n <div className=\"text-foreground mb-1 text-sm leading-tight font-medium\">\n {savingsPercentage\n ? t(\"subscribe_and_save\", {\n percentage: savingsPercentage,\n })\n : t(\"subscribe\")}\n </div>\n\n {/* Subscription Plan Dropdown*/}\n {product_subscription_plans?.length > 0 && (\n <div\n className=\"bg-muted mt-3 rounded-lg p-2\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"text-foreground mb-1 text-xs font-medium\">\n {t(\"delivery_frequency\")}\n </div>\n <Select\n value={\n currentSubscriptionPlan?.subscription_plan.id.toString() ||\n \"\"\n }\n onValueChange={handleSubscriptionPlanChange}\n disabled={product_subscription_plans?.length === 1}\n >\n <SelectTrigger className=\"bg-background text-foreground! w-full text-xs\">\n <SelectValue\n placeholder={t(\"select_delivery_schedule\")}\n />\n </SelectTrigger>\n <SelectContent>\n {subscriptionPlanOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n {showBuyOnce && (\n <div\n onClick={() => onSubscribeChange(false)}\n className={`cursor-pointer rounded-b-lg border p-4 text-left transition-all duration-200 ${showSubscribe ? \"border-t-0\" : \"rounded-t-lg\"} ${!userSelectedSubscribe ? \"bg-muted\" : \"bg-background\"}`}\n >\n <div className=\"flex items-center space-x-3\">\n <RadioGroupItem\n value=\"once\"\n onClick={(e) => e.stopPropagation()}\n className={`flex items-center justify-center [&_svg]:h-1.5 [&_svg]:w-1.5 [&_svg]:fill-white ${\n !isSubscribe\n ? \"border-contrast bg-contrast text-foreground\"\n : \"border-border bg-background text-muted-foreground\"\n }`}\n />\n <div className=\"text-foreground mb-1 text-sm leading-tight font-medium\">\n {t(\"one_time_purchase\")}\n </div>\n </div>\n </div>\n )}\n </RadioGroup>\n </div>\n );\n}\n","import type React from \"react\";\nimport {\n useState,\n useMemo,\n useEffect,\n useRef,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport {\n usePortalProductCatalog,\n usePortalProductDetail,\n type PortalProductPageParam,\n type portalProducts,\n type products,\n} from \"@fluid-app/products-core\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n} from \"@fluid-app/ui-primitives\";\nimport { SearchSort } from \"@fluid-app/ui-components/components/SearchSort\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\nimport { ArrowUpDown, ShoppingCart } from \"lucide-react\";\nimport ProductCard, {\n tagPortalProduct,\n type RenderImageProps,\n} from \"./product-card\";\nimport ImageGallery from \"./image-gallery\";\nimport QuantitySelector from \"./quantity-selector\";\nimport PurchaseOptions from \"./purchase-options\";\n\ninterface ShopAppProps {\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n /** When provided, controls which product detail to show (URL-driven routing) */\n productId?: string | null;\n /** Called when a product is selected from the listing */\n onSelectProduct?: (productId: string) => void;\n /** Called when user navigates back from product detail */\n onBack?: () => void;\n /** Optional cart button to render in the header area */\n cartButton?: ReactNode;\n}\n\nconst PAGE_SIZE = 25;\n\nfunction sanitizeHtml(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n for (const el of doc.querySelectorAll(\n \"script, iframe, object, embed, form, base, meta, link, style\",\n )) {\n el.remove();\n }\n for (const el of doc.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n if (\n attr.name.toLowerCase().startsWith(\"on\") ||\n attr.value.toLowerCase().trim().startsWith(\"javascript:\")\n ) {\n el.removeAttribute(attr.name);\n }\n }\n }\n return doc.body.innerHTML;\n}\n\nconst GRID_CLASS =\n \"grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4\";\n\nfunction SkeletonGrid({ count = 8 }: { count?: number }) {\n return (\n <div className={GRID_CLASS}>\n {Array.from({ length: count }, (_, i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n <Skeleton className=\"h-3 w-1/3\" />\n </div>\n ))}\n </div>\n );\n}\n\nfunction ProductListing({\n companyLogoUrl,\n renderImage,\n onSelectProduct,\n cartButton,\n}: {\n companyLogoUrl?: string | null;\n renderImage?: (props: RenderImageProps) => ReactNode;\n onSelectProduct: (productId: string) => void;\n cartButton?: ReactNode;\n}) {\n const observerTarget = useRef<HTMLDivElement>(null);\n const { t } = useShopTranslation();\n\n const catalog = usePortalProductCatalog({ perPage: PAGE_SIZE });\n\n const {\n data,\n isLoading,\n isFetchingNextPage,\n hasNextPage,\n fetchNextPage,\n error,\n isFetched,\n } = useInfiniteQuery({\n queryKey: catalog.queryKey,\n queryFn: ({ pageParam }) => catalog.fetchProducts(pageParam),\n getNextPageParam: catalog.getNextPageParam,\n initialPageParam: undefined as PortalProductPageParam,\n });\n\n const handleIntersect = useCallback(\n (entries: IntersectionObserverEntry[]) => {\n if (entries[0]?.isIntersecting && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n useEffect(() => {\n const target = observerTarget.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(handleIntersect, {\n threshold: 0.1,\n rootMargin: \"200px\",\n });\n observer.observe(target);\n return () => observer.disconnect();\n }, [handleIntersect]);\n\n const allProducts = data?.pages.flatMap((page) => page.products) ?? [];\n\n const sortOptions = useMemo(\n () => [\n { id: \"title_asc\", label: t(\"sort_title_asc\") },\n { id: \"title_desc\", label: t(\"sort_title_desc\") },\n { id: \"price_asc\", label: t(\"sort_price_asc\") },\n { id: \"price_desc\", label: t(\"sort_price_desc\") },\n { id: \"created_at_desc\", label: t(\"sort_recent\") },\n { id: \"created_at_asc\", label: t(\"sort_oldest\") },\n ],\n [t],\n );\n\n return (\n <div>\n <div className=\"mx-auto px-2 md:px-10\">\n {/* Search + Sort */}\n <div className=\"flex items-center justify-end gap-2 py-4\">\n <div className=\"w-full max-w-sm\">\n <SearchSort\n searchValue={catalog.searchTerm}\n onSearchChange={catalog.setSearchTerm}\n placeholder={t(\"search_placeholder\")}\n />\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n className=\"border-foreground/10 shadow-input h-9 w-9 shrink-0\"\n >\n <ArrowUpDown className=\"h-3 w-3\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-60\">\n <DropdownMenuLabel>{t(\"sort_by\")}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={catalog.currentSort}\n onValueChange={catalog.setCurrentSort}\n >\n {sortOptions.map((opt) => (\n <DropdownMenuRadioItem key={opt.id} value={opt.id}>\n {opt.label}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n {cartButton && (\n <div className=\"flex shrink-0 items-center gap-3\">{cartButton}</div>\n )}\n </div>\n </div>\n\n {/* Product Grid */}\n <div className=\"mx-auto space-y-8 px-2 md:px-10 md:py-8\">\n {isLoading ? (\n <SkeletonGrid />\n ) : error ? (\n <p className=\"mx-auto my-6 rounded-lg bg-red-100 px-3 py-2 text-red-500\">\n {t(\"error_generic\")}\n </p>\n ) : isFetched && allProducts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-muted-foreground text-sm\">\n {catalog.searchTerm\n ? t(\"no_search_results\", { term: catalog.searchTerm })\n : t(\"no_products\")}\n </p>\n </div>\n ) : (\n <>\n <div className={GRID_CLASS}>\n {allProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={tagPortalProduct(product)}\n companyLogoUrl={companyLogoUrl}\n renderImage={renderImage}\n onClick={() => onSelectProduct(String(product.id))}\n />\n ))}\n </div>\n <div ref={observerTarget} />\n {isFetchingNextPage && <SkeletonGrid count={4} />}\n </>\n )}\n </div>\n </div>\n );\n}\n\ninterface OptionGroup {\n optionId: number;\n name: string;\n values: { id: number; name: string }[];\n}\n\n/**\n * Build the variant options map by grouping option_values across non-master variants.\n * Groups by option_id, collecting unique value entries.\n */\nfunction buildOptionGroups(variants: portalProducts.Variant[]): OptionGroup[] {\n const groupMap = new Map<\n number,\n { name: string; values: Map<number, string> }\n >();\n\n for (const variant of variants) {\n if (variant.is_master) continue;\n for (const ov of variant.option_values ?? []) {\n if (ov.option_id == null || ov.id == null) continue;\n if (!groupMap.has(ov.option_id)) {\n groupMap.set(ov.option_id, {\n name: ov.option_name ?? `Option ${ov.option_id}`,\n values: new Map(),\n });\n }\n groupMap.get(ov.option_id)!.values.set(ov.id, ov.name ?? String(ov.id));\n }\n }\n\n return [...groupMap.entries()].map(([optionId, group]) => ({\n optionId,\n name: group.name,\n values: [...group.values.entries()].map(([id, name]) => ({ id, name })),\n }));\n}\n\n/**\n * Find the variant matching a set of selected option value IDs.\n * selections is a map of optionId → selected value id.\n */\nfunction findVariantBySelections(\n variants: portalProducts.Variant[],\n selections: Record<number, number>,\n): portalProducts.Variant | undefined {\n const entries = Object.entries(selections).map(\n ([k, v]) => [Number(k), v] as const,\n );\n if (entries.length === 0) return undefined;\n return variants.find(\n (v) =>\n !v.is_master &&\n entries.every(([optionId, valueId]) =>\n v.option_values?.some(\n (ov) => ov.option_id === optionId && ov.id === valueId,\n ),\n ),\n );\n}\n\n/**\n * Map BFF flat subscription plans to the legacy ProductSubscriptionPlan shape\n * expected by the PurchaseOptions component.\n */\nfunction mapToLegacySubscriptionPlans(\n plans: portalProducts.SubscriptionPlan[],\n): products.ProductSubscriptionPlan[] {\n return plans.map((plan, idx) => ({\n id: plan.id,\n default: idx === 0,\n products_count: null,\n subscribers_count: null,\n active: true,\n subscription_plan: {\n id: plan.id ?? 0,\n name: plan.name ?? \"\",\n billing_interval: plan.billing_interval ?? 1,\n billing_interval_unit: plan.billing_interval_unit ?? \"month\",\n billing_frequency_in_words:\n plan.billing_frequency ??\n `${plan.billing_interval} ${plan.billing_interval_unit}`,\n active: true,\n price_adjustment_amount: null,\n price_adjustment_type: null,\n },\n }));\n}\n\nfunction formatPrice(price: string | undefined, currency: string | undefined) {\n if (!price) return null;\n const numericPrice = Number(price);\n if (Number.isNaN(numericPrice)) return `${currency ?? \"\"}${price}`;\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency: currency || \"USD\",\n }).format(numericPrice);\n } catch {\n return `$${price}`;\n }\n}\n\nfunction ProductDetail({\n productId,\n renderImage,\n}: {\n productId: string;\n renderImage?: (props: RenderImageProps) => ReactNode;\n}) {\n const [quantity, setQuantity] = useState(1);\n // selections: optionId → selected value id\n const [selections, setSelections] = useState<Record<number, number>>({});\n const [userSelectedSubscribe, setUserSelectedSubscribe] = useState(false);\n const [selectedSubscriptionPlan, setSelectedSubscriptionPlan] = useState<\n products.ProductSubscriptionPlan | undefined\n >(undefined);\n\n const { t } = useShopTranslation();\n\n const { product, isLoading, error, images } = usePortalProductDetail({\n productId,\n });\n\n const variants = useMemo(() => product?.variants ?? [], [product?.variants]);\n const subscriptionPlans = useMemo(\n () => product?.subscription_plans ?? [],\n [product?.subscription_plans],\n );\n\n // Default to the master variant, or the first available variant\n const masterVariant = useMemo(\n () => variants.find((v) => v.is_master) ?? variants[0],\n [variants],\n );\n\n // Build option groups from non-master variants\n const optionGroups = useMemo(() => buildOptionGroups(variants), [variants]);\n\n // Title-based fallback: products that distinguish variants by `title` only\n // (no Options/OptionValues set up) — list every variant by title.\n const showVariantChoices = optionGroups.length === 0 && variants.length > 1;\n const [selectedVariantId, setSelectedVariantId] = useState<number | null>(\n null,\n );\n\n // Initialise selections from the first non-master variant (or master fallback)\n useEffect(() => {\n if (optionGroups.length === 0) return;\n const firstNonMaster = variants.find((v) => !v.is_master);\n const source = firstNonMaster ?? masterVariant;\n if (!source?.option_values?.length) return;\n const defaults: Record<number, number> = {};\n for (const ov of source.option_values) {\n if (ov.option_id != null && ov.id != null) {\n defaults[ov.option_id] = ov.id;\n }\n }\n setSelections(defaults);\n }, [optionGroups, variants, masterVariant]);\n\n // Default the title-based picker to the master variant\n useEffect(() => {\n if (!showVariantChoices) return;\n if (selectedVariantId != null) return;\n if (masterVariant?.id == null) return;\n setSelectedVariantId(masterVariant.id);\n }, [showVariantChoices, selectedVariantId, masterVariant?.id]);\n\n // Resolve the currently selected variant\n const selectedVariant = useMemo(() => {\n if (showVariantChoices) {\n return variants.find((v) => v.id === selectedVariantId) ?? masterVariant;\n }\n if (optionGroups.length === 0) return masterVariant;\n return findVariantBySelections(variants, selections) ?? masterVariant;\n }, [\n variants,\n optionGroups,\n selections,\n masterVariant,\n showVariantChoices,\n selectedVariantId,\n ]);\n\n // Map BFF subscription plans → legacy ProductSubscriptionPlan shape for PurchaseOptions\n const legacySubscriptionPlans = useMemo(\n () => mapToLegacySubscriptionPlans(subscriptionPlans),\n [subscriptionPlans],\n );\n\n // Derive subscription state from variant\n const isSubscribe =\n selectedVariant?.subscription_only === true || userSelectedSubscribe;\n const showSubscribe = legacySubscriptionPlans.length > 0;\n const showBuyOnce = selectedVariant?.subscription_only !== true;\n\n // Auto-select subscribe when variant is subscription-only\n useEffect(() => {\n if (\n selectedVariant?.subscription_only &&\n legacySubscriptionPlans.length > 0\n ) {\n setUserSelectedSubscribe(true);\n }\n }, [selectedVariant, legacySubscriptionPlans]);\n\n // Prefer variant-specific images when available, fall back to product-level images\n const galleryImages = useMemo(() => {\n const variantImages = selectedVariant?.images;\n if (variantImages && variantImages.length > 0) {\n return variantImages.map((img, idx) => ({\n id: idx,\n image_url: img.url ?? \"\",\n image_path: null as string | null,\n position: idx,\n }));\n }\n return images.map((img, idx) => ({\n id: img.id ?? idx,\n image_url: img.url,\n image_path: null as string | null,\n position: idx,\n }));\n }, [selectedVariant?.images, images]);\n\n const coverImage = galleryImages[0]?.image_url ?? null;\n\n // Pricing: use selected variant's subscription_pricing for wholesale comparison\n const selectedPricing = selectedVariant?.subscription_pricing?.find(\n (sp) =>\n sp.plan_id ===\n (selectedSubscriptionPlan?.subscription_plan.id ??\n legacySubscriptionPlans[0]?.subscription_plan.id),\n );\n const wholesalePrice = selectedVariant?.wholesale_price\n ? Number(selectedVariant.wholesale_price)\n : undefined;\n const wholesaleSubscriptionPrice = selectedPricing?.wholesale_price\n ? Number(selectedPricing.wholesale_price)\n : undefined;\n\n // Display prices\n // price = retail price, wholesale_price = rep/logged-in price\n // subscription_pricing.wholesale_price = subscription wholesale price\n const currency = selectedVariant?.currency ?? product?.currency;\n const displayPrice = formatPrice(\n selectedVariant?.price ?? product?.price,\n currency,\n );\n const displayWholesalePrice = formatPrice(\n selectedVariant?.wholesale_price ??\n selectedVariant?.price ??\n product?.price,\n currency,\n );\n const displayWholesaleSubscriptionPrice = formatPrice(\n selectedPricing?.wholesale_price ??\n selectedPricing?.price ??\n selectedVariant?.wholesale_price ??\n undefined,\n currency,\n );\n\n // Resolve subscription plan ID for the cart SDK\n const resolvedPlanId =\n selectedSubscriptionPlan?.subscription_plan.id ??\n legacySubscriptionPlans.find((p) => p.default)?.subscription_plan.id ??\n legacySubscriptionPlans[0]?.subscription_plan.id;\n\n // Cart data attributes — SDK expects variant ID, not product ID\n const cartVariantId = String(selectedVariant?.id ?? product?.id ?? \"\");\n\n if (isLoading) {\n return (\n <div className=\"mx-auto max-w-7xl py-8 pr-4 pl-0 md:pr-6 lg:pr-8 lg:pl-0\">\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-2\">\n <Skeleton className=\"aspect-square w-full rounded-lg\" />\n <div className=\"space-y-4 pl-2 lg:pl-20\">\n <Skeleton className=\"h-8 w-3/4\" />\n <Skeleton className=\"h-5 w-1/4\" />\n <Skeleton className=\"h-20 w-full\" />\n <Skeleton className=\"h-10 w-1/2\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"text-center\">\n <h3 className=\"text-foreground mb-2 text-lg font-medium\">\n {t(\"error_loading\")}\n </h3>\n <p className=\"text-muted-foreground\">{t(\"error_generic\")}</p>\n </div>\n </div>\n );\n }\n\n if (!product) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"text-center\">\n <h3 className=\"text-foreground mb-2 text-lg font-medium\">\n {t(\"product_not_found\")}\n </h3>\n <p className=\"text-muted-foreground\">\n {t(\"product_not_found_description\")}\n </p>\n </div>\n </div>\n );\n }\n\n const title = product.name || t(\"product_fallback_name\");\n const isBundle = product.bundle === true;\n const bundleUrl =\n isBundle && product.canonical_url ? product.canonical_url : null;\n\n return (\n <div className=\"pb-5 md:pl-8\">\n <div className=\"mx-auto max-w-7xl px-4 py-8 md:pr-6 md:pl-0 lg:pr-8 lg:pl-0\">\n <div className=\"grid grid-cols-1 gap-5 lg:grid-cols-2\">\n {/* Image Gallery */}\n <ImageGallery\n images={galleryImages}\n fallbackImageUrl={\n coverImage ??\n \"https://assets.fluid.app/fluid-admin/images/we-commerce/we-commerce.png\"\n }\n productTitle={title}\n renderImage={renderImage}\n />\n\n {/* Product Info */}\n <div className=\"max-w-lg pl-2 lg:pl-20\">\n <h1 className=\"text-foreground text-3xl font-bold\">{title}</h1>\n\n {/* Price — matches admin exactly */}\n {!isBundle && (\n <div className=\"mb-2 flex items-center gap-2\">\n <span className=\"text-foreground text-sm\">\n {isSubscribe\n ? displayWholesaleSubscriptionPrice\n : displayWholesalePrice}\n </span>\n {((isSubscribe &&\n displayWholesaleSubscriptionPrice !==\n displayWholesalePrice) ||\n (!isSubscribe && displayWholesalePrice !== displayPrice)) && (\n <span className=\"text-muted-foreground text-sm line-through\">\n {isSubscribe ? displayWholesalePrice : displayPrice}\n </span>\n )}\n </div>\n )}\n\n {/* Product Description */}\n <div className=\"pt-2\">\n <h3 className=\"text-foreground mb-1 text-sm font-medium\">\n {t(\"product_description\")}\n </h3>\n <div\n className=\"text-foreground mb-3 text-[12px]\"\n dangerouslySetInnerHTML={{\n __html: sanitizeHtml(product.description ?? \"\"),\n }}\n />\n </div>\n\n {product.wholesale_price != null && (\n <div className=\"text-muted-foreground mb-3 text-sm\">\n CV {selectedVariant?.cv ?? \"-\"} | QV{\" \"}\n {selectedVariant?.qv ?? \"-\"}\n </div>\n )}\n\n {isBundle ? (\n <div className=\"pt-4\">\n <Button\n variant=\"default\"\n className=\"w-full gap-2 py-2 text-base font-medium\"\n disabled={!bundleUrl}\n onClick={() => {\n if (bundleUrl)\n window.open(bundleUrl, \"_blank\", \"noopener,noreferrer\");\n }}\n >\n {t(\"purchase_bundle\")}\n </Button>\n {!bundleUrl && (\n <p className=\"text-muted-foreground mt-2 text-center text-xs\">\n {t(\"bundle_unavailable\")}\n </p>\n )}\n </div>\n ) : (\n <>\n {/* Purchase Options — matches admin PurchaseOptions component */}\n <PurchaseOptions\n showBuyOnce={showBuyOnce}\n showSubscribe={showSubscribe}\n isSubscribe={isSubscribe}\n userSelectedSubscribe={userSelectedSubscribe}\n onSubscribeChange={setUserSelectedSubscribe}\n wholesalePrice={wholesalePrice}\n wholesaleSubscriptionPrice={wholesaleSubscriptionPrice}\n product_subscription_plans={legacySubscriptionPlans}\n selectedSubscriptionPlan={selectedSubscriptionPlan}\n onSubscriptionPlanChange={setSelectedSubscriptionPlan}\n />\n\n {/* Variant Options — matches admin layout */}\n {optionGroups.length > 0 && (\n <div className=\"mb-4 pt-4\">\n {optionGroups.map((group) => (\n <div\n key={group.optionId}\n className=\"mb-3 flex items-center\"\n >\n <h3 className=\"text-md text-foreground w-24 font-bold\">\n {group.name.charAt(0).toUpperCase() +\n group.name.slice(1)}\n </h3>\n <Select\n value={String(selections[group.optionId] ?? \"\")}\n onValueChange={(value) =>\n setSelections((prev) => ({\n ...prev,\n [group.optionId]: Number(value),\n }))\n }\n >\n <SelectTrigger className=\"w-48 max-w-full\">\n <SelectValue\n placeholder={t(\"select_option\", {\n name: group.name,\n })}\n />\n </SelectTrigger>\n <SelectContent position=\"popper\" className=\"max-h-60\">\n {group.values.map((v) => (\n <SelectItem key={v.id} value={String(v.id)}>\n {v.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ))}\n </div>\n )}\n\n {/* Title-based variant picker — for products without Options */}\n {showVariantChoices && (\n <div className=\"mb-4 pt-4\">\n <div className=\"mb-3 flex items-center\">\n <h3 className=\"text-md text-foreground w-24 font-bold\">\n Variant\n </h3>\n <Select\n value={\n selectedVariantId != null\n ? String(selectedVariantId)\n : \"\"\n }\n onValueChange={(value) =>\n setSelectedVariantId(Number(value))\n }\n >\n <SelectTrigger className=\"w-48 max-w-full\">\n <SelectValue placeholder=\"Select variant\" />\n </SelectTrigger>\n <SelectContent position=\"popper\" className=\"max-h-60\">\n {variants.map((v, idx) =>\n v.id == null ? null : (\n <SelectItem key={v.id} value={String(v.id)}>\n {v.title || `Variant ${idx + 1}`}\n </SelectItem>\n ),\n )}\n </SelectContent>\n </Select>\n </div>\n </div>\n )}\n\n {/* Unavailable product message */}\n {selectedVariant?.subscription_only &&\n !selectedVariant?.allow_subscription && (\n <div className=\"text-muted-foreground text-sm\">\n {t(\"product_unavailable\")}\n </div>\n )}\n\n {/* Quantity and Add to Cart — matches admin */}\n <div className=\"mt-4 mb-3\" />\n <div className=\"flex items-center gap-3 pb-3\">\n <QuantitySelector\n quantity={quantity}\n setQuantity={setQuantity}\n />\n\n <Button\n variant=\"default\"\n className=\"flex-1 gap-2 py-2 text-base font-medium\"\n disabled={\n selectedVariant?.subscription_only === true &&\n !selectedVariant?.allow_subscription\n }\n data-fluid-add-to-cart={cartVariantId}\n data-fluid-quantity={quantity}\n data-fluid-subscribe={isSubscribe}\n data-fluid-subscription-plan-id={\n isSubscribe ? String(resolvedPlanId ?? \"\") : \"\"\n }\n data-fluid-open-cart-after-add=\"false\"\n >\n <ShoppingCart className=\"h-4 w-4\" />\n {isSubscribe ? t(\"subscribe\") : t(\"add_to_cart\")}\n </Button>\n </div>\n </>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nexport default function ShopApp({\n companyLogoUrl,\n renderImage,\n productId: controlledProductId,\n onSelectProduct: onSelectProductProp,\n onBack: _onBack,\n cartButton,\n}: ShopAppProps): React.JSX.Element {\n // Internal state used only when navigation is not controlled externally\n const [internalProductId, setInternalProductId] = useState<string | null>(\n null,\n );\n\n const isControlled = controlledProductId !== undefined;\n const activeProductId = isControlled\n ? controlledProductId\n : internalProductId;\n\n const handleSelectProduct = onSelectProductProp ?? setInternalProductId;\n\n if (activeProductId) {\n return (\n <ProductDetail productId={activeProductId} renderImage={renderImage} />\n );\n }\n\n return (\n <ProductListing\n companyLogoUrl={companyLogoUrl}\n renderImage={renderImage}\n onSelectProduct={handleSelectProduct}\n cartButton={cartButton}\n />\n );\n}\n","import { useEffect, useRef } from \"react\";\n\ninterface CartScriptProps {\n subdomain: string;\n authJwt?: string;\n /** Enable BFF mode — uses portal session cookies instead of JWT for cart auth. */\n bffMode?: boolean;\n /** Override the SDK script URL (e.g. \"http://localhost:4444/index.js\" for local dev). */\n scriptSrc?: string;\n /** Override the API base URL the SDK uses (e.g. \"http://localhost:3000\" for local dev). */\n apiBaseUrl?: string;\n /** Enable SDK debug logging. */\n debug?: boolean;\n}\n\nconst SCRIPT_ID = \"fluid-cdn-script\";\nconst LEAD_CAPTURE_ID = \"fluid-lead-capture-suppress\";\nconst DEFAULT_SCRIPT_SRC =\n \"https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js\";\n\nexport default function CartScript({\n subdomain,\n authJwt,\n bffMode,\n scriptSrc,\n apiBaseUrl,\n debug,\n}: CartScriptProps): React.ReactNode {\n // Use a ref so the script is injected once with the initial values.\n // ES modules are cached by URL — re-inserting the same script won't\n // re-execute it, so changing props after the first load has no effect.\n const authJwtRef = useRef(authJwt);\n authJwtRef.current = authJwt;\n\n useEffect(() => {\n if (!subdomain) return;\n\n // Don't add a duplicate script\n if (document.getElementById(SCRIPT_ID)) return;\n\n const script = document.createElement(\"script\");\n script.id = SCRIPT_ID;\n script.src = scriptSrc ?? DEFAULT_SCRIPT_SRC;\n script.type = \"module\";\n script.crossOrigin = \"anonymous\";\n script.dataset.fluidShop = subdomain;\n if (bffMode) {\n script.dataset.bffMode = \"true\";\n } else if (authJwtRef.current) {\n script.dataset.authJwt = authJwtRef.current;\n }\n if (apiBaseUrl) {\n script.dataset.fluidApiBaseUrl = apiBaseUrl;\n }\n if (debug) {\n script.dataset.debug = \"true\";\n }\n document.head.appendChild(script);\n\n // Suppress the SDK's auto-injected lead capture widget.\n // The SDK skips injection when it finds an existing element with hide-widget.\n const leadCapture = document.createElement(\"fluid-lead-capture-widget\");\n leadCapture.id = LEAD_CAPTURE_ID;\n leadCapture.setAttribute(\"hide-widget\", \"true\");\n document.body.appendChild(leadCapture);\n\n return () => {\n const existing = document.getElementById(SCRIPT_ID);\n if (existing) existing.remove();\n const existingLeadCapture = document.getElementById(LEAD_CAPTURE_ID);\n if (existingLeadCapture) existingLeadCapture.remove();\n };\n }, [subdomain, bffMode, scriptSrc, apiBaseUrl, debug]);\n\n return null;\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface CartWidgetProps {\n theme?: Record<string, string>;\n}\n\nexport default function CartWidget({\n theme,\n}: CartWidgetProps): React.ReactNode {\n const [mounted, setMounted] = useState(false);\n const widgetRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n useEffect(() => {\n if (!mounted) return;\n const el = widgetRef.current;\n if (!el) return;\n if (theme) {\n el.setAttribute(\"theme\", JSON.stringify(theme));\n } else {\n el.removeAttribute(\"theme\");\n }\n }, [theme, mounted]);\n\n const widget = React.createElement(\"fluid-cart-widget\", {\n ref: (el: HTMLElement | null) => {\n widgetRef.current = el;\n },\n \"data-fluid-widget\": \"true\",\n \"hide-widget\": \"true\",\n \"is-primary\": \"true\",\n });\n\n // Portal to document.body so the cart drawer escapes any\n // overflow-hidden / isolation stacking contexts in the layout.\n if (mounted) {\n return createPortal(widget, document.body);\n }\n\n return null;\n}\n","\"use client\";\n\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { ShoppingCart } from \"lucide-react\";\nimport React, { useEffect, useCallback, useState } from \"react\";\n\ndeclare global {\n interface Window {\n FluidCommerceSDK?: {\n getCheckoutUrl: () => string;\n setOnCheckout: (callback: () => void) => void;\n };\n FairShareSDK?: {\n getCartItemCount: () => number;\n isBffMode: () => boolean;\n updateLocaleSettings: (options: {\n language?: string;\n country?: string;\n }) => Promise<void>;\n };\n fluidCart?: {\n open: () => void;\n };\n }\n}\n\ninterface CartButtonProps {\n onCheckout?: (checkoutUrl: string) => void;\n}\n\nconst MAX_SDK_POLL_ATTEMPTS = 50; // 5 seconds at 100ms intervals\n\nexport function CartButton({ onCheckout }: CartButtonProps): React.ReactNode {\n const [initialCount, setInitialCount] = useState(0);\n\n const navigateToCheckout = useCallback(() => {\n if (!window.FluidCommerceSDK) {\n console.error(\"FluidCommerceSDK not available\");\n return;\n }\n\n try {\n const checkoutUrl = window.FluidCommerceSDK.getCheckoutUrl();\n if (!checkoutUrl) {\n console.error(\"No checkout URL available\");\n return;\n }\n onCheckout?.(checkoutUrl);\n } catch (error) {\n console.error(\"Error getting checkout URL:\", error);\n }\n }, [onCheckout]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let attempts = 0;\n let cancelled = false;\n\n const setupSDK = () => {\n if (cancelled) return;\n if (window.FluidCommerceSDK) {\n if (onCheckout) {\n window.FluidCommerceSDK.setOnCheckout(navigateToCheckout);\n }\n const count = window.FairShareSDK?.getCartItemCount?.();\n if (count != null) {\n setInitialCount(count);\n }\n } else if (attempts < MAX_SDK_POLL_ATTEMPTS) {\n attempts++;\n timeoutId = setTimeout(setupSDK, 100);\n }\n };\n\n setupSDK();\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n };\n }, [navigateToCheckout, onCheckout]);\n\n return (\n <Button\n className=\"bg-primary text-primary-foreground hover:bg-primary/70 relative flex h-8 items-center gap-2 rounded-sm px-3 py-1 text-xs\"\n onClick={() => {\n window.fluidCart?.open();\n }}\n >\n <div className=\"relative\">\n <ShoppingCart className=\"size-4\" />\n <span\n id=\"fluid-cart-count\"\n className=\"bg-primary-foreground text-primary absolute -top-1 -right-2 flex h-3.5 w-3.5 items-center justify-center rounded-full text-[8px] font-bold\"\n >\n {initialCount}\n </span>\n </div>\n <span>Cart</span>\n </Button>\n );\n}\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\n\ninterface ShopContainerProps {\n children: React.ReactNode;\n className?: string;\n cartScript?: React.ReactNode;\n cartWidget?: React.ReactNode;\n}\n\nexport default function ShopContainer({\n children,\n className = \"\",\n cartScript,\n cartWidget,\n}: ShopContainerProps): React.ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(\n null,\n );\n\n useEffect(() => {\n const currentContainer = containerRef.current;\n if (!currentContainer) return;\n\n const reactContentWrapper = document.createElement(\"div\");\n reactContentWrapper.id = \"react-content-wrapper\";\n reactContentWrapper.style.cssText = `\n position: relative;\n `;\n reactContentWrapper.className = \"min-h-full\";\n\n currentContainer.appendChild(reactContentWrapper);\n\n setPortalContainer(reactContentWrapper);\n\n return () => {\n if (currentContainer && reactContentWrapper) {\n try {\n currentContainer.removeChild(reactContentWrapper);\n } catch (e) {\n console.warn(\"Failed to cleanup isolated container:\", e);\n }\n }\n setPortalContainer(null);\n };\n }, []);\n\n return (\n <>\n <div\n ref={containerRef}\n className={`isolated-shop-wrapper ${className} h-full`}\n >\n {portalContainer &&\n createPortal(\n <>\n {cartScript}\n {cartWidget}\n {children}\n </>,\n portalContainer,\n )}\n </div>\n </>\n );\n}\n","export const shopEn = {\n breadcrumb: \"Shop\",\n product_fallback_name: \"Product\",\n search_placeholder: \"Search products...\",\n sort_by: \"Sort By\",\n sort_title_asc: \"Title (A-Z)\",\n sort_title_desc: \"Title (Z-A)\",\n sort_price_asc: \"Price (Low to High)\",\n sort_price_desc: \"Price (High to Low)\",\n sort_recent: \"Recently Updated\",\n sort_oldest: \"Oldest Updated\",\n no_products: \"There are no products available at the moment.\",\n no_search_results:\n 'No products match \"{{term}}\". Try a different search term.',\n error_loading: \"Unable to load product\",\n error_generic: \"Something went wrong. Please try again later.\",\n product_not_found: \"Product not found\",\n product_not_found_description:\n \"The product you're looking for doesn't exist.\",\n product_description: \"Product Description\",\n no_title: \"No title available\",\n add_to_cart: \"Add to Cart\",\n subscribe: \"Subscribe\",\n subscribe_and_save: \"Subscribe & Save {{percentage}}%\",\n one_time_purchase: \"One-Time Purchase\",\n delivery_frequency: \"Delivery Frequency:\",\n select_delivery_schedule: \"Select delivery schedule\",\n purchase_bundle: \"Purchase Bundle\",\n bundle_unavailable:\n \"Bundle configuration is unavailable. Please contact support.\",\n product_unavailable: \"This product is unavailable for purchase.\",\n ratings_and_reviews: \"Ratings and reviews\",\n ratings_count: \"{{count}} ratings\",\n select_option: \"Select {{name}}\",\n} as const satisfies Record<string, string>;\n\nexport type ShopDict = typeof shopEn;\n","import {\n createDomainTranslations,\n type DomainTranslations,\n} from \"@fluid-app/i18n/translations\";\nimport { createStaticDictAdapter } from \"@fluid-app/i18n/static-dict-adapter\";\nimport type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport { shopEn, type ShopDict } from \"./translation-dictionary\";\n\nexport const shopDomain: DomainTranslations<ShopDict> =\n createDomainTranslations<ShopDict>({\n fallback: shopEn,\n loaders: {\n de: () =>\n import(\"./locale/de.json\").then((m): ShopDict => m.default as ShopDict),\n el: () =>\n import(\"./locale/el.json\").then((m): ShopDict => m.default as ShopDict),\n es: () =>\n import(\"./locale/es.json\").then((m): ShopDict => m.default as ShopDict),\n fr: () =>\n import(\"./locale/fr.json\").then((m): ShopDict => m.default as ShopDict),\n he: () =>\n import(\"./locale/he.json\").then((m): ShopDict => m.default as ShopDict),\n hu: () =>\n import(\"./locale/hu.json\").then((m): ShopDict => m.default as ShopDict),\n id: () =>\n import(\"./locale/id.json\").then((m): ShopDict => m.default as ShopDict),\n it: () =>\n import(\"./locale/it.json\").then((m): ShopDict => m.default as ShopDict),\n ja: () =>\n import(\"./locale/ja.json\").then((m): ShopDict => m.default as ShopDict),\n ko: () =>\n import(\"./locale/ko.json\").then((m): ShopDict => m.default as ShopDict),\n nl: () =>\n import(\"./locale/nl.json\").then((m): ShopDict => m.default as ShopDict),\n pl: () =>\n import(\"./locale/pl.json\").then((m): ShopDict => m.default as ShopDict),\n pt: () =>\n import(\"./locale/pt.json\").then((m): ShopDict => m.default as ShopDict),\n ro: () =>\n import(\"./locale/ro.json\").then((m): ShopDict => m.default as ShopDict),\n ru: () =>\n import(\"./locale/ru.json\").then((m): ShopDict => m.default as ShopDict),\n th: () =>\n import(\"./locale/th.json\").then((m): ShopDict => m.default as ShopDict),\n tl: () =>\n import(\"./locale/tl.json\").then((m): ShopDict => m.default as ShopDict),\n tr: () =>\n import(\"./locale/tr.json\").then((m): ShopDict => m.default as ShopDict),\n zh_CN: () =>\n import(\"./locale/zh_CN.json\").then(\n (m): ShopDict => m.default as ShopDict,\n ),\n zh_TW: () =>\n import(\"./locale/zh_TW.json\").then(\n (m): ShopDict => m.default as ShopDict,\n ),\n },\n });\n\nexport function createShopTranslationAdapter(\n locale: string,\n dict: ShopDict,\n): TranslationApi<ShopDict> {\n return createStaticDictAdapter(locale, dict);\n}\n","import { useMemo, type ReactNode } from \"react\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useDomainDict } from \"@fluid-app/i18n/use-domain-dict\";\nimport {\n shopDomain,\n createShopTranslationAdapter,\n} from \"@fluid-app/shop-core/translation-adapter\";\nimport { ShopTranslationProvider } from \"@fluid-app/shop-core/translation-api-context\";\n\nexport function ShopTranslationBridge({\n children,\n}: {\n children: ReactNode;\n}): React.JSX.Element {\n const { locale } = useActiveLocale();\n const dict = useDomainDict(shopDomain, locale);\n const api = useMemo(\n () => createShopTranslationAdapter(locale, dict),\n [locale, dict],\n );\n return (\n <ShopTranslationProvider value={api}>{children}</ShopTranslationProvider>\n );\n}\n","import { type ComponentProps, useEffect, useMemo } from \"react\";\nimport ShopApp from \"@fluid-app/shop-ui/components/shop-app\";\nimport {\n CartButton,\n CartScript,\n CartWidget,\n ShopContainer,\n} from \"@fluid-app/cart-ui\";\nimport {\n Breadcrumb,\n BreadcrumbList,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@fluid-app/ui-primitives\";\nimport {\n useScreenHeaderBreadcrumbs,\n useScreenHeaderActions,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\nimport { ShopTranslationBridge } from \"../providers/ShopTranslationBridge\";\nimport type {\n BackgroundValue,\n BorderRadiusOptions,\n ColorOptions,\n PaddingOptions,\n} from \"../types\";\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\nimport { useFluidContext } from \"../providers/FluidProvider\";\nimport { useAppNavigation } from \"../shell/AppNavigationContext\";\nimport { useStore } from \"../hooks/use-store\";\nimport { PortalProductsApiProvider } from \"../products/PortalProductsApiProvider\";\nimport { usePortalProductDetail } from \"@fluid-app/products-core\";\n\ntype ShopScreenProps = ComponentProps<\"div\"> & {\n background?: BackgroundValue;\n textColor?: ColorOptions;\n accentColor?: ColorOptions;\n padding?: PaddingOptions;\n borderRadius?: BorderRadiusOptions;\n};\n\nexport function ShopScreen(props: ShopScreenProps): React.JSX.Element {\n return (\n <ShopTranslationBridge>\n <ShopScreenContent {...props} />\n </ShopTranslationBridge>\n );\n}\n\nfunction ShopScreenContent({\n /* eslint-disable @typescript-eslint/no-unused-vars -- destructured to exclude from divProps spread */\n background,\n textColor,\n accentColor,\n padding,\n borderRadius,\n /* eslint-enable @typescript-eslint/no-unused-vars */\n ...divProps\n}: ShopScreenProps): React.JSX.Element {\n const { config } = useFluidContext();\n const { t } = useShopTranslation();\n const { data: store } = useStore();\n const { currentSlug, navigate } = useAppNavigation();\n const countryCode = config.countryIso ?? \"US\";\n const subdomain = store?.subdomain;\n\n // Parse product ID from slug: \"shop/{productId}\"\n const parts = currentSlug.split(\"/\");\n const productId = parts[1] ?? null;\n\n const headerBreadcrumbs = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n {productId ? (\n <BreadcrumbLink\n onClick={() => navigate(\"shop\")}\n className=\"cursor-pointer font-semibold\"\n >\n {t(\"breadcrumb\")}\n </BreadcrumbLink>\n ) : (\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"breadcrumb\")}\n </BreadcrumbPage>\n )}\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [productId, navigate, t],\n );\n useScreenHeaderBreadcrumbs(headerBreadcrumbs);\n\n const headerActions = useMemo(\n () => (\n <div className=\"flex items-center gap-4\">\n <CartButton />\n </div>\n ),\n [],\n );\n useScreenHeaderActions(headerActions);\n\n // Sync country to FairShare SDK so the cart uses the correct country.\n // The SDK loads asynchronously via a script tag and also initializes its\n // session asynchronously after the object appears on window. Poll until\n // updateLocaleSettings resolves successfully (Sentry fix: FLUID-ADMIN-1FD —\n // SDK object present does not mean session is ready).\n useEffect(() => {\n if (!countryCode) return;\n type FairShareWindow = {\n FairShareSDK?: {\n updateLocaleSettings: (opts: { country: string }) => Promise<void>;\n };\n };\n const sdk = () => (window as FairShareWindow).FairShareSDK;\n let done = false;\n\n let inFlight = false;\n const tryUpdate = async () => {\n const fairShareSdk = sdk();\n if (done || inFlight || !fairShareSdk) return;\n inFlight = true;\n try {\n await fairShareSdk.updateLocaleSettings({ country: countryCode });\n done = true;\n clearInterval(id);\n } catch {\n // Session not yet initialized — keep polling until it is\n } finally {\n inFlight = false;\n }\n };\n\n let attempts = 0;\n const id = setInterval(() => {\n if (attempts >= 50) {\n console.warn(\n \"[FairShare] updateLocaleSettings: gave up after 50 attempts — session never became ready\",\n { country: countryCode },\n );\n clearInterval(id);\n return;\n }\n attempts++;\n void tryUpdate();\n }, 100);\n\n return () => {\n done = true;\n clearInterval(id);\n };\n }, [countryCode]);\n\n // TODO(portal-theme): Cart widget theming requires AppShell to expose\n // resolvedTheme via context. Currently it's local state in AppShell.\n\n return (\n <div {...divProps} className={divProps.className ?? \"\"}>\n <ShopContainer\n cartScript={\n subdomain ? (\n <CartScript\n subdomain={subdomain}\n bffMode\n scriptSrc={\n import.meta.env.DEV\n ? String(\n import.meta.env.VITE_FAIRSHARE_SDK_URL ||\n \"http://localhost:4444/index.js\",\n )\n : undefined\n }\n apiBaseUrl={\n import.meta.env.DEV\n ? String(\n import.meta.env.VITE_FAIRSHARE_API_BASE_URL ||\n \"http://api.fluid.localhost:3000\",\n )\n : undefined\n }\n debug={import.meta.env.DEV}\n />\n ) : null\n }\n cartWidget={<CartWidget />}\n >\n <PortalProductsApiProvider>\n {productId && <ProductBreadcrumb productId={productId} />}\n <ShopApp\n companyLogoUrl={store?.logo_url ?? undefined}\n productId={productId}\n onSelectProduct={(id) => navigate(`shop/${id}`)}\n onBack={() => navigate(\"shop\")}\n />\n </PortalProductsApiProvider>\n </ShopContainer>\n </div>\n );\n}\n\nexport const shopScreenPropertySchema: WidgetPropertySchema = {\n widgetType: \"ShopScreen\",\n displayName: \"Shop Screen\",\n tabsConfig: [{ id: \"styling\", label: \"Styling\" }],\n fields: [],\n} as const satisfies WidgetPropertySchema;\n\n/**\n * Renders inside PortalProductsCoreProvider to set breadcrumbs with the\n * product name: \"Shop > {product name}\". Overrides the parent's \"Shop\"\n * breadcrumb once product data loads.\n */\nfunction ProductBreadcrumb({ productId }: { productId: string }) {\n const { navigate } = useAppNavigation();\n const { t } = useShopTranslation();\n const { product } = usePortalProductDetail({ productId });\n const productName = product?.name ?? t(\"product_fallback_name\");\n\n const breadcrumb = useMemo(\n () => (\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbLink\n onClick={() => navigate(\"shop\")}\n className=\"cursor-pointer font-semibold\"\n >\n {t(\"breadcrumb\")}\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {productName}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n ),\n [productName, navigate, t],\n );\n useScreenHeaderBreadcrumbs(breadcrumb);\n\n return null;\n}\n"],"mappings":"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"}
|
|
@@ -520,7 +520,7 @@ function ProductListing({ companyLogoUrl, renderImage, onSelectProduct, cartButt
|
|
|
520
520
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_src.Button, {
|
|
521
521
|
variant: "outline",
|
|
522
522
|
size: "icon",
|
|
523
|
-
className: "h-9 w-9 shrink-0",
|
|
523
|
+
className: "border-foreground/10 shadow-input h-9 w-9 shrink-0",
|
|
524
524
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUpDown, { className: "h-3 w-3" })
|
|
525
525
|
})
|
|
526
526
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.DropdownMenuContent, {
|
|
@@ -1046,15 +1046,15 @@ function CartButton({ onCheckout }) {
|
|
|
1046
1046
|
};
|
|
1047
1047
|
}, [navigateToCheckout, onCheckout]);
|
|
1048
1048
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_src.Button, {
|
|
1049
|
-
className: "bg-primary text-primary-foreground hover:bg-primary/70 relative flex items-center gap-
|
|
1049
|
+
className: "bg-primary text-primary-foreground hover:bg-primary/70 relative flex h-8 items-center gap-2 rounded-sm px-3 py-1 text-xs",
|
|
1050
1050
|
onClick: () => {
|
|
1051
1051
|
window.fluidCart?.open();
|
|
1052
1052
|
},
|
|
1053
1053
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1054
1054
|
className: "relative",
|
|
1055
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ShoppingCart, { className: "size-
|
|
1055
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ShoppingCart, { className: "size-4" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1056
1056
|
id: "fluid-cart-count",
|
|
1057
|
-
className: "bg-primary-foreground text-primary absolute -top-1 -right-2 flex h-
|
|
1057
|
+
className: "bg-primary-foreground text-primary absolute -top-1 -right-2 flex h-3.5 w-3.5 items-center justify-center rounded-full text-[8px] font-bold",
|
|
1058
1058
|
children: initialCount
|
|
1059
1059
|
})]
|
|
1060
1060
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Cart" })]
|
|
@@ -1305,4 +1305,4 @@ Object.defineProperty(exports, "shopScreenPropertySchema", {
|
|
|
1305
1305
|
}
|
|
1306
1306
|
});
|
|
1307
1307
|
|
|
1308
|
-
//# sourceMappingURL=ShopScreen-
|
|
1308
|
+
//# sourceMappingURL=ShopScreen-C3PYT1v_.cjs.map
|