@burdenoff/microfe-billing 2026.821.1 → 2026.825.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  2. package/dist/billing/BillingRoot.js.map +1 -1
  3. package/dist/billing/BillingUserRoutes.js.map +1 -1
  4. package/dist/billing/hooks/useBillingAccountSelection.js.map +1 -1
  5. package/dist/billing/hooks/useBillingEventEmitter.js.map +1 -1
  6. package/dist/billing/hooks/useBillingNavigate.js.map +1 -1
  7. package/dist/billing/hooks/useBillingPermissions.js.map +1 -1
  8. package/dist/billing/hooks/useDefaultBillingCurrency.js.map +1 -1
  9. package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
  10. package/dist/billing/modules/addons/hooks/useAddons.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  12. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  13. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  14. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  15. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  16. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  17. package/dist/billing/modules/addons/store/addonCartStore.js.map +1 -1
  18. package/dist/billing/modules/affiliate/api.js.map +1 -1
  19. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +107 -107
  20. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  21. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +80 -81
  22. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/hooks/useInvoices.js.map +1 -1
  24. package/dist/billing/modules/billing/hooks/useRefunds.js.map +1 -1
  25. package/dist/billing/modules/billing/hooks/useTransactions.js.map +1 -1
  26. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  27. package/dist/billing/modules/billing/pages/InvoicesListPage.js +1 -1
  28. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  29. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  30. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  31. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  32. package/dist/billing/modules/billing/pages/UserRefundsPage.js +127 -127
  33. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  34. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  35. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
  36. package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
  37. package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
  38. package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
  39. package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
  40. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
  41. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
  42. package/dist/billing/modules/checkout/components/StoreShippingSection.js.map +1 -1
  43. package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
  44. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  45. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
  46. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
  47. package/dist/billing/modules/checkout/hooks/useCheckout.js +0 -1
  48. package/dist/billing/modules/checkout/hooks/useCheckout.js.map +1 -1
  49. package/dist/billing/modules/checkout/hooks/useNativeCheckout.js.map +1 -1
  50. package/dist/billing/modules/checkout/hooks/usePaymentProviders.js.map +1 -1
  51. package/dist/billing/modules/checkout/hooks/usePaymentStatusPolling.js.map +1 -1
  52. package/dist/billing/modules/checkout/hooks/useStoreShipping.js.map +1 -1
  53. package/dist/billing/modules/checkout/pages/CheckoutPage.js +412 -395
  54. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  55. package/dist/billing/modules/checkout/utils/storeOrder.js.map +1 -1
  56. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  57. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  58. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  59. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  60. package/dist/billing/modules/credits/hooks/useCredits.js.map +1 -1
  61. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  62. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  63. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  64. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  65. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  66. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  67. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  68. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  69. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  70. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  71. package/dist/billing/modules/dashboard/hooks/useBillingOverviewData.js.map +1 -1
  72. package/dist/billing/modules/dashboard/hooks/useDashboard.js.map +1 -1
  73. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  74. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  75. package/dist/billing/modules/earnings/api.js.map +1 -1
  76. package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js +145 -145
  77. package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js.map +1 -1
  78. package/dist/billing/modules/earnings/pages/EarningsPage.js +133 -133
  79. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  80. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +7 -9
  81. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  82. package/dist/billing/modules/earnings/pages/PayoutsPage.js +95 -95
  83. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  84. package/dist/billing/modules/plans/constants/featuredQuotas.js.map +1 -1
  85. package/dist/billing/modules/plans/hooks/usePlans.js.map +1 -1
  86. package/dist/billing/modules/plans/hooks/useQuotas.js.map +1 -1
  87. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  88. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  89. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  90. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  91. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  92. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  93. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  94. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  95. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  96. package/dist/billing/modules/quota/hooks/useQuotaTemplates.js.map +1 -1
  97. package/dist/billing/modules/quota/pages/QuotaAssignFreePage.js.map +1 -1
  98. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  99. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  100. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  101. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  102. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  103. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  104. package/dist/billing/modules/store/pages/StoreCheckoutPage.js +5 -6
  105. package/dist/billing/modules/store/pages/StoreCheckoutPage.js.map +1 -1
  106. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  107. package/dist/billing/modules/subscriptions/hooks/useSubscriptions.js.map +1 -1
  108. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  109. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  110. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  111. package/dist/billing/modules/usage/hooks/useUsage.js +14 -9
  112. package/dist/billing/modules/usage/hooks/useUsage.js.map +1 -1
  113. package/dist/billing/modules/usage/pages/UsagePage.js +481 -453
  114. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  115. package/dist/billing/providers/BillingProvider.js.map +1 -1
  116. package/dist/billing/shared/components/AccessDenied.js.map +1 -1
  117. package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
  118. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
  119. package/dist/billing/shared/components/EmptyState.js.map +1 -1
  120. package/dist/billing/shared/components/PageHeader.js.map +1 -1
  121. package/dist/billing/shared/components/ServerError.js.map +1 -1
  122. package/dist/billing/shared/components/StatCard.js.map +1 -1
  123. package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -1
  124. package/dist/billing/shared/types/graphql.js.map +1 -1
  125. package/dist/billing/shared/ui/Card.js.map +1 -1
  126. package/dist/billing/shared/ui/MetricCard.js.map +1 -1
  127. package/dist/billing/shared/ui/ProgressBar.js.map +1 -1
  128. package/dist/billing/shared/ui/StatusPill.js.map +1 -1
  129. package/dist/billing/shared/utils/directBillingGraphql.js.map +1 -1
  130. package/dist/billing/shared/utils/error.js.map +1 -1
  131. package/dist/billing/shared/utils/format.js.map +1 -1
  132. package/dist/billing/shared/utils/gatewayGraphql.js.map +1 -1
  133. package/dist/billing/shared/utils/navigation.js.map +1 -1
  134. package/dist/billing/shared/utils/paymentTheme.js.map +1 -1
  135. package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -1
  136. package/dist/billing/shared/utils/status.js.map +1 -1
  137. package/dist/billing/shared/utils/tokens.js.map +1 -1
  138. package/dist/generated/global-operations.js +5 -1
  139. package/dist/generated/global-operations.js.map +1 -1
  140. package/dist/generated/global-types.js.map +1 -1
  141. package/dist/generated/wspace-operations.js +1 -1
  142. package/dist/generated/wspace-operations.js.map +1 -1
  143. package/dist/node_modules/@capacitor/core/dist/index.js +1 -1
  144. package/dist/node_modules/@capacitor/core/dist/index.js.map +1 -1
  145. package/package.json +13 -10
