@fluid-app/portal-sdk 0.1.322 → 0.1.324

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/{AddressAutocompleteInput-BpbBARKk.mjs → AddressAutocompleteInput-CYWqjVql.mjs} +2 -2
  2. package/dist/{AddressAutocompleteInput-BpbBARKk.mjs.map → AddressAutocompleteInput-CYWqjVql.mjs.map} +1 -1
  3. package/dist/{AddressAutocompleteInput-DkFs_leA.cjs → AddressAutocompleteInput-DfwaaK0l.cjs} +2 -2
  4. package/dist/{AddressAutocompleteInput-DkFs_leA.cjs.map → AddressAutocompleteInput-DfwaaK0l.cjs.map} +1 -1
  5. package/dist/{ContactsScreen-DiGrOlwx.cjs → ContactsScreen-BYrRJ55W.cjs} +2 -2
  6. package/dist/{ContactsScreen-FELUSAZ_.mjs → ContactsScreen-D3_LQj6x.mjs} +2 -2
  7. package/dist/{ContactsScreen-FELUSAZ_.mjs.map → ContactsScreen-D3_LQj6x.mjs.map} +1 -1
  8. package/dist/{ContactsScreen-Cf0ShCew.cjs → ContactsScreen-Dm8b4SyY.cjs} +2 -2
  9. package/dist/{ContactsScreen-Cf0ShCew.cjs.map → ContactsScreen-Dm8b4SyY.cjs.map} +1 -1
  10. package/dist/{FluidProvider-BIeO1i4r.cjs → FluidProvider-BQjPCP_2.cjs} +4 -4
  11. package/dist/{FluidProvider-BIeO1i4r.cjs.map → FluidProvider-BQjPCP_2.cjs.map} +1 -1
  12. package/dist/{FluidProvider-LqejfvZ-.mjs → FluidProvider-DU-sFsZV.mjs} +4 -4
  13. package/dist/{FluidProvider-LqejfvZ-.mjs.map → FluidProvider-DU-sFsZV.mjs.map} +1 -1
  14. package/dist/{MessagingScreen-CQNNI4RN.cjs → MessagingScreen-CqxyLDp0.cjs} +4 -4
  15. package/dist/{MessagingScreen-BRcAo9ux.mjs → MessagingScreen-D33O66bU.mjs} +2 -2
  16. package/dist/{MessagingScreen-BRcAo9ux.mjs.map → MessagingScreen-D33O66bU.mjs.map} +1 -1
  17. package/dist/{MessagingScreen-BBkqLkcx.cjs → MessagingScreen-XtYpo1RQ.cjs} +2 -2
  18. package/dist/{MessagingScreen-BBkqLkcx.cjs.map → MessagingScreen-XtYpo1RQ.cjs.map} +1 -1
  19. package/dist/{MessagingScreen-y3AG96Gi.mjs → MessagingScreen-a58Uccvz.mjs} +4 -4
  20. package/dist/{OrdersScreen-BgcQi5Re.cjs → OrdersScreen-BAvQ38bA.cjs} +4 -4
  21. package/dist/{OrdersScreen-Do7YtBiu.mjs → OrdersScreen-BIjN8-_y.mjs} +2 -2
  22. package/dist/{OrdersScreen-Do7YtBiu.mjs.map → OrdersScreen-BIjN8-_y.mjs.map} +1 -1
  23. package/dist/{OrdersScreen-DISwVljS.mjs → OrdersScreen-BmwgXoqb.mjs} +4 -4
  24. package/dist/{OrdersScreen-CZIlrF_M.cjs → OrdersScreen-CgYMIsbc.cjs} +2 -2
  25. package/dist/{OrdersScreen-CZIlrF_M.cjs.map → OrdersScreen-CgYMIsbc.cjs.map} +1 -1
  26. package/dist/{ProfileScreen-Chk8YkMW.mjs → ProfileScreen--fDK5YsG.mjs} +3 -3
  27. package/dist/{ProfileScreen-Chk8YkMW.mjs.map → ProfileScreen--fDK5YsG.mjs.map} +1 -1
  28. package/dist/{ProfileScreen-DZFMc4E9.mjs → ProfileScreen-BIb54IBH.mjs} +5 -5
  29. package/dist/{ProfileScreen-C3zOvEhX.cjs → ProfileScreen-Bhl7VJj1.cjs} +3 -3
  30. package/dist/{ProfileScreen-C3zOvEhX.cjs.map → ProfileScreen-Bhl7VJj1.cjs.map} +1 -1
  31. package/dist/{ProfileScreen-CxOJ1D3H.cjs → ProfileScreen-DsYyPXQj.cjs} +5 -5
  32. package/dist/{ShopScreen-uOMi-jFD.mjs → ShopScreen-B4_2T5Nd.mjs} +2 -2
  33. package/dist/{ShopScreen-uOMi-jFD.mjs.map → ShopScreen-B4_2T5Nd.mjs.map} +1 -1
  34. package/dist/{ShopScreen-CjrEStJc.cjs → ShopScreen-CPTXTr7p.cjs} +2 -2
  35. package/dist/{ShopScreen-CjrEStJc.cjs.map → ShopScreen-CPTXTr7p.cjs.map} +1 -1
  36. package/dist/{ShopScreen-C4vXayCp.mjs → ShopScreen-CvZGzOUd.mjs} +4 -4
  37. package/dist/{ShopScreen-DM9jxl3w.cjs → ShopScreen-DVLxDFpf.cjs} +4 -4
  38. package/dist/{SubscriptionsScreen-CyL91O4w.cjs → SubscriptionsScreen-7ZH1s3eK.cjs} +21 -20
  39. package/dist/SubscriptionsScreen-7ZH1s3eK.cjs.map +1 -0
  40. package/dist/{SubscriptionsScreen-u_q5j115.mjs → SubscriptionsScreen-BcIUZf50.mjs} +21 -20
  41. package/dist/SubscriptionsScreen-BcIUZf50.mjs.map +1 -0
  42. package/dist/{SubscriptionsScreen-93eTwtFE.cjs → SubscriptionsScreen-CVHJ08si.cjs} +5 -5
  43. package/dist/{SubscriptionsScreen-8cPR9vpX.mjs → SubscriptionsScreen-CwseYMy5.mjs} +5 -5
  44. package/dist/{ToDoWidget-sLWwJJ_g.mjs → ToDoWidget-CShnzkw4.mjs} +3 -3
  45. package/dist/ToDoWidget-CShnzkw4.mjs.map +1 -0
  46. package/dist/{ToDoWidget-C9qX7raE.cjs → ToDoWidget-CevskOb8.cjs} +2 -2
  47. package/dist/{ToDoWidget-CImAWF3O.cjs → ToDoWidget-DA_kLqvP.cjs} +3 -3
  48. package/dist/ToDoWidget-DA_kLqvP.cjs.map +1 -0
  49. package/dist/index.cjs +22 -22
  50. package/dist/index.mjs +22 -22
  51. package/dist/{task-composer-form-ChwUNk1I.mjs → task-composer-form-CHyQZUCo.mjs} +3 -2
  52. package/dist/{task-composer-form-ChwUNk1I.mjs.map → task-composer-form-CHyQZUCo.mjs.map} +1 -1
  53. package/dist/{task-composer-form-DnRjQmkv.cjs → task-composer-form-D9KhWUI1.cjs} +3 -2
  54. package/dist/{task-composer-form-DnRjQmkv.cjs.map → task-composer-form-D9KhWUI1.cjs.map} +1 -1
  55. package/package.json +14 -14
  56. package/dist/SubscriptionsScreen-CyL91O4w.cjs.map +0 -1
  57. package/dist/SubscriptionsScreen-u_q5j115.mjs.map +0 -1
  58. package/dist/ToDoWidget-CImAWF3O.cjs.map +0 -1
  59. package/dist/ToDoWidget-sLWwJJ_g.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ShopScreen-CjrEStJc.cjs","names":["useShopTranslation","isVideoUrl","ChevronLeft","ChevronRight","Button","useShopTranslation","RadioGroup","RadioGroupItem","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Skeleton","useShopTranslation","usePortalProductCatalog","SearchSort","DropdownMenu","DropdownMenuTrigger","Button","ArrowUpDown","DropdownMenuContent","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuRadioGroup","DropdownMenuRadioItem","ProductCard","tagPortalProduct","usePortalProductDetail","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","ShoppingCart","React","useShopTranslation","Button","ShoppingCart","useFluidContext","useShopTranslation","useStore","useAppNavigation","ScreenHeaderBreadcrumbs","Breadcrumb","BreadcrumbList","BreadcrumbItem","BreadcrumbPage","ScreenHeaderActions","usePortalProductDetail","BreadcrumbLink","BreadcrumbSeparator"],"sources":["../../../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","../src/screens/ShopScreen.tsx"],"sourcesContent":["import type React from \"react\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\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 { t } = useShopTranslation();\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={t(\"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={t(\"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={t(\"go_to_image\", { index: String(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, signal }) =>\n catalog.fetchProducts(pageParam, signal),\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 !== undefined && { companyLogoUrl })}\n {...(renderImage !== undefined && { 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 ...(plan.id !== undefined && { 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 !== undefined && { 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 !== undefined && { wholesalePrice })}\n {...(wholesaleSubscriptionPrice !== undefined && {\n wholesaleSubscriptionPrice,\n })}\n product_subscription_plans={legacySubscriptionPlans}\n {...(selectedSubscriptionPlan !== undefined && {\n selectedSubscriptionPlan,\n })}\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 {t(\"variant_label\")}\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={t(\"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 ||\n t(\"variant_fallback_name\", {\n index: String(idx + 1),\n })}\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\n productId={activeProductId}\n {...(renderImage !== undefined && { renderImage })}\n />\n );\n }\n\n return (\n <ProductListing\n {...(companyLogoUrl !== undefined && { companyLogoUrl })}\n {...(renderImage !== undefined && { 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\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\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 { t } = useShopTranslation();\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>{t(\"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","import { type ComponentProps, useEffect } 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 ScreenHeaderBreadcrumbs,\n ScreenHeaderActions,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\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 { 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 <ShopScreenContent {...props} />;\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 // 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 <>\n {!productId && (\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n )}\n <ScreenHeaderActions>\n <div className=\"flex items-center gap-4\">\n <CartButton />\n </div>\n </ScreenHeaderActions>\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 {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 </ShopContainer>\n </div>\n </>\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 return (\n <ScreenHeaderBreadcrumbs>\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 </ScreenHeaderBreadcrumbs>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ShopScreen-CPTXTr7p.cjs","names":["useShopTranslation","isVideoUrl","ChevronLeft","ChevronRight","Button","useShopTranslation","RadioGroup","RadioGroupItem","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Skeleton","useShopTranslation","usePortalProductCatalog","SearchSort","DropdownMenu","DropdownMenuTrigger","Button","ArrowUpDown","DropdownMenuContent","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuRadioGroup","DropdownMenuRadioItem","ProductCard","tagPortalProduct","usePortalProductDetail","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","ShoppingCart","React","useShopTranslation","Button","ShoppingCart","useFluidContext","useShopTranslation","useStore","useAppNavigation","ScreenHeaderBreadcrumbs","Breadcrumb","BreadcrumbList","BreadcrumbItem","BreadcrumbPage","ScreenHeaderActions","usePortalProductDetail","BreadcrumbLink","BreadcrumbSeparator"],"sources":["../../../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","../src/screens/ShopScreen.tsx"],"sourcesContent":["import type React from \"react\";\nimport { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\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 { t } = useShopTranslation();\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={t(\"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={t(\"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={t(\"go_to_image\", { index: String(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, signal }) =>\n catalog.fetchProducts(pageParam, signal),\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 !== undefined && { companyLogoUrl })}\n {...(renderImage !== undefined && { 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 ...(plan.id !== undefined && { 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 !== undefined && { 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 !== undefined && { wholesalePrice })}\n {...(wholesaleSubscriptionPrice !== undefined && {\n wholesaleSubscriptionPrice,\n })}\n product_subscription_plans={legacySubscriptionPlans}\n {...(selectedSubscriptionPlan !== undefined && {\n selectedSubscriptionPlan,\n })}\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 {t(\"variant_label\")}\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={t(\"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 ||\n t(\"variant_fallback_name\", {\n index: String(idx + 1),\n })}\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\n productId={activeProductId}\n {...(renderImage !== undefined && { renderImage })}\n />\n );\n }\n\n return (\n <ProductListing\n {...(companyLogoUrl !== undefined && { companyLogoUrl })}\n {...(renderImage !== undefined && { 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\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\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 { t } = useShopTranslation();\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>{t(\"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","import { type ComponentProps, useEffect } 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 ScreenHeaderBreadcrumbs,\n ScreenHeaderActions,\n} from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { useShopTranslation } from \"@fluid-app/shop-core/translation-api-context\";\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 { 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 <ShopScreenContent {...props} />;\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 // 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 <>\n {!productId && (\n <ScreenHeaderBreadcrumbs>\n <Breadcrumb>\n <BreadcrumbList className=\"text-lg\">\n <BreadcrumbItem>\n <BreadcrumbPage className=\"font-semibold\">\n {t(\"breadcrumb\")}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </ScreenHeaderBreadcrumbs>\n )}\n <ScreenHeaderActions>\n <div className=\"flex items-center gap-4\">\n <CartButton />\n </div>\n </ScreenHeaderActions>\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 {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 </ShopContainer>\n </div>\n </>\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 return (\n <ScreenHeaderBreadcrumbs>\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 </ScreenHeaderBreadcrumbs>\n );\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import "./PortalTenantClientProvider-CqW6ae2M.mjs";
2
- import "./FluidProvider-LqejfvZ-.mjs";
2
+ import "./FluidProvider-DU-sFsZV.mjs";
3
3
  import "./ScreenRenderer-BszshjZg.mjs";
4
4
  import "./store-api-context-DViwxyG4.mjs";
5
5
  import "./mysite-api-context-kUTM3GNG.mjs";
6
6
  import "./countries-api-context-DScC_39w.mjs";
7
- import "./task-composer-form-ChwUNk1I.mjs";
7
+ import "./task-composer-form-CHyQZUCo.mjs";
8
8
  import "./registry-context-BKvTiuXB.mjs";
9
9
  import "./WidgetInteractionContext-BWH7njU7.mjs";
10
10
  import "./EmbedWidget-Be16VTGq.mjs";
@@ -39,10 +39,10 @@ import "./RecentActivityWidget-D15aOkXL.mjs";
39
39
  import "./SeparatorWidget-DugZ8ukt.mjs";
40
40
  import "./SpacerWidget-CCpCGUjM.mjs";
41
41
  import "./TableWidget-Cef29Su9.mjs";
42
- import "./ToDoWidget-sLWwJJ_g.mjs";
42
+ import "./ToDoWidget-CShnzkw4.mjs";
43
43
  import "./VideoWidget-CjOhc8KW.mjs";
44
44
  import "./SearchSort-BeYMXXIi.mjs";
45
45
  import "./ShopWidget-BKTYcwx_.mjs";
46
46
  import "./ScreenHeaderContext-Dn12BZyj.mjs";
47
- import { n as shopScreenPropertySchema, t as ShopScreen } from "./ShopScreen-uOMi-jFD.mjs";
47
+ import { n as shopScreenPropertySchema, t as ShopScreen } from "./ShopScreen-B4_2T5Nd.mjs";
48
48
  export { ShopScreen, shopScreenPropertySchema };
@@ -1,11 +1,11 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- require("./FluidProvider-BIeO1i4r.cjs");
2
+ require("./FluidProvider-BQjPCP_2.cjs");
3
3
  require("./ScreenRenderer-D_eEc6S4.cjs");
4
4
  require("./PortalTenantClientProvider-D-QE-YzB.cjs");
5
5
  require("./store-api-context-D1gZn22Z.cjs");
6
6
  require("./mysite-api-context-CilZcDS4.cjs");
7
7
  require("./countries-api-context-G-NW4BoH.cjs");
8
- require("./task-composer-form-DnRjQmkv.cjs");
8
+ require("./task-composer-form-D9KhWUI1.cjs");
9
9
  require("./registry-context-DJ5xiVnt.cjs");
10
10
  require("./WidgetInteractionContext-BhkusA95.cjs");
11
11
  require("./EmbedWidget-C3ZJCcHD.cjs");
@@ -40,11 +40,11 @@ require("./RecentActivityWidget-Cy9957Sq.cjs");
40
40
  require("./SeparatorWidget-LQ97EZd5.cjs");
41
41
  require("./SpacerWidget-CYHjTF38.cjs");
42
42
  require("./TableWidget-DfKZhS1Y.cjs");
43
- require("./ToDoWidget-CImAWF3O.cjs");
43
+ require("./ToDoWidget-DA_kLqvP.cjs");
44
44
  require("./VideoWidget-Ddt85ZZg.cjs");
45
45
  require("./SearchSort-Bi9QWJQQ.cjs");
46
46
  require("./ShopWidget-BRx2rblg.cjs");
47
47
  require("./ScreenHeaderContext-DWDN0-mb.cjs");
48
- const require_ShopScreen = require("./ShopScreen-CjrEStJc.cjs");
48
+ const require_ShopScreen = require("./ShopScreen-CPTXTr7p.cjs");
49
49
  exports.ShopScreen = require_ShopScreen.ShopScreen;
50
50
  exports.shopScreenPropertySchema = require_ShopScreen.shopScreenPropertySchema;
@@ -1,5 +1,5 @@
1
1
  require("./chunk-9hOWP6kD.cjs");
2
- const require_FluidProvider = require("./FluidProvider-BIeO1i4r.cjs");
2
+ const require_FluidProvider = require("./FluidProvider-BQjPCP_2.cjs");
3
3
  const require_PortalTenantClientProvider = require("./PortalTenantClientProvider-D-QE-YzB.cjs");
4
4
  const require_countries_api_context = require("./countries-api-context-G-NW4BoH.cjs");
5
5
  const require_static_dict_adapter = require("./static-dict-adapter-BH5PbEBP.cjs");
@@ -10,7 +10,7 @@ const require_use_store = require("./use-store-D2S1FywW.cjs");
10
10
  const require_query_keys = require("./query-keys-BpEJqQUB.cjs");
11
11
  const require_use_account = require("./use-account-BS0vV-sW.cjs");
12
12
  const require_AppNavigationContext = require("./AppNavigationContext-CDowN9gd.cjs");
13
- const require_AddressAutocompleteInput = require("./AddressAutocompleteInput-DkFs_leA.cjs");
13
+ const require_AddressAutocompleteInput = require("./AddressAutocompleteInput-DfwaaK0l.cjs");
14
14
  const require_isAfter = require("./isAfter-DoabulQ5.cjs");
15
15
  let react = require("react");
16
16
  let _tanstack_react_query = require("@tanstack/react-query");
@@ -2515,7 +2515,7 @@ function mapDetailSubscription(bff) {
2515
2515
  last_bill_date: bff.last_billing_date ?? null,
2516
2516
  timezone: bff.timezone ?? null,
2517
2517
  next_ship_date: null,
2518
- last_ship_date: null,
2518
+ last_ship_date: bff.last_shipping_date ?? null,
2519
2519
  disabled: false,
2520
2520
  attempts: 0,
2521
2521
  last_failed_at: bff.last_failed_at ?? null,
@@ -2943,6 +2943,8 @@ const subscriptionsDomain = require_static_dict_adapter.createDomainTranslations
2943
2943
  order_frequency: "Order Frequency",
2944
2944
  next_payment: "Next Payment",
2945
2945
  last_billed: "Last Billed",
2946
+ last_shipped: "Last Shipped",
2947
+ last_bill_amount: "Last Bill Amount",
2946
2948
  payment_and_shipping: "Payment & Shipping",
2947
2949
  shipping_address: "Shipping Address",
2948
2950
  payment_method: "Payment Method",
@@ -5993,7 +5995,7 @@ function SubscriptionItemsSection({ subscription, displayQuantity, displayNextBi
5993
5995
  })
5994
5996
  });
