@burdenoff/microfe-billing 2026.526.1 → 2026.528.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingAdminRoutes.js +1 -1
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/BillingUserRoutes.js +1 -1
- package/dist/billing/BillingUserRoutes.js.map +1 -1
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js +15 -8
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +13 -6
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +8 -4
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +3 -2
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +44 -23
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +60 -31
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +9 -9
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +8 -8
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +16 -10
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +24 -16
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +5 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +46 -29
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +34 -22
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +10 -10
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js +3 -3
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/NativeCheckoutView.js +7 -0
- package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js +13 -13
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/PromoCodeInput.js +5 -5
- package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +4 -4
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
- package/dist/billing/modules/checkout/components/StripePaymentForm.js +11 -11
- package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +3 -3
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +8 -8
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +61 -52
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +22 -15
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +30 -21
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +9 -8
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +11 -8
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +22 -17
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +15 -8
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +17 -11
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +15 -11
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +21 -17
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +14 -11
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +32 -26
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +14 -10
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +18 -11
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +44 -32
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +35 -26
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +15 -15
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +11 -11
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +5 -5
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +9 -4
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +13 -8
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +3 -2
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +14 -7
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +23 -9
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js +11 -7
- package/dist/billing/modules/quota/pages/QuotaPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +3 -3
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +6 -2
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +5 -4
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +26 -12
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +356 -349
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +157 -123
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +13 -8
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +15 -7
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +30 -20
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +25 -16
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +50 -34
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/components/AccessDenied.js +2 -2
- package/dist/billing/shared/components/AccessDenied.js.map +1 -1
- package/dist/billing/shared/components/ActorIdentity.js +67 -0
- package/dist/billing/shared/components/ActorIdentity.js.map +1 -0
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +6 -2
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
- package/dist/billing/shared/components/EmptyState.js +7 -21
- package/dist/billing/shared/components/EmptyState.js.map +1 -1
- package/dist/billing/shared/components/PageHeader.js +7 -19
- package/dist/billing/shared/components/PageHeader.js.map +1 -1
- package/dist/billing/shared/components/ServerError.js +5 -4
- package/dist/billing/shared/components/ServerError.js.map +1 -1
- package/dist/billing/shared/components/index.js +1 -0
- package/dist/billing/shared/hooks/index.js +1 -0
- package/dist/billing/shared/hooks/useActorProfiles.js +113 -0
- package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActiveSubscriptionCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/ActiveSubscriptionCard.tsx"],"sourcesContent":["/**\n * Active Subscription Card Component\n * Displays the active subscription details with renewal info\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Crown,\n Calendar,\n ArrowRight,\n RefreshCw,\n Clock,\n CreditCard,\n Zap,\n AlertTriangle,\n Coins,\n} from 'lucide-react';\nimport type { BillingSubscription } from '../../../shared/types';\nimport { statusTokens, alertTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface ActiveSubscriptionCardProps {\n subscription: BillingSubscription | null;\n onViewDetails?: () => void;\n onUpgrade?: () => void;\n onManage?: () => void;\n onPurchasePlan?: () => void;\n className?: string;\n}\n\nexport const ActiveSubscriptionCard: FC<ActiveSubscriptionCardProps> = ({\n subscription,\n onViewDetails,\n onUpgrade,\n onManage,\n onPurchasePlan,\n className = '',\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 const formatDate = (dateString: string | undefined) => {\n if (!dateString) return 'N/A';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\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: 2,\n }).format(amount);\n };\n\n const daysUntilEnd = useMemo(() => {\n if (!subscription?.endDate) return null;\n const end = new Date(subscription.endDate);\n const now = new Date();\n const diff = Math.ceil((end.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n return diff;\n }, [subscription?.endDate]);\n\n const isExpiringSoon = daysUntilEnd !== null && daysUntilEnd <= 7 && daysUntilEnd > 0;\n const isExpired = daysUntilEnd !== null && daysUntilEnd <= 0;\n\n const getStatusColor = () => {\n if (isExpired) return `${statusTokens.error.text} ${statusTokens.error.bg}`;\n if (isExpiringSoon) return `${statusTokens.warning.text} ${statusTokens.warning.bg}`;\n return `${statusTokens.success.text} ${statusTokens.success.bg}`;\n };\n\n const getStatusText = () => {\n if (isExpired) return 'Expired';\n if (isExpiringSoon) return 'Expiring Soon';\n return 'Active';\n };\n\n // No subscription state\n if (!subscription) {\n return (\n <section className={`border border-border rounded-lg bg-card p-6 ${className}`}>\n <div className=\"flex flex-col items-center text-center py-4\">\n <div className=\"w-16 h-16 rounded-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center mb-4\">\n <Zap className=\"w-8 h-8 text-primary\" />\n </div>\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('billing.overview.noActiveSubscription', 'No Active Subscription')}\n </h3>\n <p className=\"text-sm text-muted-foreground mb-6 max-w-sm\">\n Subscribe to a plan to unlock premium features and get the most out of your account\n </p>\n {onPurchasePlan && (\n <button\n onClick={onPurchasePlan}\n className=\"inline-flex items-center gap-2 px-6 py-2.5 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Browse Plans\n <ArrowRight className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n </section>\n );\n }\n\n const plan = subscription.plan;\n\n return (\n <section className={`border border-border rounded-lg bg-card overflow-hidden ${className}`}>\n {/* Expiring Soon Banner */}\n {isExpiringSoon && (\n <div className={`px-4 py-2 ${alertTokens.warning.container} border-b`}>\n <div className={`flex items-center gap-2 ${alertTokens.warning.text}`}>\n <AlertTriangle className=\"w-4 h-4\" />\n <span className=\"text-sm font-medium\">\n Your subscription expires in {daysUntilEnd} day{daysUntilEnd !== 1 ? 's' : ''}\n </span>\n </div>\n </div>\n )}\n\n {/* Header */}\n <header className=\"p-4 sm:p-6 border-b border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-12 h-12 rounded-lg bg-gradient-to-br from-primary to-primary/60 flex items-center justify-center flex-shrink-0\">\n <Crown className=\"w-6 h-6 text-primary-foreground\" />\n </div>\n <div>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-lg font-semibold text-foreground\">\n {plan?.name || 'Subscription'}\n </h3>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${getStatusColor()}`}\n >\n {getStatusText()}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {plan && formatCurrency(plan.price, plan.currency)} / {plan?.duration || 'month'}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {onManage && (\n <button\n onClick={onManage}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors\"\n >\n Manage\n </button>\n )}\n {onUpgrade && !isExpired && (\n <button\n onClick={onUpgrade}\n className=\"px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Upgrade\n </button>\n )}\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 sm:p-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {/* Start Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Started\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <Calendar className=\"w-4 h-4 text-muted-foreground\" />\n {formatDate(subscription.startDate)}\n </p>\n </div>\n\n {/* End Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n {isExpired ? 'Expired' : 'Ends'}\n </p>\n <p\n className={`text-sm font-medium flex items-center gap-2 ${isExpired ? statusTokens.error.text : isExpiringSoon ? statusTokens.warning.text : 'text-foreground'}`}\n >\n <Clock className=\"w-4 h-4\" />\n {formatDate(subscription.endDate)}\n </p>\n </div>\n\n {/* Auto Renewal */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Auto Renewal\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <RefreshCw\n className={`w-4 h-4 ${subscription.autoRenewal ? statusTokens.success.icon : 'text-muted-foreground'}`}\n />\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n\n {/* Payment Gateway */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Via\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n {subscription.paymentGateway === 'CREDITS' ? (\n <Coins className={`w-4 h-4 ${statusTokens.warning.icon}`} />\n ) : (\n <CreditCard className=\"w-4 h-4 text-muted-foreground\" />\n )}\n {subscription.paymentGateway === 'CREDITS'\n ? 'Credits'\n : subscription.paymentGateway === 'STRIPE'\n ? 'Stripe'\n : subscription.paymentGateway === 'RAZORPAY'\n ? 'Razorpay'\n : subscription.paymentGateway || 'N/A'}\n </p>\n </div>\n </div>\n\n {/* Next Billing */}\n {subscription.nextBillingDate && subscription.autoRenewal && !isExpired && (\n <div className=\"mt-6 p-4 rounded-lg bg-muted/50 border border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr('billing.overview.nextBillingDate', 'Next billing date')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n You'll be charged automatically\n </p>\n </div>\n <p className=\"text-sm font-semibold text-foreground\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {onViewDetails && (\n <footer className=\"px-4 sm:px-6 py-3 border-t border-border bg-muted/30\">\n <button\n onClick={onViewDetails}\n className=\"w-full flex items-center justify-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n View Subscription Details\n <ArrowRight className=\"w-4 h-4\" />\n </button>\n </footer>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActiveSubscriptionCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/ActiveSubscriptionCard.tsx"],"sourcesContent":["/**\n * Active Subscription Card Component\n * Displays the active subscription details with renewal info\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Crown,\n Calendar,\n ArrowRight,\n RefreshCw,\n Clock,\n CreditCard,\n Zap,\n AlertTriangle,\n Coins,\n} from 'lucide-react';\nimport type { BillingSubscription } from '../../../shared/types';\nimport { statusTokens, alertTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface ActiveSubscriptionCardProps {\n subscription: BillingSubscription | null;\n onViewDetails?: () => void;\n onUpgrade?: () => void;\n onManage?: () => void;\n onPurchasePlan?: () => void;\n className?: string;\n}\n\nexport const ActiveSubscriptionCard: FC<ActiveSubscriptionCardProps> = ({\n subscription,\n onViewDetails,\n onUpgrade,\n onManage,\n onPurchasePlan,\n className = '',\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 const formatDate = (dateString: string | undefined) => {\n if (!dateString) return 'N/A';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\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: 2,\n }).format(amount);\n };\n\n const daysUntilEnd = useMemo(() => {\n if (!subscription?.endDate) return null;\n const end = new Date(subscription.endDate);\n const now = new Date();\n const diff = Math.ceil((end.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n return diff;\n }, [subscription?.endDate]);\n\n const isExpiringSoon = daysUntilEnd !== null && daysUntilEnd <= 7 && daysUntilEnd > 0;\n const isExpired = daysUntilEnd !== null && daysUntilEnd <= 0;\n\n const getStatusColor = () => {\n if (isExpired) return `${statusTokens.error.text} ${statusTokens.error.bg}`;\n if (isExpiringSoon) return `${statusTokens.warning.text} ${statusTokens.warning.bg}`;\n return `${statusTokens.success.text} ${statusTokens.success.bg}`;\n };\n\n const getStatusText = () => {\n if (isExpired) return 'Expired';\n if (isExpiringSoon) return 'Expiring Soon';\n return 'Active';\n };\n\n // No subscription state\n if (!subscription) {\n return (\n <section className={`border border-border rounded-lg bg-card p-6 ${className}`}>\n <div className=\"flex flex-col items-center text-center py-4\">\n <div className=\"size-16 rounded-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center mb-4\">\n <Zap className=\"size-8 text-primary\" />\n </div>\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('billing.overview.noActiveSubscription', 'No Active Subscription')}\n </h3>\n <p className=\"text-sm text-muted-foreground mb-6 max-w-sm\">\n Subscribe to a plan to unlock premium features and get the most out of your account\n </p>\n {onPurchasePlan && (\n <button\n type=\"button\"\n onClick={onPurchasePlan}\n className=\"inline-flex items-center gap-2 px-6 py-2.5 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </button>\n )}\n </div>\n </section>\n );\n }\n\n const plan = subscription.plan;\n\n return (\n <section className={`border border-border rounded-lg bg-card overflow-hidden ${className}`}>\n {/* Expiring Soon Banner */}\n {isExpiringSoon && (\n <div className={`px-4 py-2 ${alertTokens.warning.container} border-b`}>\n <div className={`flex items-center gap-2 ${alertTokens.warning.text}`}>\n <AlertTriangle className=\"size-4\" />\n <span className=\"text-sm font-medium\">\n Your subscription expires in {daysUntilEnd} day{daysUntilEnd !== 1 ? 's' : ''}\n </span>\n </div>\n </div>\n )}\n\n {/* Header */}\n <header className=\"p-4 sm:p-6 border-b border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-12 rounded-lg bg-gradient-to-br from-primary to-primary/60 flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-6 text-primary-foreground\" />\n </div>\n <div>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-lg font-semibold text-foreground\">\n {plan?.name || 'Subscription'}\n </h3>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${getStatusColor()}`}\n >\n {getStatusText()}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {plan && formatCurrency(plan.price, plan.currency)} / {plan?.duration || 'month'}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {onManage && (\n <button\n type=\"button\"\n onClick={onManage}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors\"\n >\n Manage\n </button>\n )}\n {onUpgrade && !isExpired && (\n <button\n type=\"button\"\n onClick={onUpgrade}\n className=\"px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Upgrade\n </button>\n )}\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 sm:p-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {/* Start Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Started\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <Calendar className=\"size-4 text-muted-foreground\" />\n {formatDate(subscription.startDate)}\n </p>\n </div>\n\n {/* End Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n {isExpired ? 'Expired' : 'Ends'}\n </p>\n <p\n className={`text-sm font-medium flex items-center gap-2 ${isExpired ? statusTokens.error.text : isExpiringSoon ? statusTokens.warning.text : 'text-foreground'}`}\n >\n <Clock className=\"size-4\" />\n {formatDate(subscription.endDate)}\n </p>\n </div>\n\n {/* Auto Renewal */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Auto Renewal\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <RefreshCw\n className={`size-4 ${subscription.autoRenewal ? statusTokens.success.icon : 'text-muted-foreground'}`}\n />\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n\n {/* Payment Gateway */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Via\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n {subscription.paymentGateway === 'CREDITS' ? (\n <Coins className={`size-4 ${statusTokens.warning.icon}`} />\n ) : (\n <CreditCard className=\"size-4 text-muted-foreground\" />\n )}\n {subscription.paymentGateway === 'CREDITS'\n ? 'Credits'\n : subscription.paymentGateway === 'STRIPE'\n ? 'Stripe'\n : subscription.paymentGateway === 'RAZORPAY'\n ? 'Razorpay'\n : subscription.paymentGateway || 'N/A'}\n </p>\n </div>\n </div>\n\n {/* Next Billing */}\n {subscription.nextBillingDate && subscription.autoRenewal && !isExpired && (\n <div className=\"mt-6 p-4 rounded-lg bg-muted/50 border border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr('billing.overview.nextBillingDate', 'Next billing date')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n You'll be charged automatically\n </p>\n </div>\n <p className=\"text-sm font-semibold text-foreground\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {onViewDetails && (\n <footer className=\"px-4 sm:px-6 py-3 border-t border-border bg-muted/30\">\n <button\n type=\"button\"\n onClick={onViewDetails}\n className=\"w-full flex items-center justify-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n View Subscription Details\n <ArrowRight className=\"size-4\" />\n </button>\n </footer>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
|
|
@@ -184,8 +184,8 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
184
184
|
children: [/* @__PURE__ */ m("div", {
|
|
185
185
|
className: "flex items-center gap-3",
|
|
186
186
|
children: [/* @__PURE__ */ p("div", {
|
|
187
|
-
className: "
|
|
188
|
-
children: /* @__PURE__ */ p(u, { className: "
|
|
187
|
+
className: "size-10 rounded-lg bg-primary/10 flex items-center justify-center",
|
|
188
|
+
children: /* @__PURE__ */ p(u, { className: "size-5 text-primary" })
|
|
189
189
|
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h2", {
|
|
190
190
|
id: "modal-title",
|
|
191
191
|
className: "text-lg font-semibold text-foreground",
|
|
@@ -200,27 +200,29 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
200
200
|
]
|
|
201
201
|
})] })]
|
|
202
202
|
}), /* @__PURE__ */ p("button", {
|
|
203
|
+
type: "button",
|
|
203
204
|
onClick: v,
|
|
204
205
|
disabled: H,
|
|
205
206
|
className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50",
|
|
206
207
|
"aria-label": "Close modal",
|
|
207
|
-
children: /* @__PURE__ */ p(ee, { className: "
|
|
208
|
+
children: /* @__PURE__ */ p(ee, { className: "size-5" })
|
|
208
209
|
})]
|
|
209
210
|
}),
|
|
210
211
|
/* @__PURE__ */ p("div", {
|
|
211
212
|
className: "p-4 overflow-y-auto flex-1",
|
|
212
213
|
children: ne ? /* @__PURE__ */ p("div", {
|
|
213
214
|
className: "flex items-center justify-center py-8",
|
|
214
|
-
children: /* @__PURE__ */ p(d, { className: "
|
|
215
|
+
children: /* @__PURE__ */ p(d, { className: "size-6 animate-spin text-muted-foreground" })
|
|
215
216
|
}) : I === "select-provider" ? /* @__PURE__ */ m("div", {
|
|
216
217
|
className: "space-y-3",
|
|
217
218
|
children: [
|
|
218
219
|
F && /* @__PURE__ */ m("button", {
|
|
220
|
+
type: "button",
|
|
219
221
|
onClick: () => Z("stripe"),
|
|
220
222
|
className: "w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left",
|
|
221
223
|
children: [/* @__PURE__ */ p("div", {
|
|
222
|
-
className: "
|
|
223
|
-
children: /* @__PURE__ */ p(u, { className: "
|
|
224
|
+
className: "size-12 rounded-lg bg-accent-violet-subtle flex items-center justify-center",
|
|
225
|
+
children: /* @__PURE__ */ p(u, { className: "size-6 text-accent-violet" })
|
|
224
226
|
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
|
|
225
227
|
className: "font-medium text-foreground",
|
|
226
228
|
children: "Stripe"
|
|
@@ -230,11 +232,12 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
230
232
|
})] })]
|
|
231
233
|
}),
|
|
232
234
|
P && /* @__PURE__ */ m("button", {
|
|
235
|
+
type: "button",
|
|
233
236
|
onClick: () => Z("razorpay"),
|
|
234
237
|
className: "w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left",
|
|
235
238
|
children: [/* @__PURE__ */ p("div", {
|
|
236
|
-
className: "
|
|
237
|
-
children: /* @__PURE__ */ p(u, { className: "
|
|
239
|
+
className: "size-12 rounded-lg bg-status-info-bg-subtle flex items-center justify-center",
|
|
240
|
+
children: /* @__PURE__ */ p(u, { className: "size-6 text-accent-blue" })
|
|
238
241
|
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
|
|
239
242
|
className: "font-medium text-foreground",
|
|
240
243
|
children: "Razorpay"
|
|
@@ -246,7 +249,7 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
246
249
|
!F && !P && /* @__PURE__ */ m("div", {
|
|
247
250
|
className: "flex flex-col items-center py-8 text-center",
|
|
248
251
|
children: [
|
|
249
|
-
/* @__PURE__ */ p(c, { className: "
|
|
252
|
+
/* @__PURE__ */ p(c, { className: "size-12 text-status-warning-text mb-3" }),
|
|
250
253
|
/* @__PURE__ */ p("p", {
|
|
251
254
|
className: "font-medium text-foreground",
|
|
252
255
|
children: C("billing.overview.noPaymentProviders", "No Payment Providers Available")
|
|
@@ -263,7 +266,7 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
263
266
|
children: [
|
|
264
267
|
B && /* @__PURE__ */ m("div", {
|
|
265
268
|
className: "flex items-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/20 text-destructive",
|
|
266
|
-
children: [/* @__PURE__ */ p(c, { className: "
|
|
269
|
+
children: [/* @__PURE__ */ p(c, { className: "size-4 flex-shrink-0" }), /* @__PURE__ */ p("p", {
|
|
267
270
|
className: "text-sm",
|
|
268
271
|
children: B
|
|
269
272
|
})]
|
|
@@ -293,12 +296,12 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
293
296
|
className: "relative",
|
|
294
297
|
children: [/* @__PURE__ */ p("div", {
|
|
295
298
|
ref: T,
|
|
296
|
-
className: "
|
|
299
|
+
className: "p-3 border border-input rounded-lg bg-background min-h-[44px]"
|
|
297
300
|
}), W && /* @__PURE__ */ m("div", {
|
|
298
301
|
className: "absolute inset-0 flex items-center justify-center bg-background/80 rounded-lg",
|
|
299
|
-
children: [/* @__PURE__ */ p(d, { className: "
|
|
302
|
+
children: [/* @__PURE__ */ p(d, { className: "size-5 animate-spin text-muted-foreground" }), /* @__PURE__ */ p("span", {
|
|
300
303
|
className: "ml-2 text-sm text-muted-foreground",
|
|
301
|
-
children: "Loading Stripe
|
|
304
|
+
children: "Loading Stripe…"
|
|
302
305
|
})]
|
|
303
306
|
})]
|
|
304
307
|
})]
|
|
@@ -316,7 +319,7 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
316
319
|
type: "checkbox",
|
|
317
320
|
checked: J,
|
|
318
321
|
onChange: (e) => Y(e.target.checked),
|
|
319
|
-
className: "
|
|
322
|
+
className: "size-4 rounded border-input text-primary focus:ring-primary/20",
|
|
320
323
|
disabled: H
|
|
321
324
|
}), /* @__PURE__ */ p("span", {
|
|
322
325
|
className: "text-sm text-foreground",
|
|
@@ -338,8 +341,8 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
338
341
|
className: "flex flex-col items-center py-8 text-center",
|
|
339
342
|
children: [
|
|
340
343
|
/* @__PURE__ */ p("div", {
|
|
341
|
-
className: "
|
|
342
|
-
children: /* @__PURE__ */ p(l, { className: "
|
|
344
|
+
className: "size-16 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-4",
|
|
345
|
+
children: /* @__PURE__ */ p(l, { className: "size-8 text-status-success-text" })
|
|
343
346
|
}),
|
|
344
347
|
/* @__PURE__ */ p("p", {
|
|
345
348
|
className: "font-medium text-foreground",
|
|
@@ -371,10 +374,11 @@ var g = (e, t) => new Promise((n, r) => {
|
|
|
371
374
|
className: "px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-50",
|
|
372
375
|
children: "Cancel"
|
|
373
376
|
}), I === "add-card" && /* @__PURE__ */ p("button", {
|
|
377
|
+
type: "button",
|
|
374
378
|
onClick: ie,
|
|
375
379
|
disabled: H || j || W || R === "stripe" && !re,
|
|
376
380
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50",
|
|
377
|
-
children: H || j ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(d, { className: "
|
|
381
|
+
children: H || j ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(d, { className: "size-4 animate-spin" }), R === "razorpay" ? "Processing..." : "Saving..."] }) : W ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(d, { className: "size-4 animate-spin" }), "Loading…"] }) : R === "stripe" ? "Add Card" : "Add Payment Method (₹1)"
|
|
378
382
|
})]
|
|
379
383
|
})]
|
|
380
384
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddPaymentMethodModal.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/AddPaymentMethodModal.tsx"],"sourcesContent":["/**\n * Add Payment Method Modal Component\n * Modal for adding a new payment method via Stripe or Razorpay SDKs\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef } from 'react';\nimport { X, CreditCard, Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePaymentProviderConfig, useDashboardMutations } from '../hooks';\nimport { PaymentProviderPreference } from '../../../shared/types';\nimport { getStripeCardElementStyle } from '../../../shared/utils';\n\n// Stripe types (loaded dynamically)\ndeclare global {\n interface Window {\n Stripe?: (key: string) => StripeInstance;\n Razorpay?: new (options: RazorpayOptions) => RazorpayInstance;\n }\n}\n\ninterface StripeInstance {\n elements: () => StripeElements;\n createPaymentMethod: (options: {\n type: 'card';\n card: StripeCardElement;\n billing_details?: { name?: string };\n }) => Promise<{\n paymentMethod?: {\n id: string;\n card?: { brand: string; last4: string; exp_month: number; exp_year: number };\n };\n error?: { message: string };\n }>;\n confirmCardSetup: (\n clientSecret: string,\n options?: {\n payment_method?: {\n card: StripeCardElement;\n billing_details?: { name?: string };\n };\n }\n ) => Promise<{\n setupIntent?: {\n id: string;\n status: string;\n payment_method?: string;\n };\n error?: { message: string };\n }>;\n}\n\ninterface StripeElements {\n create: (type: 'card', options?: object) => StripeCardElement;\n}\n\ninterface StripeCardElement {\n mount: (element: HTMLElement | string) => void;\n unmount: () => void;\n on: (\n event: string,\n handler: (event: { complete: boolean; error?: { message: string } }) => void\n ) => void;\n destroy: () => void;\n}\n\ninterface RazorpayOptions {\n key: string;\n amount: number;\n currency: string;\n order_id: string;\n customer_id?: string;\n name: string;\n description: string;\n method?: 'card' | 'upi' | 'netbanking' | 'wallet' | 'emi' | 'cardless_emi';\n handler: (response: RazorpayResponse) => void;\n modal?: { ondismiss?: () => void };\n prefill?: { name?: string; email?: string; contact?: string };\n notes?: Record<string, string>;\n config?: {\n display?: {\n hide?: Array<{ method: string }>;\n preferences?: {\n show_default_blocks?: boolean;\n };\n };\n };\n}\n\ninterface RazorpayInstance {\n open: () => void;\n close: () => void;\n}\n\ninterface RazorpayResponse {\n razorpay_payment_id: string;\n razorpay_token?: string;\n}\n\ninterface AddPaymentMethodModalProps {\n isOpen: boolean;\n onClose: () => void;\n billingAccountId: string;\n currency: string; // e.g., 'INR', 'USD'\n country?: string; // e.g., 'IN', 'US' - from billing address\n onSuccess?: () => void;\n}\n\ntype ModalStep = 'select-provider' | 'add-card' | 'processing' | 'success';\n\n// Load external script dynamically\nconst loadScript = (src: string, id: string): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (document.getElementById(id)) {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.id = id;\n script.src = src;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load ${src}`));\n document.head.appendChild(script);\n });\n};\n\nexport const AddPaymentMethodModal: FC<AddPaymentMethodModalProps> = ({\n isOpen,\n onClose,\n billingAccountId,\n currency,\n country,\n onSuccess,\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 const modalRef = useRef<HTMLDivElement>(null);\n const cardElementRef = useRef<HTMLDivElement>(null);\n const stripeCardElementRef = useRef<StripeCardElement | null>(null);\n const stripeRef = useRef<StripeInstance | null>(null);\n\n // Only fetch payment provider config when modal is open\n const {\n stripeEnabled,\n stripePublishableKey,\n razorpayEnabled,\n isLoading: isLoadingConfig,\n } = usePaymentProviderConfig(!isOpen);\n const { isAddingPaymentMethod, verifyPaymentMethod } = useDashboardMutations();\n\n // Determine which providers to show based on currency and country\n // Razorpay: India only (INR currency OR IN country)\n // Stripe: International (non-INR OR non-IN country)\n const isIndianAccount = currency?.toUpperCase() === 'INR' || country?.toUpperCase() === 'IN';\n const showRazorpay = razorpayEnabled && isIndianAccount;\n const showStripe = stripeEnabled && !isIndianAccount;\n\n const [step, setStep] = useState<ModalStep>('select-provider');\n const [selectedProvider, setSelectedProvider] = useState<'stripe' | 'razorpay' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n const [isLoadingSdk, setIsLoadingSdk] = useState(false);\n const [cardholderName, setCardholderName] = useState('');\n const [setAsDefault, setSetAsDefault] = useState(true);\n const [isCardComplete, setIsCardComplete] = useState(false);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (isOpen) {\n // Always start with provider selection\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n setCardholderName('');\n setSetAsDefault(true);\n setIsCardComplete(false);\n } else {\n // Cleanup Stripe element when modal closes\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }\n }, [isOpen]);\n\n // Note: We always show provider selection, even if only one is available\n // This ensures the user explicitly chooses their payment provider\n\n // Initialize Stripe when provider is selected\n useEffect(() => {\n if (step !== 'add-card' || selectedProvider !== 'stripe' || !stripePublishableKey) return;\n\n const initStripe = async () => {\n setIsLoadingSdk(true);\n try {\n await loadScript('https://js.stripe.com/v3/', 'stripe-js');\n\n if (!window.Stripe) {\n throw new Error('Stripe SDK failed to load');\n }\n\n const stripe = window.Stripe(stripePublishableKey);\n stripeRef.current = stripe;\n\n // Wait for the card element container to be mounted in the DOM\n let attempts = 0;\n while (!cardElementRef.current && attempts < 50) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n attempts++;\n }\n\n if (!cardElementRef.current) {\n throw new Error('Card element container not found after 5 seconds');\n }\n\n if (stripeCardElementRef.current) {\n return;\n }\n\n const elements = stripe.elements();\n const cardElement = elements.create('card', {\n style: getStripeCardElementStyle('system-ui, -apple-system, sans-serif'),\n });\n\n cardElement.mount(cardElementRef.current);\n stripeCardElementRef.current = cardElement;\n\n cardElement.on('change', (event) => {\n setIsCardComplete(event.complete);\n if (event.error) {\n setError(event.error.message);\n } else {\n setError(null);\n }\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load Stripe');\n } finally {\n setIsLoadingSdk(false);\n }\n };\n\n initStripe();\n\n return () => {\n if (stripeCardElementRef.current) {\n try {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n } catch {\n // Stripe element may already be detached during modal teardown.\n }\n }\n };\n }, [step, selectedProvider, stripePublishableKey]);\n\n // Handle escape key\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isOpen && !isProcessing) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, isProcessing, onClose]);\n\n const handleSelectProvider = useCallback((provider: 'stripe' | 'razorpay') => {\n setSelectedProvider(provider);\n setStep('add-card');\n setError(null);\n }, []);\n\n const handleStripeSubmit = useCallback(async () => {\n if (!stripeRef.current || !stripeCardElementRef.current) {\n setError('Stripe not initialized');\n return;\n }\n\n if (!cardholderName.trim()) {\n setError('Please enter the cardholder name');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n try {\n // Step 1: Call backend to create SetupIntent and get client secret\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'USD',\n provider: PaymentProviderPreference.STRIPE,\n setAsDefault,\n });\n\n if (!verificationResult.clientSecret) {\n throw new Error('Failed to initiate card verification');\n }\n\n // Step 2: Confirm card setup using the client secret (no charge)\n const { setupIntent, error: stripeError } = await stripeRef.current.confirmCardSetup(\n verificationResult.clientSecret,\n {\n payment_method: {\n card: stripeCardElementRef.current,\n billing_details: { name: cardholderName },\n },\n }\n );\n\n if (stripeError) {\n throw new Error(stripeError.message);\n }\n\n if (setupIntent?.status !== 'succeeded') {\n throw new Error('Card verification failed');\n }\n\n // Step 3: Show success (webhook will save the payment method)\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [cardholderName, setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleRazorpaySubmit = useCallback(async () => {\n setIsProcessing(true);\n setError(null);\n\n try {\n // Load Razorpay SDK\n setIsLoadingSdk(true);\n try {\n await loadScript('https://checkout.razorpay.com/v1/checkout.js', 'razorpay-sdk');\n } catch {\n setError('Failed to load Razorpay SDK');\n setIsProcessing(false);\n setIsLoadingSdk(false);\n return;\n }\n setIsLoadingSdk(false);\n\n // Initiate ₹1 verification payment\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'INR',\n provider: PaymentProviderPreference.RAZORPAY,\n setAsDefault,\n });\n\n if (\n !verificationResult.success ||\n !verificationResult.orderId ||\n !verificationResult.razorpayKeyId\n ) {\n setError(verificationResult.error || 'Failed to initiate verification');\n setIsProcessing(false);\n return;\n }\n\n // Open Razorpay checkout with ALL payment methods\n if (!window.Razorpay) {\n setError('Razorpay SDK not loaded');\n setIsProcessing(false);\n return;\n }\n\n const options = {\n key: verificationResult.razorpayKeyId,\n amount: (verificationResult.amount ?? 0) * 100, // Convert to paise\n currency: verificationResult.currency.toUpperCase(),\n order_id: verificationResult.orderId,\n customer_id: verificationResult.razorpayCustomerId, // Link to customer for saving\n name: 'Add Payment Method',\n description: `₹${verificationResult.amount} verification charge (refundable)`,\n prefill: {\n name: verificationResult.prefillName,\n email: verificationResult.prefillEmail,\n contact: verificationResult.prefillContact,\n },\n notes: {\n purchaseType: 'PAYMENT_METHOD_VERIFICATION',\n default_method: setAsDefault.toString(),\n },\n handler: async () => {\n // Payment successful - payment method will be saved via webhook\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n },\n modal: {\n ondismiss: () => {\n setIsProcessing(false);\n setError('Payment cancelled');\n },\n },\n };\n\n const rzp = new window.Razorpay(options);\n rzp.open();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleSubmit = useCallback(() => {\n if (selectedProvider === 'stripe') {\n handleStripeSubmit();\n } else if (selectedProvider === 'razorpay') {\n handleRazorpaySubmit();\n }\n }, [selectedProvider, handleStripeSubmit, handleRazorpaySubmit]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n >\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\"\n onClick={!isProcessing ? onClose : undefined}\n aria-hidden=\"true\"\n />\n\n {/* Modal */}\n <div\n ref={modalRef}\n className=\"relative w-full max-w-md max-h-[90vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col\"\n >\n {/* Header */}\n <header className=\"flex items-center justify-between p-4 border-b border-border flex-shrink-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center\">\n <CreditCard className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-foreground\">\n {step === 'success' ? 'Payment Method Added' : 'Add Payment Method'}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {step === 'select-provider' && 'Choose a payment provider'}\n {step === 'add-card' &&\n (selectedProvider === 'stripe'\n ? 'Add card via Stripe'\n : 'Add payment method with ₹1 verification')}\n {step === 'processing' && 'Processing...'}\n {step === 'success' && 'Your payment method has been saved'}\n </p>\n </div>\n </div>\n <button\n onClick={onClose}\n disabled={isProcessing}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50\"\n aria-label=\"Close modal\"\n >\n <X className=\"w-5 h-5\" />\n </button>\n </header>\n\n {/* Content */}\n <div className=\"p-4 overflow-y-auto flex-1\">\n {isLoadingConfig ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"w-6 h-6 animate-spin text-muted-foreground\" />\n </div>\n ) : step === 'select-provider' ? (\n <div className=\"space-y-3\">\n {showStripe && (\n <button\n onClick={() => handleSelectProvider('stripe')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"w-12 h-12 rounded-lg bg-accent-violet-subtle flex items-center justify-center\">\n <CreditCard className=\"w-6 h-6 text-accent-violet\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Stripe</p>\n <p className=\"text-sm text-muted-foreground\">Cards, Apple Pay, Google Pay</p>\n </div>\n </button>\n )}\n {showRazorpay && (\n <button\n onClick={() => handleSelectProvider('razorpay')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"w-12 h-12 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <CreditCard className=\"w-6 h-6 text-accent-blue\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Razorpay</p>\n <p className=\"text-sm text-muted-foreground\">UPI, Netbanking, Cards</p>\n </div>\n </button>\n )}\n {!showStripe && !showRazorpay && (\n <div className=\"flex flex-col items-center py-8 text-center\">\n <AlertCircle className=\"w-12 h-12 text-status-warning-text mb-3\" />\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.noPaymentProviders', 'No Payment Providers Available')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {isIndianAccount\n ? 'Razorpay is not configured for Indian accounts. Please contact support.'\n : 'Stripe is not configured for international accounts. Please contact support.'}\n </p>\n </div>\n )}\n </div>\n ) : step === 'add-card' ? (\n <div className=\"space-y-4\">\n {/* Error Message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/20 text-destructive\">\n <AlertCircle className=\"w-4 h-4 flex-shrink-0\" />\n <p className=\"text-sm\">{error}</p>\n </div>\n )}\n\n {/* Cardholder Name - only for Stripe */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"cardholderName\"\n className=\"block text-sm font-medium text-foreground\"\n >\n Cardholder Name\n </label>\n <input\n type=\"text\"\n id=\"cardholderName\"\n value={cardholderName}\n onChange={(e) => setCardholderName(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n placeholder=\"John Doe\"\n disabled={isProcessing}\n />\n </div>\n )}\n\n {/* Stripe Card Element */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label className=\"block text-sm font-medium text-foreground\">\n {tr('billing.overview.cardDetails', 'Card Details')}\n </label>\n <div className=\"relative\">\n <div\n ref={cardElementRef}\n className=\"px-3 py-3 border border-input rounded-lg bg-background min-h-[44px]\"\n />\n {isLoadingSdk && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-background/80 rounded-lg\">\n <Loader2 className=\"w-5 h-5 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">\n Loading Stripe...\n </span>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Razorpay - Verification Info */}\n {selectedProvider === 'razorpay' && (\n <div className=\"p-3 border border-border-subtle rounded-lg bg-status-info-bg-subtle\">\n <p className=\"text-sm text-status-info-text\">\n <strong>Verification Process:</strong> A ₹1 charge will be made to verify your\n payment method. This amount will be refunded immediately after verification.\n </p>\n </div>\n )}\n\n {/* Set as default */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={setAsDefault}\n onChange={(e) => setSetAsDefault(e.target.checked)}\n className=\"w-4 h-4 rounded border-input text-primary focus:ring-primary/20\"\n disabled={isProcessing}\n />\n <span className=\"text-sm text-foreground\">\n {tr('billing.overview.setAsDefault', 'Set as default payment method')}\n </span>\n </label>\n\n {/* Provider badge */}\n <div className=\"flex items-center gap-2 pt-2 border-t border-border\">\n <span className=\"text-xs text-muted-foreground\">Secured by</span>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${\n selectedProvider === 'stripe'\n ? 'bg-accent-violet-subtle text-accent-violet'\n : 'bg-status-info-bg-subtle text-status-info-text'\n }`}\n >\n {selectedProvider === 'stripe' ? 'Stripe' : 'Razorpay'}\n </span>\n </div>\n </div>\n ) : (\n /* Success state */\n <div className=\"flex flex-col items-center py-8 text-center\">\n <div className=\"w-16 h-16 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-4\">\n <CheckCircle2 className=\"w-8 h-8 text-status-success-text\" />\n </div>\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.paymentMethodAdded', 'Payment method added successfully!')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Your payment method has been securely saved.\n </p>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {step !== 'success' && (\n <footer className=\"flex items-center justify-between gap-3 p-4 border-t border-border bg-muted/30 rounded-b-lg flex-shrink-0\">\n {step === 'add-card' && showStripe && showRazorpay ? (\n <button\n type=\"button\"\n onClick={() => {\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n // Cleanup Stripe element\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors disabled:opacity-50\"\n >\n Back\n </button>\n ) : (\n <div />\n )}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n {step === 'add-card' && (\n <button\n onClick={handleSubmit}\n disabled={\n isProcessing ||\n isAddingPaymentMethod ||\n isLoadingSdk ||\n (selectedProvider === 'stripe' && !isCardComplete)\n }\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isProcessing || isAddingPaymentMethod ? (\n <>\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n {selectedProvider === 'razorpay' ? 'Processing...' : 'Saving...'}\n </>\n ) : isLoadingSdk ? (\n <>\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n Loading...\n </>\n ) : selectedProvider === 'stripe' ? (\n 'Add Card'\n ) : (\n 'Add Payment Method (₹1)'\n )}\n </button>\n )}\n </div>\n </footer>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AddPaymentMethodModal.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/AddPaymentMethodModal.tsx"],"sourcesContent":["/**\n * Add Payment Method Modal Component\n * Modal for adding a new payment method via Stripe or Razorpay SDKs\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef } from 'react';\nimport { X, CreditCard, Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePaymentProviderConfig, useDashboardMutations } from '../hooks';\nimport { PaymentProviderPreference } from '../../../shared/types';\nimport { getStripeCardElementStyle } from '../../../shared/utils';\n\n// Stripe types (loaded dynamically)\ndeclare global {\n interface Window {\n Stripe?: (key: string) => StripeInstance;\n Razorpay?: new (options: RazorpayOptions) => RazorpayInstance;\n }\n}\n\ninterface StripeInstance {\n elements: () => StripeElements;\n createPaymentMethod: (options: {\n type: 'card';\n card: StripeCardElement;\n billing_details?: { name?: string };\n }) => Promise<{\n paymentMethod?: {\n id: string;\n card?: { brand: string; last4: string; exp_month: number; exp_year: number };\n };\n error?: { message: string };\n }>;\n confirmCardSetup: (\n clientSecret: string,\n options?: {\n payment_method?: {\n card: StripeCardElement;\n billing_details?: { name?: string };\n };\n }\n ) => Promise<{\n setupIntent?: {\n id: string;\n status: string;\n payment_method?: string;\n };\n error?: { message: string };\n }>;\n}\n\ninterface StripeElements {\n create: (type: 'card', options?: object) => StripeCardElement;\n}\n\ninterface StripeCardElement {\n mount: (element: HTMLElement | string) => void;\n unmount: () => void;\n on: (\n event: string,\n handler: (event: { complete: boolean; error?: { message: string } }) => void\n ) => void;\n destroy: () => void;\n}\n\ninterface RazorpayOptions {\n key: string;\n amount: number;\n currency: string;\n order_id: string;\n customer_id?: string;\n name: string;\n description: string;\n method?: 'card' | 'upi' | 'netbanking' | 'wallet' | 'emi' | 'cardless_emi';\n handler: (response: RazorpayResponse) => void;\n modal?: { ondismiss?: () => void };\n prefill?: { name?: string; email?: string; contact?: string };\n notes?: Record<string, string>;\n config?: {\n display?: {\n hide?: Array<{ method: string }>;\n preferences?: {\n show_default_blocks?: boolean;\n };\n };\n };\n}\n\ninterface RazorpayInstance {\n open: () => void;\n close: () => void;\n}\n\ninterface RazorpayResponse {\n razorpay_payment_id: string;\n razorpay_token?: string;\n}\n\ninterface AddPaymentMethodModalProps {\n isOpen: boolean;\n onClose: () => void;\n billingAccountId: string;\n currency: string; // e.g., 'INR', 'USD'\n country?: string; // e.g., 'IN', 'US' - from billing address\n onSuccess?: () => void;\n}\n\ntype ModalStep = 'select-provider' | 'add-card' | 'processing' | 'success';\n\n// Load external script dynamically\nconst loadScript = (src: string, id: string): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (document.getElementById(id)) {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.id = id;\n script.src = src;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load ${src}`));\n document.head.appendChild(script);\n });\n};\n\nexport const AddPaymentMethodModal: FC<AddPaymentMethodModalProps> = ({\n isOpen,\n onClose,\n billingAccountId,\n currency,\n country,\n onSuccess,\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 const modalRef = useRef<HTMLDivElement>(null);\n const cardElementRef = useRef<HTMLDivElement>(null);\n const stripeCardElementRef = useRef<StripeCardElement | null>(null);\n const stripeRef = useRef<StripeInstance | null>(null);\n\n // Only fetch payment provider config when modal is open\n const {\n stripeEnabled,\n stripePublishableKey,\n razorpayEnabled,\n isLoading: isLoadingConfig,\n } = usePaymentProviderConfig(!isOpen);\n const { isAddingPaymentMethod, verifyPaymentMethod } = useDashboardMutations();\n\n // Determine which providers to show based on currency and country\n // Razorpay: India only (INR currency OR IN country)\n // Stripe: International (non-INR OR non-IN country)\n const isIndianAccount = currency?.toUpperCase() === 'INR' || country?.toUpperCase() === 'IN';\n const showRazorpay = razorpayEnabled && isIndianAccount;\n const showStripe = stripeEnabled && !isIndianAccount;\n\n const [step, setStep] = useState<ModalStep>('select-provider');\n const [selectedProvider, setSelectedProvider] = useState<'stripe' | 'razorpay' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n const [isLoadingSdk, setIsLoadingSdk] = useState(false);\n const [cardholderName, setCardholderName] = useState('');\n const [setAsDefault, setSetAsDefault] = useState(true);\n const [isCardComplete, setIsCardComplete] = useState(false);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (isOpen) {\n // Always start with provider selection\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n setCardholderName('');\n setSetAsDefault(true);\n setIsCardComplete(false);\n } else {\n // Cleanup Stripe element when modal closes\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }\n }, [isOpen]);\n\n // Note: We always show provider selection, even if only one is available\n // This ensures the user explicitly chooses their payment provider\n\n // Initialize Stripe when provider is selected\n useEffect(() => {\n if (step !== 'add-card' || selectedProvider !== 'stripe' || !stripePublishableKey) return;\n\n const initStripe = async () => {\n setIsLoadingSdk(true);\n try {\n await loadScript('https://js.stripe.com/v3/', 'stripe-js');\n\n if (!window.Stripe) {\n throw new Error('Stripe SDK failed to load');\n }\n\n const stripe = window.Stripe(stripePublishableKey);\n stripeRef.current = stripe;\n\n // Wait for the card element container to be mounted in the DOM\n let attempts = 0;\n while (!cardElementRef.current && attempts < 50) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n attempts++;\n }\n\n if (!cardElementRef.current) {\n throw new Error('Card element container not found after 5 seconds');\n }\n\n if (stripeCardElementRef.current) {\n return;\n }\n\n const elements = stripe.elements();\n const cardElement = elements.create('card', {\n style: getStripeCardElementStyle('system-ui, -apple-system, sans-serif'),\n });\n\n cardElement.mount(cardElementRef.current);\n stripeCardElementRef.current = cardElement;\n\n cardElement.on('change', (event) => {\n setIsCardComplete(event.complete);\n if (event.error) {\n setError(event.error.message);\n } else {\n setError(null);\n }\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load Stripe');\n } finally {\n setIsLoadingSdk(false);\n }\n };\n\n initStripe();\n\n return () => {\n if (stripeCardElementRef.current) {\n try {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n } catch {\n // Stripe element may already be detached during modal teardown.\n }\n }\n };\n }, [step, selectedProvider, stripePublishableKey]);\n\n // Handle escape key\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isOpen && !isProcessing) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, isProcessing, onClose]);\n\n const handleSelectProvider = useCallback((provider: 'stripe' | 'razorpay') => {\n setSelectedProvider(provider);\n setStep('add-card');\n setError(null);\n }, []);\n\n const handleStripeSubmit = useCallback(async () => {\n if (!stripeRef.current || !stripeCardElementRef.current) {\n setError('Stripe not initialized');\n return;\n }\n\n if (!cardholderName.trim()) {\n setError('Please enter the cardholder name');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n try {\n // Step 1: Call backend to create SetupIntent and get client secret\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'USD',\n provider: PaymentProviderPreference.STRIPE,\n setAsDefault,\n });\n\n if (!verificationResult.clientSecret) {\n throw new Error('Failed to initiate card verification');\n }\n\n // Step 2: Confirm card setup using the client secret (no charge)\n const { setupIntent, error: stripeError } = await stripeRef.current.confirmCardSetup(\n verificationResult.clientSecret,\n {\n payment_method: {\n card: stripeCardElementRef.current,\n billing_details: { name: cardholderName },\n },\n }\n );\n\n if (stripeError) {\n throw new Error(stripeError.message);\n }\n\n if (setupIntent?.status !== 'succeeded') {\n throw new Error('Card verification failed');\n }\n\n // Step 3: Show success (webhook will save the payment method)\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [cardholderName, setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleRazorpaySubmit = useCallback(async () => {\n setIsProcessing(true);\n setError(null);\n\n try {\n // Load Razorpay SDK\n setIsLoadingSdk(true);\n try {\n await loadScript('https://checkout.razorpay.com/v1/checkout.js', 'razorpay-sdk');\n } catch {\n setError('Failed to load Razorpay SDK');\n setIsProcessing(false);\n setIsLoadingSdk(false);\n return;\n }\n setIsLoadingSdk(false);\n\n // Initiate ₹1 verification payment\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'INR',\n provider: PaymentProviderPreference.RAZORPAY,\n setAsDefault,\n });\n\n if (\n !verificationResult.success ||\n !verificationResult.orderId ||\n !verificationResult.razorpayKeyId\n ) {\n setError(verificationResult.error || 'Failed to initiate verification');\n setIsProcessing(false);\n return;\n }\n\n // Open Razorpay checkout with ALL payment methods\n if (!window.Razorpay) {\n setError('Razorpay SDK not loaded');\n setIsProcessing(false);\n return;\n }\n\n const options = {\n key: verificationResult.razorpayKeyId,\n amount: (verificationResult.amount ?? 0) * 100, // Convert to paise\n currency: verificationResult.currency.toUpperCase(),\n order_id: verificationResult.orderId,\n customer_id: verificationResult.razorpayCustomerId, // Link to customer for saving\n name: 'Add Payment Method',\n description: `₹${verificationResult.amount} verification charge (refundable)`,\n prefill: {\n name: verificationResult.prefillName,\n email: verificationResult.prefillEmail,\n contact: verificationResult.prefillContact,\n },\n notes: {\n purchaseType: 'PAYMENT_METHOD_VERIFICATION',\n default_method: setAsDefault.toString(),\n },\n handler: async () => {\n // Payment successful - payment method will be saved via webhook\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n },\n modal: {\n ondismiss: () => {\n setIsProcessing(false);\n setError('Payment cancelled');\n },\n },\n };\n\n const rzp = new window.Razorpay(options);\n rzp.open();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleSubmit = useCallback(() => {\n if (selectedProvider === 'stripe') {\n handleStripeSubmit();\n } else if (selectedProvider === 'razorpay') {\n handleRazorpaySubmit();\n }\n }, [selectedProvider, handleStripeSubmit, handleRazorpaySubmit]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n >\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\"\n onClick={!isProcessing ? onClose : undefined}\n aria-hidden=\"true\"\n />\n\n {/* Modal */}\n <div\n ref={modalRef}\n className=\"relative w-full max-w-md max-h-[90vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col\"\n >\n {/* Header */}\n <header className=\"flex items-center justify-between p-4 border-b border-border flex-shrink-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center\">\n <CreditCard className=\"size-5 text-primary\" />\n </div>\n <div>\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-foreground\">\n {step === 'success' ? 'Payment Method Added' : 'Add Payment Method'}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {step === 'select-provider' && 'Choose a payment provider'}\n {step === 'add-card' &&\n (selectedProvider === 'stripe'\n ? 'Add card via Stripe'\n : 'Add payment method with ₹1 verification')}\n {step === 'processing' && 'Processing...'}\n {step === 'success' && 'Your payment method has been saved'}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50\"\n aria-label=\"Close modal\"\n >\n <X className=\"size-5\" />\n </button>\n </header>\n\n {/* Content */}\n <div className=\"p-4 overflow-y-auto flex-1\">\n {isLoadingConfig ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n ) : step === 'select-provider' ? (\n <div className=\"space-y-3\">\n {showStripe && (\n <button\n type=\"button\"\n onClick={() => handleSelectProvider('stripe')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"size-12 rounded-lg bg-accent-violet-subtle flex items-center justify-center\">\n <CreditCard className=\"size-6 text-accent-violet\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Stripe</p>\n <p className=\"text-sm text-muted-foreground\">Cards, Apple Pay, Google Pay</p>\n </div>\n </button>\n )}\n {showRazorpay && (\n <button\n type=\"button\"\n onClick={() => handleSelectProvider('razorpay')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"size-12 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <CreditCard className=\"size-6 text-accent-blue\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Razorpay</p>\n <p className=\"text-sm text-muted-foreground\">UPI, Netbanking, Cards</p>\n </div>\n </button>\n )}\n {!showStripe && !showRazorpay && (\n <div className=\"flex flex-col items-center py-8 text-center\">\n <AlertCircle className=\"size-12 text-status-warning-text mb-3\" />\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.noPaymentProviders', 'No Payment Providers Available')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {isIndianAccount\n ? 'Razorpay is not configured for Indian accounts. Please contact support.'\n : 'Stripe is not configured for international accounts. Please contact support.'}\n </p>\n </div>\n )}\n </div>\n ) : step === 'add-card' ? (\n <div className=\"space-y-4\">\n {/* Error Message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/20 text-destructive\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <p className=\"text-sm\">{error}</p>\n </div>\n )}\n\n {/* Cardholder Name - only for Stripe */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"cardholderName\"\n className=\"block text-sm font-medium text-foreground\"\n >\n Cardholder Name\n </label>\n <input\n type=\"text\"\n id=\"cardholderName\"\n value={cardholderName}\n onChange={(e) => setCardholderName(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n placeholder=\"John Doe\"\n disabled={isProcessing}\n />\n </div>\n )}\n\n {/* Stripe Card Element */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label className=\"block text-sm font-medium text-foreground\">\n {tr('billing.overview.cardDetails', 'Card Details')}\n </label>\n <div className=\"relative\">\n <div\n ref={cardElementRef}\n className=\"p-3 border border-input rounded-lg bg-background min-h-[44px]\"\n />\n {isLoadingSdk && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-background/80 rounded-lg\">\n <Loader2 className=\"size-5 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">Loading Stripe…</span>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Razorpay - Verification Info */}\n {selectedProvider === 'razorpay' && (\n <div className=\"p-3 border border-border-subtle rounded-lg bg-status-info-bg-subtle\">\n <p className=\"text-sm text-status-info-text\">\n <strong>Verification Process:</strong> A ₹1 charge will be made to verify your\n payment method. This amount will be refunded immediately after verification.\n </p>\n </div>\n )}\n\n {/* Set as default */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={setAsDefault}\n onChange={(e) => setSetAsDefault(e.target.checked)}\n className=\"size-4 rounded border-input text-primary focus:ring-primary/20\"\n disabled={isProcessing}\n />\n <span className=\"text-sm text-foreground\">\n {tr('billing.overview.setAsDefault', 'Set as default payment method')}\n </span>\n </label>\n\n {/* Provider badge */}\n <div className=\"flex items-center gap-2 pt-2 border-t border-border\">\n <span className=\"text-xs text-muted-foreground\">Secured by</span>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${\n selectedProvider === 'stripe'\n ? 'bg-accent-violet-subtle text-accent-violet'\n : 'bg-status-info-bg-subtle text-status-info-text'\n }`}\n >\n {selectedProvider === 'stripe' ? 'Stripe' : 'Razorpay'}\n </span>\n </div>\n </div>\n ) : (\n /* Success state */\n <div className=\"flex flex-col items-center py-8 text-center\">\n <div className=\"size-16 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-4\">\n <CheckCircle2 className=\"size-8 text-status-success-text\" />\n </div>\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.paymentMethodAdded', 'Payment method added successfully!')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Your payment method has been securely saved.\n </p>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {step !== 'success' && (\n <footer className=\"flex items-center justify-between gap-3 p-4 border-t border-border bg-muted/30 rounded-b-lg flex-shrink-0\">\n {step === 'add-card' && showStripe && showRazorpay ? (\n <button\n type=\"button\"\n onClick={() => {\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n // Cleanup Stripe element\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors disabled:opacity-50\"\n >\n Back\n </button>\n ) : (\n <div />\n )}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n {step === 'add-card' && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={\n isProcessing ||\n isAddingPaymentMethod ||\n isLoadingSdk ||\n (selectedProvider === 'stripe' && !isCardComplete)\n }\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isProcessing || isAddingPaymentMethod ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {selectedProvider === 'razorpay' ? 'Processing...' : 'Saving...'}\n </>\n ) : isLoadingSdk ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Loading…\n </>\n ) : selectedProvider === 'stripe' ? (\n 'Add Card'\n ) : (\n 'Add Payment Method (₹1)'\n )}\n </button>\n )}\n </div>\n </footer>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -33,8 +33,8 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
|
|
|
33
33
|
children: [/* @__PURE__ */ d("div", {
|
|
34
34
|
className: "flex items-center gap-3 min-w-0",
|
|
35
35
|
children: [/* @__PURE__ */ u("div", {
|
|
36
|
-
className: "
|
|
37
|
-
children: /* @__PURE__ */ u(n, { className: "
|
|
36
|
+
className: "size-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0",
|
|
37
|
+
children: /* @__PURE__ */ u(n, { className: "size-5 text-primary" })
|
|
38
38
|
}), /* @__PURE__ */ d("div", {
|
|
39
39
|
className: "min-w-0",
|
|
40
40
|
children: [/* @__PURE__ */ d("div", {
|
|
@@ -49,22 +49,23 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
|
|
|
49
49
|
children: S("billing.overview.default", "Default")
|
|
50
50
|
}),
|
|
51
51
|
h && /* @__PURE__ */ u("span", {
|
|
52
|
-
className: "inline-flex items-center justify-center
|
|
53
|
-
children: /* @__PURE__ */ u(i, { className: "
|
|
52
|
+
className: "inline-flex items-center justify-center size-5 rounded-full bg-primary",
|
|
53
|
+
children: /* @__PURE__ */ u(i, { className: "size-3 text-primary-foreground" })
|
|
54
54
|
})
|
|
55
55
|
]
|
|
56
56
|
}), /* @__PURE__ */ d("p", {
|
|
57
57
|
className: "text-sm text-muted-foreground truncate flex items-center gap-1",
|
|
58
|
-
children: [/* @__PURE__ */ u(l, { className: "
|
|
58
|
+
children: [/* @__PURE__ */ u(l, { className: "size-3.5" }), p.email]
|
|
59
59
|
})]
|
|
60
60
|
})]
|
|
61
61
|
}), _ && /* @__PURE__ */ u("button", {
|
|
62
|
+
type: "button",
|
|
62
63
|
onClick: (e) => {
|
|
63
64
|
e.stopPropagation(), _();
|
|
64
65
|
},
|
|
65
66
|
className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors",
|
|
66
67
|
"aria-label": "Edit billing account",
|
|
67
|
-
children: /* @__PURE__ */ u(c, { className: "
|
|
68
|
+
children: /* @__PURE__ */ u(c, { className: "size-4" })
|
|
68
69
|
})]
|
|
69
70
|
})
|
|
70
71
|
}),
|
|
@@ -80,7 +81,7 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
|
|
|
80
81
|
}), /* @__PURE__ */ d("p", {
|
|
81
82
|
className: "text-lg font-semibold text-foreground flex items-center gap-1.5",
|
|
82
83
|
children: [
|
|
83
|
-
/* @__PURE__ */ u(o, { className: `
|
|
84
|
+
/* @__PURE__ */ u(o, { className: `size-4 ${e.warning.icon}` }),
|
|
84
85
|
(p.creditAmount || 0).toLocaleString(),
|
|
85
86
|
" Credits"
|
|
86
87
|
]
|
|
@@ -92,7 +93,7 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
|
|
|
92
93
|
children: "Payment Currency"
|
|
93
94
|
}), /* @__PURE__ */ d("p", {
|
|
94
95
|
className: "text-lg font-semibold text-foreground flex items-center gap-1.5",
|
|
95
|
-
children: [/* @__PURE__ */ u(s, { className: "
|
|
96
|
+
children: [/* @__PURE__ */ u(s, { className: "size-4 text-primary" }), p.currency || "USD"]
|
|
96
97
|
})]
|
|
97
98
|
})]
|
|
98
99
|
}), /* @__PURE__ */ u("div", {
|
|
@@ -100,7 +101,7 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
|
|
|
100
101
|
children: /* @__PURE__ */ d("p", {
|
|
101
102
|
className: "text-xs text-muted-foreground flex items-center gap-1",
|
|
102
103
|
children: [
|
|
103
|
-
/* @__PURE__ */ u(r, { className: "
|
|
104
|
+
/* @__PURE__ */ u(r, { className: "size-3.5" }),
|
|
104
105
|
"Created ",
|
|
105
106
|
((e) => new Date(e).toLocaleDateString("en-US", {
|
|
106
107
|
year: "numeric",
|
|
@@ -116,17 +117,19 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
|
|
|
116
117
|
children: /* @__PURE__ */ d("div", {
|
|
117
118
|
className: "flex items-center gap-2",
|
|
118
119
|
children: [v && /* @__PURE__ */ d("button", {
|
|
120
|
+
type: "button",
|
|
119
121
|
onClick: (e) => {
|
|
120
122
|
e.stopPropagation(), v();
|
|
121
123
|
},
|
|
122
124
|
className: "flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors",
|
|
123
|
-
children: ["Transactions", /* @__PURE__ */ u(a, { className: "
|
|
125
|
+
children: ["Transactions", /* @__PURE__ */ u(a, { className: "size-4" })]
|
|
124
126
|
}), y && /* @__PURE__ */ d("button", {
|
|
127
|
+
type: "button",
|
|
125
128
|
onClick: (e) => {
|
|
126
129
|
e.stopPropagation(), y();
|
|
127
130
|
},
|
|
128
131
|
className: "flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors",
|
|
129
|
-
children: ["Credit History", /* @__PURE__ */ u(a, { className: "
|
|
132
|
+
children: ["Credit History", /* @__PURE__ */ u(a, { className: "size-4" })]
|
|
130
133
|
})]
|
|
131
134
|
})
|
|
132
135
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BillingAccountCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/BillingAccountCard.tsx"],"sourcesContent":["/**\n * Billing Account Card Component\n * Displays billing account details with actions\n */\n\nimport { type FC, useState } from 'react';\nimport {\n Building2,\n Mail,\n CreditCard,\n Edit2,\n ChevronRight,\n Coins,\n Check,\n Calendar,\n} from 'lucide-react';\nimport type { BillingAccount } from '../../../shared/types';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountCardProps {\n account: BillingAccount;\n isDefault?: boolean;\n isSelected?: boolean;\n onSelect?: () => void;\n onEdit?: () => void;\n onViewTransactions?: () => void;\n onViewCreditTransactions?: () => void;\n className?: string;\n}\n\nexport const BillingAccountCard: FC<BillingAccountCardProps> = ({\n account,\n isDefault = false,\n isSelected = false,\n onSelect,\n onEdit,\n onViewTransactions,\n onViewCreditTransactions,\n className = '',\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 const [isHovered, setIsHovered] = useState(false);\n\n const formatDate = (dateString: string) => {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n return (\n <article\n className={`\n relative border rounded-lg bg-card transition-all duration-200\n ${isSelected ? 'border-primary ring-2 ring-primary/20' : 'border-border'}\n ${isHovered ? 'shadow-md' : 'shadow-sm'}\n ${onSelect ? 'cursor-pointer' : ''}\n ${className}\n `}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onClick={onSelect}\n role={onSelect ? 'button' : 'article'}\n tabIndex={onSelect ? 0 : undefined}\n onKeyDown={(e) => {\n if (onSelect && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onSelect();\n }\n }}\n >\n {/* Header */}\n <header className=\"p-4 border-b border-border\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"
|
|
1
|
+
{"version":3,"file":"BillingAccountCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/BillingAccountCard.tsx"],"sourcesContent":["/**\n * Billing Account Card Component\n * Displays billing account details with actions\n */\n\nimport { type FC, useState } from 'react';\nimport {\n Building2,\n Mail,\n CreditCard,\n Edit2,\n ChevronRight,\n Coins,\n Check,\n Calendar,\n} from 'lucide-react';\nimport type { BillingAccount } from '../../../shared/types';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountCardProps {\n account: BillingAccount;\n isDefault?: boolean;\n isSelected?: boolean;\n onSelect?: () => void;\n onEdit?: () => void;\n onViewTransactions?: () => void;\n onViewCreditTransactions?: () => void;\n className?: string;\n}\n\nexport const BillingAccountCard: FC<BillingAccountCardProps> = ({\n account,\n isDefault = false,\n isSelected = false,\n onSelect,\n onEdit,\n onViewTransactions,\n onViewCreditTransactions,\n className = '',\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 const [isHovered, setIsHovered] = useState(false);\n\n const formatDate = (dateString: string) => {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n return (\n <article\n className={`\n relative border rounded-lg bg-card transition-all duration-200\n ${isSelected ? 'border-primary ring-2 ring-primary/20' : 'border-border'}\n ${isHovered ? 'shadow-md' : 'shadow-sm'}\n ${onSelect ? 'cursor-pointer' : ''}\n ${className}\n `}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onClick={onSelect}\n role={onSelect ? 'button' : 'article'}\n tabIndex={onSelect ? 0 : undefined}\n onKeyDown={(e) => {\n if (onSelect && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onSelect();\n }\n }}\n >\n {/* Header */}\n <header className=\"p-4 border-b border-border\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-foreground truncate\">{account.name}</h3>\n {isDefault && (\n <span className=\"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary\">\n {tr('billing.overview.default', 'Default')}\n </span>\n )}\n {isSelected && (\n <span className=\"inline-flex items-center justify-center size-5 rounded-full bg-primary\">\n <Check className=\"size-3 text-primary-foreground\" />\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground truncate flex items-center gap-1\">\n <Mail className=\"size-3.5\" />\n {account.email}\n </p>\n </div>\n </div>\n {onEdit && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit();\n }}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Edit billing account\"\n >\n <Edit2 className=\"size-4\" />\n </button>\n )}\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 space-y-4\">\n {/* Stats Grid */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Credit Balance\n </p>\n <p className=\"text-lg font-semibold text-foreground flex items-center gap-1.5\">\n <Coins className={`size-4 ${statusTokens.warning.icon}`} />\n {(account.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Currency\n </p>\n <p className=\"text-lg font-semibold text-foreground flex items-center gap-1.5\">\n <CreditCard className=\"size-4 text-primary\" />\n {account.currency || 'USD'}\n </p>\n </div>\n </div>\n\n {/* Meta Info */}\n <div className=\"pt-3 border-t border-border\">\n <p className=\"text-xs text-muted-foreground flex items-center gap-1\">\n <Calendar className=\"size-3.5\" />\n Created {formatDate(account.createdAt)}\n </p>\n </div>\n </div>\n\n {/* Footer Actions */}\n {(onViewTransactions || onViewCreditTransactions) && (\n <footer className=\"px-4 py-3 border-t border-border bg-muted/30 rounded-b-lg\">\n <div className=\"flex items-center gap-2\">\n {onViewTransactions && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onViewTransactions();\n }}\n className=\"flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n Transactions\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n {onViewCreditTransactions && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onViewCreditTransactions();\n }}\n className=\"flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n Credit History\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n </footer>\n )}\n\n {/* Status indicator */}\n {!account.isActive && (\n <div className=\"absolute inset-0 bg-background/60 backdrop-blur-sm rounded-lg flex items-center justify-center\">\n <span className=\"px-3 py-1.5 rounded-full bg-destructive/10 text-destructive text-sm font-medium\">\n Inactive\n </span>\n </div>\n )}\n </article>\n );\n};\n"],"mappings":"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"}
|