@@ -1 +1 @@
1
- {"version":3,"file":"PaymentMethodsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/PaymentMethodsSection.tsx"],"sourcesContent":["/**\n * Payment Methods Section Component\n * Displays and manages payment methods\n * Note: Provider config is fetched in the modal, not here\n */\n\nimport { type FC, useState } from 'react';\nimport { CreditCard, Plus, MoreVertical, Trash2, Star, Loader2, CheckCircle2 } from 'lucide-react';\nimport type { PaymentMethod, PaymentMethodType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\ninterface PaymentMethodsSectionProps {\n paymentMethods: PaymentMethod[];\n billingAccountId: string;\n onAddPaymentMethod: () => void;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\n// Card brand icons mapping\nconst CardBrandIcon: FC<{ brand?: string; className?: string }> = ({ brand, className }) => {\n const brandLower = brand?.toLowerCase() || '';\n\n // In a real app, you'd use actual brand SVGs/images\n const brandColors: Record<string, string> = {\n visa: 'text-accent-blue',\n mastercard: 'text-accent-orange',\n amex: 'text-accent-blue',\n discover: 'text-accent-orange',\n default: 'text-text-secondary',\n };\n\n const color = brandColors[brandLower] || brandColors.default;\n\n return (\n <div className={`w-10 h-6 rounded bg-bg-sunken flex items-center justify-center ${className}`}>\n <CreditCard className={`size-5 ${color}`} />\n </div>\n );\n};\n\nconst PaymentMethodCard: FC<{\n method: PaymentMethod;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n}> = ({ method, onSetDefault, onDelete }) => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [isSettingDefault, setIsSettingDefault] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);\n\n const handleSetDefault = async () => {\n setIsMenuOpen(false);\n setIsSettingDefault(true);\n try {\n await onSetDefault(method.id);\n } finally {\n setIsSettingDefault(false);\n }\n };\n\n const handleDeleteRequest = async () => {\n setIsMenuOpen(false);\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete payment method',\n message: 'Are you sure you want to remove this payment method?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === true) {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n return;\n }\n if (confirmed === null) {\n setIsConfirmingDelete(true);\n }\n };\n\n const handleDelete = async () => {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setIsConfirmingDelete(false);\n }\n };\n\n const getMethodTypeLabel = (type: PaymentMethodType) => {\n const labels: Record<PaymentMethodType, string> = {\n CREDIT_CARD: 'Credit Card',\n DEBIT_CARD: 'Debit Card',\n BANK_TRANSFER: 'UPI',\n PAYPAL: 'PayPal',\n CRYPTO: 'Crypto',\n WALLET: 'Wallet',\n NETBANKING: 'Netbanking',\n };\n return labels[type] || type;\n };\n\n const getExpiryText = () => {\n if (method.expiryMonth && method.expiryYear) {\n const monthStr = String(method.expiryMonth).padStart(2, '0');\n const yearStr = String(method.expiryYear).slice(-2);\n return `${monthStr}/${yearStr}`;\n }\n return null;\n };\n\n const isExpired = () => {\n if (!method.expiryMonth || !method.expiryYear) return false;\n const now = new Date();\n const expiry = new Date(method.expiryYear, method.expiryMonth, 0);\n return expiry < now;\n };\n\n const getProviderBadge = () => {\n if (method.stripePaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent-violet-subtle text-accent-violet\">\n Stripe\n </span>\n );\n }\n if (method.razorpayPaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-status-info-bg-subtle text-status-info-text\">\n Razorpay\n </span>\n );\n }\n return null;\n };\n\n return (\n <div\n className={`\n relative p-4 border rounded-lg transition-all overflow-visible\n ${method.isDefault ? 'border-border-strong bg-[var(--color-accent-soft)]' : 'border-border-subtle bg-bg-surface'}\n ${isDeleting ? 'opacity-50' : ''}\n `}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <CardBrandIcon brand={method.brand} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium text-text-primary\">\n {method.brand || getMethodTypeLabel(method.type)} •••• {method.last4 || '****'}\n </p>\n {method.isDefault && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-soft)] text-text-link\">\n <Star className=\"size-2.5 fill-current\" />\n Default\n </span>\n )}\n {getProviderBadge()}\n </div>\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n {getExpiryText() && (\n <>\n <span className={isExpired() ? 'text-status-error-text' : ''}>\n Expires {getExpiryText()}\n </span>\n {isExpired() && <span className=\"text-status-error-text text-xs\">(Expired)</span>}\n </>\n )}\n {method.isVerified && (\n <span className=\"flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"size-3\" />\n Verified\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Actions Menu */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"p-2 rounded-button hover:bg-bg-sunken text-text-secondary hover:text-text-primary transition-colors\"\n disabled={isSettingDefault || isDeleting}\n aria-label=\"Payment method options\"\n >\n {isSettingDefault || isDeleting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <MoreVertical className=\"size-4\" />\n )}\n </button>\n\n {isMenuOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsMenuOpen(false)} />\n <div className=\"absolute top-full right-0 mt-1 min-w-[140px] rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3 z-20 overflow-hidden whitespace-nowrap\">\n {!method.isDefault && (\n <button\n type=\"button\"\n onClick={handleSetDefault}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-text-primary hover:bg-bg-sunken transition-colors text-left\"\n >\n <Star className=\"size-4 flex-shrink-0\" />\n Set as default\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleDeleteRequest}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-status-error-text hover:bg-status-error-bg-subtle transition-colors text-left\"\n >\n <Trash2 className=\"size-4 flex-shrink-0\" />\n Remove\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n {isConfirmingDelete && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3\">\n <p className=\"text-sm font-medium text-status-error-text\">\n Remove this payment method from your billing account?\n </p>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsConfirmingDelete(false)}\n className=\"rounded-button border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"rounded-button bg-action-danger-bg px-3 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-danger-bgHover disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isDeleting ? 'Removing…' : 'Remove'}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const PaymentMethodsSection: FC<PaymentMethodsSectionProps> = ({\n paymentMethods,\n billingAccountId: _billingAccountId,\n onAddPaymentMethod,\n onSetDefault,\n onDelete,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\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 // billingAccountId is available for future use\n void _billingAccountId;\n\n const hasPaymentMethods = paymentMethods.length > 0;\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.paymentMethods', 'Payment Methods')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Manage your payment methods for subscriptions and purchases\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onAddPaymentMethod}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n <Plus className=\"size-4\" />\n Add Method\n </button>\n </header>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n ) : hasPaymentMethods ? (\n <div className=\"space-y-3 overflow-visible\">\n {paymentMethods\n .filter((method): method is PaymentMethod => method !== null && method !== undefined)\n .map((method) => (\n <PaymentMethodCard\n key={method.id}\n method={method}\n onSetDefault={onSetDefault}\n onDelete={onDelete}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-8 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 <CreditCard className=\"size-6 text-text-secondary\" />\n </div>\n <h4 className=\"text-base font-medium text-text-primary\">No payment methods</h4>\n <p className=\"text-sm text-text-secondary mt-1 text-center max-w-sm\">\n Add a payment method to enable automatic payments for your subscriptions\n </p>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PaymentMethodsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/PaymentMethodsSection.tsx"],"sourcesContent":["/**\n * Payment Methods Section Component\n * Displays and manages payment methods\n * Note: Provider config is fetched in the modal, not here\n */\n\nimport { type FC, useState } from 'react';\nimport { CreditCard, Plus, MoreVertical, Trash2, Star, Loader2, CheckCircle2 } from 'lucide-react';\nimport type { PaymentMethod, PaymentMethodType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\ninterface PaymentMethodsSectionProps {\n paymentMethods: PaymentMethod[];\n billingAccountId: string;\n onAddPaymentMethod: () => void;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\n// Card brand icons mapping\nconst CardBrandIcon: FC<{ brand?: string; className?: string }> = ({ brand, className }) => {\n const brandLower = brand?.toLowerCase() || '';\n\n // In a real app, you'd use actual brand SVGs/images\n const brandColors: Record<string, string> = {\n visa: 'text-accent-blue',\n mastercard: 'text-accent-orange',\n amex: 'text-accent-blue',\n discover: 'text-accent-orange',\n default: 'text-text-secondary',\n };\n\n const color = brandColors[brandLower] || brandColors.default;\n\n return (\n <div className={`w-10 h-6 rounded bg-bg-sunken flex items-center justify-center ${className}`}>\n <CreditCard className={`size-5 ${color}`} />\n </div>\n );\n};\n\nconst PaymentMethodCard: FC<{\n method: PaymentMethod;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n}> = ({ method, onSetDefault, onDelete }) => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [isSettingDefault, setIsSettingDefault] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);\n\n const handleSetDefault = async () => {\n setIsMenuOpen(false);\n setIsSettingDefault(true);\n try {\n await onSetDefault(method.id);\n } finally {\n setIsSettingDefault(false);\n }\n };\n\n const handleDeleteRequest = async () => {\n setIsMenuOpen(false);\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete payment method',\n message: 'Are you sure you want to remove this payment method?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === true) {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n return;\n }\n if (confirmed === null) {\n setIsConfirmingDelete(true);\n }\n };\n\n const handleDelete = async () => {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setIsConfirmingDelete(false);\n }\n };\n\n const getMethodTypeLabel = (type: PaymentMethodType) => {\n const labels: Record<PaymentMethodType, string> = {\n CREDIT_CARD: 'Credit Card',\n DEBIT_CARD: 'Debit Card',\n BANK_TRANSFER: 'UPI',\n PAYPAL: 'PayPal',\n CRYPTO: 'Crypto',\n WALLET: 'Wallet',\n NETBANKING: 'Netbanking',\n };\n return labels[type] || type;\n };\n\n const getExpiryText = () => {\n if (method.expiryMonth && method.expiryYear) {\n const monthStr = String(method.expiryMonth).padStart(2, '0');\n const yearStr = String(method.expiryYear).slice(-2);\n return `${monthStr}/${yearStr}`;\n }\n return null;\n };\n\n const isExpired = () => {\n if (!method.expiryMonth || !method.expiryYear) return false;\n const now = new Date();\n const expiry = new Date(method.expiryYear, method.expiryMonth, 0);\n return expiry < now;\n };\n\n const getProviderBadge = () => {\n if (method.stripePaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent-violet-subtle text-accent-violet\">\n Stripe\n </span>\n );\n }\n if (method.razorpayPaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-status-info-bg-subtle text-status-info-text\">\n Razorpay\n </span>\n );\n }\n return null;\n };\n\n return (\n <div\n className={`\n relative p-4 border rounded-lg transition-all overflow-visible\n ${method.isDefault ? 'border-border-strong bg-[var(--color-accent-soft)]' : 'border-border-subtle bg-bg-surface'}\n ${isDeleting ? 'opacity-50' : ''}\n `}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <CardBrandIcon brand={method.brand} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium text-text-primary\">\n {method.brand || getMethodTypeLabel(method.type)} •••• {method.last4 || '****'}\n </p>\n {method.isDefault && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-soft)] text-text-link\">\n <Star className=\"size-2.5 fill-current\" />\n Default\n </span>\n )}\n {getProviderBadge()}\n </div>\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n {getExpiryText() && (\n <>\n <span className={isExpired() ? 'text-status-error-text' : ''}>\n Expires {getExpiryText()}\n </span>\n {isExpired() && <span className=\"text-status-error-text text-xs\">(Expired)</span>}\n </>\n )}\n {method.isVerified && (\n <span className=\"flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"size-3\" />\n Verified\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Actions Menu */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"p-2 rounded-button hover:bg-bg-sunken text-text-secondary hover:text-text-primary transition-colors\"\n disabled={isSettingDefault || isDeleting}\n aria-label=\"Payment method options\"\n >\n {isSettingDefault || isDeleting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <MoreVertical className=\"size-4\" />\n )}\n </button>\n\n {isMenuOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsMenuOpen(false)} />\n <div className=\"absolute top-full right-0 mt-1 min-w-[140px] rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3 z-20 overflow-hidden whitespace-nowrap\">\n {!method.isDefault && (\n <button\n type=\"button\"\n onClick={handleSetDefault}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-text-primary hover:bg-bg-sunken transition-colors text-left\"\n >\n <Star className=\"size-4 flex-shrink-0\" />\n Set as default\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleDeleteRequest}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-status-error-text hover:bg-status-error-bg-subtle transition-colors text-left\"\n >\n <Trash2 className=\"size-4 flex-shrink-0\" />\n Remove\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n {isConfirmingDelete && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3\">\n <p className=\"text-sm font-medium text-status-error-text\">\n Remove this payment method from your billing account?\n </p>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsConfirmingDelete(false)}\n className=\"rounded-button border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"rounded-button bg-action-danger-bg px-3 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-danger-bgHover disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isDeleting ? 'Removing…' : 'Remove'}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const PaymentMethodsSection: FC<PaymentMethodsSectionProps> = ({\n paymentMethods,\n billingAccountId: _billingAccountId,\n onAddPaymentMethod,\n onSetDefault,\n onDelete,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\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 // billingAccountId is available for future use\n void _billingAccountId;\n\n const hasPaymentMethods = paymentMethods.length > 0;\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.paymentMethods', 'Payment Methods')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Manage your payment methods for subscriptions and purchases\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onAddPaymentMethod}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n <Plus className=\"size-4\" />\n Add Method\n </button>\n </header>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n ) : hasPaymentMethods ? (\n <div className=\"space-y-3 overflow-visible\">\n {paymentMethods\n .filter((method): method is PaymentMethod => method !== null && method !== undefined)\n .map((method) => (\n <PaymentMethodCard\n key={method.id}\n method={method}\n onSetDefault={onSetDefault}\n onDelete={onDelete}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-8 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 <CreditCard className=\"size-6 text-text-secondary\" />\n </div>\n <h4 className=\"text-base font-medium text-text-primary\">No payment methods</h4>\n <p className=\"text-sm text-text-secondary mt-1 text-center max-w-sm\">\n Add a payment method to enable automatic payments for your subscriptions\n </p>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RecommendedAddonsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/RecommendedAddonsSection.tsx"],"sourcesContent":["/**\n * Recommended Addons Section Component\n * Displays recommended addons that users might want to purchase\n * Supports add to cart functionality\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport {\n Package,\n ArrowRight,\n Sparkles,\n ChevronRight,\n Loader2,\n ShoppingCart,\n Check,\n Plus,\n Minus,\n Trash2,\n} from 'lucide-react';\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\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n * e.g., 10 bots + 10 bots = 20 bots (shown as one feature)\n */\nfunction combineQuotas(features: SubscriptionFeatures[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n\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\n return Array.from(quotaMap.values());\n}\n\ninterface RecommendedAddonsSectionProps {\n addons: Addon[];\n hasSubscription: boolean;\n onViewAddon: (addonId: string) => void;\n onPurchaseAddon: (addonId: string) => void;\n onViewAllAddons: () => void;\n /** Handler for adding addon to cart */\n onAddToCart?: (addon: Addon) => void;\n /** Check if addon is in cart */\n isInCart?: (addonId: string) => boolean;\n /** Get cart quantity for addon */\n getCartQuantity?: (addonId: string) => number;\n /** Update quantity in cart */\n onUpdateQuantity?: (addonId: string, quantity: number) => void;\n /** Remove from cart */\n onRemoveFromCart?: (addonId: string) => void;\n /** Maximum number of addons to show (default: 3) */\n maxAddons?: number;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\nconst AddonCard: FC<{\n addon: Addon;\n onView: () => void;\n onPurchase: () => void;\n onAddToCart?: () => void;\n isInCart?: boolean;\n cartQuantity?: number;\n onUpdateQuantity?: (quantity: number) => void;\n onRemoveFromCart?: () => void;\n}> = ({\n addon,\n onView,\n onPurchase,\n onAddToCart,\n isInCart = false,\n cartQuantity = 0,\n onUpdateQuantity,\n onRemoveFromCart,\n}) => {\n const [justAdded, setJustAdded] = useState(false);\n\n const handleAddToCart = () => {\n if (onAddToCart) {\n onAddToCart();\n setJustAdded(true);\n setTimeout(() => setJustAdded(false), 2000);\n } else {\n onPurchase();\n }\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n };\n\n const getDurationLabel = (duration: PlanDuration) => {\n return duration === 'yearly' ? 'year' : 'month';\n };\n\n // Combine duplicate quotas (e.g., 10 bots + 10 bots = 20 bots)\n const combinedQuotas = useMemo(() => {\n const features = addon.features || [];\n return combineQuotas(features as SubscriptionFeatures[]);\n }, [addon.features]);\n\n return (\n <article className=\"flex flex-col border border-border-seam rounded-card bg-bg-surface shadow-[var(--shadow-pop)] overflow-hidden hover:shadow-elevation-2 [@media(hover:none)]: transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)]\">\n {/* Header */}\n <header className=\"p-4 border-b border-border-subtle\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Package className=\"size-5 text-status-success-text\" />\n </div>\n <div>\n <h4 className=\"font-semibold text-text-primary\">{addon.name}</h4>\n <p className=\"text-sm text-text-secondary\">\n {formatCurrency(addon.price, addon.currency)}/{getDurationLabel(addon.duration)}\n </p>\n </div>\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 flex-1\">\n {combinedQuotas.length > 0 ? (\n <ul className=\"space-y-2\">\n {combinedQuotas.slice(0, 2).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"size-1.5 rounded-full bg-status-success-bg\" />\n <span className=\"text-text-secondary\">{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 > 2 && (\n <li className=\"text-xs text-text-secondary\">\n +{combinedQuotas.length - 2} more quotas\n </li>\n )}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with additional features\n </p>\n )}\n </div>\n\n {/* Footer */}\n <footer className=\"p-4 border-t border-border-subtle bg-bg-sunken flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"flex-1 px-3 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors text-center\"\n >\n View Details\n </button>\n {isInCart && onUpdateQuantity ? (\n // Quantity controls when in cart\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onRemoveFromCart}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-button transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity - 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium text-text-primary\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity + 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n ) : (\n // Add to cart button\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={justAdded}\n className={`flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-center flex items-center justify-center gap-1.5 ${\n justAdded\n ? 'bg-status-success-bg text-action-primary-text hover:bg-status-success-bg/90'\n : 'text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover'\n }`}\n >\n {justAdded ? (\n <>\n <Check className=\"size-4\" />\n Added\n </>\n ) : (\n <>\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </>\n )}\n </button>\n )}\n </footer>\n </article>\n );\n};\n\nexport const RecommendedAddonsSection: FC<RecommendedAddonsSectionProps> = ({\n addons,\n hasSubscription,\n onViewAddon,\n onPurchaseAddon,\n onViewAllAddons,\n onAddToCart,\n isInCart,\n getCartQuantity,\n onUpdateQuantity,\n onRemoveFromCart,\n maxAddons = 3,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\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 // Only show if user has a subscription\n if (!hasSubscription) {\n return null;\n }\n\n // Limit addons to maxAddons\n const displayAddons = addons.slice(0, maxAddons);\n\n if (isLoading) {\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n <header>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.recommendedAddons', 'Recommended Add-ons')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </header>\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n </section>\n );\n }\n\n if (displayAddons.length === 0) {\n return null;\n }\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Sparkles className=\"size-5 text-status-warning-text\" />\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Recommended Add-ons</h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-link hover:text-text-link transition-colors\"\n >\n View All\n <ChevronRight className=\"size-4\" />\n </button>\n </header>\n\n {/* Addons Grid - Limited to maxAddons */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {displayAddons.map((addon) => (\n <AddonCard\n key={addon.id}\n addon={addon}\n onView={() => onViewAddon(addon.id)}\n onPurchase={() => onPurchaseAddon(addon.id)}\n onAddToCart={onAddToCart ? () => onAddToCart(addon) : undefined}\n isInCart={isInCart ? isInCart(addon.id) : false}\n cartQuantity={getCartQuantity ? getCartQuantity(addon.id) : 0}\n onUpdateQuantity={\n onUpdateQuantity ? (qty) => onUpdateQuantity(addon.id, qty) : undefined\n }\n onRemoveFromCart={onRemoveFromCart ? () => onRemoveFromCart(addon.id) : undefined}\n />\n ))}\n </div>\n\n {/* See More Link - show if there are more addons */}\n {addons.length > maxAddons && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-2 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n View all {addons.length} add-ons\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":";;;;;;AAoCA,SAAS,EAAc,GAAmD;CACxE,IAAM,oBAAW,IAAI,KAA4B;AAEjD,MAAK,IAAM,KAAW,GAAU;AAC9B,MAAI,CAAC,EAAQ,MAAO;EAEpB,IAAM,IAAU,EAAQ,MAAM,IACxB,IAAa,EAAuB,EAAQ,EAE5C,IAAW,EAAS,IAAI,EAAQ;AACtC,EAAI,IACF,EAAS,cAAc,IAEvB,EAAS,IAAI,GAAS;GACpB;GACA,MAAM,EAAQ,MAAM,QAAQ;GAC5B,YAAY;GACb,CAAC;;AAIN,QAAO,MAAM,KAAK,EAAS,QAAQ,CAAC;;AA2BtC,IAAM,KASA,EACJ,UACA,WACA,eACA,gBACA,cAAW,IACX,kBAAe,GACf,qBACA,0BACI;CACJ,IAAM,CAAC,GAAW,KAAgB,EAAS,GAAM,EAE3C,UAAwB;AAC5B,EAAI,KACF,GAAa,EACb,EAAa,GAAK,EAClB,iBAAiB,EAAa,GAAM,EAAE,IAAK,IAE3C,GAAY;IAIV,KAAkB,GAAgB,MAC/B,IAAI,KAAK,aAAa,SAAS;EACpC,OAAO;EACP,UAAU,KAAY;EACtB,uBAAuB;EACvB,uBAAuB;EACxB,CAAC,CAAC,OAAO,EAAO,EAGb,KAAoB,MACjB,MAAa,WAAW,SAAS,SAIpC,IAAiB,QAEd,EADU,EAAM,YAAY,EAAE,CACmB,EACvD,CAAC,EAAM,SAAS,CAAC;AAEpB,QACE,kBAAC,WAAD;EAAS,WAAU;YAAnB;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAChB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,mCAAoC,CAAA;OACnD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAmC,EAAM;OAAU,CAAA,EACjE,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAe,EAAM,OAAO,EAAM,SAAS;QAAC;QAAE,EAAiB,EAAM,SAAS;QAC7E;SACA,EAAA,CAAA,CACF;;KACF,CAAA;IACC,CAAA;GAGT,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAe,SAAS,IACvB,kBAAC,MAAD;KAAI,WAAU;eAAd,CACG,EAAe,MAAM,GAAG,EAAE,CAAC,KAAK,MAC/B,kBAAC,MAAD;MAAwB,WAAU;gBAAlC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD,EAAM,WAAU,8CAA+C,CAAA,EAC/D,kBAAC,QAAD;QAAM,WAAU;kBAAuB,EAAM;QAAY,CAAA,CACrD;UACN,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAM,WAAW,gBAAgB;OAC7B,CAAA,CACJ;QARI,EAAM,QAQV,CACL,EACD,EAAe,SAAS,KACvB,kBAAC,MAAD;MAAI,WAAU;gBAAd;OAA4C;OACxC,EAAe,SAAS;OAAE;OACzB;QAEJ;SAEL,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAEvC,CAAA;IAEF,CAAA;GAGN,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eACX;KAEQ,CAAA,EACR,KAAY,IAEX,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;OACtB,CAAA;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAiB,IAAe,EAAE;OACjD,WAAU;iBAEV,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;OACrB,CAAA;MACT,kBAAC,QAAD;OAAM,WAAU;iBAAiD;OAAoB,CAAA;MACrF,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAiB,IAAe,EAAE;OACjD,WAAU;iBAEV,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;OACpB,CAAA;MACL;SAGN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAW,0HACT,IACI,gFACA;eAGL,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAAA,QAE3B,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,EAAA,cAElC,EAAA,CAAA;KAEE,CAAA,CAEJ;;GACD;;GAID,KAA+D,EAC1E,WACA,oBACA,gBACA,oBACA,oBACA,gBACA,aACA,oBACA,qBACA,qBACA,eAAY,GACZ,eAAY,IACZ,eAAY,IACZ,kBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;;AAGzC,KAAI,CAAC,EACH,QAAO;CAIT,IAAM,IAAgB,EAAO,MAAM,GAAG,EAAU;AAwBhD,QAtBI,IAEA,kBAAC,WAAD;EAAS,WAAW,aAAa;EAAa,aAAW;YAAzD,CACE,kBAAC,UAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAG,sCAAsC,sBAAsB;GAC7D,CAAA,EACL,kBAAC,KAAD;GAAG,WAAU;aAA8B;GAEvC,CAAA,CACG,EAAA,CAAA,EACT,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAS,WAAU,2CAA4C,CAAA;GAC3D,CAAA,CACE;MAIV,EAAc,WAAW,IACpB,OAIP,kBAAC,WAAD;EAAS,WAAW,aAAa;EAAa,aAAW;YAAzD;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAU,WAAU,mCAAoC,CAAA,EACxD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAwB,CAAA,EAChF,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA,CACA,EAAA,CAAA,CACF;QACN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAIC,YAEC,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,CAC5B;OACF;;GAGT,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,GAAD;KAES;KACP,cAAc,EAAY,EAAM,GAAG;KACnC,kBAAkB,EAAgB,EAAM,GAAG;KAC3C,aAAa,UAAoB,EAAY,EAAM,GAAG,KAAA;KACtD,UAAU,IAAW,EAAS,EAAM,GAAG,GAAG;KAC1C,cAAc,IAAkB,EAAgB,EAAM,GAAG,GAAG;KAC5D,kBACE,KAAoB,MAAQ,EAAiB,EAAM,IAAI,EAAI,GAAG,KAAA;KAEhE,kBAAkB,UAAyB,EAAiB,EAAM,GAAG,GAAG,KAAA;KACxE,EAXK,EAAM,GAWX,CACF;IACE,CAAA;GAGL,EAAO,SAAS,KACf,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ;MAIC;MACW,EAAO;MAAO;MACxB,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;MAC1B;;IACL,CAAA;GAEA"}
1
+ {"version":3,"file":"RecommendedAddonsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/RecommendedAddonsSection.tsx"],"sourcesContent":["/**\n * Recommended Addons Section Component\n * Displays recommended addons that users might want to purchase\n * Supports add to cart functionality\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport {\n Package,\n ArrowRight,\n Sparkles,\n ChevronRight,\n Loader2,\n ShoppingCart,\n Check,\n Plus,\n Minus,\n Trash2,\n} from 'lucide-react';\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\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n * e.g., 10 bots + 10 bots = 20 bots (shown as one feature)\n */\nfunction combineQuotas(features: SubscriptionFeatures[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n\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\n return Array.from(quotaMap.values());\n}\n\ninterface RecommendedAddonsSectionProps {\n addons: Addon[];\n hasSubscription: boolean;\n onViewAddon: (addonId: string) => void;\n onPurchaseAddon: (addonId: string) => void;\n onViewAllAddons: () => void;\n /** Handler for adding addon to cart */\n onAddToCart?: (addon: Addon) => void;\n /** Check if addon is in cart */\n isInCart?: (addonId: string) => boolean;\n /** Get cart quantity for addon */\n getCartQuantity?: (addonId: string) => number;\n /** Update quantity in cart */\n onUpdateQuantity?: (addonId: string, quantity: number) => void;\n /** Remove from cart */\n onRemoveFromCart?: (addonId: string) => void;\n /** Maximum number of addons to show (default: 3) */\n maxAddons?: number;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\nconst AddonCard: FC<{\n addon: Addon;\n onView: () => void;\n onPurchase: () => void;\n onAddToCart?: () => void;\n isInCart?: boolean;\n cartQuantity?: number;\n onUpdateQuantity?: (quantity: number) => void;\n onRemoveFromCart?: () => void;\n}> = ({\n addon,\n onView,\n onPurchase,\n onAddToCart,\n isInCart = false,\n cartQuantity = 0,\n onUpdateQuantity,\n onRemoveFromCart,\n}) => {\n const [justAdded, setJustAdded] = useState(false);\n\n const handleAddToCart = () => {\n if (onAddToCart) {\n onAddToCart();\n setJustAdded(true);\n setTimeout(() => setJustAdded(false), 2000);\n } else {\n onPurchase();\n }\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n };\n\n const getDurationLabel = (duration: PlanDuration) => {\n return duration === 'yearly' ? 'year' : 'month';\n };\n\n // Combine duplicate quotas (e.g., 10 bots + 10 bots = 20 bots)\n const combinedQuotas = useMemo(() => {\n const features = addon.features || [];\n return combineQuotas(features as SubscriptionFeatures[]);\n }, [addon.features]);\n\n return (\n <article className=\"flex flex-col border border-border-seam rounded-card bg-bg-surface shadow-[var(--shadow-pop)] overflow-hidden hover:shadow-elevation-2 [@media(hover:none)]: transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)]\">\n {/* Header */}\n <header className=\"p-4 border-b border-border-subtle\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Package className=\"size-5 text-status-success-text\" />\n </div>\n <div>\n <h4 className=\"font-semibold text-text-primary\">{addon.name}</h4>\n <p className=\"text-sm text-text-secondary\">\n {formatCurrency(addon.price, addon.currency)}/{getDurationLabel(addon.duration)}\n </p>\n </div>\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 flex-1\">\n {combinedQuotas.length > 0 ? (\n <ul className=\"space-y-2\">\n {combinedQuotas.slice(0, 2).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"size-1.5 rounded-full bg-status-success-bg\" />\n <span className=\"text-text-secondary\">{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 > 2 && (\n <li className=\"text-xs text-text-secondary\">\n +{combinedQuotas.length - 2} more quotas\n </li>\n )}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with additional features\n </p>\n )}\n </div>\n\n {/* Footer */}\n <footer className=\"p-4 border-t border-border-subtle bg-bg-sunken flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"flex-1 px-3 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors text-center\"\n >\n View Details\n </button>\n {isInCart && onUpdateQuantity ? (\n // Quantity controls when in cart\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onRemoveFromCart}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-button transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity - 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium text-text-primary\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity + 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n ) : (\n // Add to cart button\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={justAdded}\n className={`flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-center flex items-center justify-center gap-1.5 ${\n justAdded\n ? 'bg-status-success-bg text-action-primary-text hover:bg-status-success-bg/90'\n : 'text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover'\n }`}\n >\n {justAdded ? (\n <>\n <Check className=\"size-4\" />\n Added\n </>\n ) : (\n <>\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </>\n )}\n </button>\n )}\n </footer>\n </article>\n );\n};\n\nexport const RecommendedAddonsSection: FC<RecommendedAddonsSectionProps> = ({\n addons,\n hasSubscription,\n onViewAddon,\n onPurchaseAddon,\n onViewAllAddons,\n onAddToCart,\n isInCart,\n getCartQuantity,\n onUpdateQuantity,\n onRemoveFromCart,\n maxAddons = 3,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\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 // Only show if user has a subscription\n if (!hasSubscription) {\n return null;\n }\n\n // Limit addons to maxAddons\n const displayAddons = addons.slice(0, maxAddons);\n\n if (isLoading) {\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n <header>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.recommendedAddons', 'Recommended Add-ons')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </header>\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n </section>\n );\n }\n\n if (displayAddons.length === 0) {\n return null;\n }\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Sparkles className=\"size-5 text-status-warning-text\" />\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Recommended Add-ons</h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-link hover:text-text-link transition-colors\"\n >\n View All\n <ChevronRight className=\"size-4\" />\n </button>\n </header>\n\n {/* Addons Grid - Limited to maxAddons */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {displayAddons.map((addon) => (\n <AddonCard\n key={addon.id}\n addon={addon}\n onView={() => onViewAddon(addon.id)}\n onPurchase={() => onPurchaseAddon(addon.id)}\n onAddToCart={onAddToCart ? () => onAddToCart(addon) : undefined}\n isInCart={isInCart ? isInCart(addon.id) : false}\n cartQuantity={getCartQuantity ? getCartQuantity(addon.id) : 0}\n onUpdateQuantity={\n onUpdateQuantity ? (qty) => onUpdateQuantity(addon.id, qty) : undefined\n }\n onRemoveFromCart={onRemoveFromCart ? () => onRemoveFromCart(addon.id) : undefined}\n />\n ))}\n </div>\n\n {/* See More Link - show if there are more addons */}\n {addons.length > maxAddons && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-2 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n View all {addons.length} add-ons\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useBillingOverviewData.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/hooks/useBillingOverviewData.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport {\n type GetBillingOverviewQuery,\n useGetBillingOverviewAccountQuery,\n useGetBillingOverviewQuery,\n} from '../../../../generated/global-operations';\nimport type {\n Addon,\n BillingAccount,\n BillingAddress,\n BillingSubscription,\n PaymentMethod,\n} from '../../../shared/types';\n\ninterface BillingOverviewDataResult {\n billingAccounts: BillingAccount[];\n currentAccount: BillingAccount | null;\n paymentMethods: PaymentMethod[];\n billingAddresses: BillingAddress[];\n activeSubscriptions: BillingSubscription[];\n hasActiveSubscription: boolean;\n recommendedAddons: Addon[];\n isLoading: boolean;\n error: Error | undefined;\n refetchAll: () => Promise<void>;\n}\n\ntype OverviewAddon = GetBillingOverviewQuery['getAllAddOns'][number];\n\nexport function useBillingOverviewData(\n selectedAccountId?: string | null,\n productId?: string | null\n): BillingOverviewDataResult {\n const {\n data: overviewData,\n loading: overviewLoading,\n error: overviewError,\n refetch: refetchOverview,\n } = useGetBillingOverviewQuery({\n variables: productId ? { addonsFilter: { productId } } : undefined,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const defaultAccount = overviewData?.getDefaultBillingAccount ?? null;\n const shouldLoadSelectedAccount = Boolean(\n selectedAccountId && defaultAccount?.id && selectedAccountId !== defaultAccount.id\n );\n\n const {\n data: selectedAccountData,\n loading: selectedAccountLoading,\n error: selectedAccountError,\n refetch: refetchSelectedAccount,\n } = useGetBillingOverviewAccountQuery({\n variables: { id: selectedAccountId ?? '' },\n skip: !shouldLoadSelectedAccount,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const billingAccounts = useMemo<BillingAccount[]>(() => {\n return overviewData?.getBillingAccountsByOrg ?? [];\n }, [overviewData?.getBillingAccountsByOrg]);\n\n const currentAccount = useMemo<BillingAccount | null>(() => {\n if (shouldLoadSelectedAccount) {\n return selectedAccountData?.getBillingAccount ?? null;\n }\n\n return defaultAccount;\n }, [defaultAccount, selectedAccountData?.getBillingAccount, shouldLoadSelectedAccount]);\n\n const paymentMethods = useMemo<PaymentMethod[]>(() => {\n return (currentAccount?.paymentMethods ?? []).filter(Boolean) as PaymentMethod[];\n }, [currentAccount?.paymentMethods]);\n\n const billingAddresses = useMemo<BillingAddress[]>(() => {\n return (currentAccount?.billingAddresses ?? []).filter(Boolean) as BillingAddress[];\n }, [currentAccount?.billingAddresses]);\n\n const activeSubscriptions = useMemo<BillingSubscription[]>(() => {\n return (\n (currentAccount?.subscriptions ?? []).filter(\n (subscription): subscription is BillingSubscription => {\n if (!subscription || subscription.status !== 'active') return false;\n if (productId) return subscription.plan?.productID === productId;\n return true;\n }\n ) as BillingSubscription[]\n ).sort(\n (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n );\n }, [currentAccount?.subscriptions, productId]);\n\n const recommendedAddons = useMemo<Addon[]>(() => {\n return (\n (overviewData?.getAllAddOns ?? []).filter(\n (addon: OverviewAddon | null): addon is OverviewAddon => {\n if (!addon) {\n return false;\n }\n\n return addon.isActive ?? false;\n }\n ) as Addon[]\n ).slice(0, 3);\n }, [overviewData?.getAllAddOns]);\n\n const refetchAll = useCallback(async () => {\n await refetchOverview();\n\n if (shouldLoadSelectedAccount) {\n await refetchSelectedAccount({ id: selectedAccountId ?? '' });\n }\n }, [refetchOverview, refetchSelectedAccount, selectedAccountId, shouldLoadSelectedAccount]);\n\n return {\n billingAccounts,\n currentAccount,\n paymentMethods,\n billingAddresses,\n activeSubscriptions,\n hasActiveSubscription: activeSubscriptions.length > 0,\n recommendedAddons,\n isLoading: overviewLoading || selectedAccountLoading,\n error: (overviewError || selectedAccountError) as Error | undefined,\n refetchAll,\n };\n}\n"],"mappings":";;;AA6BA,SAAgB,EACd,GACA,GAC2B;CAC3B,IAAM,EACJ,MAAM,GACN,SAAS,GACT,OAAO,GACP,SAAS,MACP,EAA2B;EAC7B,WAAW,IAAY,EAAE,cAAc,EAAE,cAAW,EAAE,GAAG,KAAA;EACzD,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,IAAiB,GAAc,4BAA4B,MAC3D,IAA4B,GAChC,KAAqB,GAAgB,MAAM,MAAsB,EAAe,KAG5E,EACJ,MAAM,GACN,SAAS,GACT,OAAO,GACP,SAAS,MACP,EAAkC;EACpC,WAAW,EAAE,IAAI,KAAqB,IAAI;EAC1C,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;EAClB,CAAC,EAEI,IAAkB,QACf,GAAc,2BAA2B,EAAE,EACjD,CAAC,GAAc,wBAAwB,CAAC,EAErC,IAAiB,QACjB,IACK,GAAqB,qBAAqB,OAG5C,GACN;EAAC;EAAgB,GAAqB;EAAmB;EAA0B,CAAC,EAEjF,IAAiB,SACb,GAAgB,kBAAkB,EAAE,EAAE,OAAO,QAAQ,EAC5D,CAAC,GAAgB,eAAe,CAAC,EAE9B,IAAmB,SACf,GAAgB,oBAAoB,EAAE,EAAE,OAAO,QAAQ,EAC9D,CAAC,GAAgB,iBAAiB,CAAC,EAEhC,IAAsB,SAEvB,GAAgB,iBAAiB,EAAE,EAAE,QACnC,MACK,CAAC,KAAgB,EAAa,WAAW,WAAiB,KAC1D,IAAkB,EAAa,MAAM,cAAc,IAChD,GAEV,CACD,MACC,GAAM,MAAU,IAAI,KAAK,EAAM,UAAU,CAAC,SAAS,GAAG,IAAI,KAAK,EAAK,UAAU,CAAC,SAAS,CAC1F,EACA,CAAC,GAAgB,eAAe,EAAU,CAAC,EAExC,IAAoB,SAErB,GAAc,gBAAgB,EAAE,EAAE,QAChC,MACM,IAIE,EAAM,YAAY,KAHhB,GAKZ,CACD,MAAM,GAAG,EAAE,EACZ,CAAC,GAAc,aAAa,CAAC,EAE1B,IAAa,EAAY,YAAY;AAGzC,EAFA,MAAM,GAAiB,EAEnB,KACF,MAAM,EAAuB,EAAE,IAAI,KAAqB,IAAI,CAAC;IAE9D;EAAC;EAAiB;EAAwB;EAAmB;EAA0B,CAAC;AAE3F,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,uBAAuB,EAAoB,SAAS;EACpD;EACA,WAAW,KAAmB;EAC9B,OAAQ,KAAiB;EACzB;EACD"}
1
+ {"version":3,"file":"useBillingOverviewData.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/hooks/useBillingOverviewData.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport {\n type GetBillingOverviewQuery,\n useGetBillingOverviewAccountQuery,\n useGetBillingOverviewQuery,\n} from '../../../../generated/global-operations';\nimport type {\n Addon,\n BillingAccount,\n BillingAddress,\n BillingSubscription,\n PaymentMethod,\n} from '../../../shared/types';\n\ninterface BillingOverviewDataResult {\n billingAccounts: BillingAccount[];\n currentAccount: BillingAccount | null;\n paymentMethods: PaymentMethod[];\n billingAddresses: BillingAddress[];\n activeSubscriptions: BillingSubscription[];\n hasActiveSubscription: boolean;\n recommendedAddons: Addon[];\n isLoading: boolean;\n error: Error | undefined;\n refetchAll: () => Promise<void>;\n}\n\ntype OverviewAddon = GetBillingOverviewQuery['getAllAddOns'][number];\n\nexport function useBillingOverviewData(\n selectedAccountId?: string | null,\n productId?: string | null\n): BillingOverviewDataResult {\n const {\n data: overviewData,\n loading: overviewLoading,\n error: overviewError,\n refetch: refetchOverview,\n } = useGetBillingOverviewQuery({\n variables: productId ? { addonsFilter: { productId } } : undefined,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const defaultAccount = overviewData?.getDefaultBillingAccount ?? null;\n const shouldLoadSelectedAccount = Boolean(\n selectedAccountId && defaultAccount?.id && selectedAccountId !== defaultAccount.id\n );\n\n const {\n data: selectedAccountData,\n loading: selectedAccountLoading,\n error: selectedAccountError,\n refetch: refetchSelectedAccount,\n } = useGetBillingOverviewAccountQuery({\n variables: { id: selectedAccountId ?? '' },\n skip: !shouldLoadSelectedAccount,\n fetchPolicy: 'cache-first',\n nextFetchPolicy: 'cache-first',\n });\n\n const billingAccounts = useMemo<BillingAccount[]>(() => {\n return overviewData?.getBillingAccountsByOrg ?? [];\n }, [overviewData?.getBillingAccountsByOrg]);\n\n const currentAccount = useMemo<BillingAccount | null>(() => {\n if (shouldLoadSelectedAccount) {\n return selectedAccountData?.getBillingAccount ?? null;\n }\n\n return defaultAccount;\n }, [defaultAccount, selectedAccountData?.getBillingAccount, shouldLoadSelectedAccount]);\n\n const paymentMethods = useMemo<PaymentMethod[]>(() => {\n return (currentAccount?.paymentMethods ?? []).filter(Boolean) as PaymentMethod[];\n }, [currentAccount?.paymentMethods]);\n\n const billingAddresses = useMemo<BillingAddress[]>(() => {\n return (currentAccount?.billingAddresses ?? []).filter(Boolean) as BillingAddress[];\n }, [currentAccount?.billingAddresses]);\n\n const activeSubscriptions = useMemo<BillingSubscription[]>(() => {\n return (\n (currentAccount?.subscriptions ?? []).filter(\n (subscription): subscription is BillingSubscription => {\n if (!subscription || subscription.status !== 'active') return false;\n if (productId) return subscription.plan?.productID === productId;\n return true;\n }\n ) as BillingSubscription[]\n ).sort(\n (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n );\n }, [currentAccount?.subscriptions, productId]);\n\n const recommendedAddons = useMemo<Addon[]>(() => {\n return (\n (overviewData?.getAllAddOns ?? []).filter(\n (addon: OverviewAddon | null): addon is OverviewAddon => {\n if (!addon) {\n return false;\n }\n\n return addon.isActive ?? false;\n }\n ) as Addon[]\n ).slice(0, 3);\n }, [overviewData?.getAllAddOns]);\n\n const refetchAll = useCallback(async () => {\n await refetchOverview();\n\n if (shouldLoadSelectedAccount) {\n await refetchSelectedAccount({ id: selectedAccountId ?? '' });\n }\n }, [refetchOverview, refetchSelectedAccount, selectedAccountId, shouldLoadSelectedAccount]);\n\n return {\n billingAccounts,\n currentAccount,\n paymentMethods,\n billingAddresses,\n activeSubscriptions,\n hasActiveSubscription: activeSubscriptions.length > 0,\n recommendedAddons,\n isLoading: overviewLoading || selectedAccountLoading,\n error: (overviewError || selectedAccountError) as Error | undefined,\n refetchAll,\n };\n}\n"],"mappings":";;;AA6BA,SAAgB,EACd,GACA,GAC2B;CAC3B,IAAM,EACJ,MAAM,GACN,SAAS,GACT,OAAO,GACP,SAAS,MACP,EAA2B;EAC7B,WAAW,IAAY,EAAE,cAAc,EAAE,aAAU,EAAE,IAAI,KAAA;EACzD,aAAa;EACb,iBAAiB;CACnB,CAAC,GAEK,IAAiB,GAAc,4BAA4B,MAC3D,IAA4B,GAChC,KAAqB,GAAgB,MAAM,MAAsB,EAAe,KAG5E,EACJ,MAAM,GACN,SAAS,GACT,OAAO,GACP,SAAS,MACP,EAAkC;EACpC,WAAW,EAAE,IAAI,KAAqB,GAAG;EACzC,MAAM,CAAC;EACP,aAAa;EACb,iBAAiB;CACnB,CAAC,GAEK,IAAkB,QACf,GAAc,2BAA2B,CAAC,GAChD,CAAC,GAAc,uBAAuB,CAAC,GAEpC,IAAiB,QACjB,IACK,GAAqB,qBAAqB,OAG5C,GACN;EAAC;EAAgB,GAAqB;EAAmB;CAAyB,CAAC,GAEhF,IAAiB,SACb,GAAgB,kBAAkB,CAAC,GAAG,OAAO,OAAO,GAC3D,CAAC,GAAgB,cAAc,CAAC,GAE7B,IAAmB,SACf,GAAgB,oBAAoB,CAAC,GAAG,OAAO,OAAO,GAC7D,CAAC,GAAgB,gBAAgB,CAAC,GAE/B,IAAsB,SAEvB,GAAgB,iBAAiB,CAAC,GAAG,QACnC,MACK,CAAC,KAAgB,EAAa,WAAW,WAAiB,KAC1D,IAAkB,EAAa,MAAM,cAAc,IAChD,EAEX,EACA,MACC,GAAM,MAAU,IAAI,KAAK,EAAM,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAK,SAAS,EAAE,QAAQ,CAC1F,GACC,CAAC,GAAgB,eAAe,CAAS,CAAC,GAEvC,IAAoB,SAErB,GAAc,gBAAgB,CAAC,GAAG,QAChC,MACM,IAIE,EAAM,YAAY,KAHhB,EAKb,EACA,MAAM,GAAG,CAAC,GACX,CAAC,GAAc,YAAY,CAAC,GAEzB,IAAa,EAAY,YAAY;EAGzC,AAFA,MAAM,EAAgB,GAElB,KACF,MAAM,EAAuB,EAAE,IAAI,KAAqB,GAAG,CAAC;CAEhE,GAAG;EAAC;EAAiB;EAAwB;EAAmB;CAAyB,CAAC;CAE1F,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,uBAAuB,EAAoB,SAAS;EACpD;EACA,WAAW,KAAmB;EAC9B,OAAQ,KAAiB;EACzB;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDashboard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/hooks/useDashboard.ts"],"sourcesContent":["/**\n * Dashboard Module - useDashboard Hook\n * Provides access to dashboard data and mutations for billing overview\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetPaymentProviderConfigQuery,\n useGetBillingAccountsByOrgQuery,\n useGetDefaultBillingAccountDashboardQuery,\n useGetBillingAccountDashboardQuery,\n useGetActiveSubscriptionsQuery,\n useGetPaymentMethodsDashboardQuery,\n useGetBillingAddressQuery,\n useGetCreditTransactionsSummaryQuery,\n useGetRecommendedAddonsQuery,\n useGetAvailablePlansQuery,\n useCreateBillingAccountMutation,\n useUpdateBillingAccountMutation,\n useSetDefaultBillingAccountMutation,\n useAddPaymentMethodDashboardMutation,\n useUpdatePaymentMethodDashboardMutation,\n useDeletePaymentMethodDashboardMutation,\n useSetBillingAddressMutation,\n useInitiatePaymentMutation,\n useSpendCreditsMutation,\n useVerifyPaymentMethodMutation,\n GetBillingAccountsByOrgDocument,\n GetDefaultBillingAccountDashboardDocument,\n GetBillingAddressDocument,\n GetCreditTransactionsSummaryDocument,\n} from '../../../../generated/global-operations';\nimport type {\n BillingAccount,\n BillingSubscription,\n PaymentMethod,\n BillingAddress,\n CreditTransaction,\n Addon,\n Plan,\n CreateBillingAccountInput,\n UpdateBillingAccountInput,\n CreatePaymentMethodInput,\n SetBillingAddressInput,\n InitiatePaymentInput,\n InitiatePaymentResponse,\n SpendCreditsInput,\n VerifyPaymentMethodInput,\n VerifyPaymentMethodResponse,\n} from '../../../shared/types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseBillingAccountsResult {\n billingAccounts: BillingAccount[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseDashboardAccountResult {\n billingAccount: BillingAccount | null;\n paymentMethods: PaymentMethod[];\n billingAddresses: BillingAddress[];\n activeSubscriptions: BillingSubscription[];\n hasActiveSubscription: boolean;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseActiveSubscriptionsResult {\n activeSubscriptions: BillingSubscription[];\n hasActiveSubscription: boolean;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UsePaymentMethodsResult {\n paymentMethods: PaymentMethod[];\n defaultPaymentMethod: PaymentMethod | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseCreditTransactionsResult {\n creditTransactions: CreditTransaction[];\n totalCount: number;\n hasMore: boolean;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseRecommendedAddonsResult {\n addons: Addon[];\n recommendedAddons: Addon[];\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseAvailablePlansResult {\n plans: Plan[];\n activePlans: Plan[];\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseDashboardMutationsResult {\n // Billing account\n createBillingAccount: (input: CreateBillingAccountInput) => Promise<BillingAccount>;\n updateBillingAccount: (input: UpdateBillingAccountInput) => Promise<BillingAccount>;\n setDefaultBillingAccount: (id: string) => Promise<BillingAccount>;\n isCreatingAccount: boolean;\n isUpdatingAccount: boolean;\n isSettingDefaultAccount: boolean;\n\n // Payment methods\n addPaymentMethod: (input: CreatePaymentMethodInput) => Promise<PaymentMethod>;\n setDefaultPaymentMethod: (id: string, billingAccountId: string) => Promise<PaymentMethod>;\n deletePaymentMethod: (id: string, billingAccountId: string) => Promise<boolean>;\n isAddingPaymentMethod: boolean;\n isUpdatingPaymentMethod: boolean;\n isDeletingPaymentMethod: boolean;\n\n // Billing address\n setBillingAddress: (input: SetBillingAddressInput) => Promise<BillingAddress>;\n isSettingAddress: boolean;\n\n // Payments\n initiatePayment: (input: InitiatePaymentInput) => Promise<InitiatePaymentResponse>;\n spendCredits: (input: SpendCreditsInput) => Promise<{ creditTransactionID: string }>;\n verifyPaymentMethod: (input: VerifyPaymentMethodInput) => Promise<VerifyPaymentMethodResponse>;\n isInitiatingPayment: boolean;\n isSpendingCredits: boolean;\n isVerifyingPaymentMethod: boolean;\n}\n\ninterface PaymentProviderConfig {\n stripeEnabled: boolean;\n stripePublishableKey: string | null;\n razorpayEnabled: boolean;\n razorpayKeyId: string | null;\n}\n\ninterface UsePaymentProviderConfigResult {\n config: PaymentProviderConfig | null;\n stripeEnabled: boolean;\n stripePublishableKey: string | null;\n razorpayEnabled: boolean;\n razorpayKeyId: string | null;\n isLoading: boolean;\n error: Error | undefined;\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook for fetching payment provider configuration from backend\n * @param skip - Skip the query (useful for modals that only need data when open)\n */\nexport function usePaymentProviderConfig(skip = false): UsePaymentProviderConfigResult {\n const { data, loading, error } = useGetPaymentProviderConfigQuery({\n fetchPolicy: 'cache-first', // Cache the config as it rarely changes\n skip,\n });\n\n const config = data?.getPaymentProviderConfig || null;\n\n return {\n config,\n stripeEnabled: config?.stripeEnabled ?? false,\n stripePublishableKey: config?.stripePublishableKey ?? null,\n razorpayEnabled: config?.razorpayEnabled ?? false,\n razorpayKeyId: config?.razorpayKeyId ?? null,\n isLoading: skip ? false : loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for fetching all billing accounts for an org\n */\nexport function useBillingAccounts(): UseBillingAccountsResult {\n const { data, loading, error, refetch } = useGetBillingAccountsByOrgQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n billingAccounts: data?.getBillingAccountsByOrg || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching active subscriptions for a billing account (optimized - only active status)\n */\nexport function useActiveSubscriptions(\n billingAccountId: string | undefined\n): UseActiveSubscriptionsResult {\n const { data, loading, error, refetch } = useGetActiveSubscriptionsQuery({\n variables: { billingAccountId: billingAccountId! },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const activeSubscriptions = data?.getActiveSubscriptions || [];\n\n return {\n activeSubscriptions,\n hasActiveSubscription: activeSubscriptions.length > 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching default billing account with dashboard details\n * Note: Subscriptions are NOT included - use useActiveSubscriptions separately\n */\nexport function useDefaultDashboardAccount(): Omit<\n UseDashboardAccountResult,\n 'activeSubscriptions' | 'hasActiveSubscription'\n> {\n const { data, loading, error, refetch } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getDefaultBillingAccount || null;\n const paymentMethods = (billingAccount?.paymentMethods || []).filter(Boolean) as PaymentMethod[];\n const billingAddresses = (billingAccount?.billingAddresses || []).filter(\n Boolean\n ) as BillingAddress[];\n\n return {\n billingAccount,\n paymentMethods,\n billingAddresses,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account by ID with dashboard details\n * Note: Subscriptions are NOT included - use useActiveSubscriptions separately\n */\nexport function useDashboardAccount(\n id: string | undefined\n): Omit<UseDashboardAccountResult, 'activeSubscriptions' | 'hasActiveSubscription'> {\n const { data, loading, error, refetch } = useGetBillingAccountDashboardQuery({\n variables: { id: id! },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getBillingAccount || null;\n const paymentMethods = (billingAccount?.paymentMethods || []).filter(Boolean) as PaymentMethod[];\n const billingAddresses = (billingAccount?.billingAddresses || []).filter(\n Boolean\n ) as BillingAddress[];\n\n return {\n billingAccount,\n paymentMethods,\n billingAddresses,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching payment methods for a billing account\n */\nexport function usePaymentMethods(billingAccountId: string | undefined): UsePaymentMethodsResult {\n const { data, loading, error, refetch } = useGetPaymentMethodsDashboardQuery({\n variables: { billingAccountId: billingAccountId! },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const paymentMethods = useMemo<PaymentMethod[]>(\n () => data?.getPaymentMethods || [],\n [data?.getPaymentMethods]\n );\n const defaultPaymentMethod = useMemo(() => {\n return paymentMethods.find((pm: PaymentMethod) => pm.isDefault) || null;\n }, [paymentMethods]);\n\n return {\n paymentMethods,\n defaultPaymentMethod,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing address\n */\nexport function useBillingAddress(billingAccountId: string | undefined) {\n const { data, loading, error, refetch } = useGetBillingAddressQuery({\n variables: { billingAccountId: billingAccountId! },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n billingAddress: data?.getBillingAddress || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching credit transactions summary\n */\nexport function useCreditTransactionsSummary(\n billingAccountId: string | undefined,\n days = 30,\n page = 1,\n pageSize = 5\n): UseCreditTransactionsResult {\n const { data, loading, error, refetch } = useGetCreditTransactionsSummaryQuery({\n variables: { billingAccountID: billingAccountId!, days, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const result = data?.getBillingAccountCreditTransactions || {\n creditTransactions: [],\n totalCount: 0,\n hasMore: false,\n };\n\n return {\n creditTransactions: result.creditTransactions,\n totalCount: result.totalCount,\n hasMore: result.hasMore,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching recommended addons (limited to 3 at backend level)\n */\nexport function useRecommendedAddons(): UseRecommendedAddonsResult {\n const { data, loading, error } = useGetRecommendedAddonsQuery({\n variables: { limit: 3 },\n fetchPolicy: 'cache-and-network',\n });\n\n const addons = useMemo<Addon[]>(() => data?.getAllAddOns || [], [data?.getAllAddOns]);\n\n // Filter to only active addons (limit already applied at backend)\n const recommendedAddons = useMemo(() => {\n return addons.filter((a: Addon) => a.isActive);\n }, [addons]);\n\n return {\n addons,\n recommendedAddons,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for fetching available plans\n */\nexport function useAvailablePlans(): UseAvailablePlansResult {\n const { data, loading, error } = useGetAvailablePlansQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const plans = useMemo<Plan[]>(() => data?.getAllPlans || [], [data?.getAllPlans]);\n\n const activePlans = useMemo(() => {\n return plans.filter((p: Plan) => p.isActive);\n }, [plans]);\n\n return {\n plans,\n activePlans,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for dashboard mutations\n */\nexport function useDashboardMutations(): UseDashboardMutationsResult {\n const client = useApolloClient();\n\n // Billing account mutations\n const [createBillingAccountMutation, { loading: isCreatingAccount }] =\n useCreateBillingAccountMutation();\n const [updateBillingAccountMutation, { loading: isUpdatingAccount }] =\n useUpdateBillingAccountMutation();\n const [setDefaultBillingAccountMutation, { loading: isSettingDefaultAccount }] =\n useSetDefaultBillingAccountMutation();\n\n // Payment method mutations\n const [addPaymentMethodMutation, { loading: isAddingPaymentMethod }] =\n useAddPaymentMethodDashboardMutation();\n const [updatePaymentMethodMutation, { loading: isUpdatingPaymentMethod }] =\n useUpdatePaymentMethodDashboardMutation();\n const [deletePaymentMethodMutation, { loading: isDeletingPaymentMethod }] =\n useDeletePaymentMethodDashboardMutation();\n\n // Billing address mutation\n const [setBillingAddressMutation, { loading: isSettingAddress }] = useSetBillingAddressMutation();\n\n // Payment mutations\n const [initiatePaymentMutation, { loading: isInitiatingPayment }] = useInitiatePaymentMutation();\n const [spendCreditsMutation, { loading: isSpendingCredits }] = useSpendCreditsMutation();\n const [verifyPaymentMethodMutation, { loading: isVerifyingPaymentMethod }] =\n useVerifyPaymentMethodMutation();\n\n // Billing account handlers\n const createBillingAccount = useCallback(\n async (input: CreateBillingAccountInput): Promise<BillingAccount> => {\n const { data, error } = await createBillingAccountMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to create billing account');\n }\n if (!data?.createBillingAccount) {\n throw new Error('Failed to create billing account: no data returned');\n }\n await client.refetchQueries({\n include: [GetBillingAccountsByOrgDocument, GetDefaultBillingAccountDashboardDocument],\n });\n return data.createBillingAccount;\n },\n [createBillingAccountMutation, client]\n );\n\n const updateBillingAccount = useCallback(\n async (input: UpdateBillingAccountInput): Promise<BillingAccount> => {\n const { data, error } = await updateBillingAccountMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to update billing account');\n }\n if (!data?.updateBillingAccount) {\n throw new Error('Failed to update billing account: no data returned');\n }\n // Only refetch the default dashboard - component handles selected account refetch\n await client.refetchQueries({\n include: [GetDefaultBillingAccountDashboardDocument],\n });\n return data.updateBillingAccount;\n },\n [updateBillingAccountMutation, client]\n );\n\n const setDefaultBillingAccount = useCallback(\n async (id: string): Promise<BillingAccount> => {\n const { data, error } = await setDefaultBillingAccountMutation({\n variables: { id },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to set default billing account');\n }\n if (!data?.setDefaultBillingAccount) {\n throw new Error('Failed to set default billing account: no data returned');\n }\n // Refetch all billing accounts and default account to update isDefault flags\n await client.refetchQueries({\n include: [GetBillingAccountsByOrgDocument, GetDefaultBillingAccountDashboardDocument],\n });\n return data.setDefaultBillingAccount;\n },\n [setDefaultBillingAccountMutation, client]\n );\n\n // Payment method handlers\n // Note: We don't use client.refetchQueries here because it tries to refetch ALL matching queries\n // including those that were skipped (e.g., GET_BILLING_ACCOUNT_DASHBOARD with no id).\n // The calling component should handle refetching the appropriate queries.\n const addPaymentMethod = useCallback(\n async (input: CreatePaymentMethodInput): Promise<PaymentMethod> => {\n const { data, error } = await addPaymentMethodMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to add payment method');\n }\n if (!data?.addPaymentMethod) {\n throw new Error('Failed to add payment method: no data returned');\n }\n return data.addPaymentMethod;\n },\n [addPaymentMethodMutation]\n );\n\n const setDefaultPaymentMethod = useCallback(\n async (id: string, billingAccountId: string): Promise<PaymentMethod> => {\n const { data, error } = await updatePaymentMethodMutation({\n variables: { id, isDefault: true, billingAccountId },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to update payment method');\n }\n if (!data?.updatePaymentMethod) {\n throw new Error('Failed to update payment method: no data returned');\n }\n return data.updatePaymentMethod;\n },\n [updatePaymentMethodMutation]\n );\n\n const deletePaymentMethod = useCallback(\n async (id: string, billingAccountId: string): Promise<boolean> => {\n const { data, error } = await deletePaymentMethodMutation({\n variables: { id, billingAccountId },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to delete payment method');\n }\n if (!data) {\n throw new Error('Failed to delete payment method: no data returned');\n }\n return data.deletePaymentMethod;\n },\n [deletePaymentMethodMutation]\n );\n\n // Billing address handler\n const setBillingAddress = useCallback(\n async (input: SetBillingAddressInput): Promise<BillingAddress> => {\n const { data, error } = await setBillingAddressMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to set billing address');\n }\n if (!data?.setBillingAddress) {\n throw new Error('Failed to set billing address: no data returned');\n }\n // Only refetch queries that don't have conditional required variables\n await client.refetchQueries({\n include: [GetBillingAddressDocument, GetDefaultBillingAccountDashboardDocument],\n });\n return data.setBillingAddress;\n },\n [setBillingAddressMutation, client]\n );\n\n // Payment handlers\n const initiatePayment = useCallback(\n async (input: InitiatePaymentInput): Promise<InitiatePaymentResponse> => {\n const { data, error } = await initiatePaymentMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to initiate payment');\n }\n if (!data?.initiatePayment) {\n throw new Error('Failed to initiate payment: no data returned');\n }\n return data.initiatePayment;\n },\n [initiatePaymentMutation]\n );\n\n const spendCredits = useCallback(\n async (input: SpendCreditsInput): Promise<{ creditTransactionID: string }> => {\n const { data, error } = await spendCreditsMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to spend credits');\n }\n if (!data?.spendCredits) {\n throw new Error('Failed to spend credits: no data returned');\n }\n // Only refetch queries that don't have conditional required variables\n await client.refetchQueries({\n include: [GetDefaultBillingAccountDashboardDocument, GetCreditTransactionsSummaryDocument],\n });\n return data.spendCredits;\n },\n [spendCreditsMutation, client]\n );\n\n // Verify payment method handler\n const verifyPaymentMethod = useCallback(\n async (input: VerifyPaymentMethodInput): Promise<VerifyPaymentMethodResponse> => {\n const { data, error } = await verifyPaymentMethodMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to verify payment method');\n }\n if (!data?.verifyBillingPaymentMethod) {\n throw new Error('Failed to verify payment method: no data returned');\n }\n return data.verifyBillingPaymentMethod;\n },\n [verifyPaymentMethodMutation]\n );\n\n return {\n // Billing account\n createBillingAccount,\n updateBillingAccount,\n setDefaultBillingAccount,\n isCreatingAccount,\n isUpdatingAccount,\n isSettingDefaultAccount,\n\n // Payment methods\n addPaymentMethod,\n setDefaultPaymentMethod,\n deletePaymentMethod,\n isAddingPaymentMethod,\n isUpdatingPaymentMethod,\n isDeletingPaymentMethod,\n\n // Billing address\n setBillingAddress,\n isSettingAddress,\n\n // Payments\n initiatePayment,\n spendCredits,\n verifyPaymentMethod,\n isInitiatingPayment,\n isSpendingCredits,\n isVerifyingPaymentMethod,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useDashboard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/hooks/useDashboard.ts"],"sourcesContent":["/**\n * Dashboard Module - useDashboard Hook\n * Provides access to dashboard data and mutations for billing overview\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetPaymentProviderConfigQuery,\n useGetBillingAccountsByOrgQuery,\n useGetDefaultBillingAccountDashboardQuery,\n useGetBillingAccountDashboardQuery,\n useGetActiveSubscriptionsQuery,\n useGetPaymentMethodsDashboardQuery,\n useGetBillingAddressQuery,\n useGetCreditTransactionsSummaryQuery,\n useGetRecommendedAddonsQuery,\n useGetAvailablePlansQuery,\n useCreateBillingAccountMutation,\n useUpdateBillingAccountMutation,\n useSetDefaultBillingAccountMutation,\n useAddPaymentMethodDashboardMutation,\n useUpdatePaymentMethodDashboardMutation,\n useDeletePaymentMethodDashboardMutation,\n useSetBillingAddressMutation,\n useInitiatePaymentMutation,\n useSpendCreditsMutation,\n useVerifyPaymentMethodMutation,\n GetBillingAccountsByOrgDocument,\n GetDefaultBillingAccountDashboardDocument,\n GetBillingAddressDocument,\n GetCreditTransactionsSummaryDocument,\n} from '../../../../generated/global-operations';\nimport type {\n BillingAccount,\n BillingSubscription,\n PaymentMethod,\n BillingAddress,\n CreditTransaction,\n Addon,\n Plan,\n CreateBillingAccountInput,\n UpdateBillingAccountInput,\n CreatePaymentMethodInput,\n SetBillingAddressInput,\n InitiatePaymentInput,\n InitiatePaymentResponse,\n SpendCreditsInput,\n VerifyPaymentMethodInput,\n VerifyPaymentMethodResponse,\n} from '../../../shared/types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseBillingAccountsResult {\n billingAccounts: BillingAccount[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseDashboardAccountResult {\n billingAccount: BillingAccount | null;\n paymentMethods: PaymentMethod[];\n billingAddresses: BillingAddress[];\n activeSubscriptions: BillingSubscription[];\n hasActiveSubscription: boolean;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseActiveSubscriptionsResult {\n activeSubscriptions: BillingSubscription[];\n hasActiveSubscription: boolean;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UsePaymentMethodsResult {\n paymentMethods: PaymentMethod[];\n defaultPaymentMethod: PaymentMethod | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseCreditTransactionsResult {\n creditTransactions: CreditTransaction[];\n totalCount: number;\n hasMore: boolean;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseRecommendedAddonsResult {\n addons: Addon[];\n recommendedAddons: Addon[];\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseAvailablePlansResult {\n plans: Plan[];\n activePlans: Plan[];\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseDashboardMutationsResult {\n // Billing account\n createBillingAccount: (input: CreateBillingAccountInput) => Promise<BillingAccount>;\n updateBillingAccount: (input: UpdateBillingAccountInput) => Promise<BillingAccount>;\n setDefaultBillingAccount: (id: string) => Promise<BillingAccount>;\n isCreatingAccount: boolean;\n isUpdatingAccount: boolean;\n isSettingDefaultAccount: boolean;\n\n // Payment methods\n addPaymentMethod: (input: CreatePaymentMethodInput) => Promise<PaymentMethod>;\n setDefaultPaymentMethod: (id: string, billingAccountId: string) => Promise<PaymentMethod>;\n deletePaymentMethod: (id: string, billingAccountId: string) => Promise<boolean>;\n isAddingPaymentMethod: boolean;\n isUpdatingPaymentMethod: boolean;\n isDeletingPaymentMethod: boolean;\n\n // Billing address\n setBillingAddress: (input: SetBillingAddressInput) => Promise<BillingAddress>;\n isSettingAddress: boolean;\n\n // Payments\n initiatePayment: (input: InitiatePaymentInput) => Promise<InitiatePaymentResponse>;\n spendCredits: (input: SpendCreditsInput) => Promise<{ creditTransactionID: string }>;\n verifyPaymentMethod: (input: VerifyPaymentMethodInput) => Promise<VerifyPaymentMethodResponse>;\n isInitiatingPayment: boolean;\n isSpendingCredits: boolean;\n isVerifyingPaymentMethod: boolean;\n}\n\ninterface PaymentProviderConfig {\n stripeEnabled: boolean;\n stripePublishableKey: string | null;\n razorpayEnabled: boolean;\n razorpayKeyId: string | null;\n}\n\ninterface UsePaymentProviderConfigResult {\n config: PaymentProviderConfig | null;\n stripeEnabled: boolean;\n stripePublishableKey: string | null;\n razorpayEnabled: boolean;\n razorpayKeyId: string | null;\n isLoading: boolean;\n error: Error | undefined;\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook for fetching payment provider configuration from backend\n * @param skip - Skip the query (useful for modals that only need data when open)\n */\nexport function usePaymentProviderConfig(skip = false): UsePaymentProviderConfigResult {\n const { data, loading, error } = useGetPaymentProviderConfigQuery({\n fetchPolicy: 'cache-first', // Cache the config as it rarely changes\n skip,\n });\n\n const config = data?.getPaymentProviderConfig || null;\n\n return {\n config,\n stripeEnabled: config?.stripeEnabled ?? false,\n stripePublishableKey: config?.stripePublishableKey ?? null,\n razorpayEnabled: config?.razorpayEnabled ?? false,\n razorpayKeyId: config?.razorpayKeyId ?? null,\n isLoading: skip ? false : loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for fetching all billing accounts for an org\n */\nexport function useBillingAccounts(): UseBillingAccountsResult {\n const { data, loading, error, refetch } = useGetBillingAccountsByOrgQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n billingAccounts: data?.getBillingAccountsByOrg || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching active subscriptions for a billing account (optimized - only active status)\n */\nexport function useActiveSubscriptions(\n billingAccountId: string | undefined\n): UseActiveSubscriptionsResult {\n const { data, loading, error, refetch } = useGetActiveSubscriptionsQuery({\n variables: { billingAccountId: billingAccountId! },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const activeSubscriptions = data?.getActiveSubscriptions || [];\n\n return {\n activeSubscriptions,\n hasActiveSubscription: activeSubscriptions.length > 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching default billing account with dashboard details\n * Note: Subscriptions are NOT included - use useActiveSubscriptions separately\n */\nexport function useDefaultDashboardAccount(): Omit<\n UseDashboardAccountResult,\n 'activeSubscriptions' | 'hasActiveSubscription'\n> {\n const { data, loading, error, refetch } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getDefaultBillingAccount || null;\n const paymentMethods = (billingAccount?.paymentMethods || []).filter(Boolean) as PaymentMethod[];\n const billingAddresses = (billingAccount?.billingAddresses || []).filter(\n Boolean\n ) as BillingAddress[];\n\n return {\n billingAccount,\n paymentMethods,\n billingAddresses,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account by ID with dashboard details\n * Note: Subscriptions are NOT included - use useActiveSubscriptions separately\n */\nexport function useDashboardAccount(\n id: string | undefined\n): Omit<UseDashboardAccountResult, 'activeSubscriptions' | 'hasActiveSubscription'> {\n const { data, loading, error, refetch } = useGetBillingAccountDashboardQuery({\n variables: { id: id! },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getBillingAccount || null;\n const paymentMethods = (billingAccount?.paymentMethods || []).filter(Boolean) as PaymentMethod[];\n const billingAddresses = (billingAccount?.billingAddresses || []).filter(\n Boolean\n ) as BillingAddress[];\n\n return {\n billingAccount,\n paymentMethods,\n billingAddresses,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching payment methods for a billing account\n */\nexport function usePaymentMethods(billingAccountId: string | undefined): UsePaymentMethodsResult {\n const { data, loading, error, refetch } = useGetPaymentMethodsDashboardQuery({\n variables: { billingAccountId: billingAccountId! },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const paymentMethods = useMemo<PaymentMethod[]>(\n () => data?.getPaymentMethods || [],\n [data?.getPaymentMethods]\n );\n const defaultPaymentMethod = useMemo(() => {\n return paymentMethods.find((pm: PaymentMethod) => pm.isDefault) || null;\n }, [paymentMethods]);\n\n return {\n paymentMethods,\n defaultPaymentMethod,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing address\n */\nexport function useBillingAddress(billingAccountId: string | undefined) {\n const { data, loading, error, refetch } = useGetBillingAddressQuery({\n variables: { billingAccountId: billingAccountId! },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n billingAddress: data?.getBillingAddress || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching credit transactions summary\n */\nexport function useCreditTransactionsSummary(\n billingAccountId: string | undefined,\n days = 30,\n page = 1,\n pageSize = 5\n): UseCreditTransactionsResult {\n const { data, loading, error, refetch } = useGetCreditTransactionsSummaryQuery({\n variables: { billingAccountID: billingAccountId!, days, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const result = data?.getBillingAccountCreditTransactions || {\n creditTransactions: [],\n totalCount: 0,\n hasMore: false,\n };\n\n return {\n creditTransactions: result.creditTransactions,\n totalCount: result.totalCount,\n hasMore: result.hasMore,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching recommended addons (limited to 3 at backend level)\n */\nexport function useRecommendedAddons(): UseRecommendedAddonsResult {\n const { data, loading, error } = useGetRecommendedAddonsQuery({\n variables: { limit: 3 },\n fetchPolicy: 'cache-and-network',\n });\n\n const addons = useMemo<Addon[]>(() => data?.getAllAddOns || [], [data?.getAllAddOns]);\n\n // Filter to only active addons (limit already applied at backend)\n const recommendedAddons = useMemo(() => {\n return addons.filter((a: Addon) => a.isActive);\n }, [addons]);\n\n return {\n addons,\n recommendedAddons,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for fetching available plans\n */\nexport function useAvailablePlans(): UseAvailablePlansResult {\n const { data, loading, error } = useGetAvailablePlansQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const plans = useMemo<Plan[]>(() => data?.getAllPlans || [], [data?.getAllPlans]);\n\n const activePlans = useMemo(() => {\n return plans.filter((p: Plan) => p.isActive);\n }, [plans]);\n\n return {\n plans,\n activePlans,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for dashboard mutations\n */\nexport function useDashboardMutations(): UseDashboardMutationsResult {\n const client = useApolloClient();\n\n // Billing account mutations\n const [createBillingAccountMutation, { loading: isCreatingAccount }] =\n useCreateBillingAccountMutation();\n const [updateBillingAccountMutation, { loading: isUpdatingAccount }] =\n useUpdateBillingAccountMutation();\n const [setDefaultBillingAccountMutation, { loading: isSettingDefaultAccount }] =\n useSetDefaultBillingAccountMutation();\n\n // Payment method mutations\n const [addPaymentMethodMutation, { loading: isAddingPaymentMethod }] =\n useAddPaymentMethodDashboardMutation();\n const [updatePaymentMethodMutation, { loading: isUpdatingPaymentMethod }] =\n useUpdatePaymentMethodDashboardMutation();\n const [deletePaymentMethodMutation, { loading: isDeletingPaymentMethod }] =\n useDeletePaymentMethodDashboardMutation();\n\n // Billing address mutation\n const [setBillingAddressMutation, { loading: isSettingAddress }] = useSetBillingAddressMutation();\n\n // Payment mutations\n const [initiatePaymentMutation, { loading: isInitiatingPayment }] = useInitiatePaymentMutation();\n const [spendCreditsMutation, { loading: isSpendingCredits }] = useSpendCreditsMutation();\n const [verifyPaymentMethodMutation, { loading: isVerifyingPaymentMethod }] =\n useVerifyPaymentMethodMutation();\n\n // Billing account handlers\n const createBillingAccount = useCallback(\n async (input: CreateBillingAccountInput): Promise<BillingAccount> => {\n const { data, error } = await createBillingAccountMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to create billing account');\n }\n if (!data?.createBillingAccount) {\n throw new Error('Failed to create billing account: no data returned');\n }\n await client.refetchQueries({\n include: [GetBillingAccountsByOrgDocument, GetDefaultBillingAccountDashboardDocument],\n });\n return data.createBillingAccount;\n },\n [createBillingAccountMutation, client]\n );\n\n const updateBillingAccount = useCallback(\n async (input: UpdateBillingAccountInput): Promise<BillingAccount> => {\n const { data, error } = await updateBillingAccountMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to update billing account');\n }\n if (!data?.updateBillingAccount) {\n throw new Error('Failed to update billing account: no data returned');\n }\n // Only refetch the default dashboard - component handles selected account refetch\n await client.refetchQueries({\n include: [GetDefaultBillingAccountDashboardDocument],\n });\n return data.updateBillingAccount;\n },\n [updateBillingAccountMutation, client]\n );\n\n const setDefaultBillingAccount = useCallback(\n async (id: string): Promise<BillingAccount> => {\n const { data, error } = await setDefaultBillingAccountMutation({\n variables: { id },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to set default billing account');\n }\n if (!data?.setDefaultBillingAccount) {\n throw new Error('Failed to set default billing account: no data returned');\n }\n // Refetch all billing accounts and default account to update isDefault flags\n await client.refetchQueries({\n include: [GetBillingAccountsByOrgDocument, GetDefaultBillingAccountDashboardDocument],\n });\n return data.setDefaultBillingAccount;\n },\n [setDefaultBillingAccountMutation, client]\n );\n\n // Payment method handlers\n // Note: We don't use client.refetchQueries here because it tries to refetch ALL matching queries\n // including those that were skipped (e.g., GET_BILLING_ACCOUNT_DASHBOARD with no id).\n // The calling component should handle refetching the appropriate queries.\n const addPaymentMethod = useCallback(\n async (input: CreatePaymentMethodInput): Promise<PaymentMethod> => {\n const { data, error } = await addPaymentMethodMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to add payment method');\n }\n if (!data?.addPaymentMethod) {\n throw new Error('Failed to add payment method: no data returned');\n }\n return data.addPaymentMethod;\n },\n [addPaymentMethodMutation]\n );\n\n const setDefaultPaymentMethod = useCallback(\n async (id: string, billingAccountId: string): Promise<PaymentMethod> => {\n const { data, error } = await updatePaymentMethodMutation({\n variables: { id, isDefault: true, billingAccountId },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to update payment method');\n }\n if (!data?.updatePaymentMethod) {\n throw new Error('Failed to update payment method: no data returned');\n }\n return data.updatePaymentMethod;\n },\n [updatePaymentMethodMutation]\n );\n\n const deletePaymentMethod = useCallback(\n async (id: string, billingAccountId: string): Promise<boolean> => {\n const { data, error } = await deletePaymentMethodMutation({\n variables: { id, billingAccountId },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to delete payment method');\n }\n if (!data) {\n throw new Error('Failed to delete payment method: no data returned');\n }\n return data.deletePaymentMethod;\n },\n [deletePaymentMethodMutation]\n );\n\n // Billing address handler\n const setBillingAddress = useCallback(\n async (input: SetBillingAddressInput): Promise<BillingAddress> => {\n const { data, error } = await setBillingAddressMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to set billing address');\n }\n if (!data?.setBillingAddress) {\n throw new Error('Failed to set billing address: no data returned');\n }\n // Only refetch queries that don't have conditional required variables\n await client.refetchQueries({\n include: [GetBillingAddressDocument, GetDefaultBillingAccountDashboardDocument],\n });\n return data.setBillingAddress;\n },\n [setBillingAddressMutation, client]\n );\n\n // Payment handlers\n const initiatePayment = useCallback(\n async (input: InitiatePaymentInput): Promise<InitiatePaymentResponse> => {\n const { data, error } = await initiatePaymentMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to initiate payment');\n }\n if (!data?.initiatePayment) {\n throw new Error('Failed to initiate payment: no data returned');\n }\n return data.initiatePayment;\n },\n [initiatePaymentMutation]\n );\n\n const spendCredits = useCallback(\n async (input: SpendCreditsInput): Promise<{ creditTransactionID: string }> => {\n const { data, error } = await spendCreditsMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to spend credits');\n }\n if (!data?.spendCredits) {\n throw new Error('Failed to spend credits: no data returned');\n }\n // Only refetch queries that don't have conditional required variables\n await client.refetchQueries({\n include: [GetDefaultBillingAccountDashboardDocument, GetCreditTransactionsSummaryDocument],\n });\n return data.spendCredits;\n },\n [spendCreditsMutation, client]\n );\n\n // Verify payment method handler\n const verifyPaymentMethod = useCallback(\n async (input: VerifyPaymentMethodInput): Promise<VerifyPaymentMethodResponse> => {\n const { data, error } = await verifyPaymentMethodMutation({\n variables: { input },\n });\n if (error) {\n throw new Error(error.message ?? 'Failed to verify payment method');\n }\n if (!data?.verifyBillingPaymentMethod) {\n throw new Error('Failed to verify payment method: no data returned');\n }\n return data.verifyBillingPaymentMethod;\n },\n [verifyPaymentMethodMutation]\n );\n\n return {\n // Billing account\n createBillingAccount,\n updateBillingAccount,\n setDefaultBillingAccount,\n isCreatingAccount,\n isUpdatingAccount,\n isSettingDefaultAccount,\n\n // Payment methods\n addPaymentMethod,\n setDefaultPaymentMethod,\n deletePaymentMethod,\n isAddingPaymentMethod,\n isUpdatingPaymentMethod,\n isDeletingPaymentMethod,\n\n // Billing address\n setBillingAddress,\n isSettingAddress,\n\n // Payments\n initiatePayment,\n spendCredits,\n verifyPaymentMethod,\n isInitiatingPayment,\n isSpendingCredits,\n isVerifyingPaymentMethod,\n };\n}\n"],"mappings":"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"}
@@ -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 { 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';\nimport {\n Button,\n CTAOverflowMenu,\n GlassCard,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\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-text-link\" />\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.overview.loadingInfo', 'Loading billing information...')}\n </p>\n </div>\n </div>\n );\n }\n\n // No billing accounts state. Checked against `billingAccounts` AND\n // `currentAccount` (not just the list) because getDefaultBillingAccount\n // lazy-creates a default account for a brand-new org — on that exact\n // first request, getBillingAccountsByOrg's plain read can still observe\n // zero rows in the same response that returned a real, freshly-created\n // currentAccount. A valid currentAccount always means there's a real\n // billing account regardless of what the (possibly momentarily stale)\n // list says.\n if (!isLoading && billingAccounts.length === 0 && !currentAccount) {\n return (\n <div className=\"px-6 lg:px-8 py-6\">\n <header className=\"relative overflow-hidden mb-8 rounded-card\">\n <AuroraBackground intensity=\"subtle\" />\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.emptySetupDescription',\n 'Set up billing to manage subscriptions, payments, and invoices'\n )}\n </p>\n </header>\n <IllustratedEmptyState\n illustration=\"onboarding\"\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-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\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=\"px-6 lg:px-8 py-6 space-y-6 sm:space-y-8\">\n {/* Header */}\n <header className=\"relative overflow-hidden rounded-card flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.description',\n 'Manage your billing accounts, subscriptions, and payment methods'\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.overview.purpose',\n 'Your billing home base — see your current plan, credit balance and payment methods at a glance, and jump to transactions, usage, credits or new plans. Start here to understand what you are paying for and manage it.'\n )}\n </PagePurpose>\n </div>\n <CTAOverflowMenu\n primary={\n permissions.canManageBillingAccount && billingAccounts.length > 0\n ? {\n label: tr('billing.overview.newAccount', 'New Account'),\n onSelect: () => setIsCreateModalOpen(true),\n icon: <Plus className=\"size-4\" />,\n }\n : undefined\n }\n actions={[\n {\n label: tr('billing.overview.refresh', 'Refresh'),\n onSelect: () => {\n void handleRefresh();\n },\n icon: <RefreshCw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} />,\n },\n ]}\n />\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-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Building2 className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {currentAccount?.name || tr('billing.overview.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-text-secondary\">{currentAccount?.email}</p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted 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-bg-elevated border border-border-seam rounded-card shadow-elevation-3 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-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{account.email}</p>\n </div>\n </div>\n {currentAccount?.id === account.id && (\n <Check className=\"size-4 text-text-link 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-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Crown className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {selectedSubscription?.plan?.name ||\n tr('billing.overview.selectSubscription', 'Select Subscription')}\n {selectedSubscription?.plan?.product?.name && (\n <span className=\"text-text-secondary font-normal\">\n {' '}\n · {selectedSubscription.plan.product.name}\n </span>\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {activeSubscriptions.length}{' '}\n {tr('billing.overview.activeSubscriptions', 'active subscriptions')}\n </p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted 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-bg-elevated border border-border-seam rounded-card shadow-elevation-3 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-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {subscription.plan?.name ||\n tr('billing.overview.subscription', 'Subscription')}\n </p>\n <p className=\"text-xs text-text-secondary 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-text-link 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-seam rounded-card bg-bg-surface shadow-elevation-1 p-5\"\n data-tour=\"billing-overview-quick-actions\"\n >\n <h3 className=\"font-semibold text-text-primary mb-4\">Quick Actions</h3>\n <div className=\"space-y-1.5\">\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-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <Receipt className=\"size-4 text-status-info-text\" />\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-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg 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-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <div className=\"size-9 rounded-lg 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-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Activity className=\"size-4 text-text-link\" />\n </div>\n Usage Analytics\n </button>\n </div>\n </section>\n\n {/* Get Started (if no subscription) — primary emphasis zone */}\n {!hasActiveSubscription && (\n <GlassCard treatment=\"glass\" glow className=\"p-5\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"size-10 rounded-lg bg-bg-surface flex items-center justify-center flex-shrink-0 shadow-elevation-1\">\n <Sparkles className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">Get Started</h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n Choose a plan that fits your needs to unlock all features\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigateTo('/plans')} className=\"w-full\">\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </Button>\n </GlassCard>\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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"}
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 { 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';\nimport {\n Button,\n CTAOverflowMenu,\n GlassCard,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\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-text-link\" />\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.overview.loadingInfo', 'Loading billing information...')}\n </p>\n </div>\n </div>\n );\n }\n\n // No billing accounts state. Checked against `billingAccounts` AND\n // `currentAccount` (not just the list) because getDefaultBillingAccount\n // lazy-creates a default account for a brand-new org — on that exact\n // first request, getBillingAccountsByOrg's plain read can still observe\n // zero rows in the same response that returned a real, freshly-created\n // currentAccount. A valid currentAccount always means there's a real\n // billing account regardless of what the (possibly momentarily stale)\n // list says.\n if (!isLoading && billingAccounts.length === 0 && !currentAccount) {\n return (\n <div className=\"px-6 lg:px-8 py-6\">\n <header className=\"relative overflow-hidden mb-8 rounded-card\">\n <AuroraBackground intensity=\"subtle\" />\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.emptySetupDescription',\n 'Set up billing to manage subscriptions, payments, and invoices'\n )}\n </p>\n </header>\n <IllustratedEmptyState\n illustration=\"onboarding\"\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-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\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=\"px-6 lg:px-8 py-6 space-y-6 sm:space-y-8\">\n {/* Header */}\n <header className=\"relative overflow-hidden rounded-card flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.description',\n 'Manage your billing accounts, subscriptions, and payment methods'\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.overview.purpose',\n 'Your billing home base — see your current plan, credit balance and payment methods at a glance, and jump to transactions, usage, credits or new plans. Start here to understand what you are paying for and manage it.'\n )}\n </PagePurpose>\n </div>\n <CTAOverflowMenu\n primary={\n permissions.canManageBillingAccount && billingAccounts.length > 0\n ? {\n label: tr('billing.overview.newAccount', 'New Account'),\n onSelect: () => setIsCreateModalOpen(true),\n icon: <Plus className=\"size-4\" />,\n }\n : undefined\n }\n actions={[\n {\n label: tr('billing.overview.refresh', 'Refresh'),\n onSelect: () => {\n void handleRefresh();\n },\n icon: <RefreshCw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} />,\n },\n ]}\n />\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-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Building2 className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {currentAccount?.name || tr('billing.overview.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-text-secondary\">{currentAccount?.email}</p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted 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-bg-elevated border border-border-seam rounded-card shadow-elevation-3 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-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{account.email}</p>\n </div>\n </div>\n {currentAccount?.id === account.id && (\n <Check className=\"size-4 text-text-link 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-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Crown className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {selectedSubscription?.plan?.name ||\n tr('billing.overview.selectSubscription', 'Select Subscription')}\n {selectedSubscription?.plan?.product?.name && (\n <span className=\"text-text-secondary font-normal\">\n {' '}\n · {selectedSubscription.plan.product.name}\n </span>\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {activeSubscriptions.length}{' '}\n {tr('billing.overview.activeSubscriptions', 'active subscriptions')}\n </p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted 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-bg-elevated border border-border-seam rounded-card shadow-elevation-3 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-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {subscription.plan?.name ||\n tr('billing.overview.subscription', 'Subscription')}\n </p>\n <p className=\"text-xs text-text-secondary 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-text-link 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-seam rounded-card bg-bg-surface shadow-elevation-1 p-5\"\n data-tour=\"billing-overview-quick-actions\"\n >\n <h3 className=\"font-semibold text-text-primary mb-4\">Quick Actions</h3>\n <div className=\"space-y-1.5\">\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-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <Receipt className=\"size-4 text-status-info-text\" />\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-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg 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-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <div className=\"size-9 rounded-lg 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-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Activity className=\"size-4 text-text-link\" />\n </div>\n Usage Analytics\n </button>\n </div>\n </section>\n\n {/* Get Started (if no subscription) — primary emphasis zone */}\n {!hasActiveSubscription && (\n <GlassCard treatment=\"glass\" glow className=\"p-5\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"size-10 rounded-lg bg-bg-surface flex items-center justify-center flex-shrink-0 shadow-elevation-1\">\n <Sparkles className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">Get Started</h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n Choose a plan that fits your needs to unlock all features\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigateTo('/plans')} className=\"w-full\">\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </Button>\n </GlassCard>\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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"}