5995
5997
  }
5996
- function SubscriptionManagementSection({ subscription, isActive, isPaused, isCancelled, isMutating, onSkip, onPause, onResume, onCancel, onReactivate, onEditBillDate, onUpdatePaymentClick, displayNextBillDate, renderPaymentMethod, renderShippingAddress }) {
5998
+ function SubscriptionManagementSection({ subscription, isActive, isPaused, isCancelled, isMutating, onSkip, onPause, onResume, onCancel, onReactivate, onEditBillDate, onUpdatePaymentClick, renderPaymentMethod, renderShippingAddress }) {
5997
5999
  const { t } = useSubscriptionsTranslation();
5998
6000
  const plan = subscription.subscription_plan;
5999
6001
  const quantity = subscription.quantity;
@@ -6111,32 +6113,32 @@ function SubscriptionManagementSection({ subscription, isActive, isPaused, isCan
6111
6113
  children: formatSubscriptionFrequency(plan.billing_interval_unit)
6112
6114
  })]
6113
6115
  }),
6114
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6116
+ subscription.last_ship_date && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6117
+ className: "flex-1 px-4",
6118
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6119
+ className: "text-muted-foreground text-sm",
6120
+ children: t("last_shipped")
6121
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6122
+ className: "text-foreground font-medium",
6123
+ children: formatDate(subscription.last_ship_date)
6124
+ })]
6125
+ }),
6126
+ subscription.last_bill_date && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6115
6127
  className: "flex-1 px-4",
