@burdenoff/microfe-store 2026.721.1 → 2026.728.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceHomePage.js","names":[],"sources":["../../src/pages/MarketplaceHomePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useCallback, useMemo, useId, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Search,\n Star,\n Download,\n ChevronRight,\n ChevronDown,\n Sparkles,\n TrendingUp,\n Clock,\n Bot,\n Brain,\n LayoutDashboard,\n GitBranch,\n Box,\n Puzzle,\n ShoppingCart,\n X,\n AlertCircle,\n Gift,\n Filter,\n SlidersHorizontal,\n Loader2,\n FileCode2,\n Database,\n type LucideIcon,\n} from 'lucide-react';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { CartDrawer } from '../components/cart/CartDrawer';\nimport {\n useHomeGroupedProducts,\n useBrowseStoreWithContext,\n type StoreProduct,\n} from '../hooks/useStoreGraphQL';\nimport type { BrowseStoreInput } from '../generated/global-types';\nimport { StoreSortBy, PricingModel, ProductNature } from '../generated/global-types';\nimport { useGetMyStoreInstallationsQuery } from '../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\n// ========================================\n// Helper Types and Functions\n// ========================================\n\ntype ProductTypeKey =\n | 'BOTAPP'\n | 'AGENT'\n | 'CONSOLE'\n | 'WORKFLOW'\n | 'NODE'\n | 'WIDGET'\n | 'VIBEMODULE'\n | 'DASHBOARD'\n | 'PARSER'\n | 'DATASINK'\n | 'APP'\n | 'TEMPLATE'\n | 'INTEGRATION'\n | 'EXTENSION'\n | 'THEME'\n | 'PLUGIN'\n | 'COMPONENT'\n | string;\n\nconst formatDownloads = (count: number): string => {\n if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;\n if (count >= 1000) return `${(count / 1000).toFixed(1)}K`;\n return count.toString();\n};\n\nconst getPriceDisplay = (product: StoreProduct): string => {\n if (product.pricingModel === 'FREE') return 'Free';\n if (product.price === 0) return 'Free';\n return `$${product.price}`;\n};\n\nconst getProductColor = (type: ProductTypeKey): string => {\n const colors: Record<string, string> = {\n BOTAPP: 'var(--color-accent-blue)',\n AGENT: 'var(--color-accent-purple)',\n CONSOLE: 'var(--color-accent-teal)',\n WORKFLOW: 'var(--color-status-warning-bg)',\n NODE: 'var(--color-status-error-bg)',\n WIDGET: 'var(--color-accent-purple)',\n VIBEMODULE: 'var(--color-accent-blue)',\n DASHBOARD: 'var(--color-accent-blue)',\n PARSER: 'var(--color-status-warning-bg)',\n DATASINK: 'var(--color-status-success-bg)',\n APP: 'var(--color-accent-indigo)',\n TEMPLATE: 'var(--color-accent-teal)',\n INTEGRATION: 'var(--color-status-warning-bg)',\n EXTENSION: 'var(--color-accent-purple)',\n THEME: 'var(--color-accent-purple)',\n PLUGIN: 'var(--color-status-success-bg)',\n COMPONENT: 'var(--color-accent-blue)',\n };\n return colors[type] || 'var(--color-text-muted)';\n};\n\n// Helper to get translucent background color from a token\nconst getProductBgColor = (type: ProductTypeKey): string => {\n const color = getProductColor(type);\n return `color-mix(in srgb, ${color}, transparent 85%)`;\n};\n\n// Component wrapper to render type icons\ninterface ProductTypeIconProps {\n type: ProductTypeKey;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst TYPE_ICONS: Record<string, LucideIcon> = {\n BOTAPP: Bot,\n AGENT: Brain,\n CONSOLE: LayoutDashboard,\n WORKFLOW: GitBranch,\n NODE: Box,\n WIDGET: Puzzle,\n VIBEMODULE: Puzzle,\n DASHBOARD: LayoutDashboard,\n PARSER: FileCode2,\n DATASINK: Database,\n APP: Box,\n TEMPLATE: LayoutDashboard,\n INTEGRATION: GitBranch,\n EXTENSION: Puzzle,\n THEME: Puzzle,\n PLUGIN: Puzzle,\n COMPONENT: Box,\n};\n\nconst ProductTypeIcon: FC<ProductTypeIconProps> = ({ type, className, style }) => {\n const IconComponent = TYPE_ICONS[type] || Box;\n return <IconComponent className={className} style={style} />;\n};\n\n// ========================================\n// App Card Component\n// ========================================\n\ninterface AppCardProps {\n product: StoreProduct;\n onClick: () => void;\n variant?: 'default' | 'compact' | 'featured';\n}\n\nconst AppCard: FC<AppCardProps> = ({ product, onClick, variant = 'default' }) => {\n const productColor = getProductColor(product.type);\n\n if (variant === 'featured') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group relative flex size-full cursor-pointer flex-col overflow-hidden rounded-2xl border border-border-seam bg-gradient-to-br from-bg-surface to-bg-sunken p-6 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n {product.featured && (\n <div className=\"absolute right-4 top-4 flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-1 text-xs font-medium text-status-warning-text\">\n <Sparkles className=\"size-3\" />\n Featured\n </div>\n )}\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex size-16 flex-shrink-0 items-center justify-center rounded-2xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-10\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h3>\n <p className=\"flex items-center gap-2 text-sm text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3.5\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-4 line-clamp-2 flex-1 text-sm text-text-muted\">\n {product.description || 'No description available'}\n </p>\n <div className=\"mt-4 flex items-center justify-between border-t border-border-seam pt-4\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Star className=\"size-4 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {formatDownloads(product.downloads)}\n </span>\n </div>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-sm font-medium text-text-link\">\n {getPriceDisplay(product)}\n </span>\n </div>\n </button>\n );\n }\n\n if (variant === 'compact') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-transparent p-3 text-left transition-[background-color,border-color] hover:border-border-seam hover:bg-bg-surface\"\n >\n <div\n className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-8 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-8\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-medium text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"truncate text-xs text-text-muted\">\n {product.category || product.type} · {(product.rating ?? 0).toFixed(1)} ★\n </p>\n </div>\n <span className=\"text-xs font-medium text-text-muted\">{getPriceDisplay(product)}</span>\n </button>\n );\n }\n\n // Default variant\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface p-4 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n <div\n className=\"flex size-14 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-9 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-9\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-3 line-clamp-2 text-sm text-text-muted\">\n {product.description || 'No description available'}\n </p>\n <div className=\"mt-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-0.5 text-xs text-text-muted\">\n <Star className=\"size-3.5 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-xs text-text-muted\">·</span>\n <span className=\"text-xs text-text-muted\">\n {formatDownloads(product.downloads)} downloads\n </span>\n </div>\n <span className=\"text-xs font-semibold text-text-link\">{getPriceDisplay(product)}</span>\n </div>\n </button>\n );\n};\n\n// ========================================\n// Deprecated App Card Component\n// ========================================\n\ninterface DeprecatedProduct {\n id: string;\n name: string;\n icon?: string;\n}\n\nconst DeprecatedAppCard: FC<{ product: DeprecatedProduct; onClick: () => void }> = ({\n product,\n onClick,\n}) => (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-status-warning-border/30 bg-bg-surface p-4 text-left shadow-elevation-1 transition-all hover:border-status-warning-border/60 hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"relative flex size-14 flex-shrink-0 items-center justify-center rounded-xl bg-status-warning-bg-subtle\">\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-9 rounded-lg object-cover grayscale\"\n />\n ) : (\n <Box className=\"size-9 text-status-warning-text/50\" />\n )}\n <span className=\"absolute -right-1 -top-1 flex size-4 items-center justify-center rounded-full bg-status-warning-bg-subtle border border-status-warning-border/40\">\n <AlertCircle className=\"size-2.5 text-status-warning-text\" />\n </span>\n </div>\n <div className=\"min-w-0 flex-1\">\n <h4 className=\"truncate font-semibold text-text-secondary group-hover:text-text-link\">\n {product.name}\n </h4>\n <span\n title=\"This node is no longer supported. We encourage you to migrate to newer alternatives.\"\n className=\"mt-1 inline-flex cursor-help items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\"\n >\n <AlertCircle className=\"size-3\" />\n No Longer Supported\n </span>\n </div>\n </button>\n);\n\n// ========================================\n// Section Header Component\n// ========================================\n\ninterface SectionHeaderProps {\n icon: typeof Sparkles;\n title: string;\n subtitle?: string;\n onViewAll?: () => void;\n}\n\nconst SectionHeader: FC<SectionHeaderProps> = ({ icon: Icon, title, subtitle, onViewAll }) => (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Icon className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n {subtitle && <p className=\"text-sm text-text-muted\">{subtitle}</p>}\n </div>\n </div>\n {onViewAll && (\n <button\n type=\"button\"\n onClick={onViewAll}\n className=\"cursor-pointer flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n View all\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n);\n\n// ========================================\n// Category Chip Component\n// ========================================\n\ninterface CategoryChipProps {\n typeId: ProductTypeKey;\n name: string;\n count: number;\n active?: boolean;\n onClick: () => void;\n}\n\nconst CategoryChip: FC<CategoryChipProps> = ({ typeId, name, count, active, onClick }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex cursor-pointer items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all ${\n active\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-bg-sunken'\n }`}\n >\n <ProductTypeIcon type={typeId} className=\"size-4\" />\n {name}\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${active ? 'bg-bg-surface/20' : 'bg-bg-sunken'}`}\n >\n {count}\n </span>\n </button>\n);\n\n// ========================================\n// Hero Section Component\n// ========================================\n\ninterface HeroSectionProps {\n featuredProducts: StoreProduct[];\n onProductClick: (product: StoreProduct) => void;\n /** Optional tour anchor placed on the hero root (avoids an extra wrapper div). */\n dataTour?: string;\n}\n\nconst HeroSection: FC<HeroSectionProps> = ({ featuredProducts, onProductClick, dataTour }) => {\n const [activeIndex, setActiveIndex] = useState(0);\n const heroProduct = featuredProducts[activeIndex] || featuredProducts[0];\n\n if (!heroProduct) return null;\n\n const productColor = getProductColor(heroProduct.type);\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow\n data-tour={dataTour}\n className=\"relative overflow-hidden rounded-2xl p-8\"\n >\n <div\n className=\"absolute inset-0 opacity-10\"\n style={{\n background: `linear-gradient(135deg, ${productColor} 0%, transparent 50%)`,\n }}\n />\n <div className=\"relative z-10 flex items-center gap-8\">\n <div className=\"flex-1\">\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-3 py-1 text-xs font-semibold text-status-warning-text motion-safe:animate-vc-glow-pulse\">\n <Sparkles className=\"size-3\" />\n Featured App\n </span>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-text-link\">\n {heroProduct.category || heroProduct.type}\n </span>\n </div>\n <h1 className=\"mb-3 text-3xl font-bold text-text-primary\">{heroProduct.name}</h1>\n <p className=\"mb-6 max-w-xl text-text-muted\">\n {heroProduct.description || 'No description available'}\n </p>\n <div className=\"mb-6 flex items-center gap-4\">\n <span className=\"flex items-center gap-1 text-sm\">\n <Star className=\"size-5 fill-current text-status-warning-text\" />\n <span className=\"font-semibold text-text-primary\">\n {(heroProduct.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-text-muted\">({heroProduct.reviewCount} reviews)</span>\n </span>\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {heroProduct.downloads.toLocaleString()} downloads\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => onProductClick(heroProduct)}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n View Details\n </button>\n </div>\n <div className=\"hidden flex-shrink-0 lg:block\">\n <div\n className=\"flex size-48 items-center justify-center rounded-3xl shadow-lg\"\n style={{ backgroundColor: getProductBgColor(heroProduct.type) }}\n >\n {heroProduct.icon ? (\n <img\n src={heroProduct.icon}\n alt={heroProduct.name}\n className=\"size-32 rounded-2xl object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={heroProduct.type}\n className=\"size-32\"\n style={{ color: productColor }}\n />\n )}\n </div>\n </div>\n </div>\n {/* Featured App Selector */}\n {featuredProducts.length > 1 && (\n <div className=\"relative z-10 mt-6 flex gap-2\">\n {featuredProducts.map((product, index) => (\n <button\n type=\"button\"\n key={product.id}\n onClick={() => setActiveIndex(index)}\n className={`h-2 w-8 rounded-full transition-all ${\n index === activeIndex\n ? 'bg-action-primary-bg'\n : 'bg-border-default hover:bg-action-primary-border'\n }`}\n aria-label={`View ${product.name}`}\n />\n ))}\n </div>\n )}\n </GlassCard>\n );\n};\n\n// ========================================\n// Loading Skeleton Components\n// ========================================\n\nconst ProductCardSkeleton: FC<{ variant?: 'default' | 'compact' | 'featured' }> = ({\n variant = 'default',\n}) => {\n if (variant === 'featured') {\n return (\n <div className=\"h-full animate-pulse rounded-2xl border border-border-seam bg-bg-surface p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-2xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-4 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-4 space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n if (variant === 'compact') {\n return (\n <div className=\"flex animate-pulse items-center gap-3 p-3\">\n <div className=\"size-12 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"animate-pulse rounded-xl border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"size-14 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-3 space-y-2\">\n <div className=\"h-3 w-full rounded bg-bg-sunken\" />\n <div className=\"h-3 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n};\n\n// Hero Section Skeleton\nconst HeroSkeleton: FC = () => (\n <div className=\"animate-pulse rounded-2xl bg-gradient-to-r from-bg-surface via-bg-sunken to-bg-surface p-8\">\n <div className=\"flex items-center gap-8\">\n <div className=\"flex-1 space-y-4\">\n <div className=\"flex gap-2\">\n <div className=\"h-6 w-24 rounded-full bg-bg-sunken\" />\n <div className=\"h-6 w-20 rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"h-9 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-5/6 rounded bg-bg-sunken\" />\n </div>\n <div className=\"flex gap-4\">\n <div className=\"h-5 w-24 rounded bg-bg-sunken\" />\n <div className=\"h-5 w-32 rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-36 rounded-lg bg-bg-sunken\" />\n </div>\n <div className=\"hidden size-48 rounded-3xl bg-bg-sunken lg:block\" />\n </div>\n <div className=\"mt-6 flex gap-2\">\n {[...Array(3)].map((_, i) => (\n <div key={i} className=\"h-2 w-8 rounded-full bg-bg-sunken\" />\n ))}\n </div>\n </div>\n);\n\n// Category Chips Skeleton (Browse by Type)\nconst CHIP_WIDTHS = [100, 120, 90, 110, 130, 95, 115, 105];\n\nconst CategoryChipsSkeleton: FC = () => (\n <div className=\"space-y-4\">\n <div className=\"h-6 w-32 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex flex-wrap gap-2\">\n {CHIP_WIDTHS.map((width, i) => (\n <div\n key={i}\n className=\"h-9 animate-pulse rounded-full bg-bg-sunken\"\n style={{ width: `${width}px` }}\n />\n ))}\n </div>\n </div>\n);\n\n// Section Header Skeleton\nconst SectionHeaderSkeleton: FC = () => (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 animate-pulse rounded-xl bg-bg-sunken\" />\n <div className=\"space-y-1\">\n <div className=\"h-5 w-40 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-4 w-32 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n);\n\n// Full Page Skeleton - shows complete loading state\nconst MarketplacePageSkeleton: FC = () => (\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n {/* Hero Section */}\n <HeroSkeleton />\n\n {/* Browse by Type */}\n <CategoryChipsSkeleton />\n\n {/* Most Downloaded Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* New Releases Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* Free Apps Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} variant=\"compact\" />\n ))}\n </div>\n </div>\n </div>\n);\n\n// ========================================\n// Filter Types\n// ========================================\n\ntype PricingFilter = 'all' | 'free' | 'paid' | 'subscription';\ntype RatingFilter = 'any' | '3' | '3.5' | '4' | '4.5';\ntype NatureFilter = 'all' | 'digital' | 'physical';\ntype SortOption =\n | 'relevance'\n | 'rating'\n | 'downloads'\n | 'newest'\n | 'price-low'\n | 'price-high'\n | 'name-asc';\n\ninterface FilterDropdownProps {\n label: string;\n value: string;\n options: { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nconst FilterDropdown: FC<FilterDropdownProps> = ({ label, value, options, onChange }) => {\n const selectId = useId();\n\n return (\n <label\n htmlFor={selectId}\n className=\"relative inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-within:border-action-primary-border focus-within:ring-2 focus-within:ring-action-primary-border/20\"\n >\n <span className=\"sr-only\">{label}</span>\n <span className=\"text-text-muted\">{label}:</span>\n <select\n id={selectId}\n aria-label={label}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n className=\"min-w-[8rem] appearance-none bg-transparent pr-6 text-sm font-medium text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-3 size-4 text-text-muted\" />\n </label>\n );\n};\n\n// ========================================\n// Main Page Component\n// ========================================\n\nexport const MarketplaceHomePage: FC = () => {\n const navigate = useNavigate();\n const { basePath, user } = useStore();\n const { t } = useI18n();\n const searchInputId = useId();\n const bus = useEventBus();\n const {\n cartOpen,\n closeCart,\n itemCount,\n cartItems,\n subtotal,\n tax,\n total,\n updateQuantity,\n removeProduct,\n } = useCart();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedType, setSelectedType] = useState<string>('all');\n\n // Filter states\n const [pricingFilter, setPricingFilter] = useState<PricingFilter>('all');\n const [ratingFilter, setRatingFilter] = useState<RatingFilter>('any');\n const [natureFilter, setNatureFilter] = useState<NatureFilter>('all');\n const [selectedCategory, setSelectedCategory] = useState<string>('all');\n const [selectedLicense, setSelectedLicense] = useState<string>('all');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [sortOption, setSortOption] = useState<SortOption>('relevance');\n const [showFilters, setShowFilters] = useState(false);\n\n // Debounce search query - 1.5 seconds for better UX\n const debouncedSearchQuery = useDebounce(searchQuery, 1500);\n\n // Filter options\n const pricingOptions = [\n { value: 'all', label: 'All Prices' },\n { value: 'free', label: 'Free' },\n { value: 'paid', label: 'Paid (One-time)' },\n { value: 'subscription', label: 'Subscription' },\n ];\n\n const ratingOptions = [\n { value: 'any', label: 'Any Rating' },\n { value: '3', label: '3+ Stars' },\n { value: '3.5', label: '3.5+ Stars' },\n { value: '4', label: '4+ Stars' },\n { value: '4.5', label: '4.5+ Stars' },\n ];\n\n const natureOptions = [\n { value: 'all', label: 'All Types' },\n { value: 'digital', label: 'Digital' },\n { value: 'physical', label: 'Physical' },\n ];\n\n const sortOptions = [\n { value: 'relevance', label: 'Relevance' },\n { value: 'rating', label: 'Highest Rated' },\n { value: 'downloads', label: 'Most Downloads' },\n { value: 'newest', label: 'Newest First' },\n { value: 'price-low', label: 'Price: Low to High' },\n { value: 'price-high', label: 'Price: High to Low' },\n { value: 'name-asc', label: 'Name A-Z' },\n ];\n\n // Map UI sort option to GraphQL StoreSortBy enum\n const mapSortToApi = (sort: SortOption): StoreSortBy => {\n const mapping: Record<SortOption, StoreSortBy> = {\n relevance: StoreSortBy.Relevance,\n rating: StoreSortBy.Rating,\n downloads: StoreSortBy.Downloads,\n newest: StoreSortBy.Newest,\n 'price-low': StoreSortBy.PriceLow,\n 'price-high': StoreSortBy.PriceHigh,\n 'name-asc': StoreSortBy.NameAsc,\n };\n return mapping[sort];\n };\n\n // Map UI pricing filter to GraphQL PricingModel enum\n const mapPricingToApi = (pricing: PricingFilter): PricingModel | undefined => {\n if (pricing === 'all') return undefined;\n const mapping: Record<string, PricingModel> = {\n free: PricingModel.Free,\n paid: PricingModel.PaidOnetime,\n subscription: PricingModel.Subscription,\n };\n return mapping[pricing];\n };\n\n // Check if any filter is active (using debounced search for API calls)\n const hasActiveFilters =\n debouncedSearchQuery.trim() !== '' ||\n pricingFilter !== 'all' ||\n ratingFilter !== 'any' ||\n natureFilter !== 'all' ||\n selectedCategory !== 'all' ||\n selectedLicense !== 'all' ||\n featuredOnly ||\n selectedType !== 'all' ||\n sortOption !== 'relevance';\n\n // Check if user is currently typing (search not yet debounced)\n const isSearchPending = searchQuery.trim() !== '' && searchQuery !== debouncedSearchQuery;\n\n const clearAllFilters = () => {\n setSearchQuery('');\n setPricingFilter('all');\n setRatingFilter('any');\n setNatureFilter('all');\n setSelectedCategory('all');\n setSelectedLicense('all');\n setFeaturedOnly(false);\n setSortOption('relevance');\n setSelectedType('all');\n };\n\n // Count active filters (excluding search and sort)\n const activeFilterCount =\n (pricingFilter !== 'all' ? 1 : 0) +\n (ratingFilter !== 'any' ? 1 : 0) +\n (natureFilter !== 'all' ? 1 : 0) +\n (selectedCategory !== 'all' ? 1 : 0) +\n (selectedLicense !== 'all' ? 1 : 0) +\n (featuredOnly ? 1 : 0) +\n (selectedType !== 'all' ? 1 : 0);\n\n // Build BrowseStoreInput from filter states for API call\n const browseStoreInput: BrowseStoreInput = useMemo(() => {\n const input: BrowseStoreInput = {\n sortBy: mapSortToApi(sortOption),\n pagination: { limit: 50 },\n };\n\n // Only add filter if we have active filters\n if (hasActiveFilters) {\n input.filter = {};\n\n // Search query (debounced)\n if (debouncedSearchQuery.trim()) {\n input.filter.search = debouncedSearchQuery.trim();\n }\n\n // Type filter\n if (selectedType !== 'all') {\n input.filter.type = selectedType as BrowseStoreInput['filter'] extends { type?: infer T }\n ? T\n : never;\n }\n\n // Category filter\n if (selectedCategory !== 'all') {\n input.filter.category = selectedCategory;\n }\n\n // Nature filter (digital/physical)\n if (natureFilter !== 'all') {\n input.filter.nature =\n natureFilter === 'digital' ? ProductNature.Digital : ProductNature.Physical;\n }\n\n // Pricing filter\n const pricingModel = mapPricingToApi(pricingFilter);\n if (pricingModel) {\n input.filter.pricingModel = pricingModel;\n }\n\n // Rating filter\n if (ratingFilter !== 'any') {\n input.filter.minRating = parseFloat(ratingFilter);\n }\n\n // Featured filter\n if (featuredOnly) {\n input.filter.featured = true;\n }\n\n // License filter\n if (selectedLicense !== 'all') {\n input.filter.license = selectedLicense;\n }\n }\n\n return input;\n }, [\n debouncedSearchQuery,\n selectedType,\n selectedCategory,\n natureFilter,\n pricingFilter,\n ratingFilter,\n featuredOnly,\n selectedLicense,\n sortOption,\n hasActiveFilters,\n ]);\n\n // Fetch grouped products for homepage (when no filters active)\n const {\n featured,\n mostDownloaded,\n recentlyAdded,\n freeItems,\n loading: homeLoading,\n error: homeError,\n refetch: refetchHome,\n } = useHomeGroupedProducts({ skip: hasActiveFilters });\n\n // Fetch filtered products from API when filters are active\n const {\n products: browseProducts,\n totalCount,\n facets,\n loading: browseLoading,\n error: browseError,\n refetch: refetchBrowse,\n } = useBrowseStoreWithContext(hasActiveFilters ? browseStoreInput : {}, {\n skip: !hasActiveFilters,\n });\n\n // Determine which data to show\n const loading = hasActiveFilters ? browseLoading : homeLoading;\n const error = hasActiveFilters ? browseError : homeError;\n const refetch = hasActiveFilters ? refetchBrowse : refetchHome;\n\n // Products to display when filters are active\n const filteredProducts = hasActiveFilters ? browseProducts : [];\n\n // Emit search event when debounced query resolves. Read totalCount via ref so\n // we don't re-emit when only the count changes.\n const totalCountRef = useRef(totalCount);\n totalCountRef.current = totalCount;\n const busRef = useRef(bus);\n busRef.current = bus;\n useEffect(() => {\n if (!debouncedSearchQuery.trim() || browseLoading) return;\n (busRef.current as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.marketplace.searched',\n {\n query: debouncedSearchQuery,\n resultCount: totalCountRef.current,\n }\n );\n }, [debouncedSearchQuery, browseLoading]);\n\n const handleProductClick = useCallback(\n (product: StoreProduct) => {\n navigate(`${basePath}/marketplace/product/${product.id}`);\n },\n [navigate, basePath]\n );\n\n const handleCheckout = () => {\n closeCart();\n navigate(`${basePath}/cart`);\n };\n\n // Get all unique products for the homepage \"All Apps\" section\n const allProducts = useMemo(\n () => [\n ...new Map(\n [...featured, ...mostDownloaded, ...recentlyAdded, ...freeItems].map((p) => [p.id, p])\n ).values(),\n ],\n [featured, mostDownloaded, recentlyAdded, freeItems]\n );\n\n // Fetch installations to surface deprecated (UNLISTED) apps the user has installed\n const isAuthenticated = !!user?.id;\n const { data: installationsData } = useGetMyStoreInstallationsQuery({\n variables: { limit: 100 },\n skip: !isAuthenticated,\n fetchPolicy: 'cache-and-network',\n });\n\n // Deduplicate by productId (user may have installed in multiple workspaces)\n const deprecatedInstalledProducts = useMemo<DeprecatedProduct[]>(() => {\n if (!installationsData?.myStoreAppInstallations?.items) return [];\n const seen = new Set<string>();\n const result: DeprecatedProduct[] = [];\n for (const item of installationsData.myStoreAppInstallations.items) {\n if (item.productStatus === 'UNLISTED' && !seen.has(item.productId)) {\n seen.add(item.productId);\n result.push({\n id: item.productId,\n name: item.productName ?? 'Unknown App',\n icon: item.productIcon ?? undefined,\n });\n }\n }\n return result;\n }, [installationsData]);\n\n // Build dynamic filter options from facets (when available) or from homepage products\n const typeCategories = useMemo(() => {\n // Use facets from API if available\n if (facets?.types && facets.types.length > 0) {\n return [\n { id: 'all', name: 'All Apps', count: totalCount },\n ...facets.types.map((t) => ({\n id: t.value,\n name: t.value.charAt(0) + t.value.slice(1).toLowerCase().replace(/_/g, ' '),\n count: t.count,\n })),\n ];\n }\n // Fallback to homepage products: count each type in a single pass.\n const typeCounts = allProducts.reduce<Map<string, number>>((acc, p) => {\n acc.set(p.type, (acc.get(p.type) ?? 0) + 1);\n return acc;\n }, new Map());\n const typeOptions = Array.from(typeCounts.entries())\n .flatMap(([type, count]) =>\n count > 0\n ? [\n {\n id: type,\n name: type.charAt(0) + type.slice(1).toLowerCase().replace(/_/g, ' '),\n count,\n },\n ]\n : []\n )\n .sort((a, b) => b.count - a.count);\n return [{ id: 'all', name: 'All Apps', count: allProducts.length }, ...typeOptions];\n }, [facets, allProducts, totalCount]);\n\n // Build category options from facets or products\n const categoryOptions = useMemo(() => {\n if (facets?.categories && facets.categories.length > 0) {\n return [\n { value: 'all', label: 'All Categories' },\n ...facets.categories.map((c) => ({\n value: c.value,\n label: c.value,\n })),\n ];\n }\n return [\n { value: 'all', label: 'All Categories' },\n ...Array.from(new Set(allProducts.flatMap((p) => (p.category ? [p.category] : []))))\n .map((cat) => ({\n value: cat as string,\n label: cat as string,\n }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n ];\n }, [facets, allProducts]);\n\n // Build license options from facets or static list\n const licenseOptions = useMemo(() => {\n if (facets?.licenses && facets.licenses.length > 0) {\n return [\n { value: 'all', label: 'All Licenses' },\n ...facets.licenses.map((l) => ({\n value: l.value,\n label: l.value,\n })),\n ];\n }\n return [{ value: 'all', label: 'All Licenses' }];\n }, [facets]);\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"relative overflow-hidden border-b border-border-seam bg-bg-surface px-6 py-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n App{' '}\n <GradientText>\n {t('pages.marketplace.title', { defaultValue: 'Marketplace' })}\n </GradientText>\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.marketplace.subtitle', {\n defaultValue: 'Discover apps, workflows, and integrations for your workspace',\n })}\n </p>\n </div>\n <button\n type=\"button\"\n data-tour=\"store-marketplace-cart-button\"\n onClick={() => navigate(`${basePath}/cart`)}\n className=\"cursor-pointer relative flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ShoppingCart className=\"size-5\" />\n {t('pages.marketplace.cart', { defaultValue: 'Cart' })}\n {itemCount > 0 && (\n <span className=\"absolute -right-2 -top-2 flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs font-bold text-action-primary-text\">\n {itemCount}\n </span>\n )}\n </button>\n </div>\n\n {/* Search Bar - Live filtering as you type */}\n <div className=\"mt-4\">\n <div className=\"relative\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {t('pages.marketplace.search.label', { defaultValue: 'Search marketplace products' })}\n </label>\n <Search className=\"absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-muted\" />\n <input\n id={searchInputId}\n data-tour=\"store-marketplace-search-input\"\n aria-label={t('pages.marketplace.search.label', {\n defaultValue: 'Search marketplace products',\n })}\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={t('pages.marketplace.search.placeholder', {\n defaultValue: 'Search apps, workflows, integrations...',\n })}\n className=\"w-full rounded-xl border border-border-default bg-bg-sunken py-3 pl-12 pr-4 text-text-primary placeholder-text-muted focus:border-action-primary-border focus:bg-bg-surface focus:outline-none focus:ring-2 focus:ring-action-primary-border/20\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n aria-label={t('pages.marketplace.search.clear', { defaultValue: 'Clear search' })}\n className=\"cursor-pointer absolute right-4 top-1/2 -translate-y-1/2 text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n {/* Search results count / pending indicator */}\n {searchQuery.trim() && (\n <div\n aria-live=\"polite\"\n className=\"mt-2 flex items-center gap-2 text-sm text-text-muted\"\n >\n {isSearchPending || browseLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n <span>\n {t('pages.marketplace.search.searching', { defaultValue: 'Searching...' })}\n </span>\n </>\n ) : (\n <span>\n {totalCount} result{totalCount !== 1 ? 's' : ''} for &ldquo;{debouncedSearchQuery}\n &rdquo;\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Filter Bar */}\n <div className=\"mt-4 flex flex-wrap items-center gap-3\">\n {/* Filter Toggle Button */}\n <button\n type=\"button\"\n data-tour=\"store-marketplace-filters-button\"\n onClick={() => setShowFilters(!showFilters)}\n className={`cursor-pointer flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-colors ${\n showFilters || activeFilterCount > 0\n ? 'border-action-primary-border bg-action-primary-bg/10 text-text-link'\n : 'border-border-default bg-bg-surface text-text-primary transition-colors hover:bg-bg-sunken'\n }`}\n >\n <SlidersHorizontal className=\"size-4\" />\n {t('pages.marketplace.filters', { defaultValue: 'Filters' })}\n {activeFilterCount > 0 && (\n <span className=\"flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs text-action-primary-text\">\n {activeFilterCount}\n </span>\n )}\n </button>\n\n {/* Quick Filter Chips - Always visible */}\n <div\n className=\"flex flex-wrap items-center gap-2\"\n data-tour=\"store-marketplace-quick-filters\"\n >\n <button\n type=\"button\"\n onClick={() => setPricingFilter(pricingFilter === 'free' ? 'all' : 'free')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n pricingFilter === 'free'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Gift className=\"size-3.5\" />\n Free\n </button>\n <button\n type=\"button\"\n onClick={() => setRatingFilter(ratingFilter === '4' ? 'any' : '4')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n ratingFilter === '4'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Star className=\"size-3.5\" />\n 4+ Stars\n </button>\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(!featuredOnly)}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n featuredOnly\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Sparkles className=\"size-3.5\" />\n Featured\n </button>\n </div>\n\n {/* Clear Filters */}\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n Clear all\n </button>\n )}\n\n {/* Sort Dropdown - Right aligned */}\n <div className=\"ml-auto\" data-tour=\"store-marketplace-section-sort\">\n <FilterDropdown\n label=\"Sort\"\n value={sortOption}\n options={sortOptions}\n onChange={(value) => setSortOption(value as SortOption)}\n />\n </div>\n </div>\n\n {/* Expanded Filters Panel */}\n {showFilters && (\n <div className=\"mt-4 rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex flex-wrap items-start gap-6\">\n {/* Pricing Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Pricing\n </span>\n <FilterDropdown\n label=\"Price\"\n value={pricingFilter}\n options={pricingOptions}\n onChange={(value) => setPricingFilter(value as PricingFilter)}\n />\n </div>\n\n {/* Rating Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Min Rating\n </span>\n <FilterDropdown\n label=\"Rating\"\n value={ratingFilter}\n options={ratingOptions}\n onChange={(value) => setRatingFilter(value as RatingFilter)}\n />\n </div>\n\n {/* Category Filter */}\n {categoryOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Category\n </span>\n <FilterDropdown\n label=\"Category\"\n value={selectedCategory}\n options={categoryOptions}\n onChange={setSelectedCategory}\n />\n </div>\n )}\n\n {/* Nature Filter (Digital/Physical) */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Nature\n </span>\n <FilterDropdown\n label=\"Nature\"\n value={natureFilter}\n options={natureOptions}\n onChange={(value) => setNatureFilter(value as NatureFilter)}\n />\n </div>\n\n {/* License Filter */}\n {licenseOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n License\n </span>\n <FilterDropdown\n label=\"License\"\n value={selectedLicense}\n options={licenseOptions}\n onChange={setSelectedLicense}\n />\n </div>\n )}\n </div>\n\n {/* Active Filters Summary */}\n {activeFilterCount > 0 && (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-border-default pt-4\">\n <span className=\"text-xs font-medium text-text-muted\">Active filters:</span>\n {selectedType !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedType('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Type:</span> {selectedType}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {pricingFilter !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setPricingFilter('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Price:</span>{' '}\n {pricingOptions.find((o) => o.value === pricingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {ratingFilter !== 'any' && (\n <button\n type=\"button\"\n onClick={() => setRatingFilter('any')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Rating:</span>{' '}\n {ratingOptions.find((o) => o.value === ratingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedCategory !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedCategory('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Category:</span> {selectedCategory}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {natureFilter !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setNatureFilter('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Nature:</span>{' '}\n {natureOptions.find((o) => o.value === natureFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedLicense !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedLicense('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">License:</span>{' '}\n {licenseOptions.find((o) => o.value === selectedLicense)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {featuredOnly && (\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(false)}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <Sparkles className=\"size-3\" />\n Featured Only\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </header>\n\n {/* Main Content */}\n <main className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n <PagePurpose>\n The marketplace is where you discover and install apps, agents, workflows, nodes and\n widgets that extend your workspaces. Browse featured and trending picks, filter by type\n or category, search for something specific, then open any product to view pricing and\n reviews before adding it to your cart.\n </PagePurpose>\n {/* Error State */}\n {error && (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle p-8 text-center\"\n >\n <AlertCircle className=\"mb-4 size-12 text-status-error-text\" />\n <h3 className=\"mb-2 text-lg font-semibold text-status-error-text-emphasis\">\n Failed to load marketplace\n </h3>\n <p className=\"mb-4 text-sm text-status-error-text\">\n {error.message || 'An error occurred while fetching products'}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-emphasis\"\n >\n Try Again\n </button>\n </div>\n )}\n\n {/* Loading State */}\n {loading && !error && <MarketplacePageSkeleton />}\n\n {/* Content - Only show when data is loaded */}\n {!loading && !error && (\n <>\n {/* Hero Section - Featured Products (only when no filters) */}\n {!hasActiveFilters && selectedType === 'all' && featured.length > 0 && (\n <HeroSection\n dataTour=\"store-marketplace-hero-section\"\n featuredProducts={featured}\n onProductClick={handleProductClick}\n />\n )}\n\n {/* Type Filter Categories */}\n {typeCategories.length > 1 && (\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Browse by Type</h2>\n <div\n className=\"flex flex-wrap gap-2\"\n data-tour=\"store-marketplace-type-categories\"\n >\n {typeCategories.map((category) => (\n <CategoryChip\n key={category.id}\n typeId={category.id}\n name={category.name}\n count={category.count}\n active={selectedType === category.id}\n onClick={() => setSelectedType(category.id)}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Show Filtered Results when filters are active */}\n {hasActiveFilters && (\n <section>\n <SectionHeader\n icon={Filter}\n title={debouncedSearchQuery ? `Search Results` : 'Filtered Results'}\n subtitle={`${totalCount} app${totalCount !== 1 ? 's' : ''} found`}\n />\n {filteredProducts.length > 0 ? (\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\"\n data-tour=\"store-marketplace-product-grid\"\n >\n {filteredProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No apps match your filters\"\n description=\"Try adjusting your filter criteria or clearing filters to see more results.\"\n action={\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Clear All Filters\n </button>\n }\n />\n )}\n </section>\n )}\n\n {/* Show grouped sections only when no filters are active */}\n {!hasActiveFilters && selectedType === 'all' && (\n <>\n {/* Most Downloaded / Trending Apps */}\n {mostDownloaded.length > 0 && (\n <section>\n <SectionHeader\n icon={TrendingUp}\n title=\"Most Downloaded\"\n subtitle=\"Top picks by our community\"\n />\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n data-tour=\"store-marketplace-most-downloaded\"\n >\n {mostDownloaded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Recently Added / New Releases */}\n {recentlyAdded.length > 0 && (\n <section>\n <SectionHeader\n icon={Clock}\n title=\"New Releases\"\n subtitle=\"Recently published apps\"\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {recentlyAdded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Free Items */}\n {freeItems.length > 0 && (\n <section>\n <SectionHeader\n icon={Gift}\n title=\"Free Apps\"\n subtitle=\"No cost, full functionality\"\n />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {freeItems.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n variant=\"compact\"\n />\n ))}\n </div>\n </section>\n )}\n\n {/* All Apps */}\n {allProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={Box}\n title=\"All Apps\"\n subtitle={`${allProducts.length} apps available`}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {allProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n </>\n )}\n\n {/* Deprecated Installed Apps — shown whenever user has UNLISTED products installed */}\n {isAuthenticated && deprecatedInstalledProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={AlertCircle}\n title=\"Deprecated Apps You've Installed\"\n subtitle=\"These apps are no longer supported — hover the badge for details\"\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {deprecatedInstalledProducts.map((product) => (\n <DeprecatedAppCard\n key={product.id}\n product={product}\n onClick={() => navigate(`${basePath}/marketplace/product/${product.id}`)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Empty State - No products at all */}\n {allProducts.length === 0 && (\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title=\"No apps available yet\"\n description=\"The marketplace is empty. Check back soon for new apps, workflows, and integrations.\"\n />\n )}\n </>\n )}\n </div>\n </main>\n\n {/* Cart Drawer */}\n <CartDrawer\n isOpen={cartOpen}\n onClose={closeCart}\n onCheckout={handleCheckout}\n cartItems={cartItems}\n itemCount={itemCount}\n subtotal={subtotal}\n tax={tax}\n total={total}\n updateQuantity={updateQuantity}\n removeProduct={removeProduct}\n />\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;AA2EA,IAAM,MAAmB,MACnB,KAAS,MAAgB,IAAI,IAAQ,KAAS,QAAQ,EAAE,CAAC,KACzD,KAAS,MAAa,IAAI,IAAQ,KAAM,QAAQ,EAAE,CAAC,KAChD,EAAM,UAAU,EAGnB,KAAmB,MACnB,EAAQ,iBAAiB,UACzB,EAAQ,UAAU,IAAU,SACzB,IAAI,EAAQ,SAGf,KAAmB,OACgB;CACrC,QAAQ;CACR,OAAO;CACP,SAAS;CACT,UAAU;CACV,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,WAAW;CACX,QAAQ;CACR,UAAU;CACV,KAAK;CACL,UAAU;CACV,aAAa;CACb,WAAW;CACX,OAAO;CACP,QAAQ;CACR,WAAW;CACZ,EACa,MAAS,2BAInB,KAAqB,MAElB,sBADO,EAAgB,EAAK,CACA,qBAU/B,IAAyC;CAC7C,QAAQ;CACR,OAAO;CACP,SAAS;CACT,UAAU;CACV,MAAM;CACN,QAAQ;CACR,YAAY;CACZ,WAAW;CACX,QAAQ;CACR,UAAU;CACV,KAAK;CACL,UAAU;CACV,aAAa;CACb,WAAW;CACX,OAAO;CACP,QAAQ;CACR,WAAW;CACZ,EAEK,KAA6C,EAAE,SAAM,cAAW,eAE7D,kBADe,EAAW,MAAS,GACnC;CAA0B;CAAkB;CAAS,CAAA,EAaxD,KAA6B,EAAE,YAAS,YAAS,aAAU,gBAAgB;CAC/E,IAAM,IAAe,EAAgB,EAAQ,KAAK;AAqGlD,QAnGI,MAAY,aAEZ,kBAAC,UAAD;EACE,MAAK;EACI;EACT,WAAU;YAHZ;GAKG,EAAQ,YACP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAAA,WAE3B;;GAER,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,iBAAiB,EAAkB,EAAQ,KAAK,EAAE;eAE1D,EAAQ,OACP,kBAAC,OAAD;MACE,KAAK,EAAQ;MACb,KAAK,EAAQ;MACb,WAAU;MACV,CAAA,GAEF,kBAAC,GAAD;MACE,MAAM,EAAQ;MACd,WAAU;MACV,OAAO,EAAE,OAAO,GAAc;MAC9B,CAAA;KAEA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ;MACN,CAAA,EACL,kBAAC,KAAD;MAAG,WAAU;gBAAb,CACE,kBAAC,GAAD;OAAiB,MAAM,EAAQ;OAAM,WAAU;OAAa,CAAA,EAC3D,EAAQ,YAAY,EAAQ,KAC3B;QACA;OACF;;GACN,kBAAC,KAAD;IAAG,WAAU;cACV,EAAQ,eAAe;IACtB,CAAA;GACJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAM,WAAU,gDAAiD,CAAA,GAC/D,EAAQ,UAAU,GAAG,QAAQ,EAAE,CAC5B;SACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAC9B,GAAgB,EAAQ,UAAU,CAC9B;QACH;QACN,kBAAC,QAAD;KAAM,WAAU;eACb,EAAgB,EAAQ;KACpB,CAAA,CACH;;GACC;MAIT,MAAY,YAEZ,kBAAC,UAAD;EACE,MAAK;EACI;EACT,WAAU;YAHZ;GAKE,kBAAC,OAAD;IACE,WAAU;IACV,OAAO,EAAE,iBAAiB,EAAkB,EAAQ,KAAK,EAAE;cAE1D,EAAQ,OACP,kBAAC,OAAD;KAAK,KAAK,EAAQ;KAAM,KAAK,EAAQ;KAAM,WAAU;KAAmC,CAAA,GAExF,kBAAC,GAAD;KACE,MAAM,EAAQ;KACd,WAAU;KACV,OAAO,EAAE,OAAO,GAAc;KAC9B,CAAA;IAEA,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAQ;KACN,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eAAb;MACG,EAAQ,YAAY,EAAQ;MAAK;OAAK,EAAQ,UAAU,GAAG,QAAQ,EAAE;MAAC;MACrE;OACA;;GACN,kBAAC,QAAD;IAAM,WAAU;cAAuC,EAAgB,EAAQ;IAAQ,CAAA;GAChF;MAMX,kBAAC,UAAD;EACE,MAAK;EACI;EACT,WAAU;YAHZ;GAKE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,iBAAiB,EAAkB,EAAQ,KAAK,EAAE;eAE1D,EAAQ,OACP,kBAAC,OAAD;MAAK,KAAK,EAAQ;MAAM,KAAK,EAAQ;MAAM,WAAU;MAAmC,CAAA,GAExF,kBAAC,GAAD;MACE,MAAM,EAAQ;MACd,WAAU;MACV,OAAO,EAAE,OAAO,GAAc;MAC9B,CAAA;KAEA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ;MACN,CAAA,EACL,kBAAC,KAAD;MAAG,WAAU;gBAAb,CACE,kBAAC,GAAD;OAAiB,MAAM,EAAQ;OAAM,WAAU;OAAW,CAAA,EACzD,EAAQ,YAAY,EAAQ,KAC3B;QACA;OACF;;GACN,kBAAC,KAAD;IAAG,WAAU;cACV,EAAQ,eAAe;IACtB,CAAA;GACJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,GAAD,EAAM,WAAU,kDAAmD,CAAA,GACjE,EAAQ,UAAU,GAAG,QAAQ,EAAE,CAC5B;;MACP,kBAAC,QAAD;OAAM,WAAU;iBAA0B;OAAQ,CAAA;MAClD,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,GAAgB,EAAQ,UAAU,EAAC,aAC/B;;MACH;QACN,kBAAC,QAAD;KAAM,WAAU;eAAwC,EAAgB,EAAQ;KAAQ,CAAA,CACpF;;GACC;;GAcP,MAA8E,EAClF,YACA,iBAEA,kBAAC,UAAD;CACE,MAAK;CACI;CACT,WAAU;WAHZ,CAKE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,EAAQ,OACP,kBAAC,OAAD;GACE,KAAK,EAAQ;GACb,KAAK,EAAQ;GACb,WAAU;GACV,CAAA,GAEF,kBAAC,GAAD,EAAK,WAAU,sCAAuC,CAAA,EAExD,kBAAC,QAAD;GAAM,WAAU;aACd,kBAAC,GAAD,EAAa,WAAU,qCAAsC,CAAA;GACxD,CAAA,CACH;KACN,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAQ;GACN,CAAA,EACL,kBAAC,QAAD;GACE,OAAM;GACN,WAAU;aAFZ,CAIE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EAAA,sBAE7B;KACH;IACC;IAcL,KAAyC,EAAE,MAAM,GAAM,UAAO,aAAU,mBAC5E,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAM,WAAU,yBAA0B,CAAA;GACtC,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aAA2C;GAAW,CAAA,EACnE,KAAY,kBAAC,KAAD;GAAG,WAAU;aAA2B;GAAa,CAAA,CAC9D,EAAA,CAAA,CACF;KACL,KACC,kBAAC,UAAD;EACE,MAAK;EACL,SAAS;EACT,WAAU;YAHZ,CAIC,YAEC,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,CAC5B;IAEP;IAeF,MAAuC,EAAE,WAAQ,SAAM,UAAO,WAAQ,iBAC1E,kBAAC,UAAD;CACE,MAAK;CACI;CACT,WAAW,oGACT,IACI,kDACA;WANR;EASE,kBAAC,GAAD;GAAiB,MAAM;GAAQ,WAAU;GAAW,CAAA;EACnD;EACD,kBAAC,QAAD;GACE,WAAW,sCAAsC,IAAS,qBAAqB;aAE9E;GACI,CAAA;EACA;IAcL,MAAqC,EAAE,qBAAkB,mBAAgB,kBAAe;CAC5F,IAAM,CAAC,GAAa,KAAkB,EAAS,EAAE,EAC3C,IAAc,EAAiB,MAAgB,EAAiB;AAEtE,KAAI,CAAC,EAAa,QAAO;CAEzB,IAAM,IAAe,EAAgB,EAAY,KAAK;AAEtD,QACE,kBAAC,IAAD;EACE,WAAU;EACV,MAAA;EACA,aAAW;EACX,WAAU;YAJZ;GAME,kBAAC,OAAD;IACE,WAAU;IACV,OAAO,EACL,YAAY,2BAA2B,EAAa,wBACrD;IACD,CAAA;GACF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAAA,eAE1B;WACP,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAY,YAAY,EAAY;QAChC,CAAA,CACH;;MACN,kBAAC,MAAD;OAAI,WAAU;iBAA6C,EAAY;OAAU,CAAA;MACjF,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAY,eAAe;OAC1B,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACE,kBAAC,GAAD,EAAM,WAAU,gDAAiD,CAAA;SACjE,kBAAC,QAAD;UAAM,WAAU;qBACZ,EAAY,UAAU,GAAG,QAAQ,EAAE;UAChC,CAAA;SACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WAAkC;WAAE,EAAY;WAAY;WAAgB;;SACvE;WACP,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;SAC9B,EAAY,UAAU,gBAAgB;SAAC;SACnC;UACH;;MACN,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAe,EAAY;OAC1C,WAAU;iBACX;OAEQ,CAAA;MACL;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,OAAO,EAAE,iBAAiB,EAAkB,EAAY,KAAK,EAAE;gBAE9D,EAAY,OACX,kBAAC,OAAD;OACE,KAAK,EAAY;OACjB,KAAK,EAAY;OACjB,WAAU;OACV,CAAA,GAEF,kBAAC,GAAD;OACE,MAAM,EAAY;OAClB,WAAU;OACV,OAAO,EAAE,OAAO,GAAc;OAC9B,CAAA;MAEA,CAAA;KACF,CAAA,CACF;;GAEL,EAAiB,SAAS,KACzB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAiB,KAAK,GAAS,MAC9B,kBAAC,UAAD;KACE,MAAK;KAEL,eAAe,EAAe,EAAM;KACpC,WAAW,uCACT,MAAU,IACN,yBACA;KAEN,cAAY,QAAQ,EAAQ;KAC5B,EARK,EAAQ,GAQb,CACF;IACE,CAAA;GAEE;;GAQV,KAA6E,EACjF,aAAU,gBAEN,MAAY,aAEZ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,oCAAqC,CAAA,EACpD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,EAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;KACF;KACN,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA,EACnD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;IACF;KAIN,MAAY,YAEZ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA,EACnD,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,EAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;IACF;KAKR,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA,EACnD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,EAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;KACF;KACN,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA,EACnD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;IACF;IAKJ,UACJ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAK,WAAU,sCAAuC,CAAA,EACtD,kBAAC,OAAD,EAAK,WAAU,sCAAuC,CAAA,CAClD;;IACN,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;IAClD,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA,EACnD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAK,WAAU,iCAAkC,CAAA,EACjD,kBAAC,OAAD,EAAK,WAAU,iCAAkC,CAAA,CAC7C;;IACN,kBAAC,OAAD,EAAK,WAAU,qCAAsC,CAAA;IACjD;MACN,kBAAC,OAAD,EAAK,WAAU,oDAAqD,CAAA,CAChE;KACN,kBAAC,OAAD;EAAK,WAAU;YACZ,CAAC,GAAG;;;;GAAQ,CAAC,CAAC,KAAK,GAAG,MACrB,kBAAC,OAAD,EAAa,WAAU,qCAAsC,EAAnD,EAAmD,CAC7D;EACE,CAAA,CACF;IAIF,IAAc;CAAC;CAAK;CAAK;CAAI;CAAK;CAAK;CAAI;CAAK;CAAI,EAEpD,UACJ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,EAC/D,kBAAC,OAAD;EAAK,WAAU;YACZ,EAAY,KAAK,GAAO,MACvB,kBAAC,OAAD;GAEE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,EAAM,KAAK;GAC9B,EAHK,EAGL,CACF;EACE,CAAA,CACF;IAIF,UACJ,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,iDAAkD,CAAA,EACjE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,EAC/D,kBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,CAC3D;KACF;KACN,kBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,CAC3D;IAIF,WACJ,kBAAC,OAAD;CAAK,WAAU;WAAf;EAEE,kBAAC,GAAD,EAAgB,CAAA;EAGhB,kBAAC,GAAD,EAAyB,CAAA;EAGzB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAyB,CAAA,EACzB,kBAAC,OAAD;GAAK,WAAU;aACZ,CAAC,GAAG;;;;;;;IAAQ,CAAC,CAAC,KAAK,GAAG,MACrB,kBAAC,GAAD,EAA+B,EAAL,EAAK,CAC/B;GACE,CAAA,CACF,EAAA,CAAA;EAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAyB,CAAA,EACzB,kBAAC,OAAD;GAAK,WAAU;aACZ,CAAC,GAAG;;;;;;;IAAQ,CAAC,CAAC,KAAK,GAAG,MACrB,kBAAC,GAAD,EAA+B,EAAL,EAAK,CAC/B;GACE,CAAA,CACF,EAAA,CAAA;EAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAyB,CAAA,EACzB,kBAAC,OAAD;GAAK,WAAU;aACZ,CAAC,GAAG;;;;;;;IAAQ,CAAC,CAAC,KAAK,GAAG,MACrB,kBAAC,GAAD,EAA6B,SAAQ,WAAY,EAAvB,EAAuB,CACjD;GACE,CAAA,CACF,EAAA,CAAA;EACF;IA0BF,KAA2C,EAAE,UAAO,UAAO,YAAS,kBAAe;CACvF,IAAM,IAAW,IAAO;AAExB,QACE,kBAAC,SAAD;EACE,SAAS;EACT,WAAU;YAFZ;GAIE,kBAAC,QAAD;IAAM,WAAU;cAAW;IAAa,CAAA;GACxC,kBAAC,QAAD;IAAM,WAAU;cAAhB,CAAmC,GAAM,IAAQ;;GACjD,kBAAC,UAAD;IACE,IAAI;IACJ,cAAY;IACL;IACP,WAAW,MAAU,EAAS,EAAM,OAAO,MAAM;IACjD,WAAU;cAET,EAAQ,KAAK,MACZ,kBAAC,UAAD;KAA2B,OAAO,EAAO;eACtC,EAAO;KACD,EAFI,EAAO,MAEX,CACT;IACK,CAAA;GACT,kBAAC,IAAD,EAAa,WAAU,+DAAgE,CAAA;GACjF;;GAQC,UAAgC;CAC3C,IAAM,IAAW,IAAa,EACxB,EAAE,aAAU,aAAS,GAAU,EAC/B,EAAE,SAAM,IAAS,EACjB,KAAgB,IAAO,EACvB,IAAM,IAAa,EACnB,EACJ,cACA,cACA,cACA,cACA,cACA,SACA,UACA,mBACA,qBACE,GAAS,EACP,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAc,KAAmB,EAAiB,MAAM,EAGzD,CAAC,GAAe,KAAoB,EAAwB,MAAM,EAClE,CAAC,GAAc,KAAmB,EAAuB,MAAM,EAC/D,CAAC,GAAc,MAAmB,EAAuB,MAAM,EAC/D,CAAC,GAAkB,MAAuB,EAAiB,MAAM,EACjE,CAAC,GAAiB,MAAsB,EAAiB,MAAM,EAC/D,CAAC,GAAc,MAAmB,EAAS,GAAM,EACjD,CAAC,GAAY,MAAiB,EAAqB,YAAY,EAC/D,CAAC,IAAa,MAAkB,EAAS,GAAM,EAG/C,IAAuB,GAAY,GAAa,KAAK,EAGrD,KAAiB;EACrB;GAAE,OAAO;GAAO,OAAO;GAAc;EACrC;GAAE,OAAO;GAAQ,OAAO;GAAQ;EAChC;GAAE,OAAO;GAAQ,OAAO;GAAmB;EAC3C;GAAE,OAAO;GAAgB,OAAO;GAAgB;EACjD,EAEK,KAAgB;EACpB;GAAE,OAAO;GAAO,OAAO;GAAc;EACrC;GAAE,OAAO;GAAK,OAAO;GAAY;EACjC;GAAE,OAAO;GAAO,OAAO;GAAc;EACrC;GAAE,OAAO;GAAK,OAAO;GAAY;EACjC;GAAE,OAAO;GAAO,OAAO;GAAc;EACtC,EAEK,KAAgB;EACpB;GAAE,OAAO;GAAO,OAAO;GAAa;EACpC;GAAE,OAAO;GAAW,OAAO;GAAW;EACtC;GAAE,OAAO;GAAY,OAAO;GAAY;EACzC,EAEK,KAAc;EAClB;GAAE,OAAO;GAAa,OAAO;GAAa;EAC1C;GAAE,OAAO;GAAU,OAAO;GAAiB;EAC3C;GAAE,OAAO;GAAa,OAAO;GAAkB;EAC/C;GAAE,OAAO;GAAU,OAAO;GAAgB;EAC1C;GAAE,OAAO;GAAa,OAAO;GAAsB;EACnD;GAAE,OAAO;GAAc,OAAO;GAAsB;EACpD;GAAE,OAAO;GAAY,OAAO;GAAY;EACzC,EAGK,MAAgB,OAC6B;EAC/C,WAAW,EAAY;EACvB,QAAQ,EAAY;EACpB,WAAW,EAAY;EACvB,QAAQ,EAAY;EACpB,aAAa,EAAY;EACzB,cAAc,EAAY;EAC1B,YAAY,EAAY;EACzB,EACc,IAIX,MAAmB,MAAqD;AACxE,YAAY,MAMhB,QAL8C;GAC5C,MAAM,EAAa;GACnB,MAAM,EAAa;GACnB,cAAc,EAAa;GAC5B,CACc;IAIX,IACJ,EAAqB,MAAM,KAAK,MAChC,MAAkB,SAClB,MAAiB,SACjB,MAAiB,SACjB,MAAqB,SACrB,MAAoB,SACpB,KACA,MAAiB,SACjB,MAAe,aAGX,KAAkB,EAAY,MAAM,KAAK,MAAM,MAAgB,GAE/D,WAAwB;AAS5B,EARA,EAAe,GAAG,EAClB,EAAiB,MAAM,EACvB,EAAgB,MAAM,EACtB,GAAgB,MAAM,EACtB,GAAoB,MAAM,EAC1B,GAAmB,MAAM,EACzB,GAAgB,GAAM,EACtB,GAAc,YAAY,EAC1B,EAAgB,MAAM;IAIlB,KACH,MAAkB,QAAY,IAAJ,MAC1B,MAAiB,QAAY,IAAJ,MACzB,MAAiB,QAAY,IAAJ,MACzB,MAAqB,QAAY,IAAJ,MAC7B,MAAoB,QAAY,IAAJ,KAC5B,QACA,MAAiB,QAAY,IAAJ,IAGtB,KAAqC,QAAc;EACvD,IAAM,IAA0B;GAC9B,QAAQ,GAAa,EAAW;GAChC,YAAY,EAAE,OAAO,IAAI;GAC1B;AAGD,MAAI,GAAkB;AAqBpB,GApBA,EAAM,SAAS,EAAE,EAGb,EAAqB,MAAM,KAC7B,EAAM,OAAO,SAAS,EAAqB,MAAM,GAI/C,MAAiB,UACnB,EAAM,OAAO,OAAO,IAMlB,MAAqB,UACvB,EAAM,OAAO,WAAW,IAItB,MAAiB,UACnB,EAAM,OAAO,SACX,MAAiB,YAAY,EAAc,UAAU,EAAc;GAIvE,IAAM,IAAe,GAAgB,EAAc;AAgBnD,GAfI,MACF,EAAM,OAAO,eAAe,IAI1B,MAAiB,UACnB,EAAM,OAAO,YAAY,WAAW,EAAa,GAI/C,MACF,EAAM,OAAO,WAAW,KAItB,MAAoB,UACtB,EAAM,OAAO,UAAU;;AAI3B,SAAO;IACN;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAGI,EACJ,aACA,mBACA,kBACA,cACA,SAAS,IACT,OAAO,IACP,SAAS,OACP,EAAuB,EAAE,MAAM,GAAkB,CAAC,EAGhD,EACJ,UAAU,IACV,eACA,WACA,SAAS,IACT,OAAO,IACP,SAAS,OACP,EAA0B,IAAmB,KAAmB,EAAE,EAAE,EACtE,MAAM,CAAC,GACR,CAAC,EAGI,KAAU,IAAmB,KAAgB,IAC7C,KAAQ,IAAmB,KAAc,IACzC,KAAU,IAAmB,KAAgB,IAG7C,KAAmB,IAAmB,KAAiB,EAAE,EAIzD,KAAgB,GAAO,EAAW;AACxC,IAAc,UAAU;CACxB,IAAM,KAAS,GAAO,EAAI;AAE1B,CADA,GAAO,UAAU,GACjB,SAAgB;AACV,GAAC,EAAqB,MAAM,IAAI,MACnC,GAAO,QAA0E,KAChF,8BACA;GACE,OAAO;GACP,aAAa,GAAc;GAC5B,CACF;IACA,CAAC,GAAsB,GAAc,CAAC;CAEzC,IAAM,IAAqB,IACxB,MAA0B;AACzB,IAAS,GAAG,EAAS,uBAAuB,EAAQ,KAAK;IAE3D,CAAC,GAAU,EAAS,CACrB,EAEK,WAAuB;AAE3B,EADA,GAAW,EACX,EAAS,GAAG,EAAS,OAAO;IAIxB,IAAc,QACZ,CACJ,GAAG,IAAI,IACL;EAAC,GAAG;EAAU,GAAG;EAAgB,GAAG;EAAe,GAAG;EAAU,CAAC,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CACvF,CAAC,QAAQ,CACX,EACD;EAAC;EAAU;EAAgB;EAAe;EAAU,CACrD,EAGK,KAAkB,CAAC,CAAC,IAAM,IAC1B,EAAE,MAAM,OAAsB,EAAgC;EAClE,WAAW,EAAE,OAAO,KAAK;EACzB,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAGI,KAA8B,QAAmC;AACrE,MAAI,CAAC,IAAmB,yBAAyB,MAAO,QAAO,EAAE;EACjE,IAAM,oBAAO,IAAI,KAAa,EACxB,IAA8B,EAAE;AACtC,OAAK,IAAM,KAAQ,GAAkB,wBAAwB,MAC3D,CAAI,EAAK,kBAAkB,cAAc,CAAC,EAAK,IAAI,EAAK,UAAU,KAChE,EAAK,IAAI,EAAK,UAAU,EACxB,EAAO,KAAK;GACV,IAAI,EAAK;GACT,MAAM,EAAK,eAAe;GAC1B,MAAM,EAAK,eAAe,KAAA;GAC3B,CAAC;AAGN,SAAO;IACN,CAAC,GAAkB,CAAC,EAGjB,KAAiB,QAAc;AAEnC,MAAI,GAAQ,SAAS,EAAO,MAAM,SAAS,EACzC,QAAO,CACL;GAAE,IAAI;GAAO,MAAM;GAAY,OAAO;GAAY,EAClD,GAAG,EAAO,MAAM,KAAK,OAAO;GAC1B,IAAI,EAAE;GACN,MAAM,EAAE,MAAM,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,CAAC,aAAa,CAAC,QAAQ,MAAM,IAAI;GAC3E,OAAO,EAAE;GACV,EAAE,CACJ;EAGH,IAAM,IAAa,EAAY,QAA6B,GAAK,OAC/D,EAAI,IAAI,EAAE,OAAO,EAAI,IAAI,EAAE,KAAK,IAAI,KAAK,EAAE,EACpC,oBACN,IAAI,KAAK,CAAC,EACP,IAAc,MAAM,KAAK,EAAW,SAAS,CAAC,CACjD,SAAS,CAAC,GAAM,OACf,IAAQ,IACJ,CACE;GACE,IAAI;GACJ,MAAM,EAAK,OAAO,EAAE,GAAG,EAAK,MAAM,EAAE,CAAC,aAAa,CAAC,QAAQ,MAAM,IAAI;GACrE;GACD,CACF,GACD,EAAE,CACP,CACA,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AACpC,SAAO,CAAC;GAAE,IAAI;GAAO,MAAM;GAAY,OAAO,EAAY;GAAQ,EAAE,GAAG,EAAY;IAClF;EAAC;EAAQ;EAAa;EAAW,CAAC,EAG/B,KAAkB,QAClB,GAAQ,cAAc,EAAO,WAAW,SAAS,IAC5C,CACL;EAAE,OAAO;EAAO,OAAO;EAAkB,EACzC,GAAG,EAAO,WAAW,KAAK,OAAO;EAC/B,OAAO,EAAE;EACT,OAAO,EAAE;EACV,EAAE,CACJ,GAEI,CACL;EAAE,OAAO;EAAO,OAAO;EAAkB,EACzC,GAAG,MAAM,KAAK,IAAI,IAAI,EAAY,SAAS,MAAO,EAAE,WAAW,CAAC,EAAE,SAAS,GAAG,EAAE,CAAE,CAAC,CAAC,CACjF,KAAK,OAAS;EACb,OAAO;EACP,OAAO;EACR,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC,CAClD,EACA,CAAC,GAAQ,EAAY,CAAC,EAGnB,KAAiB,QACjB,GAAQ,YAAY,EAAO,SAAS,SAAS,IACxC,CACL;EAAE,OAAO;EAAO,OAAO;EAAgB,EACvC,GAAG,EAAO,SAAS,KAAK,OAAO;EAC7B,OAAO,EAAE;EACT,OAAO,EAAE;EACV,EAAE,CACJ,GAEI,CAAC;EAAE,OAAO;EAAO,OAAO;EAAgB,CAAC,EAC/C,CAAC,EAAO,CAAC;AAEZ,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAAlB;KACE,kBAAC,IAAD,EAAkB,WAAU,UAAW,CAAA;KACvC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAd;QAAqD;QAC/C;QACJ,kBAAC,IAAD,EAAA,UACG,EAAE,2BAA2B,EAAE,cAAc,eAAe,CAAC,EACjD,CAAA;QACZ;UACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAE,8BAA8B,EAC/B,cAAc,iEACf,CAAC;OACA,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;OACE,MAAK;OACL,aAAU;OACV,eAAe,EAAS,GAAG,EAAS,OAAO;OAC3C,WAAU;iBAJZ;QAME,kBAAC,IAAD,EAAc,WAAU,UAAW,CAAA;QAClC,EAAE,0BAA0B,EAAE,cAAc,QAAQ,CAAC;QACrD,IAAY,KACX,kBAAC,QAAD;SAAM,WAAU;mBACb;SACI,CAAA;QAEF;SACL;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,SAAD;SAAO,SAAS;SAAe,WAAU;mBACtC,EAAE,kCAAkC,EAAE,cAAc,+BAA+B,CAAC;SAC/E,CAAA;QACR,kBAAC,IAAD,EAAQ,WAAU,mEAAoE,CAAA;QACtF,kBAAC,SAAD;SACE,IAAI;SACJ,aAAU;SACV,cAAY,EAAE,kCAAkC,EAC9C,cAAc,+BACf,CAAC;SACF,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;SAC/C,aAAa,EAAE,wCAAwC,EACrD,cAAc,2CACf,CAAC;SACF,WAAU;SACV,CAAA;QACD,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAe,GAAG;SACjC,cAAY,EAAE,kCAAkC,EAAE,cAAc,gBAAgB,CAAC;SACjF,WAAU;mBAEV,kBAAC,GAAD;UAAG,WAAU;UAAS,eAAY;UAAS,CAAA;SACpC,CAAA;QAEP;UAEL,EAAY,MAAM,IACjB,kBAAC,OAAD;OACE,aAAU;OACV,WAAU;iBAET,MAAmB,KAClB,kBAAA,IAAA,EAAA,UAAA,CACE,kBAAC,IAAD,EAAS,WAAU,uBAAwB,CAAA,EAC3C,kBAAC,QAAD,EAAA,UACG,EAAE,sCAAsC,EAAE,cAAc,gBAAgB,CAAC,EACrE,CAAA,CACN,EAAA,CAAA,GAEH,kBAAC,QAAD,EAAA,UAAA;QACG;QAAW;QAAQ,MAAe,IAAU,KAAN;QAAS;QAAa;QAAqB;QAE7E,EAAA,CAAA;OAEL,CAAA,CAEJ;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEE,kBAAC,UAAD;QACE,MAAK;QACL,aAAU;QACV,eAAe,GAAe,CAAC,GAAY;QAC3C,WAAW,4GACT,MAAe,IAAoB,IAC/B,wEACA;kBAPR;SAUE,kBAAC,IAAD,EAAmB,WAAU,UAAW,CAAA;SACvC,EAAE,6BAA6B,EAAE,cAAc,WAAW,CAAC;SAC3D,IAAoB,KACnB,kBAAC,QAAD;UAAM,WAAU;oBACb;UACI,CAAA;SAEF;;OAGT,kBAAC,OAAD;QACE,WAAU;QACV,aAAU;kBAFZ;SAIE,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAiB,MAAkB,SAAS,QAAQ,OAAO;UAC1E,WAAW,2GACT,MAAkB,SACd,kDACA;oBANR,CASE,kBAAC,IAAD,EAAM,WAAU,YAAa,CAAA,EAAA,OAEtB;;SACT,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAgB,MAAiB,MAAM,QAAQ,IAAI;UAClE,WAAW,2GACT,MAAiB,MACb,kDACA;oBANR,CASE,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA,EAAA,WAEtB;;SACT,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,GAAgB,CAAC,EAAa;UAC7C,WAAW,2GACT,IACI,kDACA;oBANR,CASE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAAA,WAE1B;;SACL;;OAGL,KACC,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAHZ,CAKE,kBAAC,GAAD;SAAG,WAAU;SAAS,eAAY;SAAS,CAAA,EAAA,YAEpC;;OAIX,kBAAC,OAAD;QAAK,WAAU;QAAU,aAAU;kBACjC,kBAAC,GAAD;SACE,OAAM;SACN,OAAO;SACP,SAAS;SACT,WAAW,MAAU,GAAc,EAAoB;SACvD,CAAA;QACE,CAAA;OACF;;KAGL,MACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAA+D;UAExE,CAAA,EACP,kBAAC,GAAD;UACE,OAAM;UACN,OAAO;UACP,SAAS;UACT,WAAW,MAAU,EAAiB,EAAuB;UAC7D,CAAA,CACE;;QAGN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAA+D;UAExE,CAAA,EACP,kBAAC,GAAD;UACE,OAAM;UACN,OAAO;UACP,SAAS;UACT,WAAW,MAAU,EAAgB,EAAsB;UAC3D,CAAA,CACE;;QAGL,GAAgB,SAAS,KACxB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAA+D;UAExE,CAAA,EACP,kBAAC,GAAD;UACE,OAAM;UACN,OAAO;UACP,SAAS;UACT,UAAU;UACV,CAAA,CACE;;QAIR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAA+D;UAExE,CAAA,EACP,kBAAC,GAAD;UACE,OAAM;UACN,OAAO;UACP,SAAS;UACT,WAAW,MAAU,GAAgB,EAAsB;UAC3D,CAAA,CACE;;QAGL,GAAe,SAAS,KACvB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAA+D;UAExE,CAAA,EACP,kBAAC,GAAD;UACE,OAAM;UACN,OAAO;UACP,SAAS;UACT,UAAU;UACV,CAAA,CACE;;QAEJ;UAGL,IAAoB,KACnB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAsC;SAAsB,CAAA;QAC3E,MAAiB,SAChB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAgB,MAAM;SACrC,WAAU;mBAHZ;UAKE,kBAAC,QAAD;WAAM,WAAU;qBAAkB;WAAY,CAAA;;UAAE;UAChD,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEV,MAAkB,SACjB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAiB,MAAM;SACtC,WAAU;mBAHZ;UAKE,kBAAC,QAAD;WAAM,WAAU;qBAAkB;WAAa,CAAA;UAAC;UAC/C,GAAe,MAAM,MAAM,EAAE,UAAU,EAAc,EAAE;UACxD,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEV,MAAiB,SAChB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAgB,MAAM;SACrC,WAAU;mBAHZ;UAKE,kBAAC,QAAD;WAAM,WAAU;qBAAkB;WAAc,CAAA;UAAC;UAChD,GAAc,MAAM,MAAM,EAAE,UAAU,EAAa,EAAE;UACtD,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEV,MAAqB,SACpB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,GAAoB,MAAM;SACzC,WAAU;mBAHZ;UAKE,kBAAC,QAAD;WAAM,WAAU;qBAAkB;WAAgB,CAAA;;UAAE;UACpD,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEV,MAAiB,SAChB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,GAAgB,MAAM;SACrC,WAAU;mBAHZ;UAKE,kBAAC,QAAD;WAAM,WAAU;qBAAkB;WAAc,CAAA;UAAC;UAChD,GAAc,MAAM,MAAM,EAAE,UAAU,EAAa,EAAE;UACtD,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEV,MAAoB,SACnB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,GAAmB,MAAM;SACxC,WAAU;mBAHZ;UAKE,kBAAC,QAAD;WAAM,WAAU;qBAAkB;WAAe,CAAA;UAAC;UACjD,GAAe,MAAM,MAAM,EAAE,UAAU,EAAgB,EAAE;UAC1D,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEV,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,GAAgB,GAAM;SACrC,WAAU;mBAHZ;UAKE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;;UAE/B,kBAAC,GAAD;WAAG,WAAU;WAAS,eAAY;WAAS,CAAA;UACpC;;QAEP;SAEJ;;KAED;;GAGT,kBAAC,QAAD;IAAM,WAAU;cACd,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,IAAD,EAAA,UAAa,6SAKC,CAAA;MAEb,MACC,kBAAC,OAAD;OACE,MAAK;OACL,WAAU;iBAFZ;QAIE,kBAAC,GAAD,EAAa,WAAU,uCAAwC,CAAA;QAC/D,kBAAC,MAAD;SAAI,WAAU;mBAA6D;SAEtE,CAAA;QACL,kBAAC,KAAD;SAAG,WAAU;mBACV,GAAM,WAAW;SAChB,CAAA;QACJ,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,IAAS;SACxB,WAAU;mBACX;SAEQ,CAAA;QACL;;MAIP,MAAW,CAAC,MAAS,kBAAC,IAAD,EAA2B,CAAA;MAGhD,CAAC,MAAW,CAAC,MACZ,kBAAA,IAAA,EAAA,UAAA;OAEG,CAAC,KAAoB,MAAiB,SAAS,EAAS,SAAS,KAChE,kBAAC,IAAD;QACE,UAAS;QACT,kBAAkB;QAClB,gBAAgB;QAChB,CAAA;OAIH,GAAe,SAAS,KACvB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA+C;QAAmB,CAAA,EAChF,kBAAC,OAAD;QACE,WAAU;QACV,aAAU;kBAET,GAAe,KAAK,MACnB,kBAAC,IAAD;SAEE,QAAQ,EAAS;SACjB,MAAM,EAAS;SACf,OAAO,EAAS;SAChB,QAAQ,MAAiB,EAAS;SAClC,eAAe,EAAgB,EAAS,GAAG;SAC3C,EANK,EAAS,GAMd,CACF;QACE,CAAA,CACF,EAAA,CAAA;OAIP,KACC,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QACE,MAAM;QACN,OAAO,IAAuB,mBAAmB;QACjD,UAAU,GAAG,EAAW,MAAM,MAAe,IAAU,KAAN,IAAS;QAC1D,CAAA,EACD,GAAiB,SAAS,IACzB,kBAAC,OAAD;QACE,WAAU;QACV,aAAU;kBAET,GAAiB,KAAK,MACrB,kBAAC,GAAD;SAEW;SACT,eAAe,EAAmB,EAAQ;SAC1C,EAHK,EAAQ,GAGb,CACF;QACE,CAAA,GAEN,kBAAC,IAAD;QACE,cAAa;QACb,OAAM;QACN,aAAY;QACZ,QACE,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,WAAU;mBACX;SAEQ,CAAA;QAEX,CAAA,CAEI,EAAA,CAAA;OAIX,CAAC,KAAoB,MAAiB,SACrC,kBAAA,IAAA,EAAA,UAAA;QAEG,EAAe,SAAS,KACvB,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SACE,MAAM;SACN,OAAM;SACN,UAAS;SACT,CAAA,EACF,kBAAC,OAAD;SACE,WAAU;SACV,aAAU;mBAET,EAAe,MAAM,GAAG,EAAE,CAAC,KAAK,MAC/B,kBAAC,GAAD;UAEW;UACT,eAAe,EAAmB,EAAQ;UAC1C,EAHK,EAAQ,GAGb,CACF;SACE,CAAA,CACE,EAAA,CAAA;QAIX,EAAc,SAAS,KACtB,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SACE,MAAM;SACN,OAAM;SACN,UAAS;SACT,CAAA,EACF,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAc,MAAM,GAAG,EAAE,CAAC,KAAK,MAC9B,kBAAC,GAAD;UAEW;UACT,eAAe,EAAmB,EAAQ;UAC1C,EAHK,EAAQ,GAGb,CACF;SACE,CAAA,CACE,EAAA,CAAA;QAIX,EAAU,SAAS,KAClB,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SACE,MAAM;SACN,OAAM;SACN,UAAS;SACT,CAAA,EACF,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAU,MAAM,GAAG,EAAE,CAAC,KAAK,MAC1B,kBAAC,GAAD;UAEW;UACT,eAAe,EAAmB,EAAQ;UAC1C,SAAQ;UACR,EAJK,EAAQ,GAIb,CACF;SACE,CAAA,CACE,EAAA,CAAA;QAIX,EAAY,SAAS,KACpB,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SACE,MAAM;SACN,OAAM;SACN,UAAU,GAAG,EAAY,OAAO;SAChC,CAAA,EACF,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAY,KAAK,MAChB,kBAAC,GAAD;UAEW;UACT,eAAe,EAAmB,EAAQ;UAC1C,EAHK,EAAQ,GAGb,CACF;SACE,CAAA,CACE,EAAA,CAAA;QAEX,EAAA,CAAA;OAIJ,MAAmB,GAA4B,SAAS,KACvD,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QACE,MAAM;QACN,OAAM;QACN,UAAS;QACT,CAAA,EACF,kBAAC,OAAD;QAAK,WAAU;kBACZ,GAA4B,KAAK,MAChC,kBAAC,IAAD;SAEW;SACT,eAAe,EAAS,GAAG,EAAS,uBAAuB,EAAQ,KAAK;SACxE,EAHK,EAAQ,GAGb,CACF;QACE,CAAA,CACE,EAAA,CAAA;OAIX,EAAY,WAAW,KACtB,kBAAC,IAAD;QACE,cAAa;QACb,OAAM;QACN,aAAY;QACZ,CAAA;OAEH,EAAA,CAAA;MAED;;IACD,CAAA;GAGP,kBAAC,IAAD;IACE,QAAQ;IACR,SAAS;IACT,YAAY;IACD;IACA;IACD;IACL;IACE;IACS;IACD;IACf,CAAA;GACE"}
1
+ {"version":3,"file":"MarketplaceHomePage.js","names":[],"sources":["../../src/pages/MarketplaceHomePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useCallback, useMemo, useId, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Search,\n Star,\n Download,\n ChevronRight,\n ChevronDown,\n Sparkles,\n TrendingUp,\n Clock,\n Bot,\n Brain,\n LayoutDashboard,\n GitBranch,\n Box,\n Puzzle,\n ShoppingCart,\n X,\n AlertCircle,\n Gift,\n Filter,\n SlidersHorizontal,\n Loader2,\n FileCode2,\n Database,\n type LucideIcon,\n} from 'lucide-react';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { CartDrawer } from '../components/cart/CartDrawer';\nimport {\n useHomeGroupedProducts,\n useBrowseStoreWithContext,\n type StoreProduct,\n} from '../hooks/useStoreGraphQL';\nimport type { BrowseStoreInput } from '../generated/global-types';\nimport { StoreSortBy, PricingModel, ProductNature } from '../generated/global-types';\nimport { useGetMyStoreInstallationsQuery } from '../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\n// ========================================\n// Helper Types and Functions\n// ========================================\n\ntype ProductTypeKey =\n | 'BOTAPP'\n | 'AGENT'\n | 'CONSOLE'\n | 'WORKFLOW'\n | 'NODE'\n | 'WIDGET'\n | 'VIBEMODULE'\n | 'DASHBOARD'\n | 'PARSER'\n | 'DATASINK'\n | 'APP'\n | 'TEMPLATE'\n | 'INTEGRATION'\n | 'EXTENSION'\n | 'THEME'\n | 'PLUGIN'\n | 'COMPONENT'\n | string;\n\nconst formatDownloads = (count: number): string => {\n if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;\n if (count >= 1000) return `${(count / 1000).toFixed(1)}K`;\n return count.toString();\n};\n\nconst getPriceDisplay = (product: StoreProduct): string => {\n if (product.pricingModel === 'FREE') return 'Free';\n if (product.price === 0) return 'Free';\n return `$${product.price}`;\n};\n\nconst getProductColor = (type: ProductTypeKey): string => {\n const colors: Record<string, string> = {\n BOTAPP: 'var(--color-accent-blue)',\n AGENT: 'var(--color-accent-purple)',\n CONSOLE: 'var(--color-accent-teal)',\n WORKFLOW: 'var(--color-status-warning-bg)',\n NODE: 'var(--color-status-error-bg)',\n WIDGET: 'var(--color-accent-purple)',\n VIBEMODULE: 'var(--color-accent-blue)',\n DASHBOARD: 'var(--color-accent-blue)',\n PARSER: 'var(--color-status-warning-bg)',\n DATASINK: 'var(--color-status-success-bg)',\n APP: 'var(--color-accent-indigo)',\n TEMPLATE: 'var(--color-accent-teal)',\n INTEGRATION: 'var(--color-status-warning-bg)',\n EXTENSION: 'var(--color-accent-purple)',\n THEME: 'var(--color-accent-purple)',\n PLUGIN: 'var(--color-status-success-bg)',\n COMPONENT: 'var(--color-accent-blue)',\n };\n return colors[type] || 'var(--color-text-muted)';\n};\n\n// Helper to get translucent background color from a token\nconst getProductBgColor = (type: ProductTypeKey): string => {\n const color = getProductColor(type);\n return `color-mix(in srgb, ${color}, transparent 85%)`;\n};\n\n// Component wrapper to render type icons\ninterface ProductTypeIconProps {\n type: ProductTypeKey;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst TYPE_ICONS: Record<string, LucideIcon> = {\n BOTAPP: Bot,\n AGENT: Brain,\n CONSOLE: LayoutDashboard,\n WORKFLOW: GitBranch,\n NODE: Box,\n WIDGET: Puzzle,\n VIBEMODULE: Puzzle,\n DASHBOARD: LayoutDashboard,\n PARSER: FileCode2,\n DATASINK: Database,\n APP: Box,\n TEMPLATE: LayoutDashboard,\n INTEGRATION: GitBranch,\n EXTENSION: Puzzle,\n THEME: Puzzle,\n PLUGIN: Puzzle,\n COMPONENT: Box,\n};\n\nconst ProductTypeIcon: FC<ProductTypeIconProps> = ({ type, className, style }) => {\n const IconComponent = TYPE_ICONS[type] || Box;\n return <IconComponent className={className} style={style} />;\n};\n\n// ========================================\n// App Card Component\n// ========================================\n\ninterface AppCardProps {\n product: StoreProduct;\n onClick: () => void;\n variant?: 'default' | 'compact' | 'featured';\n}\n\nconst AppCard: FC<AppCardProps> = ({ product, onClick, variant = 'default' }) => {\n const productColor = getProductColor(product.type);\n\n if (variant === 'featured') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group relative flex size-full cursor-pointer flex-col overflow-hidden rounded-2xl border border-border-seam bg-gradient-to-br from-bg-surface to-bg-sunken p-6 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n {product.featured && (\n <div className=\"absolute right-4 top-4 flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-1 text-xs font-medium text-status-warning-text\">\n <Sparkles className=\"size-3\" />\n Featured\n </div>\n )}\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex size-16 flex-shrink-0 items-center justify-center rounded-2xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-10\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h3>\n <p className=\"flex items-center gap-2 text-sm text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3.5\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-4 line-clamp-2 flex-1 text-sm text-text-muted\">\n {product.description || 'No description available'}\n </p>\n <div className=\"mt-4 flex items-center justify-between border-t border-border-seam pt-4\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Star className=\"size-4 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {formatDownloads(product.downloads)}\n </span>\n </div>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-sm font-medium text-text-link\">\n {getPriceDisplay(product)}\n </span>\n </div>\n </button>\n );\n }\n\n if (variant === 'compact') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-transparent p-3 text-left transition-[background-color,border-color] hover:border-border-seam hover:bg-bg-surface\"\n >\n <div\n className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-8 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-8\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-medium text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"truncate text-xs text-text-muted\">\n {product.category || product.type} · {(product.rating ?? 0).toFixed(1)} ★\n </p>\n </div>\n <span className=\"text-xs font-medium text-text-muted\">{getPriceDisplay(product)}</span>\n </button>\n );\n }\n\n // Default variant\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface p-4 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n <div\n className=\"flex size-14 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-9 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-9\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-3 line-clamp-2 text-sm text-text-muted\">\n {product.description || 'No description available'}\n </p>\n <div className=\"mt-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-0.5 text-xs text-text-muted\">\n <Star className=\"size-3.5 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-xs text-text-muted\">·</span>\n <span className=\"text-xs text-text-muted\">\n {formatDownloads(product.downloads)} downloads\n </span>\n </div>\n <span className=\"text-xs font-semibold text-text-link\">{getPriceDisplay(product)}</span>\n </div>\n </button>\n );\n};\n\n// ========================================\n// Deprecated App Card Component\n// ========================================\n\ninterface DeprecatedProduct {\n id: string;\n name: string;\n icon?: string;\n}\n\nconst DeprecatedAppCard: FC<{ product: DeprecatedProduct; onClick: () => void }> = ({\n product,\n onClick,\n}) => (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-status-warning-border/30 bg-bg-surface p-4 text-left shadow-elevation-1 transition-all hover:border-status-warning-border/60 hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"relative flex size-14 flex-shrink-0 items-center justify-center rounded-xl bg-status-warning-bg-subtle\">\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-9 rounded-lg object-cover grayscale\"\n />\n ) : (\n <Box className=\"size-9 text-status-warning-text/50\" />\n )}\n <span className=\"absolute -right-1 -top-1 flex size-4 items-center justify-center rounded-full bg-status-warning-bg-subtle border border-status-warning-border/40\">\n <AlertCircle className=\"size-2.5 text-status-warning-text\" />\n </span>\n </div>\n <div className=\"min-w-0 flex-1\">\n <h4 className=\"truncate font-semibold text-text-secondary group-hover:text-text-link\">\n {product.name}\n </h4>\n <span\n title=\"This node is no longer supported. We encourage you to migrate to newer alternatives.\"\n className=\"mt-1 inline-flex cursor-help items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\"\n >\n <AlertCircle className=\"size-3\" />\n No Longer Supported\n </span>\n </div>\n </button>\n);\n\n// ========================================\n// Section Header Component\n// ========================================\n\ninterface SectionHeaderProps {\n icon: typeof Sparkles;\n title: string;\n subtitle?: string;\n onViewAll?: () => void;\n}\n\nconst SectionHeader: FC<SectionHeaderProps> = ({ icon: Icon, title, subtitle, onViewAll }) => (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Icon className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n {subtitle && <p className=\"text-sm text-text-muted\">{subtitle}</p>}\n </div>\n </div>\n {onViewAll && (\n <button\n type=\"button\"\n onClick={onViewAll}\n className=\"cursor-pointer flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n View all\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n);\n\n// ========================================\n// Category Chip Component\n// ========================================\n\ninterface CategoryChipProps {\n typeId: ProductTypeKey;\n name: string;\n count: number;\n active?: boolean;\n onClick: () => void;\n}\n\nconst CategoryChip: FC<CategoryChipProps> = ({ typeId, name, count, active, onClick }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex cursor-pointer items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all ${\n active\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-bg-sunken'\n }`}\n >\n <ProductTypeIcon type={typeId} className=\"size-4\" />\n {name}\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${active ? 'bg-bg-surface/20' : 'bg-bg-sunken'}`}\n >\n {count}\n </span>\n </button>\n);\n\n// ========================================\n// Hero Section Component\n// ========================================\n\ninterface HeroSectionProps {\n featuredProducts: StoreProduct[];\n onProductClick: (product: StoreProduct) => void;\n /** Optional tour anchor placed on the hero root (avoids an extra wrapper div). */\n dataTour?: string;\n}\n\nconst HeroSection: FC<HeroSectionProps> = ({ featuredProducts, onProductClick, dataTour }) => {\n const [activeIndex, setActiveIndex] = useState(0);\n const heroProduct = featuredProducts[activeIndex] || featuredProducts[0];\n\n if (!heroProduct) return null;\n\n const productColor = getProductColor(heroProduct.type);\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow\n data-tour={dataTour}\n className=\"relative overflow-hidden rounded-2xl p-8\"\n >\n <div\n className=\"absolute inset-0 opacity-10\"\n style={{\n background: `linear-gradient(135deg, ${productColor} 0%, transparent 50%)`,\n }}\n />\n <div className=\"relative z-10 flex items-center gap-8\">\n <div className=\"flex-1\">\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-3 py-1 text-xs font-semibold text-status-warning-text motion-safe:animate-vc-glow-pulse\">\n <Sparkles className=\"size-3\" />\n Featured App\n </span>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-text-link\">\n {heroProduct.category || heroProduct.type}\n </span>\n </div>\n <h1 className=\"mb-3 text-3xl font-bold text-text-primary\">{heroProduct.name}</h1>\n <p className=\"mb-6 max-w-xl text-text-muted\">\n {heroProduct.description || 'No description available'}\n </p>\n <div className=\"mb-6 flex items-center gap-4\">\n <span className=\"flex items-center gap-1 text-sm\">\n <Star className=\"size-5 fill-current text-status-warning-text\" />\n <span className=\"font-semibold text-text-primary\">\n {(heroProduct.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-text-muted\">({heroProduct.reviewCount} reviews)</span>\n </span>\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {heroProduct.downloads.toLocaleString()} downloads\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => onProductClick(heroProduct)}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n View Details\n </button>\n </div>\n <div className=\"hidden flex-shrink-0 lg:block\">\n <div\n className=\"flex size-48 items-center justify-center rounded-3xl shadow-lg\"\n style={{ backgroundColor: getProductBgColor(heroProduct.type) }}\n >\n {heroProduct.icon ? (\n <img\n src={heroProduct.icon}\n alt={heroProduct.name}\n className=\"size-32 rounded-2xl object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={heroProduct.type}\n className=\"size-32\"\n style={{ color: productColor }}\n />\n )}\n </div>\n </div>\n </div>\n {/* Featured App Selector */}\n {featuredProducts.length > 1 && (\n <div className=\"relative z-10 mt-6 flex gap-2\">\n {featuredProducts.map((product, index) => (\n <button\n type=\"button\"\n key={product.id}\n onClick={() => setActiveIndex(index)}\n className={`h-2 w-8 rounded-full transition-all ${\n index === activeIndex\n ? 'bg-action-primary-bg'\n : 'bg-border-default hover:bg-action-primary-border'\n }`}\n aria-label={`View ${product.name}`}\n />\n ))}\n </div>\n )}\n </GlassCard>\n );\n};\n\n// ========================================\n// Loading Skeleton Components\n// ========================================\n\nconst ProductCardSkeleton: FC<{ variant?: 'default' | 'compact' | 'featured' }> = ({\n variant = 'default',\n}) => {\n if (variant === 'featured') {\n return (\n <div className=\"h-full animate-pulse rounded-2xl border border-border-seam bg-bg-surface p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-2xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-4 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-4 space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n if (variant === 'compact') {\n return (\n <div className=\"flex animate-pulse items-center gap-3 p-3\">\n <div className=\"size-12 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"animate-pulse rounded-xl border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"size-14 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-3 space-y-2\">\n <div className=\"h-3 w-full rounded bg-bg-sunken\" />\n <div className=\"h-3 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n};\n\n// Hero Section Skeleton\nconst HeroSkeleton: FC = () => (\n <div className=\"animate-pulse rounded-2xl bg-gradient-to-r from-bg-surface via-bg-sunken to-bg-surface p-8\">\n <div className=\"flex items-center gap-8\">\n <div className=\"flex-1 space-y-4\">\n <div className=\"flex gap-2\">\n <div className=\"h-6 w-24 rounded-full bg-bg-sunken\" />\n <div className=\"h-6 w-20 rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"h-9 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-5/6 rounded bg-bg-sunken\" />\n </div>\n <div className=\"flex gap-4\">\n <div className=\"h-5 w-24 rounded bg-bg-sunken\" />\n <div className=\"h-5 w-32 rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-36 rounded-lg bg-bg-sunken\" />\n </div>\n <div className=\"hidden size-48 rounded-3xl bg-bg-sunken lg:block\" />\n </div>\n <div className=\"mt-6 flex gap-2\">\n {[...Array(3)].map((_, i) => (\n <div key={i} className=\"h-2 w-8 rounded-full bg-bg-sunken\" />\n ))}\n </div>\n </div>\n);\n\n// Category Chips Skeleton (Browse by Type)\nconst CHIP_WIDTHS = [100, 120, 90, 110, 130, 95, 115, 105];\n\nconst CategoryChipsSkeleton: FC = () => (\n <div className=\"space-y-4\">\n <div className=\"h-6 w-32 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex flex-wrap gap-2\">\n {CHIP_WIDTHS.map((width, i) => (\n <div\n key={i}\n className=\"h-9 animate-pulse rounded-full bg-bg-sunken\"\n style={{ width: `${width}px` }}\n />\n ))}\n </div>\n </div>\n);\n\n// Section Header Skeleton\nconst SectionHeaderSkeleton: FC = () => (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 animate-pulse rounded-xl bg-bg-sunken\" />\n <div className=\"space-y-1\">\n <div className=\"h-5 w-40 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-4 w-32 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n);\n\n// Full Page Skeleton - shows complete loading state\nconst MarketplacePageSkeleton: FC = () => (\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n {/* Hero Section */}\n <HeroSkeleton />\n\n {/* Browse by Type */}\n <CategoryChipsSkeleton />\n\n {/* Most Downloaded Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* New Releases Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* Free Apps Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} variant=\"compact\" />\n ))}\n </div>\n </div>\n </div>\n);\n\n// ========================================\n// Filter Types\n// ========================================\n\ntype PricingFilter = 'all' | 'free' | 'paid' | 'subscription';\ntype RatingFilter = 'any' | '3' | '3.5' | '4' | '4.5';\ntype NatureFilter = 'all' | 'digital' | 'physical';\ntype SortOption =\n 'relevance' | 'rating' | 'downloads' | 'newest' | 'price-low' | 'price-high' | 'name-asc';\n\ninterface FilterDropdownProps {\n label: string;\n value: string;\n options: { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nconst FilterDropdown: FC<FilterDropdownProps> = ({ label, value, options, onChange }) => {\n const selectId = useId();\n\n return (\n <label\n htmlFor={selectId}\n className=\"relative inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-within:border-action-primary-border focus-within:ring-2 focus-within:ring-action-primary-border/20\"\n >\n <span className=\"sr-only\">{label}</span>\n <span className=\"text-text-muted\">{label}:</span>\n <select\n id={selectId}\n aria-label={label}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n className=\"min-w-[8rem] appearance-none bg-transparent pr-6 text-sm font-medium text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-3 size-4 text-text-muted\" />\n </label>\n );\n};\n\n// ========================================\n// Main Page Component\n// ========================================\n\nexport const MarketplaceHomePage: FC = () => {\n const navigate = useNavigate();\n const { basePath, user } = useStore();\n const { t } = useI18n();\n const searchInputId = useId();\n const bus = useEventBus();\n const {\n cartOpen,\n closeCart,\n itemCount,\n cartItems,\n subtotal,\n tax,\n total,\n updateQuantity,\n removeProduct,\n } = useCart();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedType, setSelectedType] = useState<string>('all');\n\n // Filter states\n const [pricingFilter, setPricingFilter] = useState<PricingFilter>('all');\n const [ratingFilter, setRatingFilter] = useState<RatingFilter>('any');\n const [natureFilter, setNatureFilter] = useState<NatureFilter>('all');\n const [selectedCategory, setSelectedCategory] = useState<string>('all');\n const [selectedLicense, setSelectedLicense] = useState<string>('all');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [sortOption, setSortOption] = useState<SortOption>('relevance');\n const [showFilters, setShowFilters] = useState(false);\n\n // Debounce search query - 1.5 seconds for better UX\n const debouncedSearchQuery = useDebounce(searchQuery, 1500);\n\n // Filter options\n const pricingOptions = [\n { value: 'all', label: 'All Prices' },\n { value: 'free', label: 'Free' },\n { value: 'paid', label: 'Paid (One-time)' },\n { value: 'subscription', label: 'Subscription' },\n ];\n\n const ratingOptions = [\n { value: 'any', label: 'Any Rating' },\n { value: '3', label: '3+ Stars' },\n { value: '3.5', label: '3.5+ Stars' },\n { value: '4', label: '4+ Stars' },\n { value: '4.5', label: '4.5+ Stars' },\n ];\n\n const natureOptions = [\n { value: 'all', label: 'All Types' },\n { value: 'digital', label: 'Digital' },\n { value: 'physical', label: 'Physical' },\n ];\n\n const sortOptions = [\n { value: 'relevance', label: 'Relevance' },\n { value: 'rating', label: 'Highest Rated' },\n { value: 'downloads', label: 'Most Downloads' },\n { value: 'newest', label: 'Newest First' },\n { value: 'price-low', label: 'Price: Low to High' },\n { value: 'price-high', label: 'Price: High to Low' },\n { value: 'name-asc', label: 'Name A-Z' },\n ];\n\n // Map UI sort option to GraphQL StoreSortBy enum\n const mapSortToApi = (sort: SortOption): StoreSortBy => {\n const mapping: Record<SortOption, StoreSortBy> = {\n relevance: StoreSortBy.Relevance,\n rating: StoreSortBy.Rating,\n downloads: StoreSortBy.Downloads,\n newest: StoreSortBy.Newest,\n 'price-low': StoreSortBy.PriceLow,\n 'price-high': StoreSortBy.PriceHigh,\n 'name-asc': StoreSortBy.NameAsc,\n };\n return mapping[sort];\n };\n\n // Map UI pricing filter to GraphQL PricingModel enum\n const mapPricingToApi = (pricing: PricingFilter): PricingModel | undefined => {\n if (pricing === 'all') return undefined;\n const mapping: Record<string, PricingModel> = {\n free: PricingModel.Free,\n paid: PricingModel.PaidOnetime,\n subscription: PricingModel.Subscription,\n };\n return mapping[pricing];\n };\n\n // Check if any filter is active (using debounced search for API calls)\n const hasActiveFilters =\n debouncedSearchQuery.trim() !== '' ||\n pricingFilter !== 'all' ||\n ratingFilter !== 'any' ||\n natureFilter !== 'all' ||\n selectedCategory !== 'all' ||\n selectedLicense !== 'all' ||\n featuredOnly ||\n selectedType !== 'all' ||\n sortOption !== 'relevance';\n\n // Check if user is currently typing (search not yet debounced)\n const isSearchPending = searchQuery.trim() !== '' && searchQuery !== debouncedSearchQuery;\n\n const clearAllFilters = () => {\n setSearchQuery('');\n setPricingFilter('all');\n setRatingFilter('any');\n setNatureFilter('all');\n setSelectedCategory('all');\n setSelectedLicense('all');\n setFeaturedOnly(false);\n setSortOption('relevance');\n setSelectedType('all');\n };\n\n // Count active filters (excluding search and sort)\n const activeFilterCount =\n (pricingFilter !== 'all' ? 1 : 0) +\n (ratingFilter !== 'any' ? 1 : 0) +\n (natureFilter !== 'all' ? 1 : 0) +\n (selectedCategory !== 'all' ? 1 : 0) +\n (selectedLicense !== 'all' ? 1 : 0) +\n (featuredOnly ? 1 : 0) +\n (selectedType !== 'all' ? 1 : 0);\n\n // Build BrowseStoreInput from filter states for API call\n const browseStoreInput: BrowseStoreInput = useMemo(() => {\n const input: BrowseStoreInput = {\n sortBy: mapSortToApi(sortOption),\n pagination: { limit: 50 },\n };\n\n // Only add filter if we have active filters\n if (hasActiveFilters) {\n input.filter = {};\n\n // Search query (debounced)\n if (debouncedSearchQuery.trim()) {\n input.filter.search = debouncedSearchQuery.trim();\n }\n\n // Type filter\n if (selectedType !== 'all') {\n input.filter.type = selectedType as BrowseStoreInput['filter'] extends { type?: infer T }\n ? T\n : never;\n }\n\n // Category filter\n if (selectedCategory !== 'all') {\n input.filter.category = selectedCategory;\n }\n\n // Nature filter (digital/physical)\n if (natureFilter !== 'all') {\n input.filter.nature =\n natureFilter === 'digital' ? ProductNature.Digital : ProductNature.Physical;\n }\n\n // Pricing filter\n const pricingModel = mapPricingToApi(pricingFilter);\n if (pricingModel) {\n input.filter.pricingModel = pricingModel;\n }\n\n // Rating filter\n if (ratingFilter !== 'any') {\n input.filter.minRating = parseFloat(ratingFilter);\n }\n\n // Featured filter\n if (featuredOnly) {\n input.filter.featured = true;\n }\n\n // License filter\n if (selectedLicense !== 'all') {\n input.filter.license = selectedLicense;\n }\n }\n\n return input;\n }, [\n debouncedSearchQuery,\n selectedType,\n selectedCategory,\n natureFilter,\n pricingFilter,\n ratingFilter,\n featuredOnly,\n selectedLicense,\n sortOption,\n hasActiveFilters,\n ]);\n\n // Fetch grouped products for homepage (when no filters active)\n const {\n featured,\n mostDownloaded,\n recentlyAdded,\n freeItems,\n loading: homeLoading,\n error: homeError,\n refetch: refetchHome,\n } = useHomeGroupedProducts({ skip: hasActiveFilters });\n\n // Fetch filtered products from API when filters are active\n const {\n products: browseProducts,\n totalCount,\n facets,\n loading: browseLoading,\n error: browseError,\n refetch: refetchBrowse,\n } = useBrowseStoreWithContext(hasActiveFilters ? browseStoreInput : {}, {\n skip: !hasActiveFilters,\n });\n\n // Determine which data to show\n const loading = hasActiveFilters ? browseLoading : homeLoading;\n const error = hasActiveFilters ? browseError : homeError;\n const refetch = hasActiveFilters ? refetchBrowse : refetchHome;\n\n // Products to display when filters are active\n const filteredProducts = hasActiveFilters ? browseProducts : [];\n\n // Emit search event when debounced query resolves. Read totalCount via ref so\n // we don't re-emit when only the count changes.\n const totalCountRef = useRef(totalCount);\n totalCountRef.current = totalCount;\n const busRef = useRef(bus);\n busRef.current = bus;\n useEffect(() => {\n if (!debouncedSearchQuery.trim() || browseLoading) return;\n (busRef.current as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.marketplace.searched',\n {\n query: debouncedSearchQuery,\n resultCount: totalCountRef.current,\n }\n );\n }, [debouncedSearchQuery, browseLoading]);\n\n const handleProductClick = useCallback(\n (product: StoreProduct) => {\n navigate(`${basePath}/marketplace/product/${product.id}`);\n },\n [navigate, basePath]\n );\n\n const handleCheckout = () => {\n closeCart();\n navigate(`${basePath}/cart`);\n };\n\n // Get all unique products for the homepage \"All Apps\" section\n const allProducts = useMemo(\n () => [\n ...new Map(\n [...featured, ...mostDownloaded, ...recentlyAdded, ...freeItems].map((p) => [p.id, p])\n ).values(),\n ],\n [featured, mostDownloaded, recentlyAdded, freeItems]\n );\n\n // Fetch installations to surface deprecated (UNLISTED) apps the user has installed\n const isAuthenticated = !!user?.id;\n const { data: installationsData } = useGetMyStoreInstallationsQuery({\n variables: { limit: 100 },\n skip: !isAuthenticated,\n fetchPolicy: 'cache-and-network',\n });\n\n // Deduplicate by productId (user may have installed in multiple workspaces)\n const deprecatedInstalledProducts = useMemo<DeprecatedProduct[]>(() => {\n if (!installationsData?.myStoreAppInstallations?.items) return [];\n const seen = new Set<string>();\n const result: DeprecatedProduct[] = [];\n for (const item of installationsData.myStoreAppInstallations.items) {\n if (item.productStatus === 'UNLISTED' && !seen.has(item.productId)) {\n seen.add(item.productId);\n result.push({\n id: item.productId,\n name: item.productName ?? 'Unknown App',\n icon: item.productIcon ?? undefined,\n });\n }\n }\n return result;\n }, [installationsData]);\n\n // Build dynamic filter options from facets (when available) or from homepage products\n const typeCategories = useMemo(() => {\n // Use facets from API if available\n if (facets?.types && facets.types.length > 0) {\n return [\n { id: 'all', name: 'All Apps', count: totalCount },\n ...facets.types.map((t) => ({\n id: t.value,\n name: t.value.charAt(0) + t.value.slice(1).toLowerCase().replace(/_/g, ' '),\n count: t.count,\n })),\n ];\n }\n // Fallback to homepage products: count each type in a single pass.\n const typeCounts = allProducts.reduce<Map<string, number>>((acc, p) => {\n acc.set(p.type, (acc.get(p.type) ?? 0) + 1);\n return acc;\n }, new Map());\n const typeOptions = Array.from(typeCounts.entries())\n .flatMap(([type, count]) =>\n count > 0\n ? [\n {\n id: type,\n name: type.charAt(0) + type.slice(1).toLowerCase().replace(/_/g, ' '),\n count,\n },\n ]\n : []\n )\n .sort((a, b) => b.count - a.count);\n return [{ id: 'all', name: 'All Apps', count: allProducts.length }, ...typeOptions];\n }, [facets, allProducts, totalCount]);\n\n // Build category options from facets or products\n const categoryOptions = useMemo(() => {\n if (facets?.categories && facets.categories.length > 0) {\n return [\n { value: 'all', label: 'All Categories' },\n ...facets.categories.map((c) => ({\n value: c.value,\n label: c.value,\n })),\n ];\n }\n return [\n { value: 'all', label: 'All Categories' },\n ...Array.from(new Set(allProducts.flatMap((p) => (p.category ? [p.category] : []))))\n .map((cat) => ({\n value: cat as string,\n label: cat as string,\n }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n ];\n }, [facets, allProducts]);\n\n // Build license options from facets or static list\n const licenseOptions = useMemo(() => {\n if (facets?.licenses && facets.licenses.length > 0) {\n return [\n { value: 'all', label: 'All Licenses' },\n ...facets.licenses.map((l) => ({\n value: l.value,\n label: l.value,\n })),\n ];\n }\n return [{ value: 'all', label: 'All Licenses' }];\n }, [facets]);\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"relative overflow-hidden border-b border-border-seam bg-bg-surface px-6 py-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n App{' '}\n <GradientText>\n {t('pages.marketplace.title', { defaultValue: 'Marketplace' })}\n </GradientText>\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.marketplace.subtitle', {\n defaultValue: 'Discover apps, workflows, and integrations for your workspace',\n })}\n </p>\n </div>\n <button\n type=\"button\"\n data-tour=\"store-marketplace-cart-button\"\n onClick={() => navigate(`${basePath}/cart`)}\n className=\"cursor-pointer relative flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ShoppingCart className=\"size-5\" />\n {t('pages.marketplace.cart', { defaultValue: 'Cart' })}\n {itemCount > 0 && (\n <span className=\"absolute -right-2 -top-2 flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs font-bold text-action-primary-text\">\n {itemCount}\n </span>\n )}\n </button>\n </div>\n\n {/* Search Bar - Live filtering as you type */}\n <div className=\"mt-4\">\n <div className=\"relative\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {t('pages.marketplace.search.label', { defaultValue: 'Search marketplace products' })}\n </label>\n <Search className=\"absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-muted\" />\n <input\n id={searchInputId}\n data-tour=\"store-marketplace-search-input\"\n aria-label={t('pages.marketplace.search.label', {\n defaultValue: 'Search marketplace products',\n })}\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={t('pages.marketplace.search.placeholder', {\n defaultValue: 'Search apps, workflows, integrations...',\n })}\n className=\"w-full rounded-xl border border-border-default bg-bg-sunken py-3 pl-12 pr-4 text-text-primary placeholder-text-muted focus:border-action-primary-border focus:bg-bg-surface focus:outline-none focus:ring-2 focus:ring-action-primary-border/20\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n aria-label={t('pages.marketplace.search.clear', { defaultValue: 'Clear search' })}\n className=\"cursor-pointer absolute right-4 top-1/2 -translate-y-1/2 text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n {/* Search results count / pending indicator */}\n {searchQuery.trim() && (\n <div\n aria-live=\"polite\"\n className=\"mt-2 flex items-center gap-2 text-sm text-text-muted\"\n >\n {isSearchPending || browseLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n <span>\n {t('pages.marketplace.search.searching', { defaultValue: 'Searching...' })}\n </span>\n </>\n ) : (\n <span>\n {totalCount} result{totalCount !== 1 ? 's' : ''} for &ldquo;{debouncedSearchQuery}\n &rdquo;\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Filter Bar */}\n <div className=\"mt-4 flex flex-wrap items-center gap-3\">\n {/* Filter Toggle Button */}\n <button\n type=\"button\"\n data-tour=\"store-marketplace-filters-button\"\n onClick={() => setShowFilters(!showFilters)}\n className={`cursor-pointer flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-colors ${\n showFilters || activeFilterCount > 0\n ? 'border-action-primary-border bg-action-primary-bg/10 text-text-link'\n : 'border-border-default bg-bg-surface text-text-primary transition-colors hover:bg-bg-sunken'\n }`}\n >\n <SlidersHorizontal className=\"size-4\" />\n {t('pages.marketplace.filters', { defaultValue: 'Filters' })}\n {activeFilterCount > 0 && (\n <span className=\"flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs text-action-primary-text\">\n {activeFilterCount}\n </span>\n )}\n </button>\n\n {/* Quick Filter Chips - Always visible */}\n <div\n className=\"flex flex-wrap items-center gap-2\"\n data-tour=\"store-marketplace-quick-filters\"\n >\n <button\n type=\"button\"\n onClick={() => setPricingFilter(pricingFilter === 'free' ? 'all' : 'free')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n pricingFilter === 'free'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Gift className=\"size-3.5\" />\n Free\n </button>\n <button\n type=\"button\"\n onClick={() => setRatingFilter(ratingFilter === '4' ? 'any' : '4')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n ratingFilter === '4'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Star className=\"size-3.5\" />\n 4+ Stars\n </button>\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(!featuredOnly)}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n featuredOnly\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Sparkles className=\"size-3.5\" />\n Featured\n </button>\n </div>\n\n {/* Clear Filters */}\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n Clear all\n </button>\n )}\n\n {/* Sort Dropdown - Right aligned */}\n <div className=\"ml-auto\" data-tour=\"store-marketplace-section-sort\">\n <FilterDropdown\n label=\"Sort\"\n value={sortOption}\n options={sortOptions}\n onChange={(value) => setSortOption(value as SortOption)}\n />\n </div>\n </div>\n\n {/* Expanded Filters Panel */}\n {showFilters && (\n <div className=\"mt-4 rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex flex-wrap items-start gap-6\">\n {/* Pricing Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Pricing\n </span>\n <FilterDropdown\n label=\"Price\"\n value={pricingFilter}\n options={pricingOptions}\n onChange={(value) => setPricingFilter(value as PricingFilter)}\n />\n </div>\n\n {/* Rating Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Min Rating\n </span>\n <FilterDropdown\n label=\"Rating\"\n value={ratingFilter}\n options={ratingOptions}\n onChange={(value) => setRatingFilter(value as RatingFilter)}\n />\n </div>\n\n {/* Category Filter */}\n {categoryOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Category\n </span>\n <FilterDropdown\n label=\"Category\"\n value={selectedCategory}\n options={categoryOptions}\n onChange={setSelectedCategory}\n />\n </div>\n )}\n\n {/* Nature Filter (Digital/Physical) */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n Nature\n </span>\n <FilterDropdown\n label=\"Nature\"\n value={natureFilter}\n options={natureOptions}\n onChange={(value) => setNatureFilter(value as NatureFilter)}\n />\n </div>\n\n {/* License Filter */}\n {licenseOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n License\n </span>\n <FilterDropdown\n label=\"License\"\n value={selectedLicense}\n options={licenseOptions}\n onChange={setSelectedLicense}\n />\n </div>\n )}\n </div>\n\n {/* Active Filters Summary */}\n {activeFilterCount > 0 && (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-border-default pt-4\">\n <span className=\"text-xs font-medium text-text-muted\">Active filters:</span>\n {selectedType !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedType('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Type:</span> {selectedType}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {pricingFilter !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setPricingFilter('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Price:</span>{' '}\n {pricingOptions.find((o) => o.value === pricingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {ratingFilter !== 'any' && (\n <button\n type=\"button\"\n onClick={() => setRatingFilter('any')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Rating:</span>{' '}\n {ratingOptions.find((o) => o.value === ratingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedCategory !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedCategory('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Category:</span> {selectedCategory}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {natureFilter !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setNatureFilter('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">Nature:</span>{' '}\n {natureOptions.find((o) => o.value === natureFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedLicense !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedLicense('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">License:</span>{' '}\n {licenseOptions.find((o) => o.value === selectedLicense)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {featuredOnly && (\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(false)}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <Sparkles className=\"size-3\" />\n Featured Only\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </header>\n\n {/* Main Content */}\n <main className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n <PagePurpose>\n The marketplace is where you discover and install apps, agents, workflows, nodes and\n widgets that extend your workspaces. Browse featured and trending picks, filter by type\n or category, search for something specific, then open any product to view pricing and\n reviews before adding it to your cart.\n </PagePurpose>\n {/* Error State */}\n {error && (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle p-8 text-center\"\n >\n <AlertCircle className=\"mb-4 size-12 text-status-error-text\" />\n <h3 className=\"mb-2 text-lg font-semibold text-status-error-text-emphasis\">\n Failed to load marketplace\n </h3>\n <p className=\"mb-4 text-sm text-status-error-text\">\n {error.message || 'An error occurred while fetching products'}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-emphasis\"\n >\n Try Again\n </button>\n </div>\n )}\n\n {/* Loading State */}\n {loading && !error && <MarketplacePageSkeleton />}\n\n {/* Content - Only show when data is loaded */}\n {!loading && !error && (\n <>\n {/* Hero Section - Featured Products (only when no filters) */}\n {!hasActiveFilters && selectedType === 'all' && featured.length > 0 && (\n <HeroSection\n dataTour=\"store-marketplace-hero-section\"\n featuredProducts={featured}\n onProductClick={handleProductClick}\n />\n )}\n\n {/* Type Filter Categories */}\n {typeCategories.length > 1 && (\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Browse by Type</h2>\n <div\n className=\"flex flex-wrap gap-2\"\n data-tour=\"store-marketplace-type-categories\"\n >\n {typeCategories.map((category) => (\n <CategoryChip\n key={category.id}\n typeId={category.id}\n name={category.name}\n count={category.count}\n active={selectedType === category.id}\n onClick={() => setSelectedType(category.id)}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Show Filtered Results when filters are active */}\n {hasActiveFilters && (\n <section>\n <SectionHeader\n icon={Filter}\n title={debouncedSearchQuery ? `Search Results` : 'Filtered Results'}\n subtitle={`${totalCount} app${totalCount !== 1 ? 's' : ''} found`}\n />\n {filteredProducts.length > 0 ? (\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\"\n data-tour=\"store-marketplace-product-grid\"\n >\n {filteredProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No apps match your filters\"\n description=\"Try adjusting your filter criteria or clearing filters to see more results.\"\n action={\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Clear All Filters\n </button>\n }\n />\n )}\n </section>\n )}\n\n {/* Show grouped sections only when no filters are active */}\n {!hasActiveFilters && selectedType === 'all' && (\n <>\n {/* Most Downloaded / Trending Apps */}\n {mostDownloaded.length > 0 && (\n <section>\n <SectionHeader\n icon={TrendingUp}\n title=\"Most Downloaded\"\n subtitle=\"Top picks by our community\"\n />\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n data-tour=\"store-marketplace-most-downloaded\"\n >\n {mostDownloaded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Recently Added / New Releases */}\n {recentlyAdded.length > 0 && (\n <section>\n <SectionHeader\n icon={Clock}\n title=\"New Releases\"\n subtitle=\"Recently published apps\"\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {recentlyAdded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Free Items */}\n {freeItems.length > 0 && (\n <section>\n <SectionHeader\n icon={Gift}\n title=\"Free Apps\"\n subtitle=\"No cost, full functionality\"\n />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {freeItems.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n variant=\"compact\"\n />\n ))}\n </div>\n </section>\n )}\n\n {/* All Apps */}\n {allProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={Box}\n title=\"All Apps\"\n subtitle={`${allProducts.length} apps available`}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {allProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n </>\n )}\n\n {/* Deprecated Installed Apps — shown whenever user has UNLISTED products installed */}\n {isAuthenticated && deprecatedInstalledProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={AlertCircle}\n title=\"Deprecated Apps You've Installed\"\n subtitle=\"These apps are no longer supported — hover the badge for details\"\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {deprecatedInstalledProducts.map((product) => (\n <DeprecatedAppCard\n key={product.id}\n product={product}\n onClick={() => navigate(`${basePath}/marketplace/product/${product.id}`)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Empty State - No products at all */}\n {allProducts.length === 0 && (\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title=\"No apps available yet\"\n description=\"The marketplace is empty. Check back soon for new apps, workflows, and integrations.\"\n />\n )}\n </>\n )}\n </div>\n </main>\n\n {/* Cart Drawer */}\n <CartDrawer\n isOpen={cartOpen}\n onClose={closeCart}\n onCheckout={handleCheckout}\n cartItems={cartItems}\n itemCount={itemCount}\n subtotal={subtotal}\n tax={tax}\n total={total}\n updateQuantity={updateQuantity}\n removeProduct={removeProduct}\n />\n </div>\n );\n};\n"],"mappings":"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"}
@@ -45,7 +45,8 @@ var Te = (e, t) => ({
45
45
  TEMPLATE: "TEMPLATE",
46
46
  INTEGRATION: "INTEGRATION",
47
47
  EXTENSION: "EXTENSION",
48
- THEME: "THEME"
48
+ THEME: "THEME",
49
+ DASHBOARD: "TEMPLATE"
49
50
  }[t] || "APP")(e.nature, e.type),
50
51
  type: ((e) => ({
51
52
  APP: "STANDALONE_APP",
@@ -61,7 +62,9 @@ var Te = (e, t) => ({
61
62
  WIDGET: "WORKSPACE_MODULE",
62
63
  VIBEMODULE: "WORKSPACE_MODULE",
63
64
  CONSOLE: "STANDALONE_APP",
64
- NODE: "WORKSPACE_MODULE"
65
+ NODE: "WORKSPACE_MODULE",
66
+ PARSER: "WORKSPACE_MODULE",
67
+ DASHBOARD: "WORKFLOW_TEMPLATE"
65
68
  })[e] || "STANDALONE_APP")(e.type),
66
69
  tags: e.tags || [],
67
70
  pricingModel: ((e) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"ProductDetailPage.js","names":[],"sources":["../../src/pages/ProductDetailPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n ArrowLeft,\n Star,\n ShieldCheck,\n Check,\n ThumbsUp,\n Package,\n AlertCircle,\n ShoppingCart,\n Loader2,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport {\n useCreateReview,\n useDeleteReview,\n useMarkReviewHelpful,\n useStoreProductDetails,\n useUpdateReview,\n} from '../hooks/useStoreGraphQL';\nimport { useCart } from '../hooks/useCart';\nimport { useInstallations } from '../hooks/useInstallations';\nimport { formatNumber, formatPrice } from '../utils';\nimport { InstallAppModal } from '../components/InstallAppModal';\nimport {\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport Markdown from 'react-markdown';\nimport type {\n Product,\n ItemType,\n ProductType,\n PricingModel as CartPricingModel,\n ProductStatus,\n} from '../types';\n\n// ============================================================================\n// Type Definitions for GraphQL Response\n// ============================================================================\n\ninterface Publisher {\n id: string;\n userId?: string;\n name: string;\n email?: string;\n websiteUrl?: string;\n logoUrl?: string;\n bio?: string;\n isVerified: boolean;\n totalSales?: number;\n totalEarnings?: number;\n createdAt?: string;\n organizationId?: string | null;\n developerOrganization?: {\n id: string;\n name: string;\n logoUrl?: string | null;\n isVerified: boolean;\n description?: string | null;\n } | null;\n}\n\ninterface ProductReview {\n id: string;\n productId: string;\n userId: string;\n rating: number;\n title?: string;\n comment?: string;\n status: string;\n helpful: number;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface RelatedProduct {\n id: string;\n name: string;\n slug?: string;\n icon?: string;\n type: string;\n price: number;\n pricingModel: string;\n rating?: number;\n reviewCount?: number;\n downloads: number;\n publisher?: {\n id: string;\n name: string;\n isVerified: boolean;\n };\n}\n\ninterface RatingDistribution {\n fiveStars: number;\n fourStars: number;\n threeStars: number;\n twoStars: number;\n oneStar: number;\n}\n\ninterface UserEntitlement {\n id: string;\n status: string;\n licenseKey?: string;\n expiresAt?: string;\n}\n\ninterface StoreProduct {\n id: string;\n name: string;\n slug?: string;\n type: string;\n nature: string;\n status: string;\n description?: string;\n longDescription?: string;\n pricingModel: string;\n price: number;\n currency: string;\n icon?: string;\n bannerUrl?: string;\n screenshots: string[];\n videoUrls?: string[];\n documentationUrl?: string;\n repositoryUrl?: string;\n category?: string;\n tags?: string[];\n featured: boolean;\n zipFileUrl?: string;\n downloads: number;\n viewCount?: number;\n rating?: number;\n reviewCount: number;\n publishedAt?: string;\n createdAt: string;\n updatedAt: string;\n // Devportal application ID (productId in store maps to devportal Application.id)\n productId?: string;\n // Manifest fields\n subType?: string;\n manifestVersion?: string;\n authorName?: string;\n license?: string;\n minPlatformVersion?: string;\n compatibleProducts?: string[];\n requiredPermissions?: string[];\n integrationDeps?: string[];\n // Physical product fields\n sku?: string;\n stockQuantity?: number;\n trackInventory?: boolean;\n requiresShipping?: boolean;\n weight?: number;\n weightUnit?: string;\n brand?: string;\n manifest?: Record<string, unknown>;\n publisher?: Publisher;\n}\n\ninterface StoreProductDetailsResponse {\n product: StoreProduct;\n publisher: Publisher;\n reviews: ProductReview[];\n reviewsCount: number;\n relatedProducts: RelatedProduct[];\n userReview?: ProductReview;\n isPurchased: boolean;\n userEntitlement?: UserEntitlement;\n ratingDistribution?: RatingDistribution;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Convert StoreProduct (GraphQL) to Product (Cart) type\n */\nconst convertToCartProduct = (storeProduct: StoreProduct, publisher?: Publisher): Product => {\n // Map GraphQL pricing model to cart pricing model\n const mapPricingModel = (model: string): CartPricingModel => {\n const mapping: Record<string, CartPricingModel> = {\n FREE: 'FREE',\n PAID_ONETIME: 'ONE_TIME',\n SUBSCRIPTION: 'SUBSCRIPTION',\n USAGE_BASED: 'USAGE_BASED',\n FREEMIUM: 'FREEMIUM',\n };\n return mapping[model] || 'ONE_TIME';\n };\n\n // Map product type\n const mapProductType = (type: string): ProductType => {\n const mapping: Record<string, ProductType> = {\n APP: 'STANDALONE_APP',\n BOTAPP: 'STANDALONE_APP',\n AGENT: 'STANDALONE_APP',\n WORKFLOW: 'WORKFLOW_TEMPLATE',\n TEMPLATE: 'WORKFLOW_TEMPLATE',\n INTEGRATION: 'API_INTEGRATION',\n EXTENSION: 'BROWSER_EXTENSION',\n THEME: 'UI_THEME',\n PLUGIN: 'WORKSPACE_MODULE',\n COMPONENT: 'WORKSPACE_MODULE',\n WIDGET: 'WORKSPACE_MODULE',\n VIBEMODULE: 'WORKSPACE_MODULE',\n CONSOLE: 'STANDALONE_APP',\n NODE: 'WORKSPACE_MODULE',\n };\n return mapping[type] || 'STANDALONE_APP';\n };\n\n // Map item type\n const mapItemType = (nature: string, type: string): ItemType => {\n if (nature === 'PHYSICAL') return 'PHYSICAL';\n const typeMapping: Record<string, ItemType> = {\n APP: 'APP',\n BOTAPP: 'APP',\n AGENT: 'APP',\n WORKFLOW: 'WORKFLOW',\n TEMPLATE: 'TEMPLATE',\n INTEGRATION: 'INTEGRATION',\n EXTENSION: 'EXTENSION',\n THEME: 'THEME',\n };\n return typeMapping[type] || 'APP';\n };\n\n return {\n id: storeProduct.id,\n publisherId: publisher?.id || '',\n publisher: {\n id: publisher?.id || '',\n name: publisher?.name || storeProduct.authorName || 'Unknown',\n displayName: publisher?.name || storeProduct.authorName || 'Unknown',\n email: publisher?.email || '',\n verified: publisher?.isVerified || false,\n totalProducts: 0,\n totalDownloads: 0,\n averageRating: 0,\n joinedAt: publisher?.createdAt || storeProduct.createdAt,\n website: publisher?.websiteUrl,\n logoUrl: publisher?.logoUrl,\n },\n name: storeProduct.name,\n slug: storeProduct.slug || storeProduct.id,\n displayName: storeProduct.name,\n description: storeProduct.longDescription || storeProduct.description || '',\n shortDescription: storeProduct.description || '',\n iconUrl: storeProduct.icon,\n screenshotUrls: storeProduct.screenshots || [],\n videoUrls: storeProduct.videoUrls || [],\n itemType: mapItemType(storeProduct.nature, storeProduct.type),\n type: mapProductType(storeProduct.type),\n tags: storeProduct.tags || [],\n pricingModel: mapPricingModel(storeProduct.pricingModel),\n basePrice: storeProduct.price,\n currency: storeProduct.currency || 'USD',\n variants: [],\n status: (storeProduct.status as ProductStatus) || 'PUBLISHED',\n featured: storeProduct.featured,\n verified: publisher?.isVerified || false,\n downloadCount: storeProduct.downloads,\n installCount: storeProduct.downloads,\n orderCount: 0,\n averageRating: storeProduct.rating || 0,\n reviewCount: storeProduct.reviewCount,\n createdAt: storeProduct.createdAt,\n updatedAt: storeProduct.updatedAt,\n publishedAt: storeProduct.publishedAt,\n };\n};\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\n/**\n * Rating bar component for rating distribution\n */\nconst RatingBar: FC<{ stars: number; percentage: number; count: number }> = ({\n stars,\n percentage,\n count,\n}) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"w-3 text-xs text-text-muted\">{stars}</span>\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-status-warning-bg-subtle\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs text-text-muted\">{formatNumber(count)}</span>\n </div>\n);\n\n/**\n * Review card component\n */\nconst ReviewCard: FC<{\n review: ProductReview;\n onMarkHelpful: (reviewId: string) => void;\n markingHelpful: boolean;\n}> = ({ review, onMarkHelpful, markingHelpful }) => {\n const formatDate = (dateString: string) => {\n try {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return dateString;\n }\n };\n\n return (\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\">\n <div className=\"mb-3 flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-bg-sunken text-text-muted\">\n <Star className=\"size-4 fill-current\" />\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">Verified Buyer</p>\n <div className=\"flex items-center gap-2\">\n <div className=\"flex gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <Star\n key={star}\n className={`size-3 ${\n star <= review.rating\n ? 'fill-current text-status-warning-text'\n : 'text-text-muted'\n }`}\n />\n ))}\n </div>\n <span className=\"text-xs text-text-muted\">{formatDate(review.createdAt)}</span>\n </div>\n </div>\n </div>\n </div>\n {review.title && <h4 className=\"mb-2 font-medium text-text-primary\">{review.title}</h4>}\n {review.comment && <p className=\"mb-3 text-sm text-text-muted\">{review.comment}</p>}\n <div className=\"flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={() => onMarkHelpful(review.id)}\n disabled={markingHelpful}\n aria-label=\"Mark this review as helpful\"\n className=\"flex items-center gap-1 text-xs text-text-muted transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-60\"\n >\n <ThumbsUp className=\"size-3.5\" />\n <span>{markingHelpful ? 'Updating…' : `Helpful (${review.helpful})`}</span>\n </button>\n </div>\n </div>\n );\n};\n\n/**\n * Loading skeleton component - comprehensive skeleton matching full page layout\n */\nconst LoadingSkeleton: FC = () => (\n <div className=\"h-full overflow-y-auto\">\n {/* Header Skeleton */}\n <div className=\"sticky top-0 z-10 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"h-6 w-48 flex-1 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex gap-2\">\n <div className=\"size-10 animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"size-10 animate-pulse rounded-lg bg-bg-sunken\" />\n </div>\n </div>\n </div>\n\n <div className=\"mx-auto max-w-6xl p-6\">\n {/* App Header Section Skeleton */}\n <div className=\"mb-8 flex flex-col gap-6 md:flex-row\">\n {/* App Icon & Basic Info */}\n <div className=\"flex gap-4 md:w-1/2\">\n <div className=\"size-28 animate-pulse rounded-2xl bg-bg-sunken\" />\n <div className=\"flex flex-1 flex-col justify-center gap-2\">\n <div className=\"h-8 w-3/4 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex items-center gap-2\">\n <div className=\"h-4 w-32 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"size-4 animate-pulse rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"flex gap-3\">\n <div className=\"h-5 w-16 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-5 w-24 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n </div>\n\n {/* Rating & Install Skeleton */}\n <div className=\"flex flex-col gap-4 md:w-1/2 md:items-end\">\n <div className=\"flex items-center gap-4\">\n <div className=\"text-center\">\n <div className=\"h-10 w-16 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-20 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-px bg-border-default\" />\n <div className=\"text-center\">\n <div className=\"h-8 w-16 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-px bg-border-default\" />\n <div className=\"text-center\">\n <div className=\"h-8 w-20 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-12 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"flex gap-3\">\n <div className=\"h-12 w-40 animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"size-12 animate-pulse rounded-lg bg-bg-sunken\" />\n </div>\n </div>\n </div>\n\n {/* Screenshots Skeleton */}\n <section className=\"mb-8\">\n <div className=\"aspect-video animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"mt-4 flex gap-4\">\n {[...Array(4)].map((_, i) => (\n <div key={i} className=\"h-20 w-36 animate-pulse rounded-lg bg-bg-sunken\" />\n ))}\n </div>\n </section>\n\n {/* About Section Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-32 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-4 w-5/6 rounded bg-bg-sunken\" />\n </div>\n <div className=\"mt-4 flex gap-2\">\n {[...Array(4)].map((_, i) => (\n <div key={i} className=\"h-6 w-16 rounded-full bg-bg-sunken\" />\n ))}\n </div>\n </div>\n </section>\n\n {/* Ratings & Reviews Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-40 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"mb-6 flex flex-col gap-6 md:flex-row\">\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4 md:w-64\">\n <div className=\"flex items-center gap-6\">\n <div className=\"text-center\">\n <div className=\"h-12 w-16 rounded bg-bg-sunken\" />\n <div className=\"my-1 flex justify-center gap-0.5\">\n {[...Array(5)].map((_, i) => (\n <div key={i} className=\"size-4 rounded bg-bg-sunken\" />\n ))}\n </div>\n <div className=\"h-3 w-20 rounded bg-bg-sunken\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n {[...Array(5)].map((_, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"h-2 w-3 rounded bg-bg-sunken\" />\n <div className=\"h-2 flex-1 rounded-full bg-bg-sunken\" />\n <div className=\"h-2 w-8 rounded bg-bg-sunken\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n <div className=\"h-12 w-32 animate-pulse rounded-lg bg-bg-sunken\" />\n </div>\n {/* Review Cards Skeleton */}\n <div className=\"space-y-4\">\n {[...Array(2)].map((_, i) => (\n <div\n key={i}\n className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"mb-3 flex items-start gap-3\">\n <div className=\"size-10 rounded-full bg-bg-sunken\" />\n <div className=\"flex-1\">\n <div className=\"h-4 w-32 rounded bg-bg-sunken\" />\n <div className=\"mt-1 flex items-center gap-2\">\n <div className=\"flex gap-0.5\">\n {[...Array(5)].map((_, j) => (\n <div key={j} className=\"size-3 rounded bg-bg-sunken\" />\n ))}\n </div>\n <div className=\"h-3 w-20 rounded bg-bg-sunken\" />\n </div>\n </div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n ))}\n </div>\n </section>\n\n {/* Additional Info Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-48 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n {[...Array(6)].map((_, i) => (\n <div key={i}>\n <div className=\"h-3 w-16 rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-5 w-24 rounded bg-bg-sunken\" />\n </div>\n ))}\n </div>\n </div>\n </section>\n\n {/* Related Products Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-40 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"grid gap-4 md:grid-cols-4\">\n {[...Array(4)].map((_, i) => (\n <div\n key={i}\n className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-14 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1\">\n <div className=\"h-4 w-24 rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-16 rounded bg-bg-sunken\" />\n <div className=\"mt-1 flex items-center gap-2\">\n <div className=\"h-3 w-8 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-12 rounded bg-bg-sunken\" />\n </div>\n </div>\n </div>\n </div>\n ))}\n </div>\n </section>\n\n {/* Publisher Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 h-6 w-36 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-12 rounded-lg bg-bg-sunken\" />\n <div>\n <div className=\"flex items-center gap-2\">\n <div className=\"h-5 w-32 rounded bg-bg-sunken\" />\n <div className=\"size-4 rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"mt-1 h-4 w-40 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-3 space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n </section>\n </div>\n </div>\n);\n\n/**\n * Error display component\n */\nconst ErrorDisplay: FC<{ error: Error; onRetry: () => void; onBack: () => void }> = ({\n error,\n onRetry,\n onBack,\n}) => (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <AlertCircle className=\"mx-auto mb-4 size-12 text-status-error-text\" />\n <h2 className=\"mb-2 text-xl font-semibold text-text-primary\">Failed to load product</h2>\n <p className=\"mb-4 text-text-muted\">\n {error.message || 'An error occurred while loading the product details.'}\n </p>\n <div className=\"flex justify-center gap-3\">\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Try Again\n </button>\n <button\n type=\"button\"\n onClick={onBack}\n className=\"rounded-lg border border-border-default px-4 py-2 text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Go Back\n </button>\n </div>\n </div>\n </div>\n);\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * Product Detail Page Component\n *\n * Displays comprehensive product details using real GraphQL data\n */\nexport const ProductDetailPage: FC = () => {\n const { productId } = useParams<{ productId: string }>();\n const navigate = useNavigate();\n const location = useLocation();\n const { basePath, workspaceId } = useStore();\n const { addProduct, cartItems, addingToCart, createOrder, checkingOut } = useCart();\n const installationsFilter = useMemo(\n () => (productId && workspaceId ? { productId, workspaceId } : undefined),\n [productId, workspaceId]\n );\n const { installations: myInstallations, refetch: refetchInstallations } = useInstallations({\n filter: installationsFilter,\n limit: 1,\n });\n const activeInstallation = myInstallations.find(\n (i) => i.productId === productId && i.workspaceId === workspaceId && i.status === 'ACTIVE'\n );\n const isInstalledInCurrentWorkspace = Boolean(activeInstallation);\n const installedVersion = activeInstallation?.version ?? null;\n\n const { createReview, loading: creatingReview } = useCreateReview();\n const { updateReview, loading: updatingReview } = useUpdateReview();\n const { deleteReview, loading: deletingReview } = useDeleteReview();\n const { markHelpful, loading: markingHelpful } = useMarkReviewHelpful();\n const bus = useEventBus();\n\n const [showFullDescription, setShowFullDescription] = useState(false);\n const [showAllReviews, setShowAllReviews] = useState(false);\n const [buyingNow, setBuyingNow] = useState(false);\n const [quantity, setQuantity] = useState(1);\n const [actionError, setActionError] = useState<string | null>(null);\n const [reviewNotice, setReviewNotice] = useState<string | null>(null);\n const [reviewNoticeTone, setReviewNoticeTone] = useState<'success' | 'error'>('success');\n const [isReviewFormOpen, setIsReviewFormOpen] = useState(false);\n const [reviewTitle, setReviewTitle] = useState('');\n const [reviewComment, setReviewComment] = useState('');\n const [reviewRating, setReviewRating] = useState(5);\n const [helpfulReviewId, setHelpfulReviewId] = useState<string | null>(null);\n\n // Install modal state\n const [isInstallModalOpen, setIsInstallModalOpen] = useState(false);\n\n // Fetch product details using GraphQL\n const { data, loading, error, refetch } = useStoreProductDetails({\n id: productId,\n slug: productId, // Also try as slug\n includeReviews: true,\n reviewsLimit: 10,\n includeRelated: true,\n relatedLimit: 4,\n includeVersions: true,\n });\n\n // Cast data to typed response\n const productDetails = data as StoreProductDetailsResponse | null;\n const product = productDetails?.product;\n const publisher = productDetails?.publisher;\n\n // Check if product is already in cart (must be before any early returns)\n const isInCart = cartItems.some((item) => item.productId === product?.id);\n\n // Pad short version strings (\"1.0\" → \"1.0.0\") so they compare equal to \"1.0.0\"\n const padVer = (v: string) => {\n const p = v.split('.');\n while (p.length < 3) p.push('0');\n return p.join('.');\n };\n const latestVersion = product?.manifestVersion ?? null;\n const hasUpdateAvailable = Boolean(\n isInstalledInCurrentWorkspace &&\n installedVersion &&\n latestVersion &&\n padVer(installedVersion) !== padVer(latestVersion)\n );\n\n // Handle Add to Cart (must be before any early returns)\n const handleAddToCart = useCallback(async () => {\n if (!product) return;\n\n setActionError(null);\n\n if (isInCart) {\n navigate(`${basePath}/cart`);\n return;\n }\n\n try {\n const cartProduct = convertToCartProduct(product, publisher);\n // Add to backend cart via GraphQL API call\n // This will make a mutation to global-public-gateway:4004 → tenantmodule-store-svc:4017\n await addProduct(cartProduct, undefined, quantity);\n // Cart drawer will open automatically after adding\n } catch (error) {\n console.error('Failed to add product to cart:', error);\n setActionError('Failed to add this product to cart. Please try again.');\n }\n }, [product, publisher, addProduct, isInCart, navigate, basePath, quantity]);\n\n // Handle Buy Now - creates an order and immediately proceeds to checkout\n const handleBuyNow = useCallback(async () => {\n if (!product) return;\n\n setActionError(null);\n setBuyingNow(true);\n\n try {\n // Get product price\n const price = product.price || 0;\n\n // Build line item for this product\n const lineItem = {\n productId: product.id,\n name: product.name,\n quantity,\n unitPrice: price,\n subtotal: price * quantity,\n itemType: product.nature?.toLowerCase() || 'digital',\n pricingModel: product.pricingModel || 'PAID_ONETIME',\n };\n\n // Create order input\n const orderInput = {\n total: price * quantity,\n lineItems: [lineItem],\n billingAccountId: '', // Will be set on billing page\n };\n\n // Call createStoreOrder mutation to create PENDING order\n const order = await createOrder(orderInput);\n\n if (order?.id) {\n // Navigate to billing page, preserving org/workspace/tenant context params\n navigate(`/billing/checkout/store/${order.id}${location.search}`);\n } else {\n console.error('Failed to create order');\n setActionError('Failed to create order. Please try again.');\n setBuyingNow(false);\n }\n } catch (error) {\n console.error('Buy Now error:', error);\n const message = error instanceof Error ? error.message : 'Failed to start checkout';\n setActionError(message);\n setBuyingNow(false);\n }\n }, [product, quantity, createOrder, navigate]);\n\n // Handle Install Free - Open modal for workspace selection (must be before any early returns)\n const handleInstallFree = useCallback(() => {\n if (!product) return;\n setIsInstallModalOpen(true);\n }, [product]);\n\n // Emit product.viewed event once product data loads. Read product via ref so\n // we only emit on id transitions, not on every field change.\n const productRef = useRef(product);\n productRef.current = product;\n const busRef = useRef(bus);\n busRef.current = bus;\n useEffect(() => {\n const p = productRef.current;\n if (!p) return;\n (busRef.current as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.product.viewed',\n {\n productId: p.id,\n productSlug: p.slug,\n productType: p.type,\n pricingModel: p.pricingModel,\n }\n );\n }, [product?.id]);\n\n // Handle successful installation (must be before any early returns)\n const handleInstallSuccess = useCallback(\n (workspaceId: string, workspaceName: string) => {\n if (!product) return;\n (bus as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.product.installed',\n {\n productId: product.id,\n workspaceId,\n }\n );\n console.log(\n `Successfully installed ${product.name} to workspace ${workspaceName} (${workspaceId})`\n );\n void refetchInstallations();\n },\n [product, bus, refetchInstallations]\n );\n\n // Handle installation error (must be before any early returns)\n const handleInstallError = useCallback((error: Error) => {\n console.error('Installation failed:', error.message);\n // Error is shown in the modal\n }, []);\n\n const openReviewForm = useCallback(() => {\n setReviewTitle(productDetails?.userReview?.title ?? '');\n setReviewComment(productDetails?.userReview?.comment ?? '');\n setReviewRating(productDetails?.userReview?.rating ?? 5);\n setReviewNotice(null);\n setReviewNoticeTone('success');\n setIsReviewFormOpen(true);\n }, [productDetails?.userReview]);\n\n const handleSubmitReview = useCallback(async () => {\n if (!product) {\n return;\n }\n\n if (reviewRating < 1 || reviewRating > 5) {\n setReviewNoticeTone('error');\n setReviewNotice('Choose a rating from 1 to 5 stars.');\n return;\n }\n\n const payload = {\n productId: product.id,\n rating: reviewRating,\n title: reviewTitle.trim() || undefined,\n comment: reviewComment.trim() || undefined,\n };\n\n const reviewResult = productDetails?.userReview\n ? {\n data: await updateReview(productDetails.userReview.id, {\n rating: payload.rating,\n title: payload.title,\n comment: payload.comment,\n }),\n errorMessage: null,\n }\n : await createReview(payload);\n\n if (!reviewResult?.data) {\n setReviewNoticeTone('error');\n setReviewNotice(\n reviewResult?.errorMessage ?? 'We could not save your review. Please try again.'\n );\n return;\n }\n\n setReviewNoticeTone('success');\n setReviewNotice(\n productDetails?.userReview ? 'Your review was updated.' : 'Your review was submitted.'\n );\n setIsReviewFormOpen(false);\n (bus as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.review.submitted',\n {\n productId: product.id,\n rating: reviewRating,\n }\n );\n await refetch();\n }, [\n bus,\n createReview,\n product,\n productDetails?.userReview,\n refetch,\n reviewComment,\n reviewRating,\n reviewTitle,\n updateReview,\n ]);\n\n const handleDeleteReview = useCallback(async () => {\n if (!productDetails?.userReview) {\n return;\n }\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete review',\n message: 'Are you sure you want to remove your review?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === false) {\n return;\n }\n\n const deleted = await deleteReview(productDetails.userReview.id);\n if (!deleted) {\n setReviewNoticeTone('error');\n setReviewNotice('We could not remove your review. Please try again.');\n void nativeNotify('error');\n return;\n }\n\n setReviewNoticeTone('success');\n setReviewNotice('Your review was removed.');\n setIsReviewFormOpen(false);\n setReviewTitle('');\n setReviewComment('');\n setReviewRating(5);\n void nativeNotify('success');\n await refetch();\n }, [deleteReview, productDetails?.userReview, refetch]);\n\n const handleMarkHelpful = useCallback(\n async (reviewId: string) => {\n setHelpfulReviewId(reviewId);\n const result = await markHelpful(reviewId);\n if (result) {\n await refetch();\n }\n setHelpfulReviewId(null);\n },\n [markHelpful, refetch]\n );\n\n // Handle loading state\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n // Handle error state\n if (error) {\n return (\n <ErrorDisplay\n error={error}\n onRetry={refetch}\n onBack={() => navigate(`${basePath}/marketplace`)}\n />\n );\n }\n\n // Handle not found state\n if (!productDetails?.product) {\n return (\n <div className=\"flex h-full items-center justify-center p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Product not found\"\n description=\"The product you are looking for does not exist or has been removed.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Back to Marketplace\n </button>\n }\n />\n </div>\n );\n }\n\n const {\n reviews,\n reviewsCount,\n relatedProducts,\n ratingDistribution,\n isPurchased,\n userEntitlement,\n } = productDetails;\n\n // Type assertion: we know product is defined because we checked !productDetails?.product above\n const safeProduct = product!;\n\n // Calculate rating distribution percentages\n const totalRatings = ratingDistribution\n ? ratingDistribution.fiveStars +\n ratingDistribution.fourStars +\n ratingDistribution.threeStars +\n ratingDistribution.twoStars +\n ratingDistribution.oneStar\n : 0;\n\n const ratingDistributionData = ratingDistribution\n ? [\n {\n stars: 5,\n count: ratingDistribution.fiveStars,\n percentage: totalRatings > 0 ? (ratingDistribution.fiveStars / totalRatings) * 100 : 0,\n },\n {\n stars: 4,\n count: ratingDistribution.fourStars,\n percentage: totalRatings > 0 ? (ratingDistribution.fourStars / totalRatings) * 100 : 0,\n },\n {\n stars: 3,\n count: ratingDistribution.threeStars,\n percentage: totalRatings > 0 ? (ratingDistribution.threeStars / totalRatings) * 100 : 0,\n },\n {\n stars: 2,\n count: ratingDistribution.twoStars,\n percentage: totalRatings > 0 ? (ratingDistribution.twoStars / totalRatings) * 100 : 0,\n },\n {\n stars: 1,\n count: ratingDistribution.oneStar,\n percentage: totalRatings > 0 ? (ratingDistribution.oneStar / totalRatings) * 100 : 0,\n },\n ]\n : [];\n\n // Parse permissions from manifest or requiredPermissions\n const permissions = safeProduct.requiredPermissions || [];\n\n // Parse integrations from integrationDeps\n const integrations = safeProduct.integrationDeps || [];\n\n // Pricing display\n const getPriceDisplay = () => {\n if (safeProduct.pricingModel === 'FREE') return 'Free';\n if (safeProduct.pricingModel === 'SUBSCRIPTION') {\n return `${formatPrice(safeProduct.price, safeProduct.currency)}/mo`;\n }\n return formatPrice(safeProduct.price, safeProduct.currency);\n };\n\n // Check if product is free\n const isFree = safeProduct.pricingModel === 'FREE' || safeProduct.price === 0;\n\n // Physical (produce) product inventory\n const isPhysical = safeProduct.nature === 'PHYSICAL';\n const tracksInventory = isPhysical && safeProduct.trackInventory === true;\n const stockQuantity = safeProduct.stockQuantity ?? 0;\n const isOutOfStock = tracksInventory && stockQuantity <= 0;\n\n return (\n <div className=\"h-full overflow-y-auto bg-bg-base\">\n {/* Hero Section - Two Column Layout */}\n <div className=\"bg-bg-surface\">\n <div className=\"mx-auto max-w-7xl\">\n <div className=\"flex flex-col lg:flex-row\">\n {/* Left Column - Product Info */}\n <div className=\"flex-1 px-6 py-8 lg:max-w-xl lg:py-12\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"mb-6 cursor-pointer inline-flex items-center gap-2 text-sm text-text-muted transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n\n {/* App Title - Large Typography */}\n <h1 className=\"mb-3 text-4xl font-normal leading-tight text-text-primary lg:text-5xl\">\n {safeProduct.name}\n </h1>\n\n {/* Installed badge — always above the fold */}\n {isInstalledInCurrentWorkspace && (\n <div className=\"mb-4 inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1 text-sm font-medium text-status-success-text\">\n <svg\n className=\"size-3.5\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"2,8 6,12 14,4\" />\n </svg>\n Installed{installedVersion ? ` v${installedVersion}` : ''}\n </div>\n )}\n\n {/* Publisher / org */}\n <div className=\"mb-2\">\n {(() => {\n const org = publisher?.developerOrganization;\n const displayName = org?.name || publisher?.name || safeProduct.authorName || 'Unknown Publisher';\n const displayLogo = org?.logoUrl || publisher?.logoUrl;\n const isVerified = org?.isVerified || publisher?.isVerified;\n const linkUrl = publisher?.websiteUrl;\n return (\n <div className=\"flex items-center gap-2\">\n {displayLogo && (\n <img src={displayLogo} alt={displayName} className=\"size-5 rounded object-cover\" />\n )}\n {linkUrl ? (\n <a href={linkUrl} target=\"_blank\" rel=\"noreferrer\" className=\"text-base font-medium text-text-link hover:underline\">\n {displayName}\n </a>\n ) : (\n <span className=\"text-base font-medium text-text-primary\">{displayName}</span>\n )}\n {isVerified && <ShieldCheck className=\"size-4 text-status-success-text\" />}\n </div>\n );\n })()}\n </div>\n\n {/* Metadata */}\n <p className=\"mb-6 text-sm text-text-muted\">\n {safeProduct.category && <span>{safeProduct.category}</span>}\n {safeProduct.category && ' · '}\n {formatNumber(safeProduct.downloads)}+ downloads\n </p>\n\n {/* Stats + primary action — the single emphasis zone for this page */}\n <EmphasisPanel className=\"mb-6 p-5\">\n {/* Stats Row */}\n <div className=\"mb-6 flex items-center gap-4\">\n {/* App Icon Badge */}\n <div className=\"flex size-12 items-center justify-center overflow-hidden rounded-xl bg-bg-sunken\">\n {safeProduct.icon ? (\n <img src={safeProduct.icon} alt=\"\" className=\"size-full object-cover\" />\n ) : (\n <span className=\"text-lg font-bold text-text-muted\">\n {safeProduct.name.charAt(0)}\n </span>\n )}\n </div>\n\n {/* Rating */}\n <div className=\"flex items-center gap-1 border-l border-border-default pl-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {(safeProduct.rating || 0).toFixed(1)}\n </span>\n <Star className=\"size-4 fill-current text-status-warning-text\" />\n <span className=\"ml-1 text-xs text-text-muted\">\n {formatNumber(reviewsCount || safeProduct.reviewCount)} reviews\n </span>\n </div>\n\n {/* Downloads */}\n <div className=\"border-l border-border-default pl-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {formatNumber(safeProduct.downloads)}+\n </span>\n <p className=\"text-xs text-text-muted\">Downloads</p>\n </div>\n\n {/* Type Badge */}\n <div className=\"border-l border-border-default pl-4\">\n <span className=\"rounded bg-bg-sunken px-2 py-1 text-xs font-medium text-text-primary\">\n {safeProduct.type}\n </span>\n <p className=\"mt-0.5 text-xs text-text-muted\">Type</p>\n </div>\n </div>\n\n {/* Action Buttons Row */}\n {actionError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-bg bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {isPhysical && (\n <div className=\"mb-4 flex flex-wrap items-center gap-4\">\n {tracksInventory &&\n (isOutOfStock ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-error-bg-subtle px-3 py-1 text-sm font-medium text-status-error-text\">\n <Package className=\"size-4\" />\n Out of stock\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1 text-sm font-medium text-status-success-text\">\n <Package className=\"size-4\" />\n {formatNumber(stockQuantity)} in stock\n </span>\n ))}\n {!isOutOfStock && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-muted\">Qty</span>\n <div className=\"flex items-center rounded-full border border-border-default\">\n <button\n type=\"button\"\n onClick={() => setQuantity((q) => Math.max(1, q - 1))}\n disabled={quantity <= 1}\n aria-label=\"Decrease quantity\"\n className=\"cursor-pointer px-3 py-1.5 text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n −\n </button>\n <span className=\"min-w-8 px-2 text-center text-sm font-medium tabular-nums text-text-primary\">\n {quantity}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n setQuantity((q) =>\n tracksInventory ? Math.min(stockQuantity, q + 1) : q + 1\n )\n }\n disabled={tracksInventory && quantity >= stockQuantity}\n aria-label=\"Increase quantity\"\n className=\"cursor-pointer px-3 py-1.5 text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n +\n </button>\n </div>\n </div>\n )}\n </div>\n )}\n\n <div className=\"mb-6 flex items-center gap-3\">\n {isInstalledInCurrentWorkspace ? (\n <>\n <button\n type=\"button\"\n disabled\n className=\"flex items-center gap-2 rounded-full bg-status-success-bg-subtle px-8 py-3 font-medium text-status-success-text cursor-default\"\n >\n <svg\n className=\"size-4\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"2,8 6,12 14,4\" />\n </svg>\n Installed\n </button>\n {hasUpdateAvailable && (\n <button\n type=\"button\"\n onClick={handleInstallFree}\n className=\"cursor-pointer rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Update to v{safeProduct.manifestVersion}\n </button>\n )}\n </>\n ) : !isPhysical && isPurchased && !isFree ? (\n <button\n type=\"button\"\n onClick={handleInstallFree}\n className=\"cursor-pointer rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Install to Workspace\n </button>\n ) : !isPhysical && isFree ? (\n <button\n type=\"button\"\n onClick={handleInstallFree}\n className=\"cursor-pointer rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Install\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleBuyNow}\n disabled={buyingNow || checkingOut || isOutOfStock}\n className=\"cursor-pointer flex items-center gap-2 rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-70\"\n >\n {buyingNow || checkingOut ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Processing…\n </>\n ) : isOutOfStock ? (\n 'Out of stock'\n ) : (\n `Buy ${getPriceDisplay()}`\n )}\n </button>\n )}\n\n {/* Add to Cart / Wishlist */}\n {!isFree && (!isPurchased || isPhysical) && (\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={addingToCart || isOutOfStock}\n className=\"cursor-pointer flex items-center gap-2 rounded-full border border-border-default px-4 py-3 text-sm text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n {addingToCart ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : isInCart ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <ShoppingCart className=\"size-4\" />\n )}\n {isInCart ? 'In Cart' : 'Add to Cart'}\n </button>\n )}\n </div>\n\n {/* Info Text */}\n {isPhysical ? (\n <>\n <p className=\"flex items-center gap-2 text-xs text-text-muted\">\n <Package className=\"size-4\" />\n {safeProduct.requiresShipping === false\n ? 'Available for pickup — no shipping required'\n : 'Ships to your delivery address'}\n </p>\n {!isFree && (\n <p className=\"text-xs text-text-tertiary\">\n Returns and refunds are handled by {publisher?.name || 'the seller'} per\n their store policy.\n </p>\n )}\n </>\n ) : (\n <>\n <p className=\"flex items-center gap-2 text-xs text-text-muted\">\n <Package className=\"size-4\" />\n This app is available for your workspace\n </p>\n {!isFree && !isPurchased && (\n <p className=\"text-xs text-text-tertiary\">\n All purchases are final. Digital app orders cannot be cancelled or refunded\n once completed.\n </p>\n )}\n\n {/* License Status */}\n {userEntitlement && (\n <p className=\"mt-2 flex items-center gap-2 text-xs text-text-muted\">\n <ShieldCheck className=\"size-4 text-status-success-text\" />\n License: {userEntitlement.status}\n {userEntitlement.expiresAt &&\n ` (Expires: ${new Date(userEntitlement.expiresAt).toLocaleDateString()})`}\n </p>\n )}\n </>\n )}\n </EmphasisPanel>\n </div>\n\n {/* Right Column - Banner or first screenshot */}\n <div className=\"relative flex-1 overflow-hidden bg-bg-sunken lg:min-h-[500px]\">\n {safeProduct.type === 'WORKFLOW' ? (\n /* Live read-only canvas preview served by the app shell's\n public /embed/workflow route (same origin, no auth). */\n <iframe\n src={`${window.location.origin}/embed/workflow?productId=${encodeURIComponent(\n safeProduct.id\n )}&cta=false&theme=${\n document.documentElement.classList.contains('dark') ? 'dark' : 'light'\n }`}\n title={`${safeProduct.name} workflow preview`}\n loading=\"lazy\"\n className=\"size-full min-h-[300px] border-0 lg:min-h-[500px]\"\n />\n ) : safeProduct.bannerUrl ? (\n <img\n src={safeProduct.bannerUrl}\n alt={`${safeProduct.name} banner`}\n className=\"size-full object-cover\"\n />\n ) : safeProduct.screenshots && safeProduct.screenshots.length > 0 ? (\n <img\n src={safeProduct.screenshots[0]}\n alt={`${safeProduct.name} preview`}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full min-h-[300px] flex-col items-center justify-center gap-3 p-8 text-center\">\n <div className=\"flex size-20 items-center justify-center rounded-2xl bg-bg-surface text-3xl font-bold text-text-muted\">\n {safeProduct.name.charAt(0)}\n </div>\n <p className=\"text-sm text-text-muted\">No preview available</p>\n <p className=\"max-w-[200px] text-xs text-text-muted opacity-60\">\n Upload screenshots in the Store Listing tab to show a preview here\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n\n {/* Apple App Store-style Screenshot Strip */}\n {safeProduct.screenshots && safeProduct.screenshots.length > 0 && (\n <div className=\"border-y border-border-default bg-bg-base\">\n <div className=\"px-6 py-6\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase tracking-wider text-text-muted\">\n Preview\n </h2>\n <div\n className=\"flex gap-4 overflow-x-auto pb-2\"\n style={{ scrollSnapType: 'x mandatory' }}\n >\n {safeProduct.screenshots.map((src, i) => (\n <div\n key={i}\n className=\"relative flex-shrink-0 overflow-hidden rounded-2xl shadow-lg\"\n style={{\n width: '72vw',\n maxWidth: 680,\n minWidth: 260,\n aspectRatio: '16/9',\n scrollSnapAlign: 'start',\n }}\n >\n <img src={src} alt={`Screenshot ${i + 1}`} className=\"size-full object-cover\" />\n </div>\n ))}\n {/* Spacer so last item doesn't stick to edge */}\n <div className=\"flex-shrink-0 w-2\" />\n </div>\n </div>\n </div>\n )}\n\n {/* Main Content Area */}\n <main className=\"mx-auto max-w-7xl px-6 py-8\">\n <PagePurpose className=\"mb-6\">\n Everything you need to decide whether to install {safeProduct.name}: what it does,\n screenshots, pricing, the permissions and integrations it needs, and real customer\n reviews. When you're ready, install it (free apps) or buy it, then add it to a workspace —\n and leave a review once you've used it.\n </PagePurpose>\n\n <NextSteps\n className=\"mb-8\"\n storageKey=\"store-product-detail\"\n steps={[\n isFree\n ? {\n id: 'install',\n label: 'Install this app',\n description: 'It’s free — add it to a workspace in a couple of clicks.',\n onClick: handleInstallFree,\n done: isInstalledInCurrentWorkspace,\n }\n : {\n id: 'buy',\n label: isPurchased ? 'Install to a workspace' : 'Buy or add to cart',\n description: isPurchased\n ? 'You already own this — install it where you need it.'\n : 'Purchase now, or add it to your cart to check out later.',\n onClick: isPurchased ? handleInstallFree : handleBuyNow,\n done: isInstalledInCurrentWorkspace,\n },\n ...(permissions.length > 0\n ? [\n {\n id: 'permissions',\n label: 'Review what it can access',\n description: 'Check the permissions this app requires before installing.',\n href: '#about-permissions',\n },\n ]\n : []),\n {\n id: 'reviews',\n label: 'Read the reviews',\n description: 'See what other customers think before committing.',\n href: '#ratings-reviews',\n },\n ]}\n />\n\n <div className=\"flex flex-col gap-8 lg:flex-row\">\n {/* Left Content */}\n <div className=\"flex-1 lg:max-w-3xl\">\n {/* About this app */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between text-left\">\n <h2 className=\"text-lg font-medium text-text-primary\">About this app</h2>\n </div>\n <div className=\"text-sm leading-relaxed text-text-secondary\">\n <div className={!showFullDescription ? 'line-clamp-4' : ''}>\n <Markdown>\n {safeProduct.longDescription ||\n safeProduct.description ||\n 'No description available.'}\n </Markdown>\n </div>\n {((safeProduct.longDescription || safeProduct.description)?.length ?? 0) > 200 && (\n <button\n type=\"button\"\n onClick={() => setShowFullDescription(!showFullDescription)}\n className=\"cursor-pointer mt-3 text-sm font-medium text-text-link\"\n >\n {showFullDescription ? 'Show less' : 'Show more'}\n </button>\n )}\n </div>\n {/* Tags */}\n {safeProduct.tags && safeProduct.tags.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-2\">\n {safeProduct.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full border border-border-seam px-3 py-1.5 text-xs text-text-muted transition-colors hover:bg-bg-sunken\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </section>\n\n {/* Ratings & Reviews */}\n <section id=\"ratings-reviews\" className=\"mb-8 scroll-mt-6\">\n <div className=\"mb-4 flex items-center justify-between text-left\">\n <h2 className=\"text-lg font-medium text-text-primary\">Ratings & Reviews</h2>\n </div>\n\n <GlassCard treatment=\"insight\" className=\"p-5\">\n {reviews && reviews.length > 0 ? (\n <div className=\"flex items-start gap-8\">\n {/* Rating Score */}\n <div className=\"text-center\">\n <p className=\"text-5xl font-light tabular-nums text-text-primary\">\n {(safeProduct.rating || 0).toFixed(1)}\n </p>\n <div className=\"my-2 flex justify-center gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <Star\n key={star}\n className={`size-4 ${\n star <= Math.round(safeProduct.rating || 0)\n ? 'fill-current text-status-warning-text'\n : 'text-text-muted'\n }`}\n />\n ))}\n </div>\n <p className=\"text-sm text-text-muted\">\n {formatNumber(reviewsCount || safeProduct.reviewCount)} reviews\n </p>\n </div>\n\n {/* Rating Distribution */}\n {ratingDistributionData.length > 0 && (\n <div className=\"flex-1 space-y-1.5\">\n {ratingDistributionData.map((item) => (\n <RatingBar key={item.stars} {...item} />\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-6 text-center\">\n <div className=\"mb-3 flex gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <Star key={star} className=\"size-5 text-border-default\" />\n ))}\n </div>\n <p className=\"font-medium text-text-primary\">No reviews yet</p>\n <p className=\"mt-1 text-sm text-text-muted\">\n Be the first to share your experience with this app.\n </p>\n </div>\n )}\n\n {/* Write Review */}\n <div className=\"mt-6 border-t border-border-seam pt-4\">\n {reviewNotice && (\n <p\n role={reviewNoticeTone === 'success' ? 'status' : 'alert'}\n className={`mb-3 rounded-lg px-3 py-2 text-sm ${\n reviewNoticeTone === 'success'\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n }`}\n >\n {reviewNotice}\n </p>\n )}\n {isInstalledInCurrentWorkspace || productDetails?.userReview ? (\n <div className=\"space-y-4\">\n <button\n type=\"button\"\n onClick={() =>\n isReviewFormOpen ? setIsReviewFormOpen(false) : openReviewForm()\n }\n className=\"w-full cursor-pointer py-2 text-center text-sm font-medium text-text-primary transition-colors hover:text-text-link\"\n >\n {productDetails?.userReview\n ? isReviewFormOpen\n ? 'Cancel review editing'\n : 'Edit your review'\n : isReviewFormOpen\n ? 'Cancel review'\n : 'Write a Review'}\n </button>\n\n {isReviewFormOpen && (\n <div className=\"rounded-xl border border-border-seam bg-bg-sunken p-4\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Your rating</p>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => setReviewRating(star)}\n className={`cursor-pointer rounded-full border px-3 py-1.5 text-sm transition-colors ${\n reviewRating === star\n ? 'border-action-primary-border bg-action-primary-bg text-action-primary-text'\n : 'border-border-default bg-bg-surface text-text-primary hover:bg-bg-surface'\n }`}\n >\n {star} ★\n </button>\n ))}\n </div>\n </div>\n\n <label className=\"mt-4 block text-sm font-medium text-text-primary\">\n Title\n <input\n aria-label=\"Review title\"\n value={reviewTitle}\n onChange={(event) => setReviewTitle(event.target.value)}\n className=\"mt-2 w-full rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-border\"\n placeholder=\"Summarize your experience\"\n />\n </label>\n\n <label className=\"mt-4 block text-sm font-medium text-text-primary\">\n Review\n <textarea\n aria-label=\"Review body\"\n value={reviewComment}\n onChange={(event) => setReviewComment(event.target.value)}\n rows={4}\n required\n aria-required=\"true\"\n className=\"mt-2 w-full rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-border\"\n placeholder=\"What worked well? What should improve?\"\n />\n </label>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={handleSubmitReview}\n disabled={creatingReview || updatingReview}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {creatingReview || updatingReview\n ? 'Saving...'\n : productDetails?.userReview\n ? 'Update Review'\n : 'Submit Review'}\n </button>\n {productDetails?.userReview && (\n <button\n type=\"button\"\n onClick={handleDeleteReview}\n disabled={deletingReview}\n className=\"cursor-pointer rounded-lg border border-status-error-border px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {deletingReview ? 'Removing...' : 'Delete Review'}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n ) : (\n <p className=\"text-center text-sm text-text-muted\">\n Install this app to leave a review.\n </p>\n )}\n </div>\n </GlassCard>\n\n {/* Reviews List */}\n {reviews && reviews.length > 0 && (\n <div className=\"mt-4 space-y-3\">\n <ul className=\"space-y-3\">\n {(showAllReviews ? reviews : reviews.slice(0, 2)).map((review) => (\n <li key={review.id}>\n <ReviewCard\n review={review}\n onMarkHelpful={handleMarkHelpful}\n markingHelpful={markingHelpful && helpfulReviewId === review.id}\n />\n </li>\n ))}\n </ul>\n {reviews.length > 2 && (\n <button\n type=\"button\"\n onClick={() => setShowAllReviews(!showAllReviews)}\n className=\"cursor-pointer text-sm font-medium text-text-link\"\n >\n {showAllReviews ? 'Show less' : `Show all ${reviews.length} reviews`}\n </button>\n )}\n </div>\n )}\n {(!reviews || reviews.length === 0) && (\n <p className=\"mt-4 text-sm text-text-muted\">\n No reviews yet. Be the first to review!\n </p>\n )}\n </section>\n\n {/* Permissions */}\n {permissions.length > 0 && (\n <section id=\"about-permissions\" className=\"mb-8 scroll-mt-6\">\n <h2 className=\"mb-4 text-lg font-medium text-text-primary\">Required Permissions</h2>\n <div className=\"space-y-2\">\n {permissions.map((permission) => (\n <div\n key={permission}\n className=\"flex items-center gap-3 rounded-lg bg-bg-surface px-4 py-3\"\n >\n <ShieldCheck className=\"size-5 text-status-success-text\" />\n <span className=\"text-sm text-text-primary\">{permission}</span>\n </div>\n ))}\n </div>\n </section>\n )}\n\n {/* Integrations */}\n {integrations.length > 0 && (\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-medium text-text-primary\">Integrations</h2>\n <div className=\"flex flex-wrap gap-2\">\n {integrations.map((integration) => (\n <span\n key={integration}\n className=\"rounded-lg bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary\"\n >\n {integration}\n </span>\n ))}\n </div>\n </section>\n )}\n\n {/* Additional Information */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-medium text-text-primary\">Additional Information</h2>\n <GlassCard treatment=\"metric\" className=\"grid grid-cols-2 gap-6 p-5 md:grid-cols-3\">\n {safeProduct.manifestVersion && (\n <div>\n <p className=\"text-xs text-text-muted\">Version</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {safeProduct.manifestVersion}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-xs text-text-muted\">Type</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">{safeProduct.type}</p>\n </div>\n {safeProduct.publishedAt && (\n <div>\n <p className=\"text-xs text-text-muted\">Published</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {new Date(safeProduct.publishedAt).toLocaleDateString()}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-xs text-text-muted\">Updated</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {new Date(safeProduct.updatedAt).toLocaleDateString()}\n </p>\n </div>\n {safeProduct.license && (\n <div>\n <p className=\"text-xs text-text-muted\">License</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {safeProduct.license}\n </p>\n </div>\n )}\n {safeProduct.minPlatformVersion && (\n <div>\n <p className=\"text-xs text-text-muted\">Min Platform Version</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {safeProduct.minPlatformVersion}\n </p>\n </div>\n )}\n </GlassCard>\n </section>\n </div>\n\n {/* Right Sidebar */}\n <div className=\"w-full lg:w-80\">\n {/* App Support */}\n {(safeProduct.documentationUrl || safeProduct.repositoryUrl || publisher?.email) && (\n <GlassCard className=\"mb-6 p-4\">\n <h3 className=\"font-medium text-text-primary\">App support</h3>\n <div className=\"mt-3 space-y-2 text-sm\">\n {safeProduct.documentationUrl && (\n <a\n href={safeProduct.documentationUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"block text-text-link hover:underline\"\n >\n Documentation\n </a>\n )}\n {safeProduct.repositoryUrl && (\n <a\n href={safeProduct.repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"block text-text-link hover:underline\"\n >\n Source repository\n </a>\n )}\n {publisher?.email && (\n <a\n href={`mailto:${publisher.email}`}\n className=\"block text-text-link hover:underline\"\n >\n Contact publisher\n </a>\n )}\n </div>\n </GlassCard>\n )}\n\n {/* Publisher / org card */}\n {publisher && (\n <GlassCard className=\"mb-6 p-4\">\n {(() => {\n const org = publisher.developerOrganization;\n const displayName = org?.name || publisher.name;\n const displayLogo = org?.logoUrl || publisher.logoUrl;\n const displayBio = org?.description || publisher.bio;\n const isVerified = org?.isVerified || publisher.isVerified;\n return (\n <>\n <div className=\"flex items-center gap-2.5\">\n {displayLogo ? (\n <img src={displayLogo} alt={displayName} className=\"size-8 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex size-8 items-center justify-center rounded-lg bg-bg-sunken text-sm font-bold text-text-muted\">\n {displayName.charAt(0)}\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"font-medium text-text-primary truncate\">{displayName}</span>\n {isVerified && <ShieldCheck className=\"size-3.5 text-status-success-text shrink-0\" />}\n </div>\n </div>\n </div>\n {displayBio && <p className=\"mt-2 text-sm text-text-muted\">{displayBio}</p>}\n {publisher.websiteUrl && (\n <a\n href={publisher.websiteUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-block text-sm text-text-link hover:underline\"\n >\n Visit website\n </a>\n )}\n </>\n );\n })()}\n </GlassCard>\n )}\n\n {/* Similar Apps */}\n {relatedProducts && relatedProducts.length > 0 && (\n <div>\n <div className=\"mb-4 flex items-center justify-between text-left\">\n <span className=\"font-medium text-text-primary\">Similar apps</span>\n </div>\n <div className=\"space-y-3\">\n {relatedProducts.slice(0, 4).map((related) => (\n <button\n type=\"button\"\n key={related.id}\n onClick={() => navigate(`${basePath}/marketplace/product/${related.id}`)}\n className=\"flex w-full cursor-pointer items-center gap-3 rounded-lg p-2 text-left transition-colors hover:bg-bg-sunken\"\n >\n {related.icon ? (\n <img\n src={related.icon}\n alt={related.name}\n className=\"size-12 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-xl bg-bg-sunken text-lg font-bold text-text-muted\">\n {related.name.charAt(0)}\n </div>\n )}\n <div className=\"flex-1 overflow-hidden\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {related.name}\n </p>\n <p className=\"text-xs text-text-muted\">{publisher?.name || 'Unknown'}</p>\n {(related.reviewCount ?? 0) > 0 && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-text-muted\">\n {(related.rating || 0).toFixed(1)}\n </span>\n <Star className=\"size-3 fill-current text-status-warning-text\" />\n </div>\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n </main>\n\n {/* Install App Modal */}\n {safeProduct && (\n <InstallAppModal\n isOpen={isInstallModalOpen}\n onClose={() => setIsInstallModalOpen(false)}\n app={{\n id: safeProduct.id,\n applicationId: safeProduct.productId,\n name: safeProduct.name,\n slug: safeProduct.slug,\n icon: safeProduct.icon,\n type: safeProduct.type,\n version: safeProduct.manifestVersion,\n permissions: permissions.length > 0 ? permissions : undefined,\n }}\n purchaseState={isPurchased && !isFree ? 'purchased' : 'free'}\n onInstallSuccess={handleInstallSuccess}\n onInstallError={handleInstallError}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductDetailPage.js","names":[],"sources":["../../src/pages/ProductDetailPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n ArrowLeft,\n Star,\n ShieldCheck,\n Check,\n ThumbsUp,\n Package,\n AlertCircle,\n ShoppingCart,\n Loader2,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport {\n useCreateReview,\n useDeleteReview,\n useMarkReviewHelpful,\n useStoreProductDetails,\n useUpdateReview,\n} from '../hooks/useStoreGraphQL';\nimport { useCart } from '../hooks/useCart';\nimport { useInstallations } from '../hooks/useInstallations';\nimport { formatNumber, formatPrice } from '../utils';\nimport { InstallAppModal } from '../components/InstallAppModal';\nimport {\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport Markdown from 'react-markdown';\nimport type {\n Product,\n ItemType,\n ProductType,\n PricingModel as CartPricingModel,\n ProductStatus,\n} from '../types';\n\n// ============================================================================\n// Type Definitions for GraphQL Response\n// ============================================================================\n\ninterface Publisher {\n id: string;\n userId?: string;\n name: string;\n email?: string;\n websiteUrl?: string;\n logoUrl?: string;\n bio?: string;\n isVerified: boolean;\n totalSales?: number;\n totalEarnings?: number;\n createdAt?: string;\n organizationId?: string | null;\n developerOrganization?: {\n id: string;\n name: string;\n logoUrl?: string | null;\n isVerified: boolean;\n description?: string | null;\n } | null;\n}\n\ninterface ProductReview {\n id: string;\n productId: string;\n userId: string;\n rating: number;\n title?: string;\n comment?: string;\n status: string;\n helpful: number;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface RelatedProduct {\n id: string;\n name: string;\n slug?: string;\n icon?: string;\n type: string;\n price: number;\n pricingModel: string;\n rating?: number;\n reviewCount?: number;\n downloads: number;\n publisher?: {\n id: string;\n name: string;\n isVerified: boolean;\n };\n}\n\ninterface RatingDistribution {\n fiveStars: number;\n fourStars: number;\n threeStars: number;\n twoStars: number;\n oneStar: number;\n}\n\ninterface UserEntitlement {\n id: string;\n status: string;\n licenseKey?: string;\n expiresAt?: string;\n}\n\ninterface StoreProduct {\n id: string;\n name: string;\n slug?: string;\n type: string;\n nature: string;\n status: string;\n description?: string;\n longDescription?: string;\n pricingModel: string;\n price: number;\n currency: string;\n icon?: string;\n bannerUrl?: string;\n screenshots: string[];\n videoUrls?: string[];\n documentationUrl?: string;\n repositoryUrl?: string;\n category?: string;\n tags?: string[];\n featured: boolean;\n zipFileUrl?: string;\n downloads: number;\n viewCount?: number;\n rating?: number;\n reviewCount: number;\n publishedAt?: string;\n createdAt: string;\n updatedAt: string;\n // Devportal application ID (productId in store maps to devportal Application.id)\n productId?: string;\n // Manifest fields\n subType?: string;\n manifestVersion?: string;\n authorName?: string;\n license?: string;\n minPlatformVersion?: string;\n compatibleProducts?: string[];\n requiredPermissions?: string[];\n integrationDeps?: string[];\n // Physical product fields\n sku?: string;\n stockQuantity?: number;\n trackInventory?: boolean;\n requiresShipping?: boolean;\n weight?: number;\n weightUnit?: string;\n brand?: string;\n manifest?: Record<string, unknown>;\n publisher?: Publisher;\n}\n\ninterface StoreProductDetailsResponse {\n product: StoreProduct;\n publisher: Publisher;\n reviews: ProductReview[];\n reviewsCount: number;\n relatedProducts: RelatedProduct[];\n userReview?: ProductReview;\n isPurchased: boolean;\n userEntitlement?: UserEntitlement;\n ratingDistribution?: RatingDistribution;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Convert StoreProduct (GraphQL) to Product (Cart) type\n */\nconst convertToCartProduct = (storeProduct: StoreProduct, publisher?: Publisher): Product => {\n // Map GraphQL pricing model to cart pricing model\n const mapPricingModel = (model: string): CartPricingModel => {\n const mapping: Record<string, CartPricingModel> = {\n FREE: 'FREE',\n PAID_ONETIME: 'ONE_TIME',\n SUBSCRIPTION: 'SUBSCRIPTION',\n USAGE_BASED: 'USAGE_BASED',\n FREEMIUM: 'FREEMIUM',\n };\n return mapping[model] || 'ONE_TIME';\n };\n\n // Map product type\n const mapProductType = (type: string): ProductType => {\n const mapping: Record<string, ProductType> = {\n APP: 'STANDALONE_APP',\n BOTAPP: 'STANDALONE_APP',\n AGENT: 'STANDALONE_APP',\n WORKFLOW: 'WORKFLOW_TEMPLATE',\n TEMPLATE: 'WORKFLOW_TEMPLATE',\n INTEGRATION: 'API_INTEGRATION',\n EXTENSION: 'BROWSER_EXTENSION',\n THEME: 'UI_THEME',\n PLUGIN: 'WORKSPACE_MODULE',\n COMPONENT: 'WORKSPACE_MODULE',\n WIDGET: 'WORKSPACE_MODULE',\n VIBEMODULE: 'WORKSPACE_MODULE',\n CONSOLE: 'STANDALONE_APP',\n NODE: 'WORKSPACE_MODULE',\n PARSER: 'WORKSPACE_MODULE',\n DASHBOARD: 'WORKFLOW_TEMPLATE',\n };\n return mapping[type] || 'STANDALONE_APP';\n };\n\n // Map item type\n const mapItemType = (nature: string, type: string): ItemType => {\n if (nature === 'PHYSICAL') return 'PHYSICAL';\n const typeMapping: Record<string, ItemType> = {\n APP: 'APP',\n BOTAPP: 'APP',\n AGENT: 'APP',\n WORKFLOW: 'WORKFLOW',\n TEMPLATE: 'TEMPLATE',\n INTEGRATION: 'INTEGRATION',\n EXTENSION: 'EXTENSION',\n THEME: 'THEME',\n DASHBOARD: 'TEMPLATE',\n };\n return typeMapping[type] || 'APP';\n };\n\n return {\n id: storeProduct.id,\n publisherId: publisher?.id || '',\n publisher: {\n id: publisher?.id || '',\n name: publisher?.name || storeProduct.authorName || 'Unknown',\n displayName: publisher?.name || storeProduct.authorName || 'Unknown',\n email: publisher?.email || '',\n verified: publisher?.isVerified || false,\n totalProducts: 0,\n totalDownloads: 0,\n averageRating: 0,\n joinedAt: publisher?.createdAt || storeProduct.createdAt,\n website: publisher?.websiteUrl,\n logoUrl: publisher?.logoUrl,\n },\n name: storeProduct.name,\n slug: storeProduct.slug || storeProduct.id,\n displayName: storeProduct.name,\n description: storeProduct.longDescription || storeProduct.description || '',\n shortDescription: storeProduct.description || '',\n iconUrl: storeProduct.icon,\n screenshotUrls: storeProduct.screenshots || [],\n videoUrls: storeProduct.videoUrls || [],\n itemType: mapItemType(storeProduct.nature, storeProduct.type),\n type: mapProductType(storeProduct.type),\n tags: storeProduct.tags || [],\n pricingModel: mapPricingModel(storeProduct.pricingModel),\n basePrice: storeProduct.price,\n currency: storeProduct.currency || 'USD',\n variants: [],\n status: (storeProduct.status as ProductStatus) || 'PUBLISHED',\n featured: storeProduct.featured,\n verified: publisher?.isVerified || false,\n downloadCount: storeProduct.downloads,\n installCount: storeProduct.downloads,\n orderCount: 0,\n averageRating: storeProduct.rating || 0,\n reviewCount: storeProduct.reviewCount,\n createdAt: storeProduct.createdAt,\n updatedAt: storeProduct.updatedAt,\n publishedAt: storeProduct.publishedAt,\n };\n};\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\n/**\n * Rating bar component for rating distribution\n */\nconst RatingBar: FC<{ stars: number; percentage: number; count: number }> = ({\n stars,\n percentage,\n count,\n}) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"w-3 text-xs text-text-muted\">{stars}</span>\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-status-warning-bg-subtle\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs text-text-muted\">{formatNumber(count)}</span>\n </div>\n);\n\n/**\n * Review card component\n */\nconst ReviewCard: FC<{\n review: ProductReview;\n onMarkHelpful: (reviewId: string) => void;\n markingHelpful: boolean;\n}> = ({ review, onMarkHelpful, markingHelpful }) => {\n const formatDate = (dateString: string) => {\n try {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return dateString;\n }\n };\n\n return (\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\">\n <div className=\"mb-3 flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-full bg-bg-sunken text-text-muted\">\n <Star className=\"size-4 fill-current\" />\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">Verified Buyer</p>\n <div className=\"flex items-center gap-2\">\n <div className=\"flex gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <Star\n key={star}\n className={`size-3 ${\n star <= review.rating\n ? 'fill-current text-status-warning-text'\n : 'text-text-muted'\n }`}\n />\n ))}\n </div>\n <span className=\"text-xs text-text-muted\">{formatDate(review.createdAt)}</span>\n </div>\n </div>\n </div>\n </div>\n {review.title && <h4 className=\"mb-2 font-medium text-text-primary\">{review.title}</h4>}\n {review.comment && <p className=\"mb-3 text-sm text-text-muted\">{review.comment}</p>}\n <div className=\"flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={() => onMarkHelpful(review.id)}\n disabled={markingHelpful}\n aria-label=\"Mark this review as helpful\"\n className=\"flex items-center gap-1 text-xs text-text-muted transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-60\"\n >\n <ThumbsUp className=\"size-3.5\" />\n <span>{markingHelpful ? 'Updating…' : `Helpful (${review.helpful})`}</span>\n </button>\n </div>\n </div>\n );\n};\n\n/**\n * Loading skeleton component - comprehensive skeleton matching full page layout\n */\nconst LoadingSkeleton: FC = () => (\n <div className=\"h-full overflow-y-auto\">\n {/* Header Skeleton */}\n <div className=\"sticky top-0 z-10 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"h-6 w-48 flex-1 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex gap-2\">\n <div className=\"size-10 animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"size-10 animate-pulse rounded-lg bg-bg-sunken\" />\n </div>\n </div>\n </div>\n\n <div className=\"mx-auto max-w-6xl p-6\">\n {/* App Header Section Skeleton */}\n <div className=\"mb-8 flex flex-col gap-6 md:flex-row\">\n {/* App Icon & Basic Info */}\n <div className=\"flex gap-4 md:w-1/2\">\n <div className=\"size-28 animate-pulse rounded-2xl bg-bg-sunken\" />\n <div className=\"flex flex-1 flex-col justify-center gap-2\">\n <div className=\"h-8 w-3/4 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex items-center gap-2\">\n <div className=\"h-4 w-32 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"size-4 animate-pulse rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"flex gap-3\">\n <div className=\"h-5 w-16 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-5 w-24 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n </div>\n\n {/* Rating & Install Skeleton */}\n <div className=\"flex flex-col gap-4 md:w-1/2 md:items-end\">\n <div className=\"flex items-center gap-4\">\n <div className=\"text-center\">\n <div className=\"h-10 w-16 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-20 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-px bg-border-default\" />\n <div className=\"text-center\">\n <div className=\"h-8 w-16 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-px bg-border-default\" />\n <div className=\"text-center\">\n <div className=\"h-8 w-20 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-12 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"flex gap-3\">\n <div className=\"h-12 w-40 animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"size-12 animate-pulse rounded-lg bg-bg-sunken\" />\n </div>\n </div>\n </div>\n\n {/* Screenshots Skeleton */}\n <section className=\"mb-8\">\n <div className=\"aspect-video animate-pulse rounded-lg bg-bg-sunken\" />\n <div className=\"mt-4 flex gap-4\">\n {[...Array(4)].map((_, i) => (\n <div key={i} className=\"h-20 w-36 animate-pulse rounded-lg bg-bg-sunken\" />\n ))}\n </div>\n </section>\n\n {/* About Section Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-32 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-4 w-5/6 rounded bg-bg-sunken\" />\n </div>\n <div className=\"mt-4 flex gap-2\">\n {[...Array(4)].map((_, i) => (\n <div key={i} className=\"h-6 w-16 rounded-full bg-bg-sunken\" />\n ))}\n </div>\n </div>\n </section>\n\n {/* Ratings & Reviews Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-40 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"mb-6 flex flex-col gap-6 md:flex-row\">\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4 md:w-64\">\n <div className=\"flex items-center gap-6\">\n <div className=\"text-center\">\n <div className=\"h-12 w-16 rounded bg-bg-sunken\" />\n <div className=\"my-1 flex justify-center gap-0.5\">\n {[...Array(5)].map((_, i) => (\n <div key={i} className=\"size-4 rounded bg-bg-sunken\" />\n ))}\n </div>\n <div className=\"h-3 w-20 rounded bg-bg-sunken\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n {[...Array(5)].map((_, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"h-2 w-3 rounded bg-bg-sunken\" />\n <div className=\"h-2 flex-1 rounded-full bg-bg-sunken\" />\n <div className=\"h-2 w-8 rounded bg-bg-sunken\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n <div className=\"h-12 w-32 animate-pulse rounded-lg bg-bg-sunken\" />\n </div>\n {/* Review Cards Skeleton */}\n <div className=\"space-y-4\">\n {[...Array(2)].map((_, i) => (\n <div\n key={i}\n className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"mb-3 flex items-start gap-3\">\n <div className=\"size-10 rounded-full bg-bg-sunken\" />\n <div className=\"flex-1\">\n <div className=\"h-4 w-32 rounded bg-bg-sunken\" />\n <div className=\"mt-1 flex items-center gap-2\">\n <div className=\"flex gap-0.5\">\n {[...Array(5)].map((_, j) => (\n <div key={j} className=\"size-3 rounded bg-bg-sunken\" />\n ))}\n </div>\n <div className=\"h-3 w-20 rounded bg-bg-sunken\" />\n </div>\n </div>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n ))}\n </div>\n </section>\n\n {/* Additional Info Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-48 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n {[...Array(6)].map((_, i) => (\n <div key={i}>\n <div className=\"h-3 w-16 rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-5 w-24 rounded bg-bg-sunken\" />\n </div>\n ))}\n </div>\n </div>\n </section>\n\n {/* Related Products Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <div className=\"size-5 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-6 w-40 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n <div className=\"grid gap-4 md:grid-cols-4\">\n {[...Array(4)].map((_, i) => (\n <div\n key={i}\n className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-14 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1\">\n <div className=\"h-4 w-24 rounded bg-bg-sunken\" />\n <div className=\"mt-1 h-3 w-16 rounded bg-bg-sunken\" />\n <div className=\"mt-1 flex items-center gap-2\">\n <div className=\"h-3 w-8 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-12 rounded bg-bg-sunken\" />\n </div>\n </div>\n </div>\n </div>\n ))}\n </div>\n </section>\n\n {/* Publisher Skeleton */}\n <section className=\"mb-8\">\n <div className=\"mb-4 h-6 w-36 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"animate-pulse rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-12 rounded-lg bg-bg-sunken\" />\n <div>\n <div className=\"flex items-center gap-2\">\n <div className=\"h-5 w-32 rounded bg-bg-sunken\" />\n <div className=\"size-4 rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"mt-1 h-4 w-40 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-3 space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n </section>\n </div>\n </div>\n);\n\n/**\n * Error display component\n */\nconst ErrorDisplay: FC<{ error: Error; onRetry: () => void; onBack: () => void }> = ({\n error,\n onRetry,\n onBack,\n}) => (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"text-center\">\n <AlertCircle className=\"mx-auto mb-4 size-12 text-status-error-text\" />\n <h2 className=\"mb-2 text-xl font-semibold text-text-primary\">Failed to load product</h2>\n <p className=\"mb-4 text-text-muted\">\n {error.message || 'An error occurred while loading the product details.'}\n </p>\n <div className=\"flex justify-center gap-3\">\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Try Again\n </button>\n <button\n type=\"button\"\n onClick={onBack}\n className=\"rounded-lg border border-border-default px-4 py-2 text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Go Back\n </button>\n </div>\n </div>\n </div>\n);\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * Product Detail Page Component\n *\n * Displays comprehensive product details using real GraphQL data\n */\nexport const ProductDetailPage: FC = () => {\n const { productId } = useParams<{ productId: string }>();\n const navigate = useNavigate();\n const location = useLocation();\n const { basePath, workspaceId } = useStore();\n const { addProduct, cartItems, addingToCart, createOrder, checkingOut } = useCart();\n const installationsFilter = useMemo(\n () => (productId && workspaceId ? { productId, workspaceId } : undefined),\n [productId, workspaceId]\n );\n const { installations: myInstallations, refetch: refetchInstallations } = useInstallations({\n filter: installationsFilter,\n limit: 1,\n });\n const activeInstallation = myInstallations.find(\n (i) => i.productId === productId && i.workspaceId === workspaceId && i.status === 'ACTIVE'\n );\n const isInstalledInCurrentWorkspace = Boolean(activeInstallation);\n const installedVersion = activeInstallation?.version ?? null;\n\n const { createReview, loading: creatingReview } = useCreateReview();\n const { updateReview, loading: updatingReview } = useUpdateReview();\n const { deleteReview, loading: deletingReview } = useDeleteReview();\n const { markHelpful, loading: markingHelpful } = useMarkReviewHelpful();\n const bus = useEventBus();\n\n const [showFullDescription, setShowFullDescription] = useState(false);\n const [showAllReviews, setShowAllReviews] = useState(false);\n const [buyingNow, setBuyingNow] = useState(false);\n const [quantity, setQuantity] = useState(1);\n const [actionError, setActionError] = useState<string | null>(null);\n const [reviewNotice, setReviewNotice] = useState<string | null>(null);\n const [reviewNoticeTone, setReviewNoticeTone] = useState<'success' | 'error'>('success');\n const [isReviewFormOpen, setIsReviewFormOpen] = useState(false);\n const [reviewTitle, setReviewTitle] = useState('');\n const [reviewComment, setReviewComment] = useState('');\n const [reviewRating, setReviewRating] = useState(5);\n const [helpfulReviewId, setHelpfulReviewId] = useState<string | null>(null);\n\n // Install modal state\n const [isInstallModalOpen, setIsInstallModalOpen] = useState(false);\n\n // Fetch product details using GraphQL\n const { data, loading, error, refetch } = useStoreProductDetails({\n id: productId,\n slug: productId, // Also try as slug\n includeReviews: true,\n reviewsLimit: 10,\n includeRelated: true,\n relatedLimit: 4,\n includeVersions: true,\n });\n\n // Cast data to typed response\n const productDetails = data as StoreProductDetailsResponse | null;\n const product = productDetails?.product;\n const publisher = productDetails?.publisher;\n\n // Check if product is already in cart (must be before any early returns)\n const isInCart = cartItems.some((item) => item.productId === product?.id);\n\n // Pad short version strings (\"1.0\" → \"1.0.0\") so they compare equal to \"1.0.0\"\n const padVer = (v: string) => {\n const p = v.split('.');\n while (p.length < 3) p.push('0');\n return p.join('.');\n };\n const latestVersion = product?.manifestVersion ?? null;\n const hasUpdateAvailable = Boolean(\n isInstalledInCurrentWorkspace &&\n installedVersion &&\n latestVersion &&\n padVer(installedVersion) !== padVer(latestVersion)\n );\n\n // Handle Add to Cart (must be before any early returns)\n const handleAddToCart = useCallback(async () => {\n if (!product) return;\n\n setActionError(null);\n\n if (isInCart) {\n navigate(`${basePath}/cart`);\n return;\n }\n\n try {\n const cartProduct = convertToCartProduct(product, publisher);\n // Add to backend cart via GraphQL API call\n // This will make a mutation to global-public-gateway:4004 → tenantmodule-store-svc:4017\n await addProduct(cartProduct, undefined, quantity);\n // Cart drawer will open automatically after adding\n } catch (error) {\n console.error('Failed to add product to cart:', error);\n setActionError('Failed to add this product to cart. Please try again.');\n }\n }, [product, publisher, addProduct, isInCart, navigate, basePath, quantity]);\n\n // Handle Buy Now - creates an order and immediately proceeds to checkout\n const handleBuyNow = useCallback(async () => {\n if (!product) return;\n\n setActionError(null);\n setBuyingNow(true);\n\n try {\n // Get product price\n const price = product.price || 0;\n\n // Build line item for this product\n const lineItem = {\n productId: product.id,\n name: product.name,\n quantity,\n unitPrice: price,\n subtotal: price * quantity,\n itemType: product.nature?.toLowerCase() || 'digital',\n pricingModel: product.pricingModel || 'PAID_ONETIME',\n };\n\n // Create order input\n const orderInput = {\n total: price * quantity,\n lineItems: [lineItem],\n billingAccountId: '', // Will be set on billing page\n };\n\n // Call createStoreOrder mutation to create PENDING order\n const order = await createOrder(orderInput);\n\n if (order?.id) {\n // Navigate to billing page, preserving org/workspace/tenant context params\n navigate(`/billing/checkout/store/${order.id}${location.search}`);\n } else {\n console.error('Failed to create order');\n setActionError('Failed to create order. Please try again.');\n setBuyingNow(false);\n }\n } catch (error) {\n console.error('Buy Now error:', error);\n const message = error instanceof Error ? error.message : 'Failed to start checkout';\n setActionError(message);\n setBuyingNow(false);\n }\n }, [product, quantity, createOrder, navigate]);\n\n // Handle Install Free - Open modal for workspace selection (must be before any early returns)\n const handleInstallFree = useCallback(() => {\n if (!product) return;\n setIsInstallModalOpen(true);\n }, [product]);\n\n // Emit product.viewed event once product data loads. Read product via ref so\n // we only emit on id transitions, not on every field change.\n const productRef = useRef(product);\n productRef.current = product;\n const busRef = useRef(bus);\n busRef.current = bus;\n useEffect(() => {\n const p = productRef.current;\n if (!p) return;\n (busRef.current as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.product.viewed',\n {\n productId: p.id,\n productSlug: p.slug,\n productType: p.type,\n pricingModel: p.pricingModel,\n }\n );\n }, [product?.id]);\n\n // Handle successful installation (must be before any early returns)\n const handleInstallSuccess = useCallback(\n (workspaceId: string, workspaceName: string) => {\n if (!product) return;\n (bus as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.product.installed',\n {\n productId: product.id,\n workspaceId,\n }\n );\n console.log(\n `Successfully installed ${product.name} to workspace ${workspaceName} (${workspaceId})`\n );\n void refetchInstallations();\n },\n [product, bus, refetchInstallations]\n );\n\n // Handle installation error (must be before any early returns)\n const handleInstallError = useCallback((error: Error) => {\n console.error('Installation failed:', error.message);\n // Error is shown in the modal\n }, []);\n\n const openReviewForm = useCallback(() => {\n setReviewTitle(productDetails?.userReview?.title ?? '');\n setReviewComment(productDetails?.userReview?.comment ?? '');\n setReviewRating(productDetails?.userReview?.rating ?? 5);\n setReviewNotice(null);\n setReviewNoticeTone('success');\n setIsReviewFormOpen(true);\n }, [productDetails?.userReview]);\n\n const handleSubmitReview = useCallback(async () => {\n if (!product) {\n return;\n }\n\n if (reviewRating < 1 || reviewRating > 5) {\n setReviewNoticeTone('error');\n setReviewNotice('Choose a rating from 1 to 5 stars.');\n return;\n }\n\n const payload = {\n productId: product.id,\n rating: reviewRating,\n title: reviewTitle.trim() || undefined,\n comment: reviewComment.trim() || undefined,\n };\n\n const reviewResult = productDetails?.userReview\n ? {\n data: await updateReview(productDetails.userReview.id, {\n rating: payload.rating,\n title: payload.title,\n comment: payload.comment,\n }),\n errorMessage: null,\n }\n : await createReview(payload);\n\n if (!reviewResult?.data) {\n setReviewNoticeTone('error');\n setReviewNotice(\n reviewResult?.errorMessage ?? 'We could not save your review. Please try again.'\n );\n return;\n }\n\n setReviewNoticeTone('success');\n setReviewNotice(\n productDetails?.userReview ? 'Your review was updated.' : 'Your review was submitted.'\n );\n setIsReviewFormOpen(false);\n (bus as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.review.submitted',\n {\n productId: product.id,\n rating: reviewRating,\n }\n );\n await refetch();\n }, [\n bus,\n createReview,\n product,\n productDetails?.userReview,\n refetch,\n reviewComment,\n reviewRating,\n reviewTitle,\n updateReview,\n ]);\n\n const handleDeleteReview = useCallback(async () => {\n if (!productDetails?.userReview) {\n return;\n }\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete review',\n message: 'Are you sure you want to remove your review?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === false) {\n return;\n }\n\n const deleted = await deleteReview(productDetails.userReview.id);\n if (!deleted) {\n setReviewNoticeTone('error');\n setReviewNotice('We could not remove your review. Please try again.');\n void nativeNotify('error');\n return;\n }\n\n setReviewNoticeTone('success');\n setReviewNotice('Your review was removed.');\n setIsReviewFormOpen(false);\n setReviewTitle('');\n setReviewComment('');\n setReviewRating(5);\n void nativeNotify('success');\n await refetch();\n }, [deleteReview, productDetails?.userReview, refetch]);\n\n const handleMarkHelpful = useCallback(\n async (reviewId: string) => {\n setHelpfulReviewId(reviewId);\n const result = await markHelpful(reviewId);\n if (result) {\n await refetch();\n }\n setHelpfulReviewId(null);\n },\n [markHelpful, refetch]\n );\n\n // Handle loading state\n if (loading) {\n return <LoadingSkeleton />;\n }\n\n // Handle error state\n if (error) {\n return (\n <ErrorDisplay\n error={error}\n onRetry={refetch}\n onBack={() => navigate(`${basePath}/marketplace`)}\n />\n );\n }\n\n // Handle not found state\n if (!productDetails?.product) {\n return (\n <div className=\"flex h-full items-center justify-center p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Product not found\"\n description=\"The product you are looking for does not exist or has been removed.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Back to Marketplace\n </button>\n }\n />\n </div>\n );\n }\n\n const {\n reviews,\n reviewsCount,\n relatedProducts,\n ratingDistribution,\n isPurchased,\n userEntitlement,\n } = productDetails;\n\n // Type assertion: we know product is defined because we checked !productDetails?.product above\n const safeProduct = product!;\n\n // Calculate rating distribution percentages\n const totalRatings = ratingDistribution\n ? ratingDistribution.fiveStars +\n ratingDistribution.fourStars +\n ratingDistribution.threeStars +\n ratingDistribution.twoStars +\n ratingDistribution.oneStar\n : 0;\n\n const ratingDistributionData = ratingDistribution\n ? [\n {\n stars: 5,\n count: ratingDistribution.fiveStars,\n percentage: totalRatings > 0 ? (ratingDistribution.fiveStars / totalRatings) * 100 : 0,\n },\n {\n stars: 4,\n count: ratingDistribution.fourStars,\n percentage: totalRatings > 0 ? (ratingDistribution.fourStars / totalRatings) * 100 : 0,\n },\n {\n stars: 3,\n count: ratingDistribution.threeStars,\n percentage: totalRatings > 0 ? (ratingDistribution.threeStars / totalRatings) * 100 : 0,\n },\n {\n stars: 2,\n count: ratingDistribution.twoStars,\n percentage: totalRatings > 0 ? (ratingDistribution.twoStars / totalRatings) * 100 : 0,\n },\n {\n stars: 1,\n count: ratingDistribution.oneStar,\n percentage: totalRatings > 0 ? (ratingDistribution.oneStar / totalRatings) * 100 : 0,\n },\n ]\n : [];\n\n // Parse permissions from manifest or requiredPermissions\n const permissions = safeProduct.requiredPermissions || [];\n\n // Parse integrations from integrationDeps\n const integrations = safeProduct.integrationDeps || [];\n\n // Pricing display\n const getPriceDisplay = () => {\n if (safeProduct.pricingModel === 'FREE') return 'Free';\n if (safeProduct.pricingModel === 'SUBSCRIPTION') {\n return `${formatPrice(safeProduct.price, safeProduct.currency)}/mo`;\n }\n return formatPrice(safeProduct.price, safeProduct.currency);\n };\n\n // Check if product is free\n const isFree = safeProduct.pricingModel === 'FREE' || safeProduct.price === 0;\n\n // Physical (produce) product inventory\n const isPhysical = safeProduct.nature === 'PHYSICAL';\n const tracksInventory = isPhysical && safeProduct.trackInventory === true;\n const stockQuantity = safeProduct.stockQuantity ?? 0;\n const isOutOfStock = tracksInventory && stockQuantity <= 0;\n\n return (\n <div className=\"h-full overflow-y-auto bg-bg-base\">\n {/* Hero Section - Two Column Layout */}\n <div className=\"bg-bg-surface\">\n <div className=\"mx-auto max-w-7xl\">\n <div className=\"flex flex-col lg:flex-row\">\n {/* Left Column - Product Info */}\n <div className=\"flex-1 px-6 py-8 lg:max-w-xl lg:py-12\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n className=\"mb-6 cursor-pointer inline-flex items-center gap-2 text-sm text-text-muted transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n\n {/* App Title - Large Typography */}\n <h1 className=\"mb-3 text-4xl font-normal leading-tight text-text-primary lg:text-5xl\">\n {safeProduct.name}\n </h1>\n\n {/* Installed badge — always above the fold */}\n {isInstalledInCurrentWorkspace && (\n <div className=\"mb-4 inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1 text-sm font-medium text-status-success-text\">\n <svg\n className=\"size-3.5\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"2,8 6,12 14,4\" />\n </svg>\n Installed{installedVersion ? ` v${installedVersion}` : ''}\n </div>\n )}\n\n {/* Publisher / org */}\n <div className=\"mb-2\">\n {(() => {\n const org = publisher?.developerOrganization;\n const displayName =\n org?.name || publisher?.name || safeProduct.authorName || 'Unknown Publisher';\n const displayLogo = org?.logoUrl || publisher?.logoUrl;\n const isVerified = org?.isVerified || publisher?.isVerified;\n const linkUrl = publisher?.websiteUrl;\n return (\n <div className=\"flex items-center gap-2\">\n {displayLogo && (\n <img\n src={displayLogo}\n alt={displayName}\n className=\"size-5 rounded object-cover\"\n />\n )}\n {linkUrl ? (\n <a\n href={linkUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-base font-medium text-text-link hover:underline\"\n >\n {displayName}\n </a>\n ) : (\n <span className=\"text-base font-medium text-text-primary\">\n {displayName}\n </span>\n )}\n {isVerified && <ShieldCheck className=\"size-4 text-status-success-text\" />}\n </div>\n );\n })()}\n </div>\n\n {/* Metadata */}\n <p className=\"mb-6 text-sm text-text-muted\">\n {safeProduct.category && <span>{safeProduct.category}</span>}\n {safeProduct.category && ' · '}\n {formatNumber(safeProduct.downloads)}+ downloads\n </p>\n\n {/* Stats + primary action — the single emphasis zone for this page */}\n <EmphasisPanel className=\"mb-6 p-5\">\n {/* Stats Row */}\n <div className=\"mb-6 flex items-center gap-4\">\n {/* App Icon Badge */}\n <div className=\"flex size-12 items-center justify-center overflow-hidden rounded-xl bg-bg-sunken\">\n {safeProduct.icon ? (\n <img src={safeProduct.icon} alt=\"\" className=\"size-full object-cover\" />\n ) : (\n <span className=\"text-lg font-bold text-text-muted\">\n {safeProduct.name.charAt(0)}\n </span>\n )}\n </div>\n\n {/* Rating */}\n <div className=\"flex items-center gap-1 border-l border-border-default pl-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {(safeProduct.rating || 0).toFixed(1)}\n </span>\n <Star className=\"size-4 fill-current text-status-warning-text\" />\n <span className=\"ml-1 text-xs text-text-muted\">\n {formatNumber(reviewsCount || safeProduct.reviewCount)} reviews\n </span>\n </div>\n\n {/* Downloads */}\n <div className=\"border-l border-border-default pl-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {formatNumber(safeProduct.downloads)}+\n </span>\n <p className=\"text-xs text-text-muted\">Downloads</p>\n </div>\n\n {/* Type Badge */}\n <div className=\"border-l border-border-default pl-4\">\n <span className=\"rounded bg-bg-sunken px-2 py-1 text-xs font-medium text-text-primary\">\n {safeProduct.type}\n </span>\n <p className=\"mt-0.5 text-xs text-text-muted\">Type</p>\n </div>\n </div>\n\n {/* Action Buttons Row */}\n {actionError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-bg bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {isPhysical && (\n <div className=\"mb-4 flex flex-wrap items-center gap-4\">\n {tracksInventory &&\n (isOutOfStock ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-error-bg-subtle px-3 py-1 text-sm font-medium text-status-error-text\">\n <Package className=\"size-4\" />\n Out of stock\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-3 py-1 text-sm font-medium text-status-success-text\">\n <Package className=\"size-4\" />\n {formatNumber(stockQuantity)} in stock\n </span>\n ))}\n {!isOutOfStock && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-muted\">Qty</span>\n <div className=\"flex items-center rounded-full border border-border-default\">\n <button\n type=\"button\"\n onClick={() => setQuantity((q) => Math.max(1, q - 1))}\n disabled={quantity <= 1}\n aria-label=\"Decrease quantity\"\n className=\"cursor-pointer px-3 py-1.5 text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n −\n </button>\n <span className=\"min-w-8 px-2 text-center text-sm font-medium tabular-nums text-text-primary\">\n {quantity}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n setQuantity((q) =>\n tracksInventory ? Math.min(stockQuantity, q + 1) : q + 1\n )\n }\n disabled={tracksInventory && quantity >= stockQuantity}\n aria-label=\"Increase quantity\"\n className=\"cursor-pointer px-3 py-1.5 text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n +\n </button>\n </div>\n </div>\n )}\n </div>\n )}\n\n <div className=\"mb-6 flex items-center gap-3\">\n {isInstalledInCurrentWorkspace ? (\n <>\n <button\n type=\"button\"\n disabled\n className=\"flex items-center gap-2 rounded-full bg-status-success-bg-subtle px-8 py-3 font-medium text-status-success-text cursor-default\"\n >\n <svg\n className=\"size-4\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"2,8 6,12 14,4\" />\n </svg>\n Installed\n </button>\n {hasUpdateAvailable && (\n <button\n type=\"button\"\n onClick={handleInstallFree}\n className=\"cursor-pointer rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Update to v{safeProduct.manifestVersion}\n </button>\n )}\n </>\n ) : !isPhysical && isPurchased && !isFree ? (\n <button\n type=\"button\"\n onClick={handleInstallFree}\n className=\"cursor-pointer rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Install to Workspace\n </button>\n ) : !isPhysical && isFree ? (\n <button\n type=\"button\"\n onClick={handleInstallFree}\n className=\"cursor-pointer rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n Install\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleBuyNow}\n disabled={buyingNow || checkingOut || isOutOfStock}\n className=\"cursor-pointer flex items-center gap-2 rounded-full bg-action-primary-bg px-8 py-3 font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-70\"\n >\n {buyingNow || checkingOut ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Processing…\n </>\n ) : isOutOfStock ? (\n 'Out of stock'\n ) : (\n `Buy ${getPriceDisplay()}`\n )}\n </button>\n )}\n\n {/* Add to Cart / Wishlist */}\n {!isFree && (!isPurchased || isPhysical) && (\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={addingToCart || isOutOfStock}\n className=\"cursor-pointer flex items-center gap-2 rounded-full border border-border-default px-4 py-3 text-sm text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n {addingToCart ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : isInCart ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <ShoppingCart className=\"size-4\" />\n )}\n {isInCart ? 'In Cart' : 'Add to Cart'}\n </button>\n )}\n </div>\n\n {/* Info Text */}\n {isPhysical ? (\n <>\n <p className=\"flex items-center gap-2 text-xs text-text-muted\">\n <Package className=\"size-4\" />\n {safeProduct.requiresShipping === false\n ? 'Available for pickup — no shipping required'\n : 'Ships to your delivery address'}\n </p>\n {!isFree && (\n <p className=\"text-xs text-text-tertiary\">\n Returns and refunds are handled by {publisher?.name || 'the seller'} per\n their store policy.\n </p>\n )}\n </>\n ) : (\n <>\n <p className=\"flex items-center gap-2 text-xs text-text-muted\">\n <Package className=\"size-4\" />\n This app is available for your workspace\n </p>\n {!isFree && !isPurchased && (\n <p className=\"text-xs text-text-tertiary\">\n All purchases are final. Digital app orders cannot be cancelled or refunded\n once completed.\n </p>\n )}\n\n {/* License Status */}\n {userEntitlement && (\n <p className=\"mt-2 flex items-center gap-2 text-xs text-text-muted\">\n <ShieldCheck className=\"size-4 text-status-success-text\" />\n License: {userEntitlement.status}\n {userEntitlement.expiresAt &&\n ` (Expires: ${new Date(userEntitlement.expiresAt).toLocaleDateString()})`}\n </p>\n )}\n </>\n )}\n </EmphasisPanel>\n </div>\n\n {/* Right Column - Banner or first screenshot */}\n <div className=\"relative flex-1 overflow-hidden bg-bg-sunken lg:min-h-[500px]\">\n {safeProduct.type === 'WORKFLOW' ? (\n /* Live read-only canvas preview served by the app shell's\n public /embed/workflow route (same origin, no auth). */\n <iframe\n src={`${window.location.origin}/embed/workflow?productId=${encodeURIComponent(\n safeProduct.id\n )}&cta=false&theme=${\n document.documentElement.classList.contains('dark') ? 'dark' : 'light'\n }`}\n title={`${safeProduct.name} workflow preview`}\n loading=\"lazy\"\n className=\"size-full min-h-[300px] border-0 lg:min-h-[500px]\"\n />\n ) : safeProduct.bannerUrl ? (\n <img\n src={safeProduct.bannerUrl}\n alt={`${safeProduct.name} banner`}\n className=\"size-full object-cover\"\n />\n ) : safeProduct.screenshots && safeProduct.screenshots.length > 0 ? (\n <img\n src={safeProduct.screenshots[0]}\n alt={`${safeProduct.name} preview`}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full min-h-[300px] flex-col items-center justify-center gap-3 p-8 text-center\">\n <div className=\"flex size-20 items-center justify-center rounded-2xl bg-bg-surface text-3xl font-bold text-text-muted\">\n {safeProduct.name.charAt(0)}\n </div>\n <p className=\"text-sm text-text-muted\">No preview available</p>\n <p className=\"max-w-[200px] text-xs text-text-muted opacity-60\">\n Upload screenshots in the Store Listing tab to show a preview here\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n\n {/* Apple App Store-style Screenshot Strip */}\n {safeProduct.screenshots && safeProduct.screenshots.length > 0 && (\n <div className=\"border-y border-border-default bg-bg-base\">\n <div className=\"px-6 py-6\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase tracking-wider text-text-muted\">\n Preview\n </h2>\n <div\n className=\"flex gap-4 overflow-x-auto pb-2\"\n style={{ scrollSnapType: 'x mandatory' }}\n >\n {safeProduct.screenshots.map((src, i) => (\n <div\n key={i}\n className=\"relative flex-shrink-0 overflow-hidden rounded-2xl shadow-lg\"\n style={{\n width: '72vw',\n maxWidth: 680,\n minWidth: 260,\n aspectRatio: '16/9',\n scrollSnapAlign: 'start',\n }}\n >\n <img src={src} alt={`Screenshot ${i + 1}`} className=\"size-full object-cover\" />\n </div>\n ))}\n {/* Spacer so last item doesn't stick to edge */}\n <div className=\"flex-shrink-0 w-2\" />\n </div>\n </div>\n </div>\n )}\n\n {/* Main Content Area */}\n <main className=\"mx-auto max-w-7xl px-6 py-8\">\n <PagePurpose className=\"mb-6\">\n Everything you need to decide whether to install {safeProduct.name}: what it does,\n screenshots, pricing, the permissions and integrations it needs, and real customer\n reviews. When you're ready, install it (free apps) or buy it, then add it to a workspace —\n and leave a review once you've used it.\n </PagePurpose>\n\n <NextSteps\n className=\"mb-8\"\n storageKey=\"store-product-detail\"\n steps={[\n isFree\n ? {\n id: 'install',\n label: 'Install this app',\n description: 'It’s free — add it to a workspace in a couple of clicks.',\n onClick: handleInstallFree,\n done: isInstalledInCurrentWorkspace,\n }\n : {\n id: 'buy',\n label: isPurchased ? 'Install to a workspace' : 'Buy or add to cart',\n description: isPurchased\n ? 'You already own this — install it where you need it.'\n : 'Purchase now, or add it to your cart to check out later.',\n onClick: isPurchased ? handleInstallFree : handleBuyNow,\n done: isInstalledInCurrentWorkspace,\n },\n ...(permissions.length > 0\n ? [\n {\n id: 'permissions',\n label: 'Review what it can access',\n description: 'Check the permissions this app requires before installing.',\n href: '#about-permissions',\n },\n ]\n : []),\n {\n id: 'reviews',\n label: 'Read the reviews',\n description: 'See what other customers think before committing.',\n href: '#ratings-reviews',\n },\n ]}\n />\n\n <div className=\"flex flex-col gap-8 lg:flex-row\">\n {/* Left Content */}\n <div className=\"flex-1 lg:max-w-3xl\">\n {/* About this app */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between text-left\">\n <h2 className=\"text-lg font-medium text-text-primary\">About this app</h2>\n </div>\n <div className=\"text-sm leading-relaxed text-text-secondary\">\n <div className={!showFullDescription ? 'line-clamp-4' : ''}>\n <Markdown>\n {safeProduct.longDescription ||\n safeProduct.description ||\n 'No description available.'}\n </Markdown>\n </div>\n {((safeProduct.longDescription || safeProduct.description)?.length ?? 0) > 200 && (\n <button\n type=\"button\"\n onClick={() => setShowFullDescription(!showFullDescription)}\n className=\"cursor-pointer mt-3 text-sm font-medium text-text-link\"\n >\n {showFullDescription ? 'Show less' : 'Show more'}\n </button>\n )}\n </div>\n {/* Tags */}\n {safeProduct.tags && safeProduct.tags.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-2\">\n {safeProduct.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full border border-border-seam px-3 py-1.5 text-xs text-text-muted transition-colors hover:bg-bg-sunken\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </section>\n\n {/* Ratings & Reviews */}\n <section id=\"ratings-reviews\" className=\"mb-8 scroll-mt-6\">\n <div className=\"mb-4 flex items-center justify-between text-left\">\n <h2 className=\"text-lg font-medium text-text-primary\">Ratings & Reviews</h2>\n </div>\n\n <GlassCard treatment=\"insight\" className=\"p-5\">\n {reviews && reviews.length > 0 ? (\n <div className=\"flex items-start gap-8\">\n {/* Rating Score */}\n <div className=\"text-center\">\n <p className=\"text-5xl font-light tabular-nums text-text-primary\">\n {(safeProduct.rating || 0).toFixed(1)}\n </p>\n <div className=\"my-2 flex justify-center gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <Star\n key={star}\n className={`size-4 ${\n star <= Math.round(safeProduct.rating || 0)\n ? 'fill-current text-status-warning-text'\n : 'text-text-muted'\n }`}\n />\n ))}\n </div>\n <p className=\"text-sm text-text-muted\">\n {formatNumber(reviewsCount || safeProduct.reviewCount)} reviews\n </p>\n </div>\n\n {/* Rating Distribution */}\n {ratingDistributionData.length > 0 && (\n <div className=\"flex-1 space-y-1.5\">\n {ratingDistributionData.map((item) => (\n <RatingBar key={item.stars} {...item} />\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-6 text-center\">\n <div className=\"mb-3 flex gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <Star key={star} className=\"size-5 text-border-default\" />\n ))}\n </div>\n <p className=\"font-medium text-text-primary\">No reviews yet</p>\n <p className=\"mt-1 text-sm text-text-muted\">\n Be the first to share your experience with this app.\n </p>\n </div>\n )}\n\n {/* Write Review */}\n <div className=\"mt-6 border-t border-border-seam pt-4\">\n {reviewNotice && (\n <p\n role={reviewNoticeTone === 'success' ? 'status' : 'alert'}\n className={`mb-3 rounded-lg px-3 py-2 text-sm ${\n reviewNoticeTone === 'success'\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-status-error-bg-subtle text-status-error-text'\n }`}\n >\n {reviewNotice}\n </p>\n )}\n {isInstalledInCurrentWorkspace || productDetails?.userReview ? (\n <div className=\"space-y-4\">\n <button\n type=\"button\"\n onClick={() =>\n isReviewFormOpen ? setIsReviewFormOpen(false) : openReviewForm()\n }\n className=\"w-full cursor-pointer py-2 text-center text-sm font-medium text-text-primary transition-colors hover:text-text-link\"\n >\n {productDetails?.userReview\n ? isReviewFormOpen\n ? 'Cancel review editing'\n : 'Edit your review'\n : isReviewFormOpen\n ? 'Cancel review'\n : 'Write a Review'}\n </button>\n\n {isReviewFormOpen && (\n <div className=\"rounded-xl border border-border-seam bg-bg-sunken p-4\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Your rating</p>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => setReviewRating(star)}\n className={`cursor-pointer rounded-full border px-3 py-1.5 text-sm transition-colors ${\n reviewRating === star\n ? 'border-action-primary-border bg-action-primary-bg text-action-primary-text'\n : 'border-border-default bg-bg-surface text-text-primary hover:bg-bg-surface'\n }`}\n >\n {star} ★\n </button>\n ))}\n </div>\n </div>\n\n <label className=\"mt-4 block text-sm font-medium text-text-primary\">\n Title\n <input\n aria-label=\"Review title\"\n value={reviewTitle}\n onChange={(event) => setReviewTitle(event.target.value)}\n className=\"mt-2 w-full rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-border\"\n placeholder=\"Summarize your experience\"\n />\n </label>\n\n <label className=\"mt-4 block text-sm font-medium text-text-primary\">\n Review\n <textarea\n aria-label=\"Review body\"\n value={reviewComment}\n onChange={(event) => setReviewComment(event.target.value)}\n rows={4}\n required\n aria-required=\"true\"\n className=\"mt-2 w-full rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-border\"\n placeholder=\"What worked well? What should improve?\"\n />\n </label>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={handleSubmitReview}\n disabled={creatingReview || updatingReview}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {creatingReview || updatingReview\n ? 'Saving...'\n : productDetails?.userReview\n ? 'Update Review'\n : 'Submit Review'}\n </button>\n {productDetails?.userReview && (\n <button\n type=\"button\"\n onClick={handleDeleteReview}\n disabled={deletingReview}\n className=\"cursor-pointer rounded-lg border border-status-error-border px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {deletingReview ? 'Removing...' : 'Delete Review'}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n ) : (\n <p className=\"text-center text-sm text-text-muted\">\n Install this app to leave a review.\n </p>\n )}\n </div>\n </GlassCard>\n\n {/* Reviews List */}\n {reviews && reviews.length > 0 && (\n <div className=\"mt-4 space-y-3\">\n <ul className=\"space-y-3\">\n {(showAllReviews ? reviews : reviews.slice(0, 2)).map((review) => (\n <li key={review.id}>\n <ReviewCard\n review={review}\n onMarkHelpful={handleMarkHelpful}\n markingHelpful={markingHelpful && helpfulReviewId === review.id}\n />\n </li>\n ))}\n </ul>\n {reviews.length > 2 && (\n <button\n type=\"button\"\n onClick={() => setShowAllReviews(!showAllReviews)}\n className=\"cursor-pointer text-sm font-medium text-text-link\"\n >\n {showAllReviews ? 'Show less' : `Show all ${reviews.length} reviews`}\n </button>\n )}\n </div>\n )}\n {(!reviews || reviews.length === 0) && (\n <p className=\"mt-4 text-sm text-text-muted\">\n No reviews yet. Be the first to review!\n </p>\n )}\n </section>\n\n {/* Permissions */}\n {permissions.length > 0 && (\n <section id=\"about-permissions\" className=\"mb-8 scroll-mt-6\">\n <h2 className=\"mb-4 text-lg font-medium text-text-primary\">Required Permissions</h2>\n <div className=\"space-y-2\">\n {permissions.map((permission) => (\n <div\n key={permission}\n className=\"flex items-center gap-3 rounded-lg bg-bg-surface px-4 py-3\"\n >\n <ShieldCheck className=\"size-5 text-status-success-text\" />\n <span className=\"text-sm text-text-primary\">{permission}</span>\n </div>\n ))}\n </div>\n </section>\n )}\n\n {/* Integrations */}\n {integrations.length > 0 && (\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-medium text-text-primary\">Integrations</h2>\n <div className=\"flex flex-wrap gap-2\">\n {integrations.map((integration) => (\n <span\n key={integration}\n className=\"rounded-lg bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary\"\n >\n {integration}\n </span>\n ))}\n </div>\n </section>\n )}\n\n {/* Additional Information */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-medium text-text-primary\">Additional Information</h2>\n <GlassCard treatment=\"metric\" className=\"grid grid-cols-2 gap-6 p-5 md:grid-cols-3\">\n {safeProduct.manifestVersion && (\n <div>\n <p className=\"text-xs text-text-muted\">Version</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {safeProduct.manifestVersion}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-xs text-text-muted\">Type</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">{safeProduct.type}</p>\n </div>\n {safeProduct.publishedAt && (\n <div>\n <p className=\"text-xs text-text-muted\">Published</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {new Date(safeProduct.publishedAt).toLocaleDateString()}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-xs text-text-muted\">Updated</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {new Date(safeProduct.updatedAt).toLocaleDateString()}\n </p>\n </div>\n {safeProduct.license && (\n <div>\n <p className=\"text-xs text-text-muted\">License</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {safeProduct.license}\n </p>\n </div>\n )}\n {safeProduct.minPlatformVersion && (\n <div>\n <p className=\"text-xs text-text-muted\">Min Platform Version</p>\n <p className=\"mt-1 text-sm font-medium text-text-primary\">\n {safeProduct.minPlatformVersion}\n </p>\n </div>\n )}\n </GlassCard>\n </section>\n </div>\n\n {/* Right Sidebar */}\n <div className=\"w-full lg:w-80\">\n {/* App Support */}\n {(safeProduct.documentationUrl || safeProduct.repositoryUrl || publisher?.email) && (\n <GlassCard className=\"mb-6 p-4\">\n <h3 className=\"font-medium text-text-primary\">App support</h3>\n <div className=\"mt-3 space-y-2 text-sm\">\n {safeProduct.documentationUrl && (\n <a\n href={safeProduct.documentationUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"block text-text-link hover:underline\"\n >\n Documentation\n </a>\n )}\n {safeProduct.repositoryUrl && (\n <a\n href={safeProduct.repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"block text-text-link hover:underline\"\n >\n Source repository\n </a>\n )}\n {publisher?.email && (\n <a\n href={`mailto:${publisher.email}`}\n className=\"block text-text-link hover:underline\"\n >\n Contact publisher\n </a>\n )}\n </div>\n </GlassCard>\n )}\n\n {/* Publisher / org card */}\n {publisher && (\n <GlassCard className=\"mb-6 p-4\">\n {(() => {\n const org = publisher.developerOrganization;\n const displayName = org?.name || publisher.name;\n const displayLogo = org?.logoUrl || publisher.logoUrl;\n const displayBio = org?.description || publisher.bio;\n const isVerified = org?.isVerified || publisher.isVerified;\n return (\n <>\n <div className=\"flex items-center gap-2.5\">\n {displayLogo ? (\n <img\n src={displayLogo}\n alt={displayName}\n className=\"size-8 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-8 items-center justify-center rounded-lg bg-bg-sunken text-sm font-bold text-text-muted\">\n {displayName.charAt(0)}\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"font-medium text-text-primary truncate\">\n {displayName}\n </span>\n {isVerified && (\n <ShieldCheck className=\"size-3.5 text-status-success-text shrink-0\" />\n )}\n </div>\n </div>\n </div>\n {displayBio && <p className=\"mt-2 text-sm text-text-muted\">{displayBio}</p>}\n {publisher.websiteUrl && (\n <a\n href={publisher.websiteUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-block text-sm text-text-link hover:underline\"\n >\n Visit website\n </a>\n )}\n </>\n );\n })()}\n </GlassCard>\n )}\n\n {/* Similar Apps */}\n {relatedProducts && relatedProducts.length > 0 && (\n <div>\n <div className=\"mb-4 flex items-center justify-between text-left\">\n <span className=\"font-medium text-text-primary\">Similar apps</span>\n </div>\n <div className=\"space-y-3\">\n {relatedProducts.slice(0, 4).map((related) => (\n <button\n type=\"button\"\n key={related.id}\n onClick={() => navigate(`${basePath}/marketplace/product/${related.id}`)}\n className=\"flex w-full cursor-pointer items-center gap-3 rounded-lg p-2 text-left transition-colors hover:bg-bg-sunken\"\n >\n {related.icon ? (\n <img\n src={related.icon}\n alt={related.name}\n className=\"size-12 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex size-12 items-center justify-center rounded-xl bg-bg-sunken text-lg font-bold text-text-muted\">\n {related.name.charAt(0)}\n </div>\n )}\n <div className=\"flex-1 overflow-hidden\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {related.name}\n </p>\n <p className=\"text-xs text-text-muted\">{publisher?.name || 'Unknown'}</p>\n {(related.reviewCount ?? 0) > 0 && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-xs text-text-muted\">\n {(related.rating || 0).toFixed(1)}\n </span>\n <Star className=\"size-3 fill-current text-status-warning-text\" />\n </div>\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n </main>\n\n {/* Install App Modal */}\n {safeProduct && (\n <InstallAppModal\n isOpen={isInstallModalOpen}\n onClose={() => setIsInstallModalOpen(false)}\n app={{\n id: safeProduct.id,\n applicationId: safeProduct.productId,\n name: safeProduct.name,\n slug: safeProduct.slug,\n icon: safeProduct.icon,\n type: safeProduct.type,\n version: safeProduct.manifestVersion,\n permissions: permissions.length > 0 ? permissions : undefined,\n }}\n purchaseState={isPurchased && !isFree ? 'purchased' : 'free'}\n onInstallSuccess={handleInstallSuccess}\n onInstallError={handleInstallError}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -41,7 +41,10 @@ function s(e) {
41
41
  EXTENSION: "Extension",
42
42
  THEME: "Theme",
43
43
  PHYSICAL: "Physical Product",
44
- BUNDLE: "Bundle"
44
+ BUNDLE: "Bundle",
45
+ PARSER: "Parser",
46
+ WIDGET: "Widget",
47
+ DASHBOARD: "Dashboard Template"
45
48
  }[e] || e;
46
49
  }
47
50
  function c(e) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["// Cache Intl.NumberFormat instances per currency to avoid per-call allocations.\nconst currencyFormatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction getCurrencyFormatter(currency: string): Intl.NumberFormat {\n const cached = currencyFormatterCache.get(currency);\n if (cached) return cached;\n const fmt = new Intl.NumberFormat('en-US', { style: 'currency', currency });\n currencyFormatterCache.set(currency, fmt);\n return fmt;\n}\n\n/**\n * Format price with currency\n */\nexport function formatPrice(price: number, currency: string = 'USD'): string {\n return getCurrencyFormatter(currency).format(price);\n}\n\n/**\n * Format price honoring a pricing model. Returns \"Free\" for FREE model.\n */\nexport function formatPriceForModel(price: number, currency: string, model: string): string {\n if (model === 'FREE') return 'Free';\n return getCurrencyFormatter(currency || 'USD').format(price);\n}\n\n/**\n * Format number with abbreviation (1.2K, 3.4M, etc.)\n */\nexport function formatNumber(num: number): string {\n if (num >= 1000000) {\n return `${(num / 1000000).toFixed(1)}M`;\n }\n if (num >= 1000) {\n return `${(num / 1000).toFixed(1)}K`;\n }\n return num.toString();\n}\n\n/**\n * Calculate cart totals\n */\nexport function calculateCartTotals(\n items: Array<{ quantity: number; unitPrice: number }>,\n taxRate: number = 0.085\n) {\n const subtotal = items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);\n const tax = subtotal * taxRate;\n const total = subtotal + tax;\n\n return { subtotal, tax, total };\n}\n\n/**\n * Generate rating stars array\n */\nexport function getRatingStars(rating: number): Array<'full' | 'half' | 'empty'> {\n const stars: Array<'full' | 'half' | 'empty'> = [];\n const fullStars = Math.floor(rating);\n const hasHalfStar = rating % 1 >= 0.5;\n\n for (let i = 0; i < fullStars; i++) {\n stars.push('full');\n }\n\n if (hasHalfStar) {\n stars.push('half');\n }\n\n while (stars.length < 5) {\n stars.push('empty');\n }\n\n return stars;\n}\n\n/**\n * Get item type display name\n */\nexport function getItemTypeName(itemType: string): string {\n const names: Record<string, string> = {\n APP: 'App',\n WORKFLOW: 'Workflow',\n TEMPLATE: 'Template',\n INTEGRATION: 'Integration',\n EXTENSION: 'Extension',\n THEME: 'Theme',\n PHYSICAL: 'Physical Product',\n BUNDLE: 'Bundle',\n };\n\n return names[itemType] || itemType;\n}\n\n/**\n * Get pricing model display name\n */\nexport function getPricingModelName(model: string): string {\n const names: Record<string, string> = {\n FREE: 'Free',\n ONE_TIME: 'One-time Purchase',\n SUBSCRIPTION: 'Subscription',\n USAGE_BASED: 'Usage-based',\n FREEMIUM: 'Freemium',\n PAY_WHAT_YOU_WANT: 'Pay What You Want',\n };\n\n return names[model] || model;\n}\n\n/**\n * Get status color variant\n */\nexport function getStatusColor(\n status: string\n): 'default' | 'success' | 'warning' | 'error' | 'info' {\n const colors: Record<string, 'default' | 'success' | 'warning' | 'error' | 'info'> = {\n // Product status\n DRAFT: 'default',\n PENDING_REVIEW: 'warning',\n APPROVED: 'success',\n PUBLISHED: 'success',\n REJECTED: 'error',\n DEPRECATED: 'warning',\n UNLISTED: 'default',\n\n // Installation status\n PENDING: 'warning',\n INSTALLING: 'info',\n CONFIGURING: 'info',\n ACTIVE: 'success',\n INACTIVE: 'default',\n FAILED: 'error',\n UNINSTALLING: 'warning',\n\n // License status (ACTIVE already defined above)\n EXPIRED: 'error',\n REVOKED: 'error',\n SUSPENDED: 'warning',\n PENDING_ACTIVATION: 'warning',\n\n // Order status (FAILED already defined above)\n PROCESSING: 'info',\n COMPLETED: 'success',\n REFUNDED: 'warning',\n };\n\n return colors[status] || 'default';\n}\n\n/**\n * Validate license key format\n */\nexport function validateLicenseKey(key: string): boolean {\n // Expected format: XXXX-XXXX-XXXX-XXXX\n const regex = /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;\n return regex.test(key);\n}\n\n/**\n * Mask license key for display\n */\nexport function maskLicenseKey(key: string): string {\n const parts = key.split('-');\n if (parts.length !== 4) return key;\n\n return `${parts[0]}-${parts[1]}-****-****`;\n}\n\n/**\n * Calculate days until expiry\n */\nexport function getDaysUntilExpiry(expiryDate: string): number {\n const now = new Date();\n const expiry = new Date(expiryDate);\n const diffTime = expiry.getTime() - now.getTime();\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Check if license is expiring soon (within 30 days)\n */\nexport function isLicenseExpiringSoon(expiryDate: string): boolean {\n const daysUntilExpiry = getDaysUntilExpiry(expiryDate);\n return daysUntilExpiry > 0 && daysUntilExpiry <= 30;\n}\n\n/**\n * Format file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Generate unique cart item ID\n */\nexport function generateCartItemId(productId: string, variantId?: string): string {\n return variantId ? `${productId}-${variantId}` : productId;\n}\n\n/**\n * Debounce function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n\n/**\n * Truncate text with ellipsis\n */\nexport function truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${text.substring(0, maxLength)}...`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["// Cache Intl.NumberFormat instances per currency to avoid per-call allocations.\nconst currencyFormatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction getCurrencyFormatter(currency: string): Intl.NumberFormat {\n const cached = currencyFormatterCache.get(currency);\n if (cached) return cached;\n const fmt = new Intl.NumberFormat('en-US', { style: 'currency', currency });\n currencyFormatterCache.set(currency, fmt);\n return fmt;\n}\n\n/**\n * Format price with currency\n */\nexport function formatPrice(price: number, currency: string = 'USD'): string {\n return getCurrencyFormatter(currency).format(price);\n}\n\n/**\n * Format price honoring a pricing model. Returns \"Free\" for FREE model.\n */\nexport function formatPriceForModel(price: number, currency: string, model: string): string {\n if (model === 'FREE') return 'Free';\n return getCurrencyFormatter(currency || 'USD').format(price);\n}\n\n/**\n * Format number with abbreviation (1.2K, 3.4M, etc.)\n */\nexport function formatNumber(num: number): string {\n if (num >= 1000000) {\n return `${(num / 1000000).toFixed(1)}M`;\n }\n if (num >= 1000) {\n return `${(num / 1000).toFixed(1)}K`;\n }\n return num.toString();\n}\n\n/**\n * Calculate cart totals\n */\nexport function calculateCartTotals(\n items: Array<{ quantity: number; unitPrice: number }>,\n taxRate: number = 0.085\n) {\n const subtotal = items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);\n const tax = subtotal * taxRate;\n const total = subtotal + tax;\n\n return { subtotal, tax, total };\n}\n\n/**\n * Generate rating stars array\n */\nexport function getRatingStars(rating: number): Array<'full' | 'half' | 'empty'> {\n const stars: Array<'full' | 'half' | 'empty'> = [];\n const fullStars = Math.floor(rating);\n const hasHalfStar = rating % 1 >= 0.5;\n\n for (let i = 0; i < fullStars; i++) {\n stars.push('full');\n }\n\n if (hasHalfStar) {\n stars.push('half');\n }\n\n while (stars.length < 5) {\n stars.push('empty');\n }\n\n return stars;\n}\n\n/**\n * Get item type display name\n */\nexport function getItemTypeName(itemType: string): string {\n const names: Record<string, string> = {\n APP: 'App',\n WORKFLOW: 'Workflow',\n TEMPLATE: 'Template',\n INTEGRATION: 'Integration',\n EXTENSION: 'Extension',\n THEME: 'Theme',\n PHYSICAL: 'Physical Product',\n BUNDLE: 'Bundle',\n PARSER: 'Parser',\n WIDGET: 'Widget',\n DASHBOARD: 'Dashboard Template',\n };\n\n return names[itemType] || itemType;\n}\n\n/**\n * Get pricing model display name\n */\nexport function getPricingModelName(model: string): string {\n const names: Record<string, string> = {\n FREE: 'Free',\n ONE_TIME: 'One-time Purchase',\n SUBSCRIPTION: 'Subscription',\n USAGE_BASED: 'Usage-based',\n FREEMIUM: 'Freemium',\n PAY_WHAT_YOU_WANT: 'Pay What You Want',\n };\n\n return names[model] || model;\n}\n\n/**\n * Get status color variant\n */\nexport function getStatusColor(\n status: string\n): 'default' | 'success' | 'warning' | 'error' | 'info' {\n const colors: Record<string, 'default' | 'success' | 'warning' | 'error' | 'info'> = {\n // Product status\n DRAFT: 'default',\n PENDING_REVIEW: 'warning',\n APPROVED: 'success',\n PUBLISHED: 'success',\n REJECTED: 'error',\n DEPRECATED: 'warning',\n UNLISTED: 'default',\n\n // Installation status\n PENDING: 'warning',\n INSTALLING: 'info',\n CONFIGURING: 'info',\n ACTIVE: 'success',\n INACTIVE: 'default',\n FAILED: 'error',\n UNINSTALLING: 'warning',\n\n // License status (ACTIVE already defined above)\n EXPIRED: 'error',\n REVOKED: 'error',\n SUSPENDED: 'warning',\n PENDING_ACTIVATION: 'warning',\n\n // Order status (FAILED already defined above)\n PROCESSING: 'info',\n COMPLETED: 'success',\n REFUNDED: 'warning',\n };\n\n return colors[status] || 'default';\n}\n\n/**\n * Validate license key format\n */\nexport function validateLicenseKey(key: string): boolean {\n // Expected format: XXXX-XXXX-XXXX-XXXX\n const regex = /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;\n return regex.test(key);\n}\n\n/**\n * Mask license key for display\n */\nexport function maskLicenseKey(key: string): string {\n const parts = key.split('-');\n if (parts.length !== 4) return key;\n\n return `${parts[0]}-${parts[1]}-****-****`;\n}\n\n/**\n * Calculate days until expiry\n */\nexport function getDaysUntilExpiry(expiryDate: string): number {\n const now = new Date();\n const expiry = new Date(expiryDate);\n const diffTime = expiry.getTime() - now.getTime();\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Check if license is expiring soon (within 30 days)\n */\nexport function isLicenseExpiringSoon(expiryDate: string): boolean {\n const daysUntilExpiry = getDaysUntilExpiry(expiryDate);\n return daysUntilExpiry > 0 && daysUntilExpiry <= 30;\n}\n\n/**\n * Format file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Generate unique cart item ID\n */\nexport function generateCartItemId(productId: string, variantId?: string): string {\n return variantId ? `${productId}-${variantId}` : productId;\n}\n\n/**\n * Debounce function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n\n/**\n * Truncate text with ellipsis\n */\nexport function truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${text.substring(0, maxLength)}...`;\n}\n"],"mappings":";AACA,IAAM,oBAAyB,IAAI,KAAgC;AAEnE,SAAS,EAAqB,GAAqC;CACjE,IAAM,IAAS,EAAuB,IAAI,EAAS;AACnD,KAAI,EAAQ,QAAO;CACnB,IAAM,IAAM,IAAI,KAAK,aAAa,SAAS;EAAE,OAAO;EAAY;EAAU,CAAC;AAE3E,QADA,EAAuB,IAAI,GAAU,EAAI,EAClC;;AAMT,SAAgB,EAAY,GAAe,IAAmB,OAAe;AAC3E,QAAO,EAAqB,EAAS,CAAC,OAAO,EAAM;;AAMrD,SAAgB,EAAoB,GAAe,GAAkB,GAAuB;AAE1F,QADI,MAAU,SAAe,SACtB,EAAqB,KAAY,MAAM,CAAC,OAAO,EAAM;;AAM9D,SAAgB,EAAa,GAAqB;AAOhD,QANI,KAAO,MACF,IAAI,IAAM,KAAS,QAAQ,EAAE,CAAC,KAEnC,KAAO,MACF,IAAI,IAAM,KAAM,QAAQ,EAAE,CAAC,KAE7B,EAAI,UAAU;;AAMvB,SAAgB,EACd,GACA,IAAkB,MAClB;CACA,IAAM,IAAW,EAAM,QAAQ,GAAK,MAAS,IAAM,EAAK,WAAW,EAAK,WAAW,EAAE,EAC/E,IAAM,IAAW;AAGvB,QAAO;EAAE;EAAU;EAAK,OAFV,IAAW;EAEM;;AAMjC,SAAgB,EAAe,GAAkD;CAC/E,IAAM,IAA0C,EAAE,EAC5C,IAAY,KAAK,MAAM,EAAO,EAC9B,IAAc,IAAS,KAAK;AAElC,MAAK,IAAI,IAAI,GAAG,IAAI,GAAW,IAC7B,GAAM,KAAK,OAAO;AAOpB,MAJI,KACF,EAAM,KAAK,OAAO,EAGb,EAAM,SAAS,GACpB,GAAM,KAAK,QAAQ;AAGrB,QAAO;;AAMT,SAAgB,EAAgB,GAA0B;AAexD,QAdsC;EACpC,KAAK;EACL,UAAU;EACV,UAAU;EACV,aAAa;EACb,WAAW;EACX,OAAO;EACP,UAAU;EACV,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,WAAW;EACZ,CAEY,MAAa;;AAM5B,SAAgB,EAAoB,GAAuB;AAUzD,QATsC;EACpC,MAAM;EACN,UAAU;EACV,cAAc;EACd,aAAa;EACb,UAAU;EACV,mBAAmB;EACpB,CAEY,MAAU;;AAMzB,SAAgB,EACd,GACsD;AAgCtD,QA/BqF;EAEnF,OAAO;EACP,gBAAgB;EAChB,UAAU;EACV,WAAW;EACX,UAAU;EACV,YAAY;EACZ,UAAU;EAGV,SAAS;EACT,YAAY;EACZ,aAAa;EACb,QAAQ;EACR,UAAU;EACV,QAAQ;EACR,cAAc;EAGd,SAAS;EACT,SAAS;EACT,WAAW;EACX,oBAAoB;EAGpB,YAAY;EACZ,WAAW;EACX,UAAU;EACX,CAEa,MAAW;;AAM3B,SAAgB,EAAmB,GAAsB;AAGvD,QADc,oDACD,KAAK,EAAI;;AAMxB,SAAgB,EAAe,GAAqB;CAClD,IAAM,IAAQ,EAAI,MAAM,IAAI;AAG5B,QAFI,EAAM,WAAW,IAEd,GAAG,EAAM,GAAG,GAAG,EAAM,GAAG,cAFA;;AAQjC,SAAgB,EAAmB,GAA4B;CAC7D,IAAM,oBAAM,IAAI,MAAM,EAEhB,IADS,IAAI,KAAK,EAAW,CACX,SAAS,GAAG,EAAI,SAAS;AACjD,QAAO,KAAK,KAAK,KAAY,MAAO,KAAK,KAAK,IAAI;;AAMpD,SAAgB,EAAsB,GAA6B;CACjE,IAAM,IAAkB,EAAmB,EAAW;AACtD,QAAO,IAAkB,KAAK,KAAmB;;AAMnD,SAAgB,EAAe,GAAuB;AACpD,KAAI,MAAU,EAAG,QAAO;CAExB,IAAM,IAAI,MACJ,IAAQ;EAAC;EAAS;EAAM;EAAM;EAAK,EACnC,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AAEnD,QAAO,GAAG,YAAY,IAAiB,MAAG,GAAI,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM;;AAMrE,SAAgB,EAAmB,GAAmB,GAA4B;AAChF,QAAO,IAAY,GAAG,EAAU,GAAG,MAAc;;AAMnD,SAAgB,EACd,GACA,GACkC;CAClC,IAAI,IAAgD;AAEpD,QAAO,SAA0B,GAAG,GAAqB;AASvD,EAHI,KACF,aAAa,EAAQ,EAEvB,IAAU,iBARU;AAElB,GADA,IAAU,MACV,EAAK,GAAG,EAAK;KAMa,EAAK;;;AAOrC,SAAgB,EAAS,GAAc,GAA2B;AAEhE,QADI,EAAK,UAAU,IAAkB,IAC9B,GAAG,EAAK,UAAU,GAAG,EAAU,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-store",
3
- "version": "2026.721.1",
3
+ "version": "2026.728.1",
4
4
  "description": "Store microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [
@@ -48,6 +48,7 @@
48
48
  "lucide-react": "^0.468.0",
49
49
  "react": "^19.0.0",
50
50
  "react-dom": "^19.0.0",
51
+ "react-markdown": "^10.1.0",
51
52
  "react-router-dom": "^7.1.3",
52
53
  "tailwind-merge": "^3.4.0",
53
54
  "zustand": "^5.0.3"