@burdenoff/microfe-billing 2026.623.3 → 2026.624.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.
- package/dist/billing/BillingUserRoutes.js +3 -3
- package/dist/billing/BillingUserRoutes.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +34 -34
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +94 -94
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +40 -40
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +10 -10
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +62 -62
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +7 -7
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +21 -21
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +4 -4
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +33 -33
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +4 -4
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +57 -57
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +33 -33
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/shared/components/AccessDenied.js +4 -4
- package/dist/billing/shared/components/AccessDenied.js.map +1 -1
- package/dist/billing/shared/components/ActorIdentity.js +8 -8
- package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +11 -11
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddonsBrowsePage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonsBrowsePage.tsx"],"sourcesContent":["/**\n * Addons Module - Addons Browse Page (User-facing)\n * Browse available addons and add to cart. No create/edit/toggle actions.\n */\n\nimport { useState, useMemo, type FC } from 'react';\nimport { useDefaultBillingCurrency } from '../../../hooks/useDefaultBillingCurrency';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { ShoppingCart, Plus, Minus, Trash2, X, Package } from 'lucide-react';\nimport { useAddons } from '../hooks';\nimport { useAddonCartStore } from '../store/addonCartStore';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n} from '../../../shared/utils/format';\nimport type { Addon, PlanDuration, SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\n\ntype ViewMode = 'grid' | 'list';\n\n// ---------------------------------------------------------------------------\n// Grouping logic\n// Addons named \"Group — Variant\" (em dash) are grouped together.\n// Addons with a unique prefix or no em dash are shown standalone.\n// ---------------------------------------------------------------------------\n\ntype AddonGroupItem =\n | { type: 'group'; name: string; addons: Addon[] }\n | { type: 'standalone'; addon: Addon };\n\nfunction groupAddons(addons: Addon[]): AddonGroupItem[] {\n const groups = new Map<string, Addon[]>();\n for (const addon of addons) {\n const sepIdx = addon.name.indexOf(' — ');\n const key = sepIdx !== -1 ? addon.name.slice(0, sepIdx).trim() : addon.name;\n const bucket = groups.get(key) ?? [];\n bucket.push(addon);\n groups.set(key, bucket);\n }\n\n const items: AddonGroupItem[] = [];\n for (const [name, groupAddons] of groups) {\n if (groupAddons.length > 1) {\n items.push({ type: 'group', name, addons: groupAddons });\n } else {\n items.push({ type: 'standalone', addon: groupAddons[0] });\n }\n }\n return items;\n}\n\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\nfunction combineQuotas(features: SubscriptionFeatures[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n for (const feature of features) {\n if (!feature.quota) continue;\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n return Array.from(quotaMap.values());\n}\n\nfunction getAddonDisplayPrice(\n addon: Addon,\n displayCurrency: string = 'USD'\n): { price: number; currency: string } {\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const currencyPricesObj = addon.currencyPrices as Record<string, number>;\n if (\n currencyPricesObj[displayCurrency] !== undefined &&\n currencyPricesObj[displayCurrency] !== null\n ) {\n return { price: currencyPricesObj[displayCurrency], currency: displayCurrency };\n }\n }\n return { price: addon.price, currency: addon.currency || 'USD' };\n}\n\nexport const AddonsBrowsePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const { productId } = useBilling();\n const { addons, isLoading, error, refetch } = useAddons({\n includeFree: false,\n productId: productId ?? undefined,\n });\n const { addToCart, removeFromCart, updateQuantity, isInCart, getCartItem } = useAddonCartStore();\n\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [billingInterval, setBillingInterval] = useState<PlanDuration | 'all'>('all');\n const displayCurrency = useDefaultBillingCurrency();\n\n // Only show active addons to users\n const filteredAddons = addons.filter((addon) => {\n if (!addon.isActive) return false;\n if (billingInterval !== 'all' && addon.duration !== billingInterval) return false;\n return true;\n });\n\n const groupedItems = useMemo(() => groupAddons(filteredAddons), [filteredAddons]);\n\n if (isLoading && addons.length === 0) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"border border-border rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-muted animate-pulse rounded\" />\n <div className=\"h-8 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2, 3].map((j) => (\n <div key={j} className=\"h-4 w-full bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.addons.failedToLoad', 'Failed to load addons')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.addons.title', 'Addons')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr('billing.addons.subtitle', 'Enhance your subscription with additional features')}\n </p>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border\">\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50\">\n {(['all', 'monthly', 'yearly'] as const).map((interval) => (\n <button\n type=\"button\"\n key={interval}\n onClick={() => setBillingInterval(interval as PlanDuration | 'all')}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n billingInterval === interval\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {interval === 'all' ? 'All' : interval === 'monthly' ? 'Monthly' : 'Yearly'}\n </button>\n ))}\n </div>\n\n <div className=\"ml-auto inline-flex rounded-lg border border-border p-1 bg-muted/50\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'grid'\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n title=\"Grid view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'list'\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n title=\"List view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6h16M4 12h16M4 18h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Addons */}\n {filteredAddons.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <svg\n className=\"size-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">\n {tr('billing.addons.noAddonsAvailable', 'No addons available')}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr('billing.addons.checkBackLater', 'Check back later for new addons.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-8\">\n {groupedItems.map((item) => {\n if (item.type === 'group') {\n return (\n <AddonGroupSection\n key={item.name}\n groupName={item.name}\n addons={item.addons}\n viewMode={viewMode}\n getCartItem={getCartItem}\n isInCart={isInCart}\n addToCart={addToCart}\n updateQuantity={updateQuantity}\n removeFromCart={removeFromCart}\n displayCurrency={displayCurrency}\n navigateTo={navigateTo}\n />\n );\n }\n const addon = item.addon;\n const cartItem = getCartItem(addon.id);\n return viewMode === 'grid' ? (\n <BrowseAddonCard\n key={addon.id}\n addon={addon}\n onView={() => navigateTo(`/addons/${addon.id}`)}\n isInCart={isInCart(addon.id)}\n cartQuantity={cartItem?.quantity || 0}\n onAddToCart={addToCart}\n onUpdateQuantity={updateQuantity}\n onRemoveFromCart={removeFromCart}\n displayCurrency={displayCurrency}\n />\n ) : (\n <BrowseAddonRow\n key={addon.id}\n addon={addon}\n onView={() => navigateTo(`/addons/${addon.id}`)}\n isInCart={isInCart(addon.id)}\n cartQuantity={cartItem?.quantity || 0}\n onAddToCart={addToCart}\n onUpdateQuantity={updateQuantity}\n onRemoveFromCart={removeFromCart}\n displayCurrency={displayCurrency}\n />\n );\n })}\n </div>\n )}\n\n <FloatingCartDrawer displayCurrency={displayCurrency} />\n </div>\n );\n};\n\n// ============================================================================\n// Addon Group Section\n// ============================================================================\n\ninterface AddonGroupSectionProps {\n groupName: string;\n addons: Addon[];\n viewMode: ViewMode;\n getCartItem: (id: string) => { quantity: number } | undefined;\n isInCart: (id: string) => boolean;\n addToCart: (addon: Addon) => void;\n updateQuantity: (id: string, qty: number) => void;\n removeFromCart: (id: string) => void;\n displayCurrency: string;\n navigateTo: (path: string) => void;\n}\n\nconst AddonGroupSection: FC<AddonGroupSectionProps> = ({\n groupName,\n addons,\n viewMode,\n getCartItem,\n isInCart,\n addToCart,\n updateQuantity,\n removeFromCart,\n displayCurrency,\n navigateTo,\n}) => {\n const cartCount = addons.filter((a) => isInCart(a.id)).length;\n\n return (\n <div className=\"border border-border rounded-xl overflow-hidden\">\n {/* Group header */}\n <div className=\"flex items-center justify-between px-5 py-3 bg-muted/40 border-b border-border\">\n <div className=\"flex items-center gap-2\">\n <Package className=\"size-4 text-muted-foreground\" />\n <h2 className=\"font-semibold text-foreground\">{groupName}</h2>\n <span className=\"text-xs text-muted-foreground\">\n {addons.length} option{addons.length !== 1 ? 's' : ''}\n </span>\n </div>\n {cartCount > 0 && (\n <span className=\"text-xs font-medium px-2 py-0.5 bg-primary text-primary-foreground rounded-full\">\n {cartCount} in cart\n </span>\n )}\n </div>\n\n {/* Variants */}\n {viewMode === 'grid' ? (\n <div className=\"p-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">\n {addons.map((addon) => {\n const cartItem = getCartItem(addon.id);\n const variantName = addon.name.includes(' — ')\n ? addon.name.slice(addon.name.indexOf(' — ') + 3).trim()\n : addon.name;\n const inCart = isInCart(addon.id);\n const qty = cartItem?.quantity ?? 0;\n const { price, currency } = (() => {\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const prices = addon.currencyPrices as Record<string, number>;\n if (prices[displayCurrency] != null)\n return { price: prices[displayCurrency], currency: displayCurrency };\n }\n return { price: addon.price, currency: addon.currency || 'USD' };\n })();\n\n return (\n <div\n key={addon.id}\n className={`relative flex flex-col rounded-lg border p-4 transition-all ${\n inCart\n ? 'border-primary bg-primary/5'\n : 'border-border bg-card hover:border-primary/40 hover:shadow-sm'\n }`}\n >\n {inCart && (\n <span className=\"absolute top-2 right-2 text-xs font-medium bg-primary text-primary-foreground px-1.5 py-0.5 rounded\">\n ×{qty}\n </span>\n )}\n <p className=\"font-semibold text-foreground text-sm mb-1\">{variantName}</p>\n <p className=\"text-xs text-muted-foreground mb-3\">\n {formatPlanDurationLabel(addon.duration)}\n </p>\n <p className=\"text-lg font-bold text-foreground mb-3\">\n {formatCurrency(price, currency)}\n <span className=\"text-xs font-normal text-muted-foreground ml-1\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </p>\n <div className=\"mt-auto flex flex-col gap-2\">\n {inCart ? (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty - 1)}\n className=\"p-1 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Minus className=\"size-3\" />\n </button>\n <span className=\"flex-1 text-center text-sm font-medium\">{qty}</span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty + 1)}\n className=\"p-1 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Plus className=\"size-3\" />\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(addon.id)}\n className=\"p-1 rounded text-destructive hover:bg-destructive/10 transition-colors\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => addToCart(addon)}\n className=\"w-full py-1.5 text-xs font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors flex items-center justify-center gap-1\"\n >\n <ShoppingCart className=\"size-3\" />\n Add to Cart\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addon.id}`)}\n className=\"w-full py-1.5 text-xs font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Details\n </button>\n </div>\n </div>\n );\n })}\n </div>\n ) : (\n <div className=\"divide-y divide-border\">\n {addons.map((addon) => {\n const cartItem = getCartItem(addon.id);\n const variantName = addon.name.includes(' — ')\n ? addon.name.slice(addon.name.indexOf(' — ') + 3).trim()\n : addon.name;\n const inCart = isInCart(addon.id);\n const qty = cartItem?.quantity ?? 0;\n const { price, currency } = (() => {\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const prices = addon.currencyPrices as Record<string, number>;\n if (prices[displayCurrency] != null)\n return { price: prices[displayCurrency], currency: displayCurrency };\n }\n return { price: addon.price, currency: addon.currency || 'USD' };\n })();\n\n return (\n <div\n key={addon.id}\n className={`flex items-center gap-4 px-5 py-3 transition-colors ${\n inCart ? 'bg-primary/5' : 'hover:bg-muted/30'\n }`}\n >\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-foreground text-sm\">{variantName}</p>\n <p className=\"text-xs text-muted-foreground\">\n {formatPlanDurationLabel(addon.duration)}\n </p>\n </div>\n <p className=\"font-semibold text-foreground text-sm whitespace-nowrap\">\n {formatCurrency(price, currency)}\n <span className=\"text-xs font-normal text-muted-foreground ml-1\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </p>\n {inCart ? (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty - 1)}\n className=\"p-1 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Minus className=\"size-3\" />\n </button>\n <span className=\"w-6 text-center text-sm font-medium\">{qty}</span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty + 1)}\n className=\"p-1 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Plus className=\"size-3\" />\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(addon.id)}\n className=\"p-1 rounded text-destructive hover:bg-destructive/10 transition-colors\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => addToCart(addon)}\n className=\"px-2 py-1 text-xs font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors flex items-center gap-1\"\n >\n <ShoppingCart className=\"size-3\" />\n Add\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addon.id}`)}\n className=\"px-2 py-1 text-xs font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Details\n </button>\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Browse Addon Card (no edit/toggle)\n// ============================================================================\n\ninterface BrowseAddonCardProps {\n addon: Addon;\n onView: () => void;\n isInCart: boolean;\n cartQuantity: number;\n onAddToCart: (addon: Addon) => void;\n onUpdateQuantity: (addonId: string, quantity: number) => void;\n onRemoveFromCart: (addonId: string) => void;\n displayCurrency: string;\n}\n\nconst BrowseAddonCard: FC<BrowseAddonCardProps> = ({\n addon,\n onView,\n isInCart,\n cartQuantity,\n onAddToCart,\n onUpdateQuantity,\n onRemoveFromCart,\n displayCurrency,\n}) => {\n const combinedQuotas = useMemo(() => combineQuotas(addon.features || []), [addon.features]);\n\n return (\n <div\n className={`relative border rounded-lg p-6 transition-all hover:shadow-md flex flex-col h-full ${\n isInCart ? 'border-primary bg-primary/5' : 'border-border bg-card'\n }`}\n >\n {isInCart && (\n <span className=\"absolute top-4 right-4 px-2 py-1 text-xs font-medium bg-primary text-primary-foreground rounded\">\n In Cart ({cartQuantity})\n </span>\n )}\n\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center mb-4\">\n <svg className=\"size-5 text-primary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\"\n />\n </svg>\n </div>\n\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-bold text-foreground\">{addon.name}</h3>\n <span className=\"text-sm text-muted-foreground\">\n {formatPlanDurationLabel(addon.duration)}\n </span>\n </div>\n\n <div className=\"flex items-baseline gap-1 mb-4\">\n <span className=\"text-2xl font-bold text-foreground\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price, currency);\n })()}\n </span>\n <span className=\"text-muted-foreground text-sm\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </div>\n\n {combinedQuotas.length > 0 && (\n <ul className=\"space-y-2 mb-4\">\n {combinedQuotas.slice(0, 4).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <svg\n className=\"size-4 text-status-success-text shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n <span className=\"text-foreground\">{quota.name}</span>\n </div>\n <span className=\"font-medium text-foreground\">\n {quota.totalValue.toLocaleString()}\n </span>\n </li>\n ))}\n {combinedQuotas.length > 4 && (\n <li className=\"text-sm text-muted-foreground pl-6\">\n +{combinedQuotas.length - 4} more quotas\n </li>\n )}\n </ul>\n )}\n\n <div className=\"space-y-2 pt-4 border-t border-border mt-auto\">\n {isInCart ? (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity - 1)}\n className=\"p-1.5 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-10 text-center font-medium\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity + 1)}\n className=\"p-1.5 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n <button\n type=\"button\"\n onClick={() => onRemoveFromCart(addon.id)}\n className=\"p-1.5 rounded text-destructive hover:bg-destructive/10 transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => onAddToCart(addon)}\n className=\"w-full px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors flex items-center justify-center gap-2\"\n >\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </button>\n )}\n <button\n type=\"button\"\n onClick={onView}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n View Details\n </button>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Browse Addon Row (no edit/toggle)\n// ============================================================================\n\ninterface BrowseAddonRowProps {\n addon: Addon;\n onView: () => void;\n isInCart: boolean;\n cartQuantity: number;\n onAddToCart: (addon: Addon) => void;\n onUpdateQuantity: (addonId: string, quantity: number) => void;\n onRemoveFromCart: (addonId: string) => void;\n displayCurrency: string;\n}\n\nconst BrowseAddonRow: FC<BrowseAddonRowProps> = ({\n addon,\n onView,\n isInCart,\n cartQuantity,\n onAddToCart,\n onUpdateQuantity,\n onRemoveFromCart,\n displayCurrency,\n}) => {\n const combinedQuotas = useMemo(() => combineQuotas(addon.features || []), [addon.features]);\n\n return (\n <div\n className={`flex items-center gap-4 p-4 border rounded-lg transition-all hover:shadow-sm ${\n isInCart ? 'border-primary bg-primary/5' : 'border-border bg-card'\n }`}\n >\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0\">\n <svg className=\"size-5 text-primary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\"\n />\n </svg>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-foreground truncate\">{addon.name}</h3>\n {isInCart && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-primary text-primary-foreground rounded\">\n In Cart ({cartQuantity})\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {combinedQuotas.length} quota{combinedQuotas.length !== 1 ? 's' : ''}\n </p>\n </div>\n\n <div className=\"text-right\">\n <div className=\"font-semibold text-foreground\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price, currency);\n })()}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n per {formatPlanDuration(addon.duration)}\n </div>\n </div>\n\n {isInCart ? (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity - 1)}\n className=\"p-1.5 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity + 1)}\n className=\"p-1.5 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onRemoveFromCart(addon.id)}\n className=\"p-1.5 rounded text-destructive hover:bg-destructive/10 transition-colors ml-1\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => onAddToCart(addon)}\n className=\"px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors flex items-center gap-1.5\"\n >\n <ShoppingCart className=\"size-4\" />\n Add\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={onView}\n className=\"px-3 py-1.5 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n View\n </button>\n </div>\n );\n};\n\n// ============================================================================\n// Floating Cart Drawer\n// ============================================================================\n\nconst FloatingCartDrawer: FC<{ displayCurrency: string }> = ({ displayCurrency }) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const { items, isCartOpen, toggleCart, closeCart, removeFromCart, updateQuantity, clearCart } =\n useAddonCartStore();\n\n const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);\n\n if (items.length === 0 && !isCartOpen) return null;\n\n return (\n <>\n {!isCartOpen && items.length > 0 && (\n <button\n type=\"button\"\n onClick={toggleCart}\n className=\"fixed bottom-6 right-6 z-50 flex items-center gap-2 px-4 py-3 bg-primary text-primary-foreground rounded-full shadow-lg hover:bg-primary/90 transition-all\"\n >\n <ShoppingCart className=\"size-5\" />\n <span className=\"font-medium\">\n {itemCount} item{itemCount !== 1 ? 's' : ''}\n </span>\n <span className=\"px-2 py-0.5 bg-primary-foreground/20 rounded-full text-sm\">\n {(() => {\n const total = items.reduce((sum, { addon, quantity }) => {\n const { price } = getAddonDisplayPrice(addon, displayCurrency);\n return sum + price * quantity;\n }, 0);\n return formatCurrency(total, displayCurrency);\n })()}\n </span>\n </button>\n )}\n\n {isCartOpen && (\n <div className=\"fixed inset-0 z-50 flex items-end sm:items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={closeCart} />\n <div className=\"relative w-full max-w-md max-h-[80vh] bg-card border border-border rounded-t-xl sm:rounded-xl shadow-xl overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border bg-muted/30\">\n <div className=\"flex items-center gap-2\">\n <ShoppingCart className=\"size-5 text-foreground\" />\n <h3 className=\"font-semibold text-foreground\">\n {tr('billing.addons.yourCart', 'Your Cart')}\n </h3>\n <span className=\"px-2 py-0.5 text-xs bg-primary text-primary-foreground rounded-full\">\n {itemCount} item{itemCount !== 1 ? 's' : ''}\n </span>\n </div>\n <button\n type=\"button\"\n onClick={closeCart}\n className=\"p-1.5 rounded hover:bg-muted transition-colors\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"overflow-y-auto max-h-[50vh] divide-y divide-border\">\n {items.map(({ addon, quantity }) => (\n <div key={addon.id} className=\"p-4 flex items-center gap-3\">\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium text-foreground truncate\">{addon.name}</h4>\n <p className=\"text-sm text-muted-foreground\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price, currency);\n })()}{' '}\n / {formatPlanDuration(addon.duration)}\n </p>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, quantity - 1)}\n className=\"p-1 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Minus className=\"size-3\" />\n </button>\n <span className=\"w-6 text-center text-sm font-medium\">{quantity}</span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, quantity + 1)}\n className=\"p-1 rounded border border-border hover:bg-muted transition-colors\"\n >\n <Plus className=\"size-3\" />\n </button>\n </div>\n <p className=\"font-medium text-foreground\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price * quantity, currency);\n })()}\n </p>\n <button\n type=\"button\"\n onClick={() => removeFromCart(addon.id)}\n className=\"p-1.5 rounded text-destructive hover:bg-destructive/10 transition-colors\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ))}\n </div>\n\n <div className=\"border-t border-border p-4 space-y-3 bg-muted/20\">\n <div className=\"flex items-center justify-between text-lg font-semibold\">\n <span className=\"text-foreground\">{tr('billing.addons.total', 'Total')}</span>\n <span className=\"text-foreground\">\n {(() => {\n const total = items.reduce((sum, { addon, quantity }) => {\n const { price } = getAddonDisplayPrice(addon, displayCurrency);\n return sum + price * quantity;\n }, 0);\n return formatCurrency(total, displayCurrency);\n })()}\n </span>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={clearCart}\n className=\"flex-1 px-4 py-2.5 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Clear Cart\n </button>\n <button\n type=\"button\"\n onClick={() => {\n closeCart();\n navigateTo('/checkout/addons');\n }}\n className=\"flex-1 px-4 py-2.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Checkout\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AddonsBrowsePage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonsBrowsePage.tsx"],"sourcesContent":["/**\n * Addons Module - Addons Browse Page (User-facing)\n * Browse available addons and add to cart. No create/edit/toggle actions.\n */\n\nimport { useState, useMemo, type FC } from 'react';\nimport { useDefaultBillingCurrency } from '../../../hooks/useDefaultBillingCurrency';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { ShoppingCart, Plus, Minus, Trash2, X, Package } from 'lucide-react';\nimport { useAddons } from '../hooks';\nimport { useAddonCartStore } from '../store/addonCartStore';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n} from '../../../shared/utils/format';\nimport type { Addon, PlanDuration, SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\n\ntype ViewMode = 'grid' | 'list';\n\n// ---------------------------------------------------------------------------\n// Grouping logic\n// Addons named \"Group — Variant\" (em dash) are grouped together.\n// Addons with a unique prefix or no em dash are shown standalone.\n// ---------------------------------------------------------------------------\n\ntype AddonGroupItem =\n | { type: 'group'; name: string; addons: Addon[] }\n | { type: 'standalone'; addon: Addon };\n\nfunction groupAddons(addons: Addon[]): AddonGroupItem[] {\n const groups = new Map<string, Addon[]>();\n for (const addon of addons) {\n const sepIdx = addon.name.indexOf(' — ');\n const key = sepIdx !== -1 ? addon.name.slice(0, sepIdx).trim() : addon.name;\n const bucket = groups.get(key) ?? [];\n bucket.push(addon);\n groups.set(key, bucket);\n }\n\n const items: AddonGroupItem[] = [];\n for (const [name, groupAddons] of groups) {\n if (groupAddons.length > 1) {\n items.push({ type: 'group', name, addons: groupAddons });\n } else {\n items.push({ type: 'standalone', addon: groupAddons[0] });\n }\n }\n return items;\n}\n\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\nfunction combineQuotas(features: SubscriptionFeatures[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n for (const feature of features) {\n if (!feature.quota) continue;\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n return Array.from(quotaMap.values());\n}\n\nfunction getAddonDisplayPrice(\n addon: Addon,\n displayCurrency: string = 'USD'\n): { price: number; currency: string } {\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const currencyPricesObj = addon.currencyPrices as Record<string, number>;\n if (\n currencyPricesObj[displayCurrency] !== undefined &&\n currencyPricesObj[displayCurrency] !== null\n ) {\n return { price: currencyPricesObj[displayCurrency], currency: displayCurrency };\n }\n }\n return { price: addon.price, currency: addon.currency || 'USD' };\n}\n\nexport const AddonsBrowsePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const { productId } = useBilling();\n const { addons, isLoading, error, refetch } = useAddons({\n includeFree: false,\n productId: productId ?? undefined,\n });\n const { addToCart, removeFromCart, updateQuantity, isInCart, getCartItem } = useAddonCartStore();\n\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [billingInterval, setBillingInterval] = useState<PlanDuration | 'all'>('all');\n const displayCurrency = useDefaultBillingCurrency();\n\n // Only show active addons to users\n const filteredAddons = addons.filter((addon) => {\n if (!addon.isActive) return false;\n if (billingInterval !== 'all' && addon.duration !== billingInterval) return false;\n return true;\n });\n\n const groupedItems = useMemo(() => groupAddons(filteredAddons), [filteredAddons]);\n\n if (isLoading && addons.length === 0) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2, 3].map((j) => (\n <div key={j} className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.addons.failedToLoad', 'Failed to load addons')}\n </h2>\n <p className=\"text-sm text-text-secondary\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.addons.title', 'Addons')}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('billing.addons.subtitle', 'Enhance your subscription with additional features')}\n </p>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50\">\n {(['all', 'monthly', 'yearly'] as const).map((interval) => (\n <button\n type=\"button\"\n key={interval}\n onClick={() => setBillingInterval(interval as PlanDuration | 'all')}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n billingInterval === interval\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n {interval === 'all' ? 'All' : interval === 'monthly' ? 'Monthly' : 'Yearly'}\n </button>\n ))}\n </div>\n\n <div className=\"ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title=\"Grid view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n title=\"List view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6h16M4 12h16M4 18h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Addons */}\n {filteredAddons.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border-subtle rounded-lg\">\n <div className=\"size-12 rounded-full bg-bg-sunken flex items-center justify-center mb-4\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-text-primary\">\n {tr('billing.addons.noAddonsAvailable', 'No addons available')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('billing.addons.checkBackLater', 'Check back later for new addons.')}\n </p>\n </div>\n ) : (\n <div className=\"space-y-8\">\n {groupedItems.map((item) => {\n if (item.type === 'group') {\n return (\n <AddonGroupSection\n key={item.name}\n groupName={item.name}\n addons={item.addons}\n viewMode={viewMode}\n getCartItem={getCartItem}\n isInCart={isInCart}\n addToCart={addToCart}\n updateQuantity={updateQuantity}\n removeFromCart={removeFromCart}\n displayCurrency={displayCurrency}\n navigateTo={navigateTo}\n />\n );\n }\n const addon = item.addon;\n const cartItem = getCartItem(addon.id);\n return viewMode === 'grid' ? (\n <BrowseAddonCard\n key={addon.id}\n addon={addon}\n onView={() => navigateTo(`/addons/${addon.id}`)}\n isInCart={isInCart(addon.id)}\n cartQuantity={cartItem?.quantity || 0}\n onAddToCart={addToCart}\n onUpdateQuantity={updateQuantity}\n onRemoveFromCart={removeFromCart}\n displayCurrency={displayCurrency}\n />\n ) : (\n <BrowseAddonRow\n key={addon.id}\n addon={addon}\n onView={() => navigateTo(`/addons/${addon.id}`)}\n isInCart={isInCart(addon.id)}\n cartQuantity={cartItem?.quantity || 0}\n onAddToCart={addToCart}\n onUpdateQuantity={updateQuantity}\n onRemoveFromCart={removeFromCart}\n displayCurrency={displayCurrency}\n />\n );\n })}\n </div>\n )}\n\n <FloatingCartDrawer displayCurrency={displayCurrency} />\n </div>\n );\n};\n\n// ============================================================================\n// Addon Group Section\n// ============================================================================\n\ninterface AddonGroupSectionProps {\n groupName: string;\n addons: Addon[];\n viewMode: ViewMode;\n getCartItem: (id: string) => { quantity: number } | undefined;\n isInCart: (id: string) => boolean;\n addToCart: (addon: Addon) => void;\n updateQuantity: (id: string, qty: number) => void;\n removeFromCart: (id: string) => void;\n displayCurrency: string;\n navigateTo: (path: string) => void;\n}\n\nconst AddonGroupSection: FC<AddonGroupSectionProps> = ({\n groupName,\n addons,\n viewMode,\n getCartItem,\n isInCart,\n addToCart,\n updateQuantity,\n removeFromCart,\n displayCurrency,\n navigateTo,\n}) => {\n const cartCount = addons.filter((a) => isInCart(a.id)).length;\n\n return (\n <div className=\"border border-border-subtle rounded-xl overflow-hidden\">\n {/* Group header */}\n <div className=\"flex items-center justify-between px-5 py-3 bg-bg-sunken/40 border-b border-border-subtle\">\n <div className=\"flex items-center gap-2\">\n <Package className=\"size-4 text-text-secondary\" />\n <h2 className=\"font-semibold text-text-primary\">{groupName}</h2>\n <span className=\"text-xs text-text-secondary\">\n {addons.length} option{addons.length !== 1 ? 's' : ''}\n </span>\n </div>\n {cartCount > 0 && (\n <span className=\"text-xs font-medium px-2 py-0.5 bg-action-primary-bg text-action-primary-text rounded-full\">\n {cartCount} in cart\n </span>\n )}\n </div>\n\n {/* Variants */}\n {viewMode === 'grid' ? (\n <div className=\"p-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">\n {addons.map((addon) => {\n const cartItem = getCartItem(addon.id);\n const variantName = addon.name.includes(' — ')\n ? addon.name.slice(addon.name.indexOf(' — ') + 3).trim()\n : addon.name;\n const inCart = isInCart(addon.id);\n const qty = cartItem?.quantity ?? 0;\n const { price, currency } = (() => {\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const prices = addon.currencyPrices as Record<string, number>;\n if (prices[displayCurrency] != null)\n return { price: prices[displayCurrency], currency: displayCurrency };\n }\n return { price: addon.price, currency: addon.currency || 'USD' };\n })();\n\n return (\n <div\n key={addon.id}\n className={`relative flex flex-col rounded-lg border p-4 transition-all ${\n inCart\n ? 'border-primary bg-action-primary-bg/5'\n : 'border-border-subtle bg-bg-surface hover:border-primary/40 hover:shadow-sm'\n }`}\n >\n {inCart && (\n <span className=\"absolute top-2 right-2 text-xs font-medium bg-action-primary-bg text-action-primary-text px-1.5 py-0.5 rounded\">\n ×{qty}\n </span>\n )}\n <p className=\"font-semibold text-text-primary text-sm mb-1\">{variantName}</p>\n <p className=\"text-xs text-text-secondary mb-3\">\n {formatPlanDurationLabel(addon.duration)}\n </p>\n <p className=\"text-lg font-bold text-text-primary mb-3\">\n {formatCurrency(price, currency)}\n <span className=\"text-xs font-normal text-text-secondary ml-1\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </p>\n <div className=\"mt-auto flex flex-col gap-2\">\n {inCart ? (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty - 1)}\n className=\"p-1 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Minus className=\"size-3\" />\n </button>\n <span className=\"flex-1 text-center text-sm font-medium\">{qty}</span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty + 1)}\n className=\"p-1 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"size-3\" />\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(addon.id)}\n className=\"p-1 rounded text-status-error-text hover:bg-status-error-bg/10 transition-colors\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => addToCart(addon)}\n className=\"w-full py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 transition-colors flex items-center justify-center gap-1\"\n >\n <ShoppingCart className=\"size-3\" />\n Add to Cart\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addon.id}`)}\n className=\"w-full py-1.5 text-xs font-medium border border-border-subtle text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n Details\n </button>\n </div>\n </div>\n );\n })}\n </div>\n ) : (\n <div className=\"divide-y divide-border\">\n {addons.map((addon) => {\n const cartItem = getCartItem(addon.id);\n const variantName = addon.name.includes(' — ')\n ? addon.name.slice(addon.name.indexOf(' — ') + 3).trim()\n : addon.name;\n const inCart = isInCart(addon.id);\n const qty = cartItem?.quantity ?? 0;\n const { price, currency } = (() => {\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const prices = addon.currencyPrices as Record<string, number>;\n if (prices[displayCurrency] != null)\n return { price: prices[displayCurrency], currency: displayCurrency };\n }\n return { price: addon.price, currency: addon.currency || 'USD' };\n })();\n\n return (\n <div\n key={addon.id}\n className={`flex items-center gap-4 px-5 py-3 transition-colors ${\n inCart ? 'bg-action-primary-bg/5' : 'hover:bg-bg-sunken/30'\n }`}\n >\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-text-primary text-sm\">{variantName}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatPlanDurationLabel(addon.duration)}\n </p>\n </div>\n <p className=\"font-semibold text-text-primary text-sm whitespace-nowrap\">\n {formatCurrency(price, currency)}\n <span className=\"text-xs font-normal text-text-secondary ml-1\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </p>\n {inCart ? (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty - 1)}\n className=\"p-1 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Minus className=\"size-3\" />\n </button>\n <span className=\"w-6 text-center text-sm font-medium\">{qty}</span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, qty + 1)}\n className=\"p-1 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"size-3\" />\n </button>\n <button\n type=\"button\"\n onClick={() => removeFromCart(addon.id)}\n className=\"p-1 rounded text-status-error-text hover:bg-status-error-bg/10 transition-colors\"\n >\n <Trash2 className=\"size-3\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => addToCart(addon)}\n className=\"px-2 py-1 text-xs font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 transition-colors flex items-center gap-1\"\n >\n <ShoppingCart className=\"size-3\" />\n Add\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addon.id}`)}\n className=\"px-2 py-1 text-xs font-medium border border-border-subtle text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n Details\n </button>\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Browse Addon Card (no edit/toggle)\n// ============================================================================\n\ninterface BrowseAddonCardProps {\n addon: Addon;\n onView: () => void;\n isInCart: boolean;\n cartQuantity: number;\n onAddToCart: (addon: Addon) => void;\n onUpdateQuantity: (addonId: string, quantity: number) => void;\n onRemoveFromCart: (addonId: string) => void;\n displayCurrency: string;\n}\n\nconst BrowseAddonCard: FC<BrowseAddonCardProps> = ({\n addon,\n onView,\n isInCart,\n cartQuantity,\n onAddToCart,\n onUpdateQuantity,\n onRemoveFromCart,\n displayCurrency,\n}) => {\n const combinedQuotas = useMemo(() => combineQuotas(addon.features || []), [addon.features]);\n\n return (\n <div\n className={`relative border rounded-lg p-6 transition-all hover:shadow-md flex flex-col h-full ${\n isInCart ? 'border-primary bg-action-primary-bg/5' : 'border-border-subtle bg-bg-surface'\n }`}\n >\n {isInCart && (\n <span className=\"absolute top-4 right-4 px-2 py-1 text-xs font-medium bg-action-primary-bg text-action-primary-text rounded\">\n In Cart ({cartQuantity})\n </span>\n )}\n\n <div className=\"size-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center mb-4\">\n <svg className=\"size-5 text-primary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\"\n />\n </svg>\n </div>\n\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-bold text-text-primary\">{addon.name}</h3>\n <span className=\"text-sm text-text-secondary\">\n {formatPlanDurationLabel(addon.duration)}\n </span>\n </div>\n\n <div className=\"flex items-baseline gap-1 mb-4\">\n <span className=\"text-2xl font-bold text-text-primary\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price, currency);\n })()}\n </span>\n <span className=\"text-text-secondary text-sm\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </div>\n\n {combinedQuotas.length > 0 && (\n <ul className=\"space-y-2 mb-4\">\n {combinedQuotas.slice(0, 4).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <svg\n className=\"size-4 text-status-success-text shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n <span className=\"text-text-primary\">{quota.name}</span>\n </div>\n <span className=\"font-medium text-text-primary\">\n {quota.totalValue.toLocaleString()}\n </span>\n </li>\n ))}\n {combinedQuotas.length > 4 && (\n <li className=\"text-sm text-text-secondary pl-6\">\n +{combinedQuotas.length - 4} more quotas\n </li>\n )}\n </ul>\n )}\n\n <div className=\"space-y-2 pt-4 border-t border-border-subtle mt-auto\">\n {isInCart ? (\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity - 1)}\n className=\"p-1.5 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-10 text-center font-medium\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity + 1)}\n className=\"p-1.5 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n <button\n type=\"button\"\n onClick={() => onRemoveFromCart(addon.id)}\n className=\"p-1.5 rounded text-status-error-text hover:bg-status-error-bg/10 transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => onAddToCart(addon)}\n className=\"w-full px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 transition-colors flex items-center justify-center gap-2\"\n >\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </button>\n )}\n <button\n type=\"button\"\n onClick={onView}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n View Details\n </button>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Browse Addon Row (no edit/toggle)\n// ============================================================================\n\ninterface BrowseAddonRowProps {\n addon: Addon;\n onView: () => void;\n isInCart: boolean;\n cartQuantity: number;\n onAddToCart: (addon: Addon) => void;\n onUpdateQuantity: (addonId: string, quantity: number) => void;\n onRemoveFromCart: (addonId: string) => void;\n displayCurrency: string;\n}\n\nconst BrowseAddonRow: FC<BrowseAddonRowProps> = ({\n addon,\n onView,\n isInCart,\n cartQuantity,\n onAddToCart,\n onUpdateQuantity,\n onRemoveFromCart,\n displayCurrency,\n}) => {\n const combinedQuotas = useMemo(() => combineQuotas(addon.features || []), [addon.features]);\n\n return (\n <div\n className={`flex items-center gap-4 p-4 border rounded-lg transition-all hover:shadow-sm ${\n isInCart ? 'border-primary bg-action-primary-bg/5' : 'border-border-subtle bg-bg-surface'\n }`}\n >\n <div className=\"size-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center shrink-0\">\n <svg className=\"size-5 text-primary\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4\"\n />\n </svg>\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-text-primary truncate\">{addon.name}</h3>\n {isInCart && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-action-primary-bg text-action-primary-text rounded\">\n In Cart ({cartQuantity})\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary\">\n {combinedQuotas.length} quota{combinedQuotas.length !== 1 ? 's' : ''}\n </p>\n </div>\n\n <div className=\"text-right\">\n <div className=\"font-semibold text-text-primary\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price, currency);\n })()}\n </div>\n <div className=\"text-sm text-text-secondary\">\n per {formatPlanDuration(addon.duration)}\n </div>\n </div>\n\n {isInCart ? (\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity - 1)}\n className=\"p-1.5 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(addon.id, cartQuantity + 1)}\n className=\"p-1.5 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onRemoveFromCart(addon.id)}\n className=\"p-1.5 rounded text-status-error-text hover:bg-status-error-bg/10 transition-colors ml-1\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => onAddToCart(addon)}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 transition-colors flex items-center gap-1.5\"\n >\n <ShoppingCart className=\"size-4\" />\n Add\n </button>\n )}\n\n <button\n type=\"button\"\n onClick={onView}\n className=\"px-3 py-1.5 text-sm font-medium border border-border-subtle text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n View\n </button>\n </div>\n );\n};\n\n// ============================================================================\n// Floating Cart Drawer\n// ============================================================================\n\nconst FloatingCartDrawer: FC<{ displayCurrency: string }> = ({ displayCurrency }) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const { items, isCartOpen, toggleCart, closeCart, removeFromCart, updateQuantity, clearCart } =\n useAddonCartStore();\n\n const itemCount = items.reduce((sum, item) => sum + item.quantity, 0);\n\n if (items.length === 0 && !isCartOpen) return null;\n\n return (\n <>\n {!isCartOpen && items.length > 0 && (\n <button\n type=\"button\"\n onClick={toggleCart}\n className=\"fixed bottom-6 right-6 z-50 flex items-center gap-2 px-4 py-3 bg-action-primary-bg text-action-primary-text rounded-full shadow-lg hover:bg-action-primary-bg/90 transition-all\"\n >\n <ShoppingCart className=\"size-5\" />\n <span className=\"font-medium\">\n {itemCount} item{itemCount !== 1 ? 's' : ''}\n </span>\n <span className=\"px-2 py-0.5 bg-primary-foreground/20 rounded-full text-sm\">\n {(() => {\n const total = items.reduce((sum, { addon, quantity }) => {\n const { price } = getAddonDisplayPrice(addon, displayCurrency);\n return sum + price * quantity;\n }, 0);\n return formatCurrency(total, displayCurrency);\n })()}\n </span>\n </button>\n )}\n\n {isCartOpen && (\n <div className=\"fixed inset-0 z-50 flex items-end sm:items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={closeCart} />\n <div className=\"relative w-full max-w-md max-h-[80vh] bg-bg-surface border border-border-subtle rounded-t-xl sm:rounded-xl shadow-xl overflow-hidden\">\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-subtle bg-bg-sunken/30\">\n <div className=\"flex items-center gap-2\">\n <ShoppingCart className=\"size-5 text-text-primary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.yourCart', 'Your Cart')}\n </h3>\n <span className=\"px-2 py-0.5 text-xs bg-action-primary-bg text-action-primary-text rounded-full\">\n {itemCount} item{itemCount !== 1 ? 's' : ''}\n </span>\n </div>\n <button\n type=\"button\"\n onClick={closeCart}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"overflow-y-auto max-h-[50vh] divide-y divide-border\">\n {items.map(({ addon, quantity }) => (\n <div key={addon.id} className=\"p-4 flex items-center gap-3\">\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium text-text-primary truncate\">{addon.name}</h4>\n <p className=\"text-sm text-text-secondary\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price, currency);\n })()}{' '}\n / {formatPlanDuration(addon.duration)}\n </p>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, quantity - 1)}\n className=\"p-1 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Minus className=\"size-3\" />\n </button>\n <span className=\"w-6 text-center text-sm font-medium\">{quantity}</span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(addon.id, quantity + 1)}\n className=\"p-1 rounded border border-border-subtle hover:bg-bg-sunken transition-colors\"\n >\n <Plus className=\"size-3\" />\n </button>\n </div>\n <p className=\"font-medium text-text-primary\">\n {(() => {\n const { price, currency } = getAddonDisplayPrice(addon, displayCurrency);\n return formatCurrency(price * quantity, currency);\n })()}\n </p>\n <button\n type=\"button\"\n onClick={() => removeFromCart(addon.id)}\n className=\"p-1.5 rounded text-status-error-text hover:bg-status-error-bg/10 transition-colors\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ))}\n </div>\n\n <div className=\"border-t border-border-subtle p-4 space-y-3 bg-bg-sunken/20\">\n <div className=\"flex items-center justify-between text-lg font-semibold\">\n <span className=\"text-text-primary\">{tr('billing.addons.total', 'Total')}</span>\n <span className=\"text-text-primary\">\n {(() => {\n const total = items.reduce((sum, { addon, quantity }) => {\n const { price } = getAddonDisplayPrice(addon, displayCurrency);\n return sum + price * quantity;\n }, 0);\n return formatCurrency(total, displayCurrency);\n })()}\n </span>\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={clearCart}\n className=\"flex-1 px-4 py-2.5 text-sm font-medium border border-border-subtle text-text-primary rounded-md hover:bg-bg-sunken transition-colors\"\n >\n Clear Cart\n </button>\n <button\n type=\"button\"\n onClick={() => {\n closeCart();\n navigateTo('/checkout/addons');\n }}\n className=\"flex-1 px-4 py-2.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 transition-colors\"\n >\n Checkout\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n};\n"],"mappings":"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"}
|
|
@@ -146,7 +146,7 @@ var A = () => {
|
|
|
146
146
|
actions: /* @__PURE__ */ k("button", {
|
|
147
147
|
type: "button",
|
|
148
148
|
onClick: () => void X(),
|
|
149
|
-
className: "inline-flex items-center gap-2 rounded-md border border-border bg-
|
|
149
|
+
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary",
|
|
150
150
|
children: [/* @__PURE__ */ O(C, { className: "size-4" }), "Refresh"]
|
|
151
151
|
})
|
|
152
152
|
}),
|
|
@@ -157,7 +157,7 @@ var A = () => {
|
|
|
157
157
|
2,
|
|
158
158
|
3,
|
|
159
159
|
4
|
|
160
|
-
].map((e) => /* @__PURE__ */ O("div", { className: "h-28 animate-pulse rounded-lg border border-border bg-
|
|
160
|
+
].map((e) => /* @__PURE__ */ O("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
161
161
|
}) : z ? /* @__PURE__ */ O("div", {
|
|
162
162
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
163
163
|
children: ne.map((e) => /* @__PURE__ */ O(r, {
|
|
@@ -167,20 +167,20 @@ var A = () => {
|
|
|
167
167
|
}, e.title))
|
|
168
168
|
}) : null,
|
|
169
169
|
U && !l(U) ? /* @__PURE__ */ O("div", {
|
|
170
|
-
className: "rounded-lg border border-border bg-
|
|
170
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
171
171
|
children: U.message
|
|
172
172
|
}) : null,
|
|
173
173
|
/* @__PURE__ */ k("div", {
|
|
174
174
|
className: "grid gap-6 xl:grid-cols-[1.1fr,1.9fr]",
|
|
175
175
|
children: [/* @__PURE__ */ k("section", {
|
|
176
|
-
className: "rounded-lg border border-border bg-
|
|
176
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
177
177
|
children: [
|
|
178
178
|
/* @__PURE__ */ k("div", {
|
|
179
179
|
className: "mb-4 flex items-center justify-between",
|
|
180
180
|
children: [/* @__PURE__ */ O("h2", {
|
|
181
|
-
className: "text-lg font-semibold text-
|
|
181
|
+
className: "text-lg font-semibold text-text-primary",
|
|
182
182
|
children: Z ? "Update affiliate code" : "Create affiliate code"
|
|
183
|
-
}), Z ? null : /* @__PURE__ */ O(S, { className: "size-4 text-
|
|
183
|
+
}), Z ? null : /* @__PURE__ */ O(S, { className: "size-4 text-text-secondary" })]
|
|
184
184
|
}),
|
|
185
185
|
/* @__PURE__ */ k("div", {
|
|
186
186
|
className: "space-y-3",
|
|
@@ -192,7 +192,7 @@ var A = () => {
|
|
|
192
192
|
label: e.target.value
|
|
193
193
|
})),
|
|
194
194
|
placeholder: "Label",
|
|
195
|
-
className: "w-full rounded-md border border-border bg-
|
|
195
|
+
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
196
196
|
}),
|
|
197
197
|
/* @__PURE__ */ O("input", {
|
|
198
198
|
value: q.productSlug,
|
|
@@ -201,7 +201,7 @@ var A = () => {
|
|
|
201
201
|
productSlug: e.target.value
|
|
202
202
|
})),
|
|
203
203
|
placeholder: "Product slug (eg. bigconsole)",
|
|
204
|
-
className: "w-full rounded-md border border-border bg-
|
|
204
|
+
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
205
205
|
disabled: !!Z
|
|
206
206
|
}),
|
|
207
207
|
/* @__PURE__ */ O("input", {
|
|
@@ -211,7 +211,7 @@ var A = () => {
|
|
|
211
211
|
destinationPath: e.target.value
|
|
212
212
|
})),
|
|
213
213
|
placeholder: "Destination path (eg. /pricing)",
|
|
214
|
-
className: "w-full rounded-md border border-border bg-
|
|
214
|
+
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
215
215
|
}),
|
|
216
216
|
/* @__PURE__ */ O("input", {
|
|
217
217
|
value: q.destinationUrl,
|
|
@@ -220,7 +220,7 @@ var A = () => {
|
|
|
220
220
|
destinationUrl: e.target.value
|
|
221
221
|
})),
|
|
222
222
|
placeholder: "Absolute destination URL (optional)",
|
|
223
|
-
className: "w-full rounded-md border border-border bg-
|
|
223
|
+
className: "w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
224
224
|
}),
|
|
225
225
|
/* @__PURE__ */ O("textarea", {
|
|
226
226
|
value: q.description,
|
|
@@ -229,7 +229,7 @@ var A = () => {
|
|
|
229
229
|
description: e.target.value
|
|
230
230
|
})),
|
|
231
231
|
placeholder: "Description",
|
|
232
|
-
className: "min-h-24 w-full rounded-md border border-border bg-
|
|
232
|
+
className: "min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
233
233
|
}),
|
|
234
234
|
/* @__PURE__ */ O("textarea", {
|
|
235
235
|
value: q.notes,
|
|
@@ -238,7 +238,7 @@ var A = () => {
|
|
|
238
238
|
notes: e.target.value
|
|
239
239
|
})),
|
|
240
240
|
placeholder: "Internal notes",
|
|
241
|
-
className: "min-h-24 w-full rounded-md border border-border bg-
|
|
241
|
+
className: "min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
242
242
|
})
|
|
243
243
|
]
|
|
244
244
|
}),
|
|
@@ -247,7 +247,7 @@ var A = () => {
|
|
|
247
247
|
children: [/* @__PURE__ */ O("button", {
|
|
248
248
|
type: "button",
|
|
249
249
|
onClick: () => void Q(),
|
|
250
|
-
className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-
|
|
250
|
+
className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
251
251
|
children: Z ? "Save changes" : "Create code"
|
|
252
252
|
}), Z ? /* @__PURE__ */ O("button", {
|
|
253
253
|
type: "button",
|
|
@@ -261,7 +261,7 @@ var A = () => {
|
|
|
261
261
|
destinationUrl: ""
|
|
262
262
|
});
|
|
263
263
|
},
|
|
264
|
-
className: "rounded-md border border-border bg-
|
|
264
|
+
className: "rounded-md border border-border-subtle bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary",
|
|
265
265
|
children: "Cancel"
|
|
266
266
|
}) : null]
|
|
267
267
|
})
|
|
@@ -269,9 +269,9 @@ var A = () => {
|
|
|
269
269
|
}), /* @__PURE__ */ k("section", {
|
|
270
270
|
className: "space-y-4",
|
|
271
271
|
children: [/* @__PURE__ */ k("div", {
|
|
272
|
-
className: "rounded-lg border border-border bg-
|
|
272
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
273
273
|
children: [/* @__PURE__ */ O("h2", {
|
|
274
|
-
className: "mb-4 text-lg font-semibold text-
|
|
274
|
+
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
275
275
|
children: "My codes"
|
|
276
276
|
}), F.length === 0 && !V ? /* @__PURE__ */ O(i, {
|
|
277
277
|
icon: /* @__PURE__ */ O(T, { className: "size-8" }),
|
|
@@ -282,17 +282,17 @@ var A = () => {
|
|
|
282
282
|
children: F.map((e) => {
|
|
283
283
|
let t = e.destinationUrl || `https://${e.productSlug}.com${e.destinationPath || ""}?ref=${e.code}`;
|
|
284
284
|
return /* @__PURE__ */ k("div", {
|
|
285
|
-
className: "rounded-lg border border-border bg-
|
|
285
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
286
286
|
children: [
|
|
287
287
|
/* @__PURE__ */ k("div", {
|
|
288
288
|
className: "flex flex-wrap items-start justify-between gap-4",
|
|
289
289
|
children: [/* @__PURE__ */ k("div", { children: [
|
|
290
290
|
/* @__PURE__ */ O("p", {
|
|
291
|
-
className: "text-sm font-semibold text-
|
|
291
|
+
className: "text-sm font-semibold text-text-primary",
|
|
292
292
|
children: e.label || e.code
|
|
293
293
|
}),
|
|
294
294
|
/* @__PURE__ */ k("p", {
|
|
295
|
-
className: "text-xs text-
|
|
295
|
+
className: "text-xs text-text-secondary",
|
|
296
296
|
children: [
|
|
297
297
|
e.code,
|
|
298
298
|
" · ",
|
|
@@ -300,7 +300,7 @@ var A = () => {
|
|
|
300
300
|
]
|
|
301
301
|
}),
|
|
302
302
|
/* @__PURE__ */ O("p", {
|
|
303
|
-
className: "mt-1 break-all text-xs text-
|
|
303
|
+
className: "mt-1 break-all text-xs text-text-secondary",
|
|
304
304
|
children: t
|
|
305
305
|
})
|
|
306
306
|
] }), /* @__PURE__ */ k("div", {
|
|
@@ -309,19 +309,19 @@ var A = () => {
|
|
|
309
309
|
/* @__PURE__ */ O("button", {
|
|
310
310
|
type: "button",
|
|
311
311
|
onClick: () => $(e),
|
|
312
|
-
className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-
|
|
312
|
+
className: "rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary",
|
|
313
313
|
children: "Edit"
|
|
314
314
|
}),
|
|
315
315
|
/* @__PURE__ */ k("button", {
|
|
316
316
|
type: "button",
|
|
317
317
|
onClick: () => void te(e.id),
|
|
318
|
-
className: "inline-flex items-center gap-1 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-
|
|
318
|
+
className: "inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary",
|
|
319
319
|
children: [/* @__PURE__ */ O(w, { className: "size-3.5" }), "Note"]
|
|
320
320
|
}),
|
|
321
321
|
e.status === "ACTIVE" ? /* @__PURE__ */ O("button", {
|
|
322
322
|
type: "button",
|
|
323
323
|
onClick: () => void ee(e.id),
|
|
324
|
-
className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-
|
|
324
|
+
className: "rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text",
|
|
325
325
|
children: "Deactivate"
|
|
326
326
|
}) : null
|
|
327
327
|
]
|
|
@@ -331,49 +331,49 @@ var A = () => {
|
|
|
331
331
|
className: "mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4",
|
|
332
332
|
children: [
|
|
333
333
|
/* @__PURE__ */ k("div", {
|
|
334
|
-
className: "rounded-md border border-border bg-
|
|
334
|
+
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
335
335
|
children: [/* @__PURE__ */ O("p", {
|
|
336
|
-
className: "text-xs text-
|
|
336
|
+
className: "text-xs text-text-secondary",
|
|
337
337
|
children: "Visits"
|
|
338
338
|
}), /* @__PURE__ */ O("p", {
|
|
339
|
-
className: "text-lg font-semibold text-
|
|
339
|
+
className: "text-lg font-semibold text-text-primary",
|
|
340
340
|
children: e.totalVisits
|
|
341
341
|
})]
|
|
342
342
|
}),
|
|
343
343
|
/* @__PURE__ */ k("div", {
|
|
344
|
-
className: "rounded-md border border-border bg-
|
|
344
|
+
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
345
345
|
children: [/* @__PURE__ */ O("p", {
|
|
346
|
-
className: "text-xs text-
|
|
346
|
+
className: "text-xs text-text-secondary",
|
|
347
347
|
children: "Signups"
|
|
348
348
|
}), /* @__PURE__ */ O("p", {
|
|
349
|
-
className: "text-lg font-semibold text-
|
|
349
|
+
className: "text-lg font-semibold text-text-primary",
|
|
350
350
|
children: e.totalSignups
|
|
351
351
|
})]
|
|
352
352
|
}),
|
|
353
353
|
/* @__PURE__ */ k("div", {
|
|
354
|
-
className: "rounded-md border border-border bg-
|
|
354
|
+
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
355
355
|
children: [/* @__PURE__ */ O("p", {
|
|
356
|
-
className: "text-xs text-
|
|
356
|
+
className: "text-xs text-text-secondary",
|
|
357
357
|
children: "Paid conversions"
|
|
358
358
|
}), /* @__PURE__ */ O("p", {
|
|
359
|
-
className: "text-lg font-semibold text-
|
|
359
|
+
className: "text-lg font-semibold text-text-primary",
|
|
360
360
|
children: e.totalPaidConversions + e.totalStorePurchases
|
|
361
361
|
})]
|
|
362
362
|
}),
|
|
363
363
|
/* @__PURE__ */ k("div", {
|
|
364
|
-
className: "rounded-md border border-border bg-
|
|
364
|
+
className: "rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
365
365
|
children: [/* @__PURE__ */ O("p", {
|
|
366
|
-
className: "text-xs text-
|
|
366
|
+
className: "text-xs text-text-secondary",
|
|
367
367
|
children: "Rewards earned"
|
|
368
368
|
}), /* @__PURE__ */ O("p", {
|
|
369
|
-
className: "text-lg font-semibold text-
|
|
369
|
+
className: "text-lg font-semibold text-text-primary",
|
|
370
370
|
children: s(e.totalRewardAmount, "USD")
|
|
371
371
|
})]
|
|
372
372
|
})
|
|
373
373
|
]
|
|
374
374
|
}),
|
|
375
375
|
e.notes ? /* @__PURE__ */ O("p", {
|
|
376
|
-
className: "mt-3 text-sm text-
|
|
376
|
+
className: "mt-3 text-sm text-text-secondary",
|
|
377
377
|
children: e.notes
|
|
378
378
|
}) : null
|
|
379
379
|
]
|
|
@@ -381,9 +381,9 @@ var A = () => {
|
|
|
381
381
|
})
|
|
382
382
|
})]
|
|
383
383
|
}), /* @__PURE__ */ k("div", {
|
|
384
|
-
className: "rounded-lg border border-border bg-
|
|
384
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
385
385
|
children: [/* @__PURE__ */ O("h2", {
|
|
386
|
-
className: "mb-4 text-lg font-semibold text-
|
|
386
|
+
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
387
387
|
children: "Recent attributed journeys"
|
|
388
388
|
}), L.length === 0 ? /* @__PURE__ */ O(i, {
|
|
389
389
|
icon: /* @__PURE__ */ O(D, { className: "size-8" }),
|
|
@@ -394,7 +394,7 @@ var A = () => {
|
|
|
394
394
|
children: /* @__PURE__ */ k("table", {
|
|
395
395
|
className: "min-w-full text-sm",
|
|
396
396
|
children: [/* @__PURE__ */ O("thead", { children: /* @__PURE__ */ k("tr", {
|
|
397
|
-
className: "border-b border-border text-left text-
|
|
397
|
+
className: "border-b border-border-subtle text-left text-text-secondary",
|
|
398
398
|
children: [
|
|
399
399
|
/* @__PURE__ */ O("th", {
|
|
400
400
|
className: "px-3 py-2",
|
|
@@ -422,7 +422,7 @@ var A = () => {
|
|
|
422
422
|
})
|
|
423
423
|
]
|
|
424
424
|
}) }), /* @__PURE__ */ O("tbody", { children: L.map((e) => /* @__PURE__ */ k("tr", {
|
|
425
|
-
className: "border-b border-border/70 text-
|
|
425
|
+
className: "border-b border-border-subtle/70 text-text-primary",
|
|
426
426
|
children: [
|
|
427
427
|
/* @__PURE__ */ O("td", {
|
|
428
428
|
className: "px-3 py-2 font-medium",
|