6116
6128
  children: [
6117
6129
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6118
6130
  className: "text-muted-foreground text-sm",
6119
- children: t("next_payment")
6131
+ children: t("last_bill_amount")
6120
6132
  }),
6121
6133
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6122
6134
  className: "text-foreground font-medium",
6123
6135
  children: totalPrice
6124
6136
  }),
6125
- subscription.status !== "past_due" && (displayNextBillDate ?? subscription.next_bill_date) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6137
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6126
6138
  className: "text-muted-foreground text-xs",
6127
- children: formatDate(displayNextBillDate ?? subscription.next_bill_date ?? "")
6139
+ children: formatDate(subscription.last_bill_date)
6128
6140
  })
6129
6141
  ]
6130
- }),
6131
- subscription.last_bill_date && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6132
- className: "flex-1 pl-4 text-right",
6133
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6134
- className: "text-muted-foreground text-sm",
6135
- children: t("last_billed")
6136
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6137
- className: "text-foreground font-medium",
6138
- children: formatDate(subscription.last_bill_date)
6139
- })]
6140
6142
  })
6141
6143
  ]
6142
6144
  })]
@@ -6466,7 +6468,6 @@ function SubscriptionDetail({ token, onNotFound, onError, onSuccess, onMutationE
6466
6468
  onReactivate: handleReactivate,
6467
6469
  onEditBillDate: () => setShowEditBillDateModal(true),
6468
6470
  onUpdatePaymentClick,
6469
- displayNextBillDate: pendingNextBillDate ?? void 0,
6470
6471
  renderPaymentMethod,
6471
6472
  renderShippingAddress
6472
6473
  })]
@@ -7431,4 +7432,4 @@ Object.defineProperty(exports, "subscriptionsScreenPropertySchema", {
7431
7432
  }
7432
7433
  });
7433
7434
 
7434
- //# sourceMappingURL=SubscriptionsScreen-CyL91O4w.cjs.map
7435
+ //# sourceMappingURL=SubscriptionsScreen-7ZH1s3eK.cjs.map