@burdenoff/microfe-billing 2026.528.1 → 2026.530.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/modules/dashboard/components/ActiveSubscriptionCard.js +27 -25
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +9 -8
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +7 -6
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +9 -7
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +7 -0
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +82 -81
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +105 -104
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +162 -161
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +85 -84
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +115 -114
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/shared/utils/index.js +1 -0
- package/dist/billing/shared/utils/planFeatureQuantity.js +11 -0
- package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -0
- package/dist/generated/global-operations.js +1 -0
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -202,6 +202,7 @@ var g = () => {
|
|
|
202
202
|
}),
|
|
203
203
|
N.length > 1 && /* @__PURE__ */ h("div", {
|
|
204
204
|
className: "relative",
|
|
205
|
+
"data-tour": "billing-overview-account-selector",
|
|
205
206
|
children: [/* @__PURE__ */ h("button", {
|
|
206
207
|
type: "button",
|
|
207
208
|
onClick: () => k(!O),
|
|
@@ -252,6 +253,7 @@ var g = () => {
|
|
|
252
253
|
}),
|
|
253
254
|
P && /* @__PURE__ */ h("div", {
|
|
254
255
|
className: "grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4",
|
|
256
|
+
"data-tour": "billing-overview-stats-row",
|
|
255
257
|
children: [
|
|
256
258
|
/* @__PURE__ */ m(n, {
|
|
257
259
|
label: _("billing.overview.stats.creditBalance", "Credit Balance"),
|
|
@@ -281,6 +283,7 @@ var g = () => {
|
|
|
281
283
|
className: "lg:col-span-2 space-y-6",
|
|
282
284
|
children: [
|
|
283
285
|
P && /* @__PURE__ */ m(le, {
|
|
286
|
+
dataTour: "billing-overview-billing-account-card",
|
|
284
287
|
account: P,
|
|
285
288
|
isDefault: P.isDefault ?? !1,
|
|
286
289
|
onEdit: () => P && Q(`/settings?billingAccountId=${P.id}`),
|
|
@@ -359,6 +362,7 @@ var g = () => {
|
|
|
359
362
|
}, e.id))
|
|
360
363
|
})] })]
|
|
361
364
|
}), /* @__PURE__ */ m(de, {
|
|
365
|
+
dataTour: "billing-overview-active-subscription-card",
|
|
362
366
|
subscription: Z,
|
|
363
367
|
onViewDetails: () => Z && Q(i(`/subscriptions/${Z.id}`, Z.billingAccountId ?? P?.id)),
|
|
364
368
|
onUpgrade: () => Z && Q(i(`/subscriptions/${Z.id}/upgrade`, Z.billingAccountId ?? P?.id)),
|
|
@@ -367,6 +371,7 @@ var g = () => {
|
|
|
367
371
|
})]
|
|
368
372
|
}),
|
|
369
373
|
P && /* @__PURE__ */ m(ue, {
|
|
374
|
+
dataTour: "billing-overview-payment-methods-section",
|
|
370
375
|
paymentMethods: F,
|
|
371
376
|
billingAccountId: P.id,
|
|
372
377
|
onAddPaymentMethod: Fe,
|
|
@@ -379,6 +384,7 @@ var g = () => {
|
|
|
379
384
|
className: "space-y-6",
|
|
380
385
|
children: [/* @__PURE__ */ h("section", {
|
|
381
386
|
className: "border border-border rounded-lg bg-card p-4",
|
|
387
|
+
"data-tour": "billing-overview-quick-actions",
|
|
382
388
|
children: [/* @__PURE__ */ m("h3", {
|
|
383
389
|
className: "font-semibold text-foreground mb-4",
|
|
384
390
|
children: "Quick Actions"
|
|
@@ -450,6 +456,7 @@ var g = () => {
|
|
|
450
456
|
})]
|
|
451
457
|
}),
|
|
452
458
|
/* @__PURE__ */ m(fe, {
|
|
459
|
+
dataTour: "billing-overview-recommended-addons",
|
|
453
460
|
addons: Ae,
|
|
454
461
|
hasSubscription: L,
|
|
455
462
|
onViewAddon: (e) => Q(`/addons/${e}`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Billing Overview Page\n * Main dashboard for billing showing accounts, subscriptions, payment methods, and addons\n */\n\nimport { type FC, useState, useCallback, useEffect } from 'react';\nimport {\n Plus,\n Building2,\n RefreshCw,\n Loader2,\n Wallet,\n ChevronDown,\n Check,\n Activity,\n Receipt,\n ArrowRight,\n Sparkles,\n Crown,\n Coins,\n} from 'lucide-react';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { StatCard } from '../../../shared/components/StatCard';\nimport { EmptyState } from '../../../shared/components/EmptyState';\nimport { ServerError } from '../../../shared/components/ServerError';\nimport { AccessDenied } from '../../../shared/components/AccessDenied';\nimport { isServerError } from '../../../shared/utils';\nimport { useBillingOverviewData, useDashboardMutations } from '../hooks';\nimport {\n BillingAccountCard,\n PaymentMethodsSection,\n ActiveSubscriptionCard,\n RecommendedAddonsSection,\n CreateBillingAccountModal,\n AddPaymentMethodModal,\n} from '../components';\nimport { useAddonCartStore } from '../../addons/store/addonCartStore';\nimport { FloatingCartDrawer } from '../../addons/components/FloatingCartDrawer';\nimport {\n getStoredBillingAccountId,\n setStoredBillingAccountId,\n} from '../../../hooks/useBillingAccountSelection';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport type {\n BillingAccount,\n CreateBillingAccountInput,\n SetBillingAddressInput,\n Addon,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const OverviewPage: 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 { navigate, basePath, orgId, productId } = useBilling();\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n\n const [selectedAccountId, setSelectedAccountIdState] = useState<string | null>(() =>\n getStoredBillingAccountId(orgId)\n );\n\n const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [isAccountSelectorOpen, setIsAccountSelectorOpen] = useState(false);\n const [isAddPaymentModalOpen, setIsAddPaymentModalOpen] = useState(false);\n const [selectedSubscriptionIndex, setSelectedSubscriptionIndex] = useState(0);\n const [isSubscriptionSelectorOpen, setIsSubscriptionSelectorOpen] = useState(false);\n\n const {\n billingAccounts,\n currentAccount,\n paymentMethods,\n activeSubscriptions,\n hasActiveSubscription,\n recommendedAddons,\n isLoading,\n error,\n refetchAll,\n } = useBillingOverviewData(selectedAccountId, productId);\n\n const currentAccountId = currentAccount?.id;\n const isLoadingAddons = isLoading;\n\n const setSelectedAccountId = useCallback(\n (accountId: string | null) => {\n setSelectedAccountIdState(accountId);\n setStoredBillingAccountId(orgId, accountId);\n },\n [orgId]\n );\n\n // Addon cart store for add to cart functionality\n const { addToCart, isInCart, getCartItem, updateQuantity, removeFromCart } = useAddonCartStore();\n\n const {\n createBillingAccount,\n setBillingAddress,\n setDefaultPaymentMethod,\n deletePaymentMethod,\n isCreatingAccount,\n } = useDashboardMutations();\n\n // Reset subscription selector when account changes or subscriptions change\n useEffect(() => {\n setSelectedSubscriptionIndex(0);\n setIsSubscriptionSelectorOpen(false);\n }, [currentAccountId, activeSubscriptions.length]);\n\n useEffect(() => {\n if (selectedAccountId && billingAccounts.length > 0) {\n const accountExists = billingAccounts.some((account) => account.id === selectedAccountId);\n if (!accountExists) {\n setSelectedAccountId(null);\n }\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n useEffect(() => {\n if (!selectedAccountId && billingAccounts.length > 0) {\n const defaultAccount =\n billingAccounts.find((account) => account.isDefault) ?? billingAccounts[0];\n setSelectedAccountId(defaultAccount.id);\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n // Derive selected subscription from active subscriptions\n const selectedSubscription =\n activeSubscriptions[selectedSubscriptionIndex] || activeSubscriptions[0] || null;\n\n // Helper for navigation - properly joins basePath and path\n const navigateTo = useCallback(\n (path: string) => {\n // Handle path joining to avoid double slashes (e.g., / + /plans = //plans)\n let fullPath = path;\n if (basePath && basePath !== '/') {\n // Remove trailing slash from basePath and leading slash from path if needed\n const base = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n fullPath = `${base}${suffix}`;\n }\n navigate(fullPath);\n },\n [navigate, basePath]\n );\n\n // Handlers\n const handleCreateAccount = useCallback(\n async (\n input: CreateBillingAccountInput,\n address?: Omit<SetBillingAddressInput, 'billingAccountId'>\n ) => {\n setCreateError(null);\n try {\n const newAccount = await createBillingAccount(input);\n\n // If address data provided, set the billing address\n if (address && newAccount.id) {\n await setBillingAddress({\n billingAccountId: newAccount.id,\n ...address,\n });\n }\n\n setIsCreateModalOpen(false);\n setSelectedAccountId(newAccount.id);\n emit('billing.billing_account.created', {\n route: '/billing/overview',\n entityId: newAccount.id,\n source: 'overview',\n });\n await refetchAll();\n } catch (err) {\n setCreateError(err instanceof Error ? err.message : 'Failed to create billing account');\n }\n },\n [createBillingAccount, emit, refetchAll, setBillingAddress, setSelectedAccountId]\n );\n\n const handleAddPaymentMethod = useCallback(() => {\n setIsAddPaymentModalOpen(true);\n }, []);\n\n // Helper to refetch the current account data\n const refetchCurrentAccount = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n const handlePaymentMethodAdded = useCallback(async () => {\n emit('billing.payment_method.added', {\n route: '/billing/overview',\n entityId: currentAccountId,\n source: 'overview',\n });\n await refetchCurrentAccount();\n }, [currentAccountId, emit, refetchCurrentAccount]);\n\n const handleSetDefaultPaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await setDefaultPaymentMethod(id, currentAccountId);\n emit('billing.payment_method.default_set', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, emit, setDefaultPaymentMethod, refetchCurrentAccount]\n );\n\n const handleDeletePaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await deletePaymentMethod(id, currentAccountId);\n emit('billing.payment_method.deleted', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, deletePaymentMethod, emit, refetchCurrentAccount]\n );\n\n const handleSelectAccount = useCallback(\n (account: BillingAccount) => {\n setSelectedAccountId(account.id);\n setIsAccountSelectorOpen(false);\n },\n [setSelectedAccountId]\n );\n\n const handleRefresh = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n // Handler for adding addon to cart\n const handleAddToCart = useCallback(\n (addon: Addon) => {\n addToCart(addon);\n },\n [addToCart]\n );\n\n // Handler to check if addon is in cart\n const handleIsInCart = useCallback(\n (addonId: string) => {\n return isInCart(addonId);\n },\n [isInCart]\n );\n\n // Handler to get cart quantity for addon\n const handleGetCartQuantity = useCallback(\n (addonId: string) => {\n const item = getCartItem(addonId);\n return item?.quantity || 0;\n },\n [getCartItem]\n );\n\n // Handler to update quantity in cart\n const handleUpdateQuantity = useCallback(\n (addonId: string, quantity: number) => {\n if (quantity <= 0) {\n removeFromCart(addonId);\n } else {\n updateQuantity(addonId, quantity);\n }\n },\n [updateQuantity, removeFromCart]\n );\n\n // Handler to remove from cart\n const handleRemoveFromCart = useCallback(\n (addonId: string) => {\n removeFromCart(addonId);\n },\n [removeFromCart]\n );\n\n // Permission check - must be after all hooks are defined\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view billing information.\" />;\n }\n\n // Error state\n if (error && isServerError(error) && !isLoading) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load billing overview. The server might be down or experiencing issues.\"\n onRetry={handleRefresh}\n showRetry\n />\n </div>\n );\n }\n\n // Loading state\n if (isLoading && !currentAccount) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"flex flex-col items-center gap-3\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.overview.loadingInfo', 'Loading billing information...')}\n </p>\n </div>\n </div>\n );\n }\n\n // No billing accounts state\n if (!isLoading && billingAccounts.length === 0) {\n return (\n <div className=\"p-6\">\n <header className=\"mb-8\">\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.overview.title', 'Billing Overview')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr(\n 'billing.overview.emptySetupDescription',\n 'Set up billing to manage subscriptions, payments, and invoices'\n )}\n </p>\n </header>\n <EmptyState\n icon={<Building2 className=\"size-6 text-muted-foreground\" />}\n title={tr('billing.overview.emptyTitle', 'No billing account')}\n description={tr(\n 'billing.overview.emptyDescription',\n 'Create a billing account to start managing your subscriptions and payments'\n )}\n action={\n permissions.canManageBillingAccount ? (\n <button\n type=\"button\"\n onClick={() => setIsCreateModalOpen(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('billing.overview.createBillingAccount', 'Create Billing Account')}\n </button>\n ) : undefined\n }\n />\n\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n </div>\n );\n }\n\n return (\n <div className=\"p-4 sm:p-6 space-y-6 sm:space-y-8\">\n {/* Header */}\n <header className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.overview.title', 'Billing Overview')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr(\n 'billing.overview.description',\n 'Manage your billing accounts, subscriptions, and payment methods'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleRefresh}\n className=\"p-2 rounded-lg border border-border hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n aria-label={tr('billing.overview.refresh', 'Refresh')}\n >\n <RefreshCw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} />\n </button>\n {permissions.canManageBillingAccount && billingAccounts.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setIsCreateModalOpen(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tr('billing.overview.newAccount', 'New Account')}\n </span>\n </button>\n )}\n </div>\n </header>\n\n {/* Account Selector (if multiple accounts) */}\n {billingAccounts.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsAccountSelectorOpen(!isAccountSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border rounded-lg bg-card hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-lg bg-primary/10 flex items-center justify-center\">\n <Building2 className=\"size-4 text-primary\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-foreground\">\n {currentAccount?.name || tr('billing.overview.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-muted-foreground\">{currentAccount?.email}</p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-muted-foreground transition-transform ${isAccountSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isAccountSelectorOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsAccountSelectorOpen(false)} />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto\">\n {billingAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => handleSelectAccount(account)}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-muted transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-8 rounded-lg bg-muted flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-4 text-muted-foreground\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-foreground truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">{account.email}</p>\n </div>\n </div>\n {currentAccount?.id === account.id && (\n <Check className=\"size-4 text-primary flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Stats Row */}\n {currentAccount && (\n <div className=\"grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4\">\n <StatCard\n label={tr('billing.overview.stats.creditBalance', 'Credit Balance')}\n value={`${(currentAccount.creditAmount || 0).toLocaleString()} Credits`}\n icon={<Wallet className=\"size-5\" />}\n />\n <StatCard\n label={tr('billing.overview.stats.activePlans', 'Active Plans')}\n value={\n activeSubscriptions.length > 0\n ? activeSubscriptions.length === 1\n ? activeSubscriptions[0]?.plan?.name || '1 Plan'\n : `${activeSubscriptions.length} ${tr('billing.overview.stats.plansSuffix', 'Plans')}`\n : tr('billing.overview.stats.none', 'None')\n }\n icon={<RefreshCw className=\"size-5\" />}\n />\n <StatCard\n label={tr('billing.overview.stats.paymentMethods', 'Payment Methods')}\n value={paymentMethods.length}\n icon={<Building2 className=\"size-5\" />}\n />\n <StatCard\n label={tr('billing.overview.stats.paymentCurrency', 'Payment Currency')}\n value={currentAccount.currency || 'USD'}\n icon={<Building2 className=\"size-5\" />}\n />\n </div>\n )}\n\n {/* Main Content Grid */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Left Column - Billing Account & Subscription */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Billing Account Card */}\n {currentAccount && (\n <BillingAccountCard\n account={currentAccount}\n isDefault={currentAccount.isDefault ?? false}\n onEdit={() =>\n currentAccount && navigateTo(`/settings?billingAccountId=${currentAccount.id}`)\n }\n onViewTransactions={() => navigateTo(`/transactions/${currentAccount.id}`)}\n onViewCreditTransactions={() =>\n navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n />\n )}\n\n {/* Active Subscriptions */}\n <div className=\"space-y-3\">\n {/* Subscription Selector (if multiple subscriptions) */}\n {activeSubscriptions.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsSubscriptionSelectorOpen(!isSubscriptionSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border rounded-lg bg-card hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-lg bg-primary/10 flex items-center justify-center\">\n <Crown className=\"size-4 text-primary\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-foreground\">\n {selectedSubscription?.plan?.name ||\n tr('billing.overview.selectSubscription', 'Select Subscription')}\n {selectedSubscription?.plan?.product?.name && (\n <span className=\"text-muted-foreground font-normal\">\n {' '}\n · {selectedSubscription.plan.product.name}\n </span>\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {activeSubscriptions.length}{' '}\n {tr('billing.overview.activeSubscriptions', 'active subscriptions')}\n </p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-muted-foreground transition-transform ${isSubscriptionSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isSubscriptionSelectorOpen && (\n <>\n <div\n className=\"fixed inset-0 z-10\"\n onClick={() => setIsSubscriptionSelectorOpen(false)}\n />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto\">\n {activeSubscriptions.map((subscription, index) => (\n <button\n type=\"button\"\n key={subscription.id}\n onClick={() => {\n setSelectedSubscriptionIndex(index);\n setIsSubscriptionSelectorOpen(false);\n }}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-muted transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-8 rounded-lg bg-muted flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-4 text-muted-foreground\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-foreground truncate\">\n {subscription.plan?.name ||\n tr('billing.overview.subscription', 'Subscription')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {subscription.plan?.product?.name ||\n tr('billing.overview.unknownProduct', 'Unknown Product')}{' '}\n · {tr('billing.overview.ends', 'Ends')}{' '}\n {new Date(subscription.endDate).toLocaleDateString()}\n </p>\n </div>\n </div>\n {selectedSubscriptionIndex === index && (\n <Check className=\"size-4 text-primary flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Single Subscription Card */}\n <ActiveSubscriptionCard\n subscription={selectedSubscription}\n onViewDetails={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onUpgrade={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}/upgrade`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onManage={() =>\n currentAccount && navigateTo(`/subscriptions?billingAccountId=${currentAccount.id}`)\n }\n onPurchasePlan={() => navigateTo('/plans')}\n />\n </div>\n\n {/* Payment Methods */}\n {currentAccount && (\n <PaymentMethodsSection\n paymentMethods={paymentMethods}\n billingAccountId={currentAccount.id}\n onAddPaymentMethod={handleAddPaymentMethod}\n onSetDefault={handleSetDefaultPaymentMethod}\n onDelete={handleDeletePaymentMethod}\n isLoading={isLoading}\n />\n )}\n </div>\n\n {/* Right Column - Quick Actions & Info */}\n <div className=\"space-y-6\">\n {/* Quick Actions */}\n <section className=\"border border-border rounded-lg bg-card p-4\">\n <h3 className=\"font-semibold text-foreground mb-4\">Quick Actions</h3>\n <div className=\"space-y-2\">\n <button\n type=\"button\"\n onClick={() => currentAccount && navigateTo(`/transactions/${currentAccount.id}`)}\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-8 rounded bg-status-info-bg-subtle flex items-center justify-center\">\n <Receipt className=\"size-4 text-accent-blue\" />\n </div>\n View Transactions\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-8 rounded bg-status-warning-bg-subtle flex items-center justify-center\">\n <Wallet className=\"size-4 text-status-warning-text\" />\n </div>\n Credit History\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/checkout/credits')}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <div className=\"size-8 rounded bg-status-success-bg-subtle flex items-center justify-center\">\n <Coins className=\"size-4 text-status-success-text\" />\n </div>\n Purchase Credits\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/usage?billingAccountId=${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-8 rounded bg-accent-violet-subtle flex items-center justify-center\">\n <Activity className=\"size-4 text-accent-violet\" />\n </div>\n Usage Analytics\n </button>\n </div>\n </section>\n\n {/* Get Started (if no subscription) */}\n {!hasActiveSubscription && (\n <section className=\"border border-border rounded-lg bg-gradient-to-br from-primary/5 to-primary/10 p-4\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Sparkles className=\"size-5 text-primary\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-foreground\">Get Started</h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Choose a plan that fits your needs to unlock all features\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"w-full flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </button>\n </section>\n )}\n </div>\n </div>\n\n {/* Recommended Addons (only if has subscription) - Limited to 3 with cart support */}\n <RecommendedAddonsSection\n addons={recommendedAddons}\n hasSubscription={hasActiveSubscription}\n onViewAddon={(addonId) => navigateTo(`/addons/${addonId}`)}\n onPurchaseAddon={(addonId) => navigateTo(`/addons/${addonId}?action=purchase`)}\n onViewAllAddons={() => navigateTo('/addons')}\n onAddToCart={handleAddToCart}\n isInCart={handleIsInCart}\n getCartQuantity={handleGetCartQuantity}\n onUpdateQuantity={handleUpdateQuantity}\n onRemoveFromCart={handleRemoveFromCart}\n maxAddons={3}\n isLoading={isLoadingAddons}\n />\n\n {/* Floating Cart Drawer - shows when items are in cart */}\n <FloatingCartDrawer />\n\n {/* Create Billing Account Modal */}\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n\n {/* Add Payment Method Modal */}\n {currentAccount && (\n <AddPaymentMethodModal\n isOpen={isAddPaymentModalOpen}\n onClose={() => setIsAddPaymentModalOpen(false)}\n billingAccountId={currentAccount.id}\n currency={currentAccount.currency}\n country={currentAccount.billingAddresses?.[0]?.country}\n onSuccess={handlePaymentMethodAdded}\n />\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,IAAa,UAAyB;CACpC,IAAM,EAAE,SAAM,IAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,EAAE,aAAU,aAAU,UAAO,kBAAc,GAAY,EACvD,IAAc,IAAuB,EACrC,EAAE,YAAS,IAAwB,EAEnC,CAAC,GAAmB,MAA6B,QACrD,GAA0B,EAAM,CACjC,EAEK,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAuB,KAA4B,EAAS,GAAM,EACnE,CAAC,IAAuB,KAA4B,EAAS,GAAM,EACnE,CAAC,IAA2B,MAAgC,EAAS,EAAE,EACvE,CAAC,GAA4B,KAAiC,EAAS,GAAM,EAE7E,EACJ,oBACA,mBACA,mBACA,wBACA,0BACA,uBACA,cACA,UACA,kBACE,GAAuB,GAAmB,GAAU,EAElD,IAAmB,GAAgB,IACnC,KAAkB,GAElB,IAAuB,GAC1B,MAA6B;AAE5B,EADA,GAA0B,EAAU,EACpC,GAA0B,GAAO,EAAU;IAE7C,CAAC,EAAM,CACR,EAGK,EAAE,cAAW,aAAU,gBAAa,mBAAgB,sBAAmB,IAAmB,EAE1F,EACJ,yBACA,sBACA,4BACA,yBACA,0BACE,IAAuB;AAiB3B,CAdA,QAAgB;AAEd,EADA,GAA6B,EAAE,EAC/B,EAA8B,GAAM;IACnC,CAAC,GAAkB,EAAoB,OAAO,CAAC,EAElD,QAAgB;AACd,EAAI,KAAqB,EAAgB,SAAS,MAC1B,EAAgB,MAAM,MAAY,EAAQ,OAAO,EAAkB,IAEvF,EAAqB,KAAK;IAG7B;EAAC;EAAiB;EAAmB;EAAqB,CAAC,EAE9D,QAAgB;AACd,EAAI,CAAC,KAAqB,EAAgB,SAAS,KAGjD,GADE,EAAgB,MAAM,MAAY,EAAQ,UAAU,IAAI,EAAgB,IACtC,GAAG;IAExC;EAAC;EAAiB;EAAmB;EAAqB,CAAC;CAG9D,IAAM,IACJ,EAAoB,OAA8B,EAAoB,MAAM,MAGxE,IAAa,GAChB,MAAiB;EAEhB,IAAI,IAAW;AAOf,EANI,KAAY,MAAa,QAI3B,IAAW,GAFE,EAAS,SAAS,IAAI,GAAG,EAAS,MAAM,GAAG,GAAG,GAAG,IAC/C,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI,QAGnD,EAAS,EAAS;IAEpB,CAAC,GAAU,EAAS,CACrB,EAGK,KAAsB,EAC1B,OACE,GACA,MACG;AACH,IAAe,KAAK;AACpB,MAAI;GACF,IAAM,IAAa,MAAM,EAAqB,EAAM;AAiBpD,GAdI,KAAW,EAAW,MACxB,MAAM,EAAkB;IACtB,kBAAkB,EAAW;IAC7B,GAAG;IACJ,CAAC,EAGJ,EAAqB,GAAM,EAC3B,EAAqB,EAAW,GAAG,EACnC,EAAK,mCAAmC;IACtC,OAAO;IACP,UAAU,EAAW;IACrB,QAAQ;IACT,CAAC,EACF,MAAM,GAAY;WACX,GAAK;AACZ,KAAe,aAAe,QAAQ,EAAI,UAAU,mCAAmC;;IAG3F;EAAC;EAAsB;EAAM;EAAY;EAAmB;EAAqB,CAClF,EAEK,KAAyB,QAAkB;AAC/C,IAAyB,GAAK;IAC7B,EAAE,CAAC,EAGA,IAAwB,EAAY,YAAY;AACpD,QAAM,GAAY;IACjB,CAAC,EAAW,CAAC,EAEV,KAA2B,EAAY,YAAY;AAMvD,EALA,EAAK,gCAAgC;GACnC,OAAO;GACP,UAAU;GACV,QAAQ;GACT,CAAC,EACF,MAAM,GAAuB;IAC5B;EAAC;EAAkB;EAAM;EAAsB,CAAC,EAE7C,KAAgC,EACpC,OAAO,MAAe;AACf,QACL,MAAM,EAAwB,GAAI,EAAiB,EACnD,EAAK,sCAAsC;GACzC,OAAO;GACP,UAAU;GACV,QAAQ;GACT,CAAC,EACF,MAAM,GAAuB;IAE/B;EAAC;EAAkB;EAAM;EAAyB;EAAsB,CACzE,EAEK,KAA4B,EAChC,OAAO,MAAe;AACf,QACL,MAAM,GAAoB,GAAI,EAAiB,EAC/C,EAAK,kCAAkC;GACrC,OAAO;GACP,UAAU;GACV,QAAQ;GACT,CAAC,EACF,MAAM,GAAuB;IAE/B;EAAC;EAAkB;EAAqB;EAAM;EAAsB,CACrE,EAEK,KAAsB,GACzB,MAA4B;AAE3B,EADA,EAAqB,EAAQ,GAAG,EAChC,EAAyB,GAAM;IAEjC,CAAC,EAAqB,CACvB,EAEK,KAAgB,EAAY,YAAY;AAC5C,QAAM,GAAY;IACjB,CAAC,EAAW,CAAC,EAGV,KAAkB,GACrB,MAAiB;AAChB,IAAU,EAAM;IAElB,CAAC,EAAU,CACZ,EAGK,KAAiB,GACpB,MACQ,EAAS,EAAQ,EAE1B,CAAC,EAAS,CACX,EAGK,KAAwB,GAC3B,MACc,EAAY,EAAQ,EACpB,YAAY,GAE3B,CAAC,EAAY,CACd,EAGK,KAAuB,GAC1B,GAAiB,MAAqB;AACrC,EAAI,KAAY,IACd,EAAe,EAAQ,GAEvB,EAAe,GAAS,EAAS;IAGrC,CAAC,GAAgB,EAAe,CACjC,EAGK,KAAuB,GAC1B,MAAoB;AACnB,IAAe,EAAQ;IAEzB,CAAC,EAAe,CACjB;AAqFD,QAlFK,EAAY,wBAKb,KAAS,GAAc,EAAM,IAAI,CAAC,IAElC,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,IAAD;GACE,OAAM;GACN,SAAQ;GACR,SAAS;GACT,WAAA;GACA,CAAA;EACE,CAAA,GAKN,KAAa,CAAC,IAEd,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,IAAD,EAAS,WAAU,oCAAqC,CAAA,EACxD,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,gCAAgC,iCAAiC;IACnE,CAAA,CACA;;EACF,CAAA,GAKN,CAAC,KAAa,EAAgB,WAAW,IAEzC,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,0BAA0B,mBAAmB;KAC9C,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,0CACA,iEACD;KACC,CAAA,CACG;;GACT,kBAAC,IAAD;IACE,MAAM,kBAAC,GAAD,EAAW,WAAU,gCAAiC,CAAA;IAC5D,OAAO,EAAG,+BAA+B,qBAAqB;IAC9D,aAAa,EACX,qCACA,6EACD;IACD,QACE,EAAY,0BACV,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAqB,GAAK;KACzC,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,yCAAyC,yBAAyB,CAC/D;SACP,KAAA;IAEN,CAAA;GAEF,kBAAC,GAAD;IACE,QAAQ;IACR,eAAe;AAEb,KADA,EAAqB,GAAM,EAC3B,EAAe,KAAK;;IAEtB,UAAU;IACV,cAAc;IACd,OAAO;IACP,CAAA;GACE;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,0BAA0B,mBAAmB;KAC9C,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,gCACA,mEACD;KACC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,cAAY,EAAG,4BAA4B,UAAU;gBAErD,kBAAC,GAAD,EAAW,WAAW,UAAU,IAAY,iBAAiB,MAAQ,CAAA;MAC9D,CAAA,EACR,EAAY,2BAA2B,EAAgB,SAAS,KAC/D,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAqB,GAAK;MACzC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC3B,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,+BAA+B,cAAc;OAC5C,CAAA,CACA;QAEP;OACC;;GAGR,EAAgB,SAAS,KACxB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAyB,CAAC,EAAsB;KAC/D,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAW,WAAU,uBAAwB,CAAA;OACzC,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,GAAgB,QAAQ,EAAG,kCAAkC,iBAAiB;QAC7E,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAiC,GAAgB;QAAU,CAAA,CACpE;SACF;SACN,kBAAC,IAAD,EACE,WAAW,qDAAqD,IAAwB,eAAe,MACvG,CAAA,CACK;QAER,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAqB,eAAe,EAAyB,GAAM;KAAI,CAAA,EACtF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAgB,KAAK,MACpB,kBAAC,UAAD;MACE,MAAK;MAEL,eAAe,GAAoB,EAAQ;MAC3C,WAAU;gBAJZ,CAME,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAW,WAAU,gCAAiC,CAAA;QAClD,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAQ;SACP,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAA0C,EAAQ;SAAU,CAAA,CACrE;UACF;UACL,GAAgB,OAAO,EAAQ,MAC9B,kBAAC,IAAD,EAAO,WAAU,qCAAsC,CAAA,CAElD;QAlBF,EAAQ,GAkBN,CACT;KACE,CAAA,CACL,EAAA,CAAA,CAED;;GAIP,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,OAAO,EAAG,wCAAwC,iBAAiB;MACnE,OAAO,IAAI,EAAe,gBAAgB,GAAG,gBAAgB,CAAC;MAC9D,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;MACnC,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAG,sCAAsC,eAAe;MAC/D,OACE,EAAoB,SAAS,IACzB,EAAoB,WAAW,IAC7B,EAAoB,IAAI,MAAM,QAAQ,WACtC,GAAG,EAAoB,OAAO,GAAG,EAAG,sCAAsC,QAAQ,KACpF,EAAG,+BAA+B,OAAO;MAE/C,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;MACtC,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAG,yCAAyC,kBAAkB;MACrE,OAAO,EAAe;MACtB,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;MACtC,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAG,0CAA0C,mBAAmB;MACvE,OAAO,EAAe,YAAY;MAClC,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;MACtC,CAAA;KACE;;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,KACC,kBAAC,IAAD;OACE,SAAS;OACT,WAAW,EAAe,aAAa;OACvC,cACE,KAAkB,EAAW,8BAA8B,EAAe,KAAK;OAEjF,0BAA0B,EAAW,iBAAiB,EAAe,KAAK;OAC1E,gCACE,EAAW,uBAAuB,EAAe,KAAK;OAExD,CAAA;MAIJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAEG,EAAoB,SAAS,KAC5B,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAA8B,CAAC,EAA2B;SACzE,WAAU;mBAHZ,CAKE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAO,WAAU,uBAAwB,CAAA;WACrC,CAAA,EACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,KAAD;YAAG,WAAU;sBAAb,CACG,GAAsB,MAAM,QAC3B,EAAG,uCAAuC,sBAAsB,EACjE,GAAsB,MAAM,SAAS,QACpC,kBAAC,QAAD;aAAM,WAAU;uBAAhB;cACG;cAAI;cACF,EAAqB,KAAK,QAAQ;cAChC;eAEP;eACJ,kBAAC,KAAD;YAAG,WAAU;sBAAb;aACG,EAAoB;aAAQ;aAC5B,EAAG,wCAAwC,uBAAuB;aACjE;cACA;aACF;aACN,kBAAC,IAAD,EACE,WAAW,qDAAqD,IAA6B,eAAe,MAC5G,CAAA,CACK;YAER,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;SACE,WAAU;SACV,eAAe,EAA8B,GAAM;SACnD,CAAA,EACF,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAoB,KAAK,GAAc,MACtC,kBAAC,UAAD;UACE,MAAK;UAEL,eAAe;AAEb,WADA,GAA6B,EAAM,EACnC,EAA8B,GAAM;;UAEtC,WAAU;oBAPZ,CASE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,OAAD;YAAK,WAAU;sBACb,kBAAC,GAAD,EAAO,WAAU,gCAAiC,CAAA;YAC9C,CAAA,EACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,KAAD;aAAG,WAAU;uBACV,EAAa,MAAM,QAClB,EAAG,iCAAiC,eAAe;aACnD,CAAA,EACJ,kBAAC,KAAD;aAAG,WAAU;uBAAb;cACG,EAAa,MAAM,SAAS,QAC3B,EAAG,mCAAmC,kBAAkB;cAAE;cAAI;cAC7D,EAAG,yBAAyB,OAAO;cAAE;cACvC,IAAI,KAAK,EAAa,QAAQ,CAAC,oBAAoB;cAClD;eACA;cACF;cACL,OAA8B,KAC7B,kBAAC,IAAD,EAAO,WAAU,qCAAsC,CAAA,CAElD;YA3BF,EAAa,GA2BX,CACT;SACE,CAAA,CACL,EAAA,CAAA,CAED;WAIR,kBAAC,IAAD;QACE,cAAc;QACd,qBACE,KACA,EACE,EACE,kBAAkB,EAAqB,MACvC,EAAqB,oBAAoB,GAAgB,GAC1D,CACF;QAEH,iBACE,KACA,EACE,EACE,kBAAkB,EAAqB,GAAG,WAC1C,EAAqB,oBAAoB,GAAgB,GAC1D,CACF;QAEH,gBACE,KAAkB,EAAW,mCAAmC,EAAe,KAAK;QAEtF,sBAAsB,EAAW,SAAS;QAC1C,CAAA,CACE;;MAGL,KACC,kBAAC,IAAD;OACkB;OAChB,kBAAkB,EAAe;OACjC,oBAAoB;OACpB,cAAc;OACd,UAAU;OACC;OACX,CAAA;MAEA;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAqC;OAAkB,CAAA,EACrE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,KAAkB,EAAW,iBAAiB,EAAe,KAAK;SACjF,UAAU,CAAC;SACX,WAAU;mBAJZ,CAME,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,IAAD,EAAS,WAAU,2BAA4B,CAAA;UAC3C,CAAA,EAAA,oBAEC;;QACT,kBAAC,UAAD;SACE,MAAK;SACL,eACE,KAAkB,EAAW,uBAAuB,EAAe,KAAK;SAE1E,UAAU,CAAC;SACX,WAAU;mBANZ,CAQE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAQ,WAAU,mCAAoC,CAAA;UAClD,CAAA,EAAA,iBAEC;;QACT,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,oBAAoB;SAC9C,WAAU;mBAHZ,CAKE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,IAAD,EAAO,WAAU,mCAAoC,CAAA;UACjD,CAAA,EAAA,mBAEC;;QACT,kBAAC,UAAD;SACE,MAAK;SACL,eACE,KAAkB,EAAW,2BAA2B,EAAe,KAAK;SAE9E,UAAU,CAAC;SACX,WAAU;mBANZ,CAQE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,IAAD,EAAU,WAAU,6BAA8B,CAAA;UAC9C,CAAA,EAAA,kBAEC;;QACL;SACE;SAGT,CAAC,KACA,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,IAAD,EAAU,WAAU,uBAAwB,CAAA;QACxC,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAAgC;QAAgB,CAAA,EAC9D,kBAAC,KAAD;QAAG,WAAU;kBAAqC;QAE9C,CAAA,CACA,EAAA,CAAA,CACF;UACN,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,SAAS;OACnC,WAAU;iBAHZ,CAIC,gBAEC,kBAAC,IAAD,EAAY,WAAU,UAAW,CAAA,CAC1B;SACD;QAER;OACF;;GAGN,kBAAC,IAAD;IACE,QAAQ;IACR,iBAAiB;IACjB,cAAc,MAAY,EAAW,WAAW,IAAU;IAC1D,kBAAkB,MAAY,EAAW,WAAW,EAAQ,kBAAkB;IAC9E,uBAAuB,EAAW,UAAU;IAC5C,aAAa;IACb,UAAU;IACV,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;IAClB,WAAW;IACX,WAAW;IACX,CAAA;GAGF,kBAAC,IAAD,EAAsB,CAAA;GAGtB,kBAAC,GAAD;IACE,QAAQ;IACR,eAAe;AAEb,KADA,EAAqB,GAAM,EAC3B,EAAe,KAAK;;IAEtB,UAAU;IACV,cAAc;IACd,OAAO;IACP,CAAA;GAGD,KACC,kBAAC,IAAD;IACE,QAAQ;IACR,eAAe,EAAyB,GAAM;IAC9C,kBAAkB,EAAe;IACjC,UAAU,EAAe;IACzB,SAAS,EAAe,mBAAmB,IAAI;IAC/C,WAAW;IACX,CAAA;GAEA;MA3dC,kBAAC,GAAD,EAAc,SAAQ,0DAA2D,CAAA"}
|
|
1
|
+
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Billing Overview Page\n * Main dashboard for billing showing accounts, subscriptions, payment methods, and addons\n */\n\nimport { type FC, useState, useCallback, useEffect } from 'react';\nimport {\n Plus,\n Building2,\n RefreshCw,\n Loader2,\n Wallet,\n ChevronDown,\n Check,\n Activity,\n Receipt,\n ArrowRight,\n Sparkles,\n Crown,\n Coins,\n} from 'lucide-react';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { StatCard } from '../../../shared/components/StatCard';\nimport { EmptyState } from '../../../shared/components/EmptyState';\nimport { ServerError } from '../../../shared/components/ServerError';\nimport { AccessDenied } from '../../../shared/components/AccessDenied';\nimport { isServerError } from '../../../shared/utils';\nimport { useBillingOverviewData, useDashboardMutations } from '../hooks';\nimport {\n BillingAccountCard,\n PaymentMethodsSection,\n ActiveSubscriptionCard,\n RecommendedAddonsSection,\n CreateBillingAccountModal,\n AddPaymentMethodModal,\n} from '../components';\nimport { useAddonCartStore } from '../../addons/store/addonCartStore';\nimport { FloatingCartDrawer } from '../../addons/components/FloatingCartDrawer';\nimport {\n getStoredBillingAccountId,\n setStoredBillingAccountId,\n} from '../../../hooks/useBillingAccountSelection';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport type {\n BillingAccount,\n CreateBillingAccountInput,\n SetBillingAddressInput,\n Addon,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const OverviewPage: 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 { navigate, basePath, orgId, productId } = useBilling();\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n\n const [selectedAccountId, setSelectedAccountIdState] = useState<string | null>(() =>\n getStoredBillingAccountId(orgId)\n );\n\n const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [isAccountSelectorOpen, setIsAccountSelectorOpen] = useState(false);\n const [isAddPaymentModalOpen, setIsAddPaymentModalOpen] = useState(false);\n const [selectedSubscriptionIndex, setSelectedSubscriptionIndex] = useState(0);\n const [isSubscriptionSelectorOpen, setIsSubscriptionSelectorOpen] = useState(false);\n\n const {\n billingAccounts,\n currentAccount,\n paymentMethods,\n activeSubscriptions,\n hasActiveSubscription,\n recommendedAddons,\n isLoading,\n error,\n refetchAll,\n } = useBillingOverviewData(selectedAccountId, productId);\n\n const currentAccountId = currentAccount?.id;\n const isLoadingAddons = isLoading;\n\n const setSelectedAccountId = useCallback(\n (accountId: string | null) => {\n setSelectedAccountIdState(accountId);\n setStoredBillingAccountId(orgId, accountId);\n },\n [orgId]\n );\n\n // Addon cart store for add to cart functionality\n const { addToCart, isInCart, getCartItem, updateQuantity, removeFromCart } = useAddonCartStore();\n\n const {\n createBillingAccount,\n setBillingAddress,\n setDefaultPaymentMethod,\n deletePaymentMethod,\n isCreatingAccount,\n } = useDashboardMutations();\n\n // Reset subscription selector when account changes or subscriptions change\n useEffect(() => {\n setSelectedSubscriptionIndex(0);\n setIsSubscriptionSelectorOpen(false);\n }, [currentAccountId, activeSubscriptions.length]);\n\n useEffect(() => {\n if (selectedAccountId && billingAccounts.length > 0) {\n const accountExists = billingAccounts.some((account) => account.id === selectedAccountId);\n if (!accountExists) {\n setSelectedAccountId(null);\n }\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n useEffect(() => {\n if (!selectedAccountId && billingAccounts.length > 0) {\n const defaultAccount =\n billingAccounts.find((account) => account.isDefault) ?? billingAccounts[0];\n setSelectedAccountId(defaultAccount.id);\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n // Derive selected subscription from active subscriptions\n const selectedSubscription =\n activeSubscriptions[selectedSubscriptionIndex] || activeSubscriptions[0] || null;\n\n // Helper for navigation - properly joins basePath and path\n const navigateTo = useCallback(\n (path: string) => {\n // Handle path joining to avoid double slashes (e.g., / + /plans = //plans)\n let fullPath = path;\n if (basePath && basePath !== '/') {\n // Remove trailing slash from basePath and leading slash from path if needed\n const base = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n fullPath = `${base}${suffix}`;\n }\n navigate(fullPath);\n },\n [navigate, basePath]\n );\n\n // Handlers\n const handleCreateAccount = useCallback(\n async (\n input: CreateBillingAccountInput,\n address?: Omit<SetBillingAddressInput, 'billingAccountId'>\n ) => {\n setCreateError(null);\n try {\n const newAccount = await createBillingAccount(input);\n\n // If address data provided, set the billing address\n if (address && newAccount.id) {\n await setBillingAddress({\n billingAccountId: newAccount.id,\n ...address,\n });\n }\n\n setIsCreateModalOpen(false);\n setSelectedAccountId(newAccount.id);\n emit('billing.billing_account.created', {\n route: '/billing/overview',\n entityId: newAccount.id,\n source: 'overview',\n });\n await refetchAll();\n } catch (err) {\n setCreateError(err instanceof Error ? err.message : 'Failed to create billing account');\n }\n },\n [createBillingAccount, emit, refetchAll, setBillingAddress, setSelectedAccountId]\n );\n\n const handleAddPaymentMethod = useCallback(() => {\n setIsAddPaymentModalOpen(true);\n }, []);\n\n // Helper to refetch the current account data\n const refetchCurrentAccount = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n const handlePaymentMethodAdded = useCallback(async () => {\n emit('billing.payment_method.added', {\n route: '/billing/overview',\n entityId: currentAccountId,\n source: 'overview',\n });\n await refetchCurrentAccount();\n }, [currentAccountId, emit, refetchCurrentAccount]);\n\n const handleSetDefaultPaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await setDefaultPaymentMethod(id, currentAccountId);\n emit('billing.payment_method.default_set', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, emit, setDefaultPaymentMethod, refetchCurrentAccount]\n );\n\n const handleDeletePaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await deletePaymentMethod(id, currentAccountId);\n emit('billing.payment_method.deleted', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, deletePaymentMethod, emit, refetchCurrentAccount]\n );\n\n const handleSelectAccount = useCallback(\n (account: BillingAccount) => {\n setSelectedAccountId(account.id);\n setIsAccountSelectorOpen(false);\n },\n [setSelectedAccountId]\n );\n\n const handleRefresh = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n // Handler for adding addon to cart\n const handleAddToCart = useCallback(\n (addon: Addon) => {\n addToCart(addon);\n },\n [addToCart]\n );\n\n // Handler to check if addon is in cart\n const handleIsInCart = useCallback(\n (addonId: string) => {\n return isInCart(addonId);\n },\n [isInCart]\n );\n\n // Handler to get cart quantity for addon\n const handleGetCartQuantity = useCallback(\n (addonId: string) => {\n const item = getCartItem(addonId);\n return item?.quantity || 0;\n },\n [getCartItem]\n );\n\n // Handler to update quantity in cart\n const handleUpdateQuantity = useCallback(\n (addonId: string, quantity: number) => {\n if (quantity <= 0) {\n removeFromCart(addonId);\n } else {\n updateQuantity(addonId, quantity);\n }\n },\n [updateQuantity, removeFromCart]\n );\n\n // Handler to remove from cart\n const handleRemoveFromCart = useCallback(\n (addonId: string) => {\n removeFromCart(addonId);\n },\n [removeFromCart]\n );\n\n // Permission check - must be after all hooks are defined\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view billing information.\" />;\n }\n\n // Error state\n if (error && isServerError(error) && !isLoading) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load billing overview. The server might be down or experiencing issues.\"\n onRetry={handleRefresh}\n showRetry\n />\n </div>\n );\n }\n\n // Loading state\n if (isLoading && !currentAccount) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"flex flex-col items-center gap-3\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.overview.loadingInfo', 'Loading billing information...')}\n </p>\n </div>\n </div>\n );\n }\n\n // No billing accounts state\n if (!isLoading && billingAccounts.length === 0) {\n return (\n <div className=\"p-6\">\n <header className=\"mb-8\">\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.overview.title', 'Billing Overview')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr(\n 'billing.overview.emptySetupDescription',\n 'Set up billing to manage subscriptions, payments, and invoices'\n )}\n </p>\n </header>\n <EmptyState\n icon={<Building2 className=\"size-6 text-muted-foreground\" />}\n title={tr('billing.overview.emptyTitle', 'No billing account')}\n description={tr(\n 'billing.overview.emptyDescription',\n 'Create a billing account to start managing your subscriptions and payments'\n )}\n action={\n permissions.canManageBillingAccount ? (\n <button\n type=\"button\"\n onClick={() => setIsCreateModalOpen(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('billing.overview.createBillingAccount', 'Create Billing Account')}\n </button>\n ) : undefined\n }\n />\n\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n </div>\n );\n }\n\n return (\n <div className=\"p-4 sm:p-6 space-y-6 sm:space-y-8\">\n {/* Header */}\n <header className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.overview.title', 'Billing Overview')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr(\n 'billing.overview.description',\n 'Manage your billing accounts, subscriptions, and payment methods'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleRefresh}\n className=\"p-2 rounded-lg border border-border hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n aria-label={tr('billing.overview.refresh', 'Refresh')}\n >\n <RefreshCw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} />\n </button>\n {permissions.canManageBillingAccount && billingAccounts.length > 0 && (\n <button\n type=\"button\"\n onClick={() => setIsCreateModalOpen(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"size-4\" />\n <span className=\"hidden sm:inline\">\n {tr('billing.overview.newAccount', 'New Account')}\n </span>\n </button>\n )}\n </div>\n </header>\n\n {/* Account Selector (if multiple accounts) */}\n {billingAccounts.length > 1 && (\n <div className=\"relative\" data-tour=\"billing-overview-account-selector\">\n <button\n type=\"button\"\n onClick={() => setIsAccountSelectorOpen(!isAccountSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border rounded-lg bg-card hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-lg bg-primary/10 flex items-center justify-center\">\n <Building2 className=\"size-4 text-primary\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-foreground\">\n {currentAccount?.name || tr('billing.overview.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-muted-foreground\">{currentAccount?.email}</p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-muted-foreground transition-transform ${isAccountSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isAccountSelectorOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsAccountSelectorOpen(false)} />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto\">\n {billingAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => handleSelectAccount(account)}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-muted transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-8 rounded-lg bg-muted flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-4 text-muted-foreground\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-foreground truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">{account.email}</p>\n </div>\n </div>\n {currentAccount?.id === account.id && (\n <Check className=\"size-4 text-primary flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Stats Row */}\n {currentAccount && (\n <div\n className=\"grid grid-cols-1 xs:grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4\"\n data-tour=\"billing-overview-stats-row\"\n >\n <StatCard\n label={tr('billing.overview.stats.creditBalance', 'Credit Balance')}\n value={`${(currentAccount.creditAmount || 0).toLocaleString()} Credits`}\n icon={<Wallet className=\"size-5\" />}\n />\n <StatCard\n label={tr('billing.overview.stats.activePlans', 'Active Plans')}\n value={\n activeSubscriptions.length > 0\n ? activeSubscriptions.length === 1\n ? activeSubscriptions[0]?.plan?.name || '1 Plan'\n : `${activeSubscriptions.length} ${tr('billing.overview.stats.plansSuffix', 'Plans')}`\n : tr('billing.overview.stats.none', 'None')\n }\n icon={<RefreshCw className=\"size-5\" />}\n />\n <StatCard\n label={tr('billing.overview.stats.paymentMethods', 'Payment Methods')}\n value={paymentMethods.length}\n icon={<Building2 className=\"size-5\" />}\n />\n <StatCard\n label={tr('billing.overview.stats.paymentCurrency', 'Payment Currency')}\n value={currentAccount.currency || 'USD'}\n icon={<Building2 className=\"size-5\" />}\n />\n </div>\n )}\n\n {/* Main Content Grid */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Left Column - Billing Account & Subscription */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Billing Account Card */}\n {currentAccount && (\n <BillingAccountCard\n dataTour=\"billing-overview-billing-account-card\"\n account={currentAccount}\n isDefault={currentAccount.isDefault ?? false}\n onEdit={() =>\n currentAccount && navigateTo(`/settings?billingAccountId=${currentAccount.id}`)\n }\n onViewTransactions={() => navigateTo(`/transactions/${currentAccount.id}`)}\n onViewCreditTransactions={() =>\n navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n />\n )}\n\n {/* Active Subscriptions */}\n <div className=\"space-y-3\">\n {/* Subscription Selector (if multiple subscriptions) */}\n {activeSubscriptions.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsSubscriptionSelectorOpen(!isSubscriptionSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border rounded-lg bg-card hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-lg bg-primary/10 flex items-center justify-center\">\n <Crown className=\"size-4 text-primary\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-foreground\">\n {selectedSubscription?.plan?.name ||\n tr('billing.overview.selectSubscription', 'Select Subscription')}\n {selectedSubscription?.plan?.product?.name && (\n <span className=\"text-muted-foreground font-normal\">\n {' '}\n · {selectedSubscription.plan.product.name}\n </span>\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {activeSubscriptions.length}{' '}\n {tr('billing.overview.activeSubscriptions', 'active subscriptions')}\n </p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-muted-foreground transition-transform ${isSubscriptionSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isSubscriptionSelectorOpen && (\n <>\n <div\n className=\"fixed inset-0 z-10\"\n onClick={() => setIsSubscriptionSelectorOpen(false)}\n />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto\">\n {activeSubscriptions.map((subscription, index) => (\n <button\n type=\"button\"\n key={subscription.id}\n onClick={() => {\n setSelectedSubscriptionIndex(index);\n setIsSubscriptionSelectorOpen(false);\n }}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-muted transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-8 rounded-lg bg-muted flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-4 text-muted-foreground\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-foreground truncate\">\n {subscription.plan?.name ||\n tr('billing.overview.subscription', 'Subscription')}\n </p>\n <p className=\"text-xs text-muted-foreground truncate\">\n {subscription.plan?.product?.name ||\n tr('billing.overview.unknownProduct', 'Unknown Product')}{' '}\n · {tr('billing.overview.ends', 'Ends')}{' '}\n {new Date(subscription.endDate).toLocaleDateString()}\n </p>\n </div>\n </div>\n {selectedSubscriptionIndex === index && (\n <Check className=\"size-4 text-primary flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Single Subscription Card */}\n <ActiveSubscriptionCard\n dataTour=\"billing-overview-active-subscription-card\"\n subscription={selectedSubscription}\n onViewDetails={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onUpgrade={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}/upgrade`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onManage={() =>\n currentAccount && navigateTo(`/subscriptions?billingAccountId=${currentAccount.id}`)\n }\n onPurchasePlan={() => navigateTo('/plans')}\n />\n </div>\n\n {/* Payment Methods */}\n {currentAccount && (\n <PaymentMethodsSection\n dataTour=\"billing-overview-payment-methods-section\"\n paymentMethods={paymentMethods}\n billingAccountId={currentAccount.id}\n onAddPaymentMethod={handleAddPaymentMethod}\n onSetDefault={handleSetDefaultPaymentMethod}\n onDelete={handleDeletePaymentMethod}\n isLoading={isLoading}\n />\n )}\n </div>\n\n {/* Right Column - Quick Actions & Info */}\n <div className=\"space-y-6\">\n {/* Quick Actions */}\n <section\n className=\"border border-border rounded-lg bg-card p-4\"\n data-tour=\"billing-overview-quick-actions\"\n >\n <h3 className=\"font-semibold text-foreground mb-4\">Quick Actions</h3>\n <div className=\"space-y-2\">\n <button\n type=\"button\"\n onClick={() => currentAccount && navigateTo(`/transactions/${currentAccount.id}`)}\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-8 rounded bg-status-info-bg-subtle flex items-center justify-center\">\n <Receipt className=\"size-4 text-accent-blue\" />\n </div>\n View Transactions\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-8 rounded bg-status-warning-bg-subtle flex items-center justify-center\">\n <Wallet className=\"size-4 text-status-warning-text\" />\n </div>\n Credit History\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/checkout/credits')}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <div className=\"size-8 rounded bg-status-success-bg-subtle flex items-center justify-center\">\n <Coins className=\"size-4 text-status-success-text\" />\n </div>\n Purchase Credits\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/usage?billingAccountId=${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-foreground hover:bg-muted rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-8 rounded bg-accent-violet-subtle flex items-center justify-center\">\n <Activity className=\"size-4 text-accent-violet\" />\n </div>\n Usage Analytics\n </button>\n </div>\n </section>\n\n {/* Get Started (if no subscription) */}\n {!hasActiveSubscription && (\n <section className=\"border border-border rounded-lg bg-gradient-to-br from-primary/5 to-primary/10 p-4\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Sparkles className=\"size-5 text-primary\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-foreground\">Get Started</h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Choose a plan that fits your needs to unlock all features\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"w-full flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </button>\n </section>\n )}\n </div>\n </div>\n\n {/* Recommended Addons (only if has subscription) - Limited to 3 with cart support */}\n <RecommendedAddonsSection\n dataTour=\"billing-overview-recommended-addons\"\n addons={recommendedAddons}\n hasSubscription={hasActiveSubscription}\n onViewAddon={(addonId) => navigateTo(`/addons/${addonId}`)}\n onPurchaseAddon={(addonId) => navigateTo(`/addons/${addonId}?action=purchase`)}\n onViewAllAddons={() => navigateTo('/addons')}\n onAddToCart={handleAddToCart}\n isInCart={handleIsInCart}\n getCartQuantity={handleGetCartQuantity}\n onUpdateQuantity={handleUpdateQuantity}\n onRemoveFromCart={handleRemoveFromCart}\n maxAddons={3}\n isLoading={isLoadingAddons}\n />\n\n {/* Floating Cart Drawer - shows when items are in cart */}\n <FloatingCartDrawer />\n\n {/* Create Billing Account Modal */}\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n\n {/* Add Payment Method Modal */}\n {currentAccount && (\n <AddPaymentMethodModal\n isOpen={isAddPaymentModalOpen}\n onClose={() => setIsAddPaymentModalOpen(false)}\n billingAccountId={currentAccount.id}\n currency={currentAccount.currency}\n country={currentAccount.billingAddresses?.[0]?.country}\n onSuccess={handlePaymentMethodAdded}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -2,120 +2,121 @@ import { ServerError as e } from "../../../shared/components/ServerError.js";
|
|
|
2
2
|
import "../../../shared/components/index.js";
|
|
3
3
|
import { formatCurrency as t, formatPlanDuration as n, formatPlanDurationLabel as r } from "../../../shared/utils/format.js";
|
|
4
4
|
import { isServerError as i } from "../../../shared/utils/error.js";
|
|
5
|
+
import { getPlanFeatureQuantity as a } from "../../../shared/utils/planFeatureQuantity.js";
|
|
5
6
|
import "../../../shared/utils/index.js";
|
|
6
|
-
import { useBillingNavigate as
|
|
7
|
-
import { usePlan as
|
|
7
|
+
import { useBillingNavigate as o } from "../../../hooks/useBillingNavigate.js";
|
|
8
|
+
import { usePlan as s } from "../hooks/usePlans.js";
|
|
8
9
|
import "../hooks/index.js";
|
|
9
|
-
import { useMemo as
|
|
10
|
-
import { useParams as
|
|
11
|
-
import { jsx as
|
|
12
|
-
import { useI18n as
|
|
10
|
+
import { useMemo as c } from "react";
|
|
11
|
+
import { useParams as l } from "react-router-dom";
|
|
12
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
13
|
+
import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
14
|
//#region src/billing/modules/plans/pages/PlanBrowseDetailPage.tsx
|
|
14
|
-
function
|
|
15
|
+
function p(e) {
|
|
15
16
|
let t = /* @__PURE__ */ new Map();
|
|
16
17
|
for (let n of e) {
|
|
17
18
|
if (!n.quota) continue;
|
|
18
|
-
let e = n.quota.id, r = n.quota.limits
|
|
19
|
-
o ? o.totalValue +=
|
|
19
|
+
let e = n.quota.id, r = n.quota.limits?.type ?? "unknown", i = a(n), o = t.get(e);
|
|
20
|
+
o ? o.totalValue += i : t.set(e, {
|
|
20
21
|
quotaId: e,
|
|
21
22
|
name: n.quota.name ?? "Unknown Quota",
|
|
22
23
|
quotaType: n.quota.quotaType ?? "",
|
|
23
|
-
limitType:
|
|
24
|
-
totalValue:
|
|
24
|
+
limitType: r,
|
|
25
|
+
totalValue: i
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
return Array.from(t.values());
|
|
28
29
|
}
|
|
29
|
-
var
|
|
30
|
-
let { t:
|
|
31
|
-
let n =
|
|
30
|
+
var m = () => {
|
|
31
|
+
let { t: a } = f(), m = (e, t) => {
|
|
32
|
+
let n = a(e);
|
|
32
33
|
return n === e ? t : n;
|
|
33
|
-
}, { planId: h } =
|
|
34
|
-
return v ? /* @__PURE__ */
|
|
34
|
+
}, { planId: h } = l(), g = o(), { plan: _, isLoading: v, error: y, refetch: b } = s(h), x = c(() => p(_?.features || []), [_?.features]);
|
|
35
|
+
return v ? /* @__PURE__ */ d("div", {
|
|
35
36
|
className: "space-y-6 p-6",
|
|
36
|
-
children: [/* @__PURE__ */
|
|
37
|
+
children: [/* @__PURE__ */ d("div", {
|
|
37
38
|
className: "flex items-center gap-4",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
39
|
-
}), /* @__PURE__ */
|
|
39
|
+
children: [/* @__PURE__ */ u("div", { className: "size-10 bg-muted animate-pulse rounded" }), /* @__PURE__ */ u("div", { className: "h-8 w-48 bg-muted animate-pulse rounded" })]
|
|
40
|
+
}), /* @__PURE__ */ d("div", {
|
|
40
41
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
41
|
-
children: [/* @__PURE__ */
|
|
42
|
+
children: [/* @__PURE__ */ u("div", {
|
|
42
43
|
className: "lg:col-span-2 space-y-6",
|
|
43
|
-
children: /* @__PURE__ */
|
|
44
|
+
children: /* @__PURE__ */ d("div", {
|
|
44
45
|
className: "border border-border rounded-lg p-6 space-y-4",
|
|
45
|
-
children: [/* @__PURE__ */
|
|
46
|
+
children: [/* @__PURE__ */ u("div", { className: "h-6 w-32 bg-muted animate-pulse rounded" }), /* @__PURE__ */ u("div", { className: "h-4 w-full bg-muted animate-pulse rounded" })]
|
|
46
47
|
})
|
|
47
|
-
}), /* @__PURE__ */
|
|
48
|
+
}), /* @__PURE__ */ d("div", {
|
|
48
49
|
className: "border border-border rounded-lg p-6 space-y-4",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: [/* @__PURE__ */ u("div", { className: "h-6 w-24 bg-muted animate-pulse rounded" }), /* @__PURE__ */ u("div", { className: "h-10 w-full bg-muted animate-pulse rounded" })]
|
|
50
51
|
})]
|
|
51
52
|
})]
|
|
52
|
-
}) : y && i(y) ? /* @__PURE__ */
|
|
53
|
+
}) : y && i(y) ? /* @__PURE__ */ u("div", {
|
|
53
54
|
className: "p-6",
|
|
54
|
-
children: /* @__PURE__ */
|
|
55
|
+
children: /* @__PURE__ */ u(e, {
|
|
55
56
|
title: m("billing.plans.serverUnavailable", "Server Unavailable"),
|
|
56
57
|
message: m("billing.plans.unableToLoadPlanDetails", "Unable to load plan details."),
|
|
57
58
|
onRetry: () => b(),
|
|
58
59
|
showRetry: !0
|
|
59
60
|
})
|
|
60
|
-
}) : _ ? /* @__PURE__ */
|
|
61
|
+
}) : _ ? /* @__PURE__ */ d("div", {
|
|
61
62
|
className: "space-y-6 p-6",
|
|
62
|
-
children: [/* @__PURE__ */
|
|
63
|
+
children: [/* @__PURE__ */ d("div", {
|
|
63
64
|
className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
|
|
64
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ d("div", {
|
|
65
66
|
className: "flex items-start gap-4",
|
|
66
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ u("button", {
|
|
67
68
|
type: "button",
|
|
68
69
|
onClick: () => g("/plans"),
|
|
69
70
|
className: "p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors",
|
|
70
|
-
children: /* @__PURE__ */
|
|
71
|
+
children: /* @__PURE__ */ u("svg", {
|
|
71
72
|
className: "size-5",
|
|
72
73
|
fill: "none",
|
|
73
74
|
viewBox: "0 0 24 24",
|
|
74
75
|
stroke: "currentColor",
|
|
75
|
-
children: /* @__PURE__ */
|
|
76
|
+
children: /* @__PURE__ */ u("path", {
|
|
76
77
|
strokeLinecap: "round",
|
|
77
78
|
strokeLinejoin: "round",
|
|
78
79
|
strokeWidth: 2,
|
|
79
80
|
d: "M15 19l-7-7 7-7"
|
|
80
81
|
})
|
|
81
82
|
})
|
|
82
|
-
}), /* @__PURE__ */
|
|
83
|
+
}), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
|
|
83
84
|
className: "text-2xl font-bold text-foreground",
|
|
84
85
|
children: _.name
|
|
85
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ d("p", {
|
|
86
87
|
className: "text-sm text-muted-foreground mt-1",
|
|
87
88
|
children: [r(_.duration), " billing plan"]
|
|
88
89
|
})] })]
|
|
89
|
-
}), /* @__PURE__ */
|
|
90
|
+
}), /* @__PURE__ */ u("button", {
|
|
90
91
|
type: "button",
|
|
91
92
|
onClick: () => g(`/checkout/${_.id}`),
|
|
92
93
|
className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
93
94
|
children: "Subscribe to Plan"
|
|
94
95
|
})]
|
|
95
|
-
}), /* @__PURE__ */
|
|
96
|
+
}), /* @__PURE__ */ d("div", {
|
|
96
97
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
98
|
+
children: [/* @__PURE__ */ d("div", {
|
|
98
99
|
className: "lg:col-span-2 space-y-6",
|
|
99
|
-
children: [/* @__PURE__ */
|
|
100
|
+
children: [/* @__PURE__ */ d("div", {
|
|
100
101
|
className: "border border-border rounded-lg bg-card overflow-hidden",
|
|
101
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ u("div", {
|
|
102
103
|
className: "p-6 border-b border-border bg-muted/30",
|
|
103
|
-
children: /* @__PURE__ */
|
|
104
|
+
children: /* @__PURE__ */ u("h2", {
|
|
104
105
|
className: "text-lg font-semibold text-foreground",
|
|
105
106
|
children: m("billing.plans.pricing", "Pricing")
|
|
106
107
|
})
|
|
107
|
-
}), /* @__PURE__ */
|
|
108
|
+
}), /* @__PURE__ */ d("div", {
|
|
108
109
|
className: "p-6",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
110
|
+
children: [/* @__PURE__ */ d("div", {
|
|
110
111
|
className: "flex items-baseline gap-1",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
112
|
+
children: [/* @__PURE__ */ u("span", {
|
|
112
113
|
className: "text-4xl font-bold text-foreground",
|
|
113
114
|
children: t(_.price, _.currency)
|
|
114
|
-
}), /* @__PURE__ */
|
|
115
|
+
}), /* @__PURE__ */ d("span", {
|
|
115
116
|
className: "text-lg text-muted-foreground",
|
|
116
117
|
children: ["/ ", n(_.duration)]
|
|
117
118
|
})]
|
|
118
|
-
}), /* @__PURE__ */
|
|
119
|
+
}), /* @__PURE__ */ d("p", {
|
|
119
120
|
className: "text-sm text-muted-foreground mt-2",
|
|
120
121
|
children: [
|
|
121
122
|
m("billing.plans.billed", "Billed"),
|
|
@@ -124,11 +125,11 @@ var p = () => {
|
|
|
124
125
|
]
|
|
125
126
|
})]
|
|
126
127
|
})]
|
|
127
|
-
}), /* @__PURE__ */
|
|
128
|
+
}), /* @__PURE__ */ d("div", {
|
|
128
129
|
className: "border border-border rounded-lg bg-card overflow-hidden",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ u("div", {
|
|
130
131
|
className: "p-6 border-b border-border bg-muted/30",
|
|
131
|
-
children: /* @__PURE__ */
|
|
132
|
+
children: /* @__PURE__ */ d("h2", {
|
|
132
133
|
className: "text-lg font-semibold text-foreground",
|
|
133
134
|
children: [
|
|
134
135
|
m("billing.plans.featuresAndQuotas", "Features & Quotas"),
|
|
@@ -137,46 +138,46 @@ var p = () => {
|
|
|
137
138
|
")"
|
|
138
139
|
]
|
|
139
140
|
})
|
|
140
|
-
}), /* @__PURE__ */
|
|
141
|
+
}), /* @__PURE__ */ u("div", {
|
|
141
142
|
className: "p-6",
|
|
142
|
-
children: x.length === 0 ? /* @__PURE__ */
|
|
143
|
+
children: x.length === 0 ? /* @__PURE__ */ u("p", {
|
|
143
144
|
className: "text-sm text-muted-foreground",
|
|
144
145
|
children: m("billing.plans.noFeatures", "No features configured for this plan.")
|
|
145
|
-
}) : /* @__PURE__ */
|
|
146
|
+
}) : /* @__PURE__ */ u("ul", {
|
|
146
147
|
className: "space-y-3",
|
|
147
|
-
children: x.map((e) => /* @__PURE__ */
|
|
148
|
+
children: x.map((e) => /* @__PURE__ */ d("li", {
|
|
148
149
|
className: "flex items-start gap-3 p-3 rounded-lg bg-muted/30",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
150
|
+
children: [/* @__PURE__ */ u("div", {
|
|
150
151
|
className: "size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0",
|
|
151
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ u("svg", {
|
|
152
153
|
className: "size-4 text-status-success-text",
|
|
153
154
|
fill: "none",
|
|
154
155
|
viewBox: "0 0 24 24",
|
|
155
156
|
stroke: "currentColor",
|
|
156
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ u("path", {
|
|
157
158
|
strokeLinecap: "round",
|
|
158
159
|
strokeLinejoin: "round",
|
|
159
160
|
strokeWidth: 2,
|
|
160
161
|
d: "M5 13l4 4L19 7"
|
|
161
162
|
})
|
|
162
163
|
})
|
|
163
|
-
}), /* @__PURE__ */
|
|
164
|
+
}), /* @__PURE__ */ d("div", {
|
|
164
165
|
className: "flex-1",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
166
|
+
children: [/* @__PURE__ */ d("div", {
|
|
166
167
|
className: "flex items-center justify-between",
|
|
167
|
-
children: [/* @__PURE__ */
|
|
168
|
+
children: [/* @__PURE__ */ u("p", {
|
|
168
169
|
className: "font-medium text-foreground",
|
|
169
170
|
children: e.name
|
|
170
|
-
}), /* @__PURE__ */
|
|
171
|
+
}), /* @__PURE__ */ u("span", {
|
|
171
172
|
className: "font-semibold text-foreground",
|
|
172
173
|
children: e.totalValue.toLocaleString()
|
|
173
174
|
})]
|
|
174
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ d("div", {
|
|
175
176
|
className: "flex items-center gap-2 mt-1",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
177
|
+
children: [/* @__PURE__ */ d("span", {
|
|
177
178
|
className: "text-sm text-muted-foreground capitalize",
|
|
178
179
|
children: ["Type: ", e.limitType]
|
|
179
|
-
}), e.quotaType && /* @__PURE__ */
|
|
180
|
+
}), e.quotaType && /* @__PURE__ */ u("span", {
|
|
180
181
|
className: "px-2 py-0.5 text-xs font-medium bg-muted text-muted-foreground rounded",
|
|
181
182
|
children: e.quotaType
|
|
182
183
|
})]
|
|
@@ -186,23 +187,23 @@ var p = () => {
|
|
|
186
187
|
})
|
|
187
188
|
})]
|
|
188
189
|
})]
|
|
189
|
-
}), /* @__PURE__ */
|
|
190
|
+
}), /* @__PURE__ */ d("div", {
|
|
190
191
|
className: "space-y-6",
|
|
191
|
-
children: [/* @__PURE__ */
|
|
192
|
+
children: [/* @__PURE__ */ d("div", {
|
|
192
193
|
className: "border border-border rounded-lg bg-card overflow-hidden",
|
|
193
|
-
children: [/* @__PURE__ */
|
|
194
|
+
children: [/* @__PURE__ */ u("div", {
|
|
194
195
|
className: "p-6 border-b border-border bg-muted/30",
|
|
195
|
-
children: /* @__PURE__ */
|
|
196
|
+
children: /* @__PURE__ */ u("h2", {
|
|
196
197
|
className: "text-lg font-semibold text-foreground",
|
|
197
198
|
children: m("billing.plans.subscribeTitle", "Subscribe")
|
|
198
199
|
})
|
|
199
|
-
}), /* @__PURE__ */
|
|
200
|
+
}), /* @__PURE__ */ d("div", {
|
|
200
201
|
className: "p-6 space-y-4",
|
|
201
202
|
children: [
|
|
202
|
-
/* @__PURE__ */
|
|
203
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
203
204
|
className: "text-sm text-muted-foreground",
|
|
204
205
|
children: m("billing.plans.price", "Price")
|
|
205
|
-
}), /* @__PURE__ */
|
|
206
|
+
}), /* @__PURE__ */ d("p", {
|
|
206
207
|
className: "text-sm font-medium text-foreground mt-0.5",
|
|
207
208
|
children: [
|
|
208
209
|
t(_.price, _.currency),
|
|
@@ -210,21 +211,21 @@ var p = () => {
|
|
|
210
211
|
n(_.duration)
|
|
211
212
|
]
|
|
212
213
|
})] }),
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
214
215
|
className: "text-sm text-muted-foreground",
|
|
215
216
|
children: m("billing.plans.billingPeriod", "Billing Period")
|
|
216
|
-
}), /* @__PURE__ */
|
|
217
|
+
}), /* @__PURE__ */ u("p", {
|
|
217
218
|
className: "text-sm text-foreground mt-0.5",
|
|
218
219
|
children: r(_.duration)
|
|
219
220
|
})] }),
|
|
220
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
221
222
|
className: "text-sm text-muted-foreground",
|
|
222
223
|
children: m("billing.plans.quotasIncluded", "Quotas Included")
|
|
223
|
-
}), /* @__PURE__ */
|
|
224
|
+
}), /* @__PURE__ */ u("p", {
|
|
224
225
|
className: "text-sm font-medium text-foreground mt-0.5",
|
|
225
226
|
children: x.length
|
|
226
227
|
})] }),
|
|
227
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ u("button", {
|
|
228
229
|
type: "button",
|
|
229
230
|
onClick: () => g(`/checkout/${_.id}`),
|
|
230
231
|
className: "w-full px-4 py-2.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
@@ -232,7 +233,7 @@ var p = () => {
|
|
|
232
233
|
})
|
|
233
234
|
]
|
|
234
235
|
})]
|
|
235
|
-
}), /* @__PURE__ */
|
|
236
|
+
}), /* @__PURE__ */ u("button", {
|
|
236
237
|
type: "button",
|
|
237
238
|
onClick: () => g("/plans"),
|
|
238
239
|
className: "w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors",
|
|
@@ -240,20 +241,20 @@ var p = () => {
|
|
|
240
241
|
})]
|
|
241
242
|
})]
|
|
242
243
|
})]
|
|
243
|
-
}) : /* @__PURE__ */
|
|
244
|
+
}) : /* @__PURE__ */ u("div", {
|
|
244
245
|
className: "flex items-center justify-center h-full min-h-[400px]",
|
|
245
|
-
children: /* @__PURE__ */
|
|
246
|
+
children: /* @__PURE__ */ d("div", {
|
|
246
247
|
className: "text-center space-y-4",
|
|
247
248
|
children: [
|
|
248
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ u("h2", {
|
|
249
250
|
className: "text-lg font-semibold text-foreground",
|
|
250
251
|
children: m("billing.plans.planNotFound", "Plan not found")
|
|
251
252
|
}),
|
|
252
|
-
/* @__PURE__ */
|
|
253
|
+
/* @__PURE__ */ u("p", {
|
|
253
254
|
className: "text-sm text-muted-foreground",
|
|
254
255
|
children: y?.message || m("billing.plans.planNotFoundMessage", "The plan you are looking for does not exist.")
|
|
255
256
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ u("button", {
|
|
257
258
|
type: "button",
|
|
258
259
|
onClick: () => g("/plans"),
|
|
259
260
|
className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
@@ -264,6 +265,6 @@ var p = () => {
|
|
|
264
265
|
});
|
|
265
266
|
};
|
|
266
267
|
//#endregion
|
|
267
|
-
export {
|
|
268
|
+
export { m as PlanBrowseDetailPage };
|
|
268
269
|
|
|
269
270
|
//# sourceMappingURL=PlanBrowseDetailPage.js.map
|