@burdenoff/microfe-billing 2026.614.1 → 2026.623.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/billing/BillingAdminRoutes.js +7 -6
  2. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  3. package/dist/billing/BillingRoot.js +1 -1
  4. package/dist/billing/BillingRoot.js.map +1 -1
  5. package/dist/billing/modules/addons/components/FloatingCartDrawer.js +22 -22
  6. package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
  7. package/dist/billing/modules/addons/pages/AddonCreatePage.js +31 -31
  8. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  9. package/dist/billing/modules/addons/pages/AddonDetailPage.js +39 -39
  10. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonEditPage.js +29 -29
  12. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  13. package/dist/billing/modules/addons/pages/AddonsListPage.js +91 -91
  14. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  15. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +40 -40
  16. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  17. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +115 -115
  18. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  19. package/dist/billing/modules/billing/pages/InvoicesListPage.js +75 -72
  20. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  21. package/dist/billing/modules/billing/pages/RefundsPage.js +126 -126
  22. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/pages/UserRefundsPage.js +45 -45
  24. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  25. package/dist/billing/modules/checkout/components/BillingAccountSelector.js +25 -25
  26. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  27. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +31 -35
  28. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
  29. package/dist/billing/modules/checkout/components/CreditsAmountInput.js +9 -9
  30. package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
  31. package/dist/billing/modules/checkout/components/OrderSummary.js +31 -31
  32. package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
  33. package/dist/billing/modules/checkout/components/PromoCodeInput.js +8 -8
  34. package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
  35. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
  36. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
  37. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +9 -9
  38. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
  39. package/dist/billing/modules/checkout/components/StripePaymentForm.js +21 -21
  40. package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
  41. package/dist/billing/modules/checkout/components/SubscriptionSelector.js +11 -11
  42. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  43. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +31 -31
  44. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
  45. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +23 -23
  46. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
  47. package/dist/billing/modules/checkout/pages/CheckoutPage.js +91 -91
  48. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  49. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +62 -62
  50. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  51. package/dist/billing/modules/coupons/pages/CouponsListPage.js +72 -60
  52. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  53. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +66 -66
  54. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  55. package/dist/billing/modules/coupons/pages/EditCouponPage.js +46 -46
  56. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  57. package/dist/billing/modules/credits/pages/AdminCreditsPage.js +19 -19
  58. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  59. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +37 -37
  60. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  61. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +90 -90
  62. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  63. package/dist/billing/modules/credits/pages/CreditsPage.js +41 -41
  64. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  65. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +31 -31
  66. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  67. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +35 -35
  68. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  69. package/dist/billing/modules/dashboard/components/BillingAccountCard.js +24 -24
  70. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  71. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +99 -99
  72. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  73. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +23 -23
  74. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  75. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +22 -22
  76. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  77. package/dist/billing/modules/dashboard/pages/OverviewPage.js +58 -58
  78. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  79. package/dist/billing/modules/discounts/pages/DiscountsListPage.js +126 -126
  80. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  81. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +83 -79
  82. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  83. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +16 -16
  84. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  85. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +40 -40
  86. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  87. package/dist/billing/modules/plans/pages/PlanCreatePage.js +35 -35
  88. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  89. package/dist/billing/modules/plans/pages/PlanDetailPage.js +57 -57
  90. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  91. package/dist/billing/modules/plans/pages/PlanEditPage.js +35 -35
  92. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  93. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +57 -57
  94. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  95. package/dist/billing/modules/plans/pages/PlansListPage.js +52 -52
  96. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  97. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +40 -39
  98. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  99. package/dist/billing/modules/promotions/pages/PromotionsListPage.js +22 -19
  100. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  101. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +34 -34
  102. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  103. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +47 -47
  104. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  105. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +39 -39
  106. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  107. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +52 -52
  108. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  109. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +23 -23
  110. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  111. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -246
  112. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  113. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +88 -85
  114. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  115. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +51 -51
  116. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  117. package/dist/billing/modules/usage/pages/UsagePage.js +206 -196
  118. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  119. package/dist/billing/shared/components/ServerError.js +4 -4
  120. package/dist/billing/shared/components/ServerError.js.map +1 -1
  121. package/dist/billing/shared/components/StatCard.js +11 -33
  122. package/dist/billing/shared/components/StatCard.js.map +1 -1
  123. package/dist/billing/shared/ui/Card.js +15 -0
  124. package/dist/billing/shared/ui/Card.js.map +1 -0
  125. package/dist/billing/shared/ui/MetricCard.js +44 -0
  126. package/dist/billing/shared/ui/MetricCard.js.map +1 -0
  127. package/dist/billing/shared/ui/ProgressBar.js +30 -0
  128. package/dist/billing/shared/ui/ProgressBar.js.map +1 -0
  129. package/dist/billing/shared/ui/SectionHeader.js +2 -0
  130. package/dist/billing/shared/ui/StatusPill.js +28 -0
  131. package/dist/billing/shared/ui/StatusPill.js.map +1 -0
  132. package/dist/billing/shared/ui/index.js +5 -0
  133. package/dist/billing/shared/utils/tokens.js +13 -30
  134. package/dist/billing/shared/utils/tokens.js.map +1 -1
  135. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"UnifiedPaymentMethodSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/UnifiedPaymentMethodSelector.tsx"],"sourcesContent":["/**\n * Unified Payment Method Selector\n * Combines payment providers and saved payment methods into one clear interface\n */\n\nimport { type FC, useState } from 'react';\nimport { Coins, CreditCard, Wallet, ChevronDown } from 'lucide-react';\nimport type { SavedPaymentMethod } from '../types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface UnifiedPaymentMethodSelectorProps {\n // Saved cards\n savedPaymentMethods: SavedPaymentMethod[];\n selectedPaymentMethodId: string | null;\n onSelectSavedCard: (id: string | null) => void;\n\n // Provider selection (for new cards)\n selectedProvider: 'CREDITS' | 'STRIPE' | 'RAZORPAY' | 'AUTO';\n onSelectProvider: (provider: 'CREDITS' | 'STRIPE' | 'RAZORPAY') => void;\n\n // Provider availability\n stripeEnabled: boolean;\n razorpayEnabled: boolean;\n canPayWithCredits: boolean;\n availableCredits: number;\n totalAmount: number;\n billingCountry?: string;\n\n // Save card option\n saveCard: boolean;\n onSaveCardChange: (save: boolean) => void;\n\n // Auto-renewal (when enabled, card must be saved - checkbox hidden)\n autoRenewEnabled?: boolean;\n\n isLoading?: boolean;\n}\n\nconst getCardIcon = (type: string) => {\n if (type === 'WALLET') return <Wallet className=\"size-4\" />;\n return <CreditCard className=\"size-4\" />;\n};\n\nconst getCardDisplay = (pm: SavedPaymentMethod): string => {\n const type =\n pm.type === 'CREDIT_CARD' ? 'Credit Card' : pm.type === 'DEBIT_CARD' ? 'Debit Card' : pm.type;\n const last4 = pm.last4 ? ` •••• ${pm.last4}` : '';\n const brand = pm.brand ? ` (${pm.brand})` : '';\n return `${type}${last4}${brand}`;\n};\n\nexport const UnifiedPaymentMethodSelector: FC<UnifiedPaymentMethodSelectorProps> = ({\n savedPaymentMethods,\n selectedPaymentMethodId,\n onSelectSavedCard,\n selectedProvider,\n onSelectProvider,\n stripeEnabled,\n razorpayEnabled,\n canPayWithCredits,\n availableCredits,\n totalAmount,\n billingCountry,\n saveCard,\n onSaveCardChange,\n autoRenewEnabled = false,\n isLoading = false,\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 [showAllSavedCards, setShowAllSavedCards] = useState(false);\n const hasEnoughCredits = availableCredits >= totalAmount;\n const isIndia = billingCountry === 'IN';\n\n // Get default card or first card for collapsible view\n const defaultSavedCard = savedPaymentMethods.find((pm) => pm.isDefault) || savedPaymentMethods[0];\n const otherSavedCards = savedPaymentMethods.filter((pm) => pm.id !== defaultSavedCard?.id);\n\n const handleSelect = (type: 'saved' | 'new', value: string) => {\n if (type === 'saved') {\n // When selecting saved card, set it and auto-determine provider\n const selectedCard = savedPaymentMethods.find((pm) => pm.id === value);\n if (selectedCard) {\n onSelectSavedCard(value);\n // Auto-set provider based on card type\n if (selectedCard.razorpayPaymentMethodId) {\n onSelectProvider('RAZORPAY');\n } else if (selectedCard.stripePaymentMethodId) {\n onSelectProvider('STRIPE');\n }\n }\n } else {\n // When selecting new card payment, clear saved card selection\n onSelectSavedCard(null);\n onSelectProvider(value as 'CREDITS' | 'STRIPE' | 'RAZORPAY');\n }\n };\n\n const isSelected = (type: 'saved' | 'new', value: string): boolean => {\n if (type === 'saved') {\n return selectedPaymentMethodId === value;\n } else {\n return !selectedPaymentMethodId && selectedProvider === value;\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-medium text-foreground\">\n {tr('billing.checkout.paymentMethod', 'Payment Method')}\n </h3>\n\n {isLoading ? (\n <div className=\"space-y-2\">\n <div className=\"h-16 rounded-lg border border-border bg-muted animate-pulse\" />\n <div className=\"h-16 rounded-lg border border-border bg-muted animate-pulse\" />\n </div>\n ) : (\n <div className=\"space-y-2\">\n {/* Credits Option */}\n {canPayWithCredits && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'CREDITS')}\n disabled={!hasEnoughCredits}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'CREDITS')\n ? 'border-primary bg-primary/5 ring-1 ring-primary'\n : 'border-border hover:border-primary/50 hover:bg-muted/50'\n }\n ${!hasEnoughCredits && 'opacity-50 cursor-not-allowed'}\n `}\n >\n <Coins\n className={`size-5 shrink-0 ${isSelected('new', 'CREDITS') ? 'text-primary' : 'text-muted-foreground'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-foreground\">Pay with Credits</div>\n <div className=\"text-xs text-muted-foreground\">\n {hasEnoughCredits\n ? `Available: ${availableCredits.toLocaleString()} credits`\n : `Insufficient credits (need ${totalAmount.toLocaleString()})`}\n </div>\n </div>\n {hasEnoughCredits && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-success/10 text-success\">\n Instant\n </span>\n )}\n </button>\n )}\n\n {/* Saved Cards Section */}\n {savedPaymentMethods.length > 0 && (\n <>\n <div className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide pt-2\">\n Saved Cards\n </div>\n\n {/* Default/First Card - Always Shown */}\n {defaultSavedCard && (\n <button\n type=\"button\"\n onClick={() => handleSelect('saved', defaultSavedCard.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', defaultSavedCard.id)\n ? 'border-primary bg-primary/5 ring-1 ring-primary'\n : 'border-border hover:border-primary/50 hover:bg-muted/50'\n }\n `}\n >\n {getCardIcon(defaultSavedCard.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-foreground\">\n {getCardDisplay(defaultSavedCard)}\n </div>\n {defaultSavedCard.expiryMonth && defaultSavedCard.expiryYear && (\n <div className=\"text-xs text-muted-foreground\">\n Expires {defaultSavedCard.expiryMonth.toString().padStart(2, '0')}/\n {defaultSavedCard.expiryYear}\n </div>\n )}\n </div>\n {defaultSavedCard.isDefault && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-primary/10 text-primary\">\n Default\n </span>\n )}\n </button>\n )}\n\n {/* Other Cards - Collapsible */}\n {otherSavedCards.length > 0 && (\n <>\n {showAllSavedCards ? (\n <>\n {otherSavedCards.map((pm) => (\n <button\n key={pm.id}\n type=\"button\"\n onClick={() => handleSelect('saved', pm.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', pm.id)\n ? 'border-primary bg-primary/5 ring-1 ring-primary'\n : 'border-border hover:border-primary/50 hover:bg-muted/50'\n }\n `}\n >\n {getCardIcon(pm.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-foreground\">\n {getCardDisplay(pm)}\n </div>\n {pm.expiryMonth && pm.expiryYear && (\n <div className=\"text-xs text-muted-foreground\">\n Expires {pm.expiryMonth.toString().padStart(2, '0')}/{pm.expiryYear}\n </div>\n )}\n </div>\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(false)}\n className=\"text-xs text-primary hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3 rotate-180\" />\n Hide other cards\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(true)}\n className=\"text-xs text-primary hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3\" />\n Show {otherSavedCards.length} more card\n {otherSavedCards.length > 1 ? 's' : ''}\n </button>\n )}\n </>\n )}\n </>\n )}\n\n {/* New Card Options */}\n <div className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide pt-2\">\n {savedPaymentMethods.length > 0\n ? 'Or use new card'\n : canPayWithCredits\n ? 'Or pay with card'\n : 'Pay with Card'}\n </div>\n\n {/* Razorpay (India) */}\n {razorpayEnabled && isIndia && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'RAZORPAY')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'RAZORPAY')\n ? 'border-primary bg-primary/5 ring-1 ring-primary'\n : 'border-border hover:border-primary/50 hover:bg-muted/50'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'RAZORPAY') ? 'text-primary' : 'text-muted-foreground'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-foreground\">UPI / Cards / Netbanking</div>\n <div className=\"text-xs text-muted-foreground\">Razorpay</div>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n </button>\n )}\n\n {/* Stripe (International) */}\n {stripeEnabled && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'STRIPE')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'STRIPE')\n ? 'border-primary bg-primary/5 ring-1 ring-primary'\n : 'border-border hover:border-primary/50 hover:bg-muted/50'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'STRIPE') ? 'text-primary' : 'text-muted-foreground'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-foreground\">Credit / Debit Cards</div>\n <div className=\"text-xs text-muted-foreground\">Stripe - Multi-currency</div>\n </div>\n {!isIndia && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n )}\n </button>\n )}\n\n {/* Save Card Checkbox - Only show when using new card (not saved card, not credits) */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n !autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border\">\n <label className=\"flex items-start gap-3 cursor-pointer group\">\n <input\n type=\"checkbox\"\n checked={saveCard}\n onChange={(e) => onSaveCardChange(e.target.checked)}\n className=\"mt-0.5 size-4 rounded border-border text-primary focus:ring-primary focus:ring-offset-0\"\n />\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-foreground group-hover:text-primary transition-colors\">\n Save card for future use\n </div>\n <div className=\"text-xs text-muted-foreground mt-0.5\">\n Securely save this payment method for faster checkout next time\n </div>\n </div>\n </label>\n </div>\n )}\n\n {/* Auto-renewal notice - Show when auto-renewal is enabled */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border\">\n <div className=\"flex items-start gap-2 text-xs text-muted-foreground bg-muted/50 p-3 rounded-lg\">\n <svg\n className=\"size-4 mt-0.5 flex-shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <div>Your payment method will be securely saved for auto-renewal</div>\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"UnifiedPaymentMethodSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/UnifiedPaymentMethodSelector.tsx"],"sourcesContent":["/**\n * Unified Payment Method Selector\n * Combines payment providers and saved payment methods into one clear interface\n */\n\nimport { type FC, useState } from 'react';\nimport { Coins, CreditCard, Wallet, ChevronDown } from 'lucide-react';\nimport type { SavedPaymentMethod } from '../types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface UnifiedPaymentMethodSelectorProps {\n // Saved cards\n savedPaymentMethods: SavedPaymentMethod[];\n selectedPaymentMethodId: string | null;\n onSelectSavedCard: (id: string | null) => void;\n\n // Provider selection (for new cards)\n selectedProvider: 'CREDITS' | 'STRIPE' | 'RAZORPAY' | 'AUTO';\n onSelectProvider: (provider: 'CREDITS' | 'STRIPE' | 'RAZORPAY') => void;\n\n // Provider availability\n stripeEnabled: boolean;\n razorpayEnabled: boolean;\n canPayWithCredits: boolean;\n availableCredits: number;\n totalAmount: number;\n billingCountry?: string;\n\n // Save card option\n saveCard: boolean;\n onSaveCardChange: (save: boolean) => void;\n\n // Auto-renewal (when enabled, card must be saved - checkbox hidden)\n autoRenewEnabled?: boolean;\n\n isLoading?: boolean;\n}\n\nconst getCardIcon = (type: string) => {\n if (type === 'WALLET') return <Wallet className=\"size-4\" />;\n return <CreditCard className=\"size-4\" />;\n};\n\nconst getCardDisplay = (pm: SavedPaymentMethod): string => {\n const type =\n pm.type === 'CREDIT_CARD' ? 'Credit Card' : pm.type === 'DEBIT_CARD' ? 'Debit Card' : pm.type;\n const last4 = pm.last4 ? ` •••• ${pm.last4}` : '';\n const brand = pm.brand ? ` (${pm.brand})` : '';\n return `${type}${last4}${brand}`;\n};\n\nexport const UnifiedPaymentMethodSelector: FC<UnifiedPaymentMethodSelectorProps> = ({\n savedPaymentMethods,\n selectedPaymentMethodId,\n onSelectSavedCard,\n selectedProvider,\n onSelectProvider,\n stripeEnabled,\n razorpayEnabled,\n canPayWithCredits,\n availableCredits,\n totalAmount,\n billingCountry,\n saveCard,\n onSaveCardChange,\n autoRenewEnabled = false,\n isLoading = false,\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 [showAllSavedCards, setShowAllSavedCards] = useState(false);\n const hasEnoughCredits = availableCredits >= totalAmount;\n const isIndia = billingCountry === 'IN';\n\n // Get default card or first card for collapsible view\n const defaultSavedCard = savedPaymentMethods.find((pm) => pm.isDefault) || savedPaymentMethods[0];\n const otherSavedCards = savedPaymentMethods.filter((pm) => pm.id !== defaultSavedCard?.id);\n\n const handleSelect = (type: 'saved' | 'new', value: string) => {\n if (type === 'saved') {\n // When selecting saved card, set it and auto-determine provider\n const selectedCard = savedPaymentMethods.find((pm) => pm.id === value);\n if (selectedCard) {\n onSelectSavedCard(value);\n // Auto-set provider based on card type\n if (selectedCard.razorpayPaymentMethodId) {\n onSelectProvider('RAZORPAY');\n } else if (selectedCard.stripePaymentMethodId) {\n onSelectProvider('STRIPE');\n }\n }\n } else {\n // When selecting new card payment, clear saved card selection\n onSelectSavedCard(null);\n onSelectProvider(value as 'CREDITS' | 'STRIPE' | 'RAZORPAY');\n }\n };\n\n const isSelected = (type: 'saved' | 'new', value: string): boolean => {\n if (type === 'saved') {\n return selectedPaymentMethodId === value;\n } else {\n return !selectedPaymentMethodId && selectedProvider === value;\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.paymentMethod', 'Payment Method')}\n </h3>\n\n {isLoading ? (\n <div className=\"space-y-2\">\n <div className=\"h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse\" />\n <div className=\"h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse\" />\n </div>\n ) : (\n <div className=\"space-y-2\">\n {/* Credits Option */}\n {canPayWithCredits && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'CREDITS')}\n disabled={!hasEnoughCredits}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'CREDITS')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n ${!hasEnoughCredits && 'opacity-50 cursor-not-allowed'}\n `}\n >\n <Coins\n className={`size-5 shrink-0 ${isSelected('new', 'CREDITS') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">Pay with Credits</div>\n <div className=\"text-xs text-text-secondary\">\n {hasEnoughCredits\n ? `Available: ${availableCredits.toLocaleString()} credits`\n : `Insufficient credits (need ${totalAmount.toLocaleString()})`}\n </div>\n </div>\n {hasEnoughCredits && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-success/10 text-success\">\n Instant\n </span>\n )}\n </button>\n )}\n\n {/* Saved Cards Section */}\n {savedPaymentMethods.length > 0 && (\n <>\n <div className=\"text-xs font-medium text-text-secondary uppercase tracking-wide pt-2\">\n Saved Cards\n </div>\n\n {/* Default/First Card - Always Shown */}\n {defaultSavedCard && (\n <button\n type=\"button\"\n onClick={() => handleSelect('saved', defaultSavedCard.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', defaultSavedCard.id)\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n {getCardIcon(defaultSavedCard.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {getCardDisplay(defaultSavedCard)}\n </div>\n {defaultSavedCard.expiryMonth && defaultSavedCard.expiryYear && (\n <div className=\"text-xs text-text-secondary\">\n Expires {defaultSavedCard.expiryMonth.toString().padStart(2, '0')}/\n {defaultSavedCard.expiryYear}\n </div>\n )}\n </div>\n {defaultSavedCard.isDefault && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-[var(--color-accent-soft)] text-text-link\">\n Default\n </span>\n )}\n </button>\n )}\n\n {/* Other Cards - Collapsible */}\n {otherSavedCards.length > 0 && (\n <>\n {showAllSavedCards ? (\n <>\n {otherSavedCards.map((pm) => (\n <button\n key={pm.id}\n type=\"button\"\n onClick={() => handleSelect('saved', pm.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', pm.id)\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n {getCardIcon(pm.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {getCardDisplay(pm)}\n </div>\n {pm.expiryMonth && pm.expiryYear && (\n <div className=\"text-xs text-text-secondary\">\n Expires {pm.expiryMonth.toString().padStart(2, '0')}/{pm.expiryYear}\n </div>\n )}\n </div>\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(false)}\n className=\"text-xs text-text-link hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3 rotate-180\" />\n Hide other cards\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(true)}\n className=\"text-xs text-text-link hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3\" />\n Show {otherSavedCards.length} more card\n {otherSavedCards.length > 1 ? 's' : ''}\n </button>\n )}\n </>\n )}\n </>\n )}\n\n {/* New Card Options */}\n <div className=\"text-xs font-medium text-text-secondary uppercase tracking-wide pt-2\">\n {savedPaymentMethods.length > 0\n ? 'Or use new card'\n : canPayWithCredits\n ? 'Or pay with card'\n : 'Pay with Card'}\n </div>\n\n {/* Razorpay (India) */}\n {razorpayEnabled && isIndia && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'RAZORPAY')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'RAZORPAY')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'RAZORPAY') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n UPI / Cards / Netbanking\n </div>\n <div className=\"text-xs text-text-secondary\">Razorpay</div>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n </button>\n )}\n\n {/* Stripe (International) */}\n {stripeEnabled && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'STRIPE')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'STRIPE')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'STRIPE') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">Credit / Debit Cards</div>\n <div className=\"text-xs text-text-secondary\">Stripe - Multi-currency</div>\n </div>\n {!isIndia && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n )}\n </button>\n )}\n\n {/* Save Card Checkbox - Only show when using new card (not saved card, not credits) */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n !autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <label className=\"flex items-start gap-3 cursor-pointer group\">\n <input\n type=\"checkbox\"\n checked={saveCard}\n onChange={(e) => onSaveCardChange(e.target.checked)}\n className=\"mt-0.5 size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)] focus:ring-offset-0\"\n />\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-text-primary group-hover:text-text-link transition-colors\">\n Save card for future use\n </div>\n <div className=\"text-xs text-text-secondary mt-0.5\">\n Securely save this payment method for faster checkout next time\n </div>\n </div>\n </label>\n </div>\n )}\n\n {/* Auto-renewal notice - Show when auto-renewal is enabled */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <div className=\"flex items-start gap-2 text-xs text-text-secondary bg-bg-sunken p-3 rounded-lg\">\n <svg\n className=\"size-4 mt-0.5 flex-shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <div>Your payment method will be securely saved for auto-renewal</div>\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -30,20 +30,20 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
30
30
  g
31
31
  ]);
32
32
  return b ? /* @__PURE__ */ l("div", {
33
- className: "fixed inset-0 bg-background/80 backdrop-blur-sm z-50 flex items-center justify-center p-4",
33
+ className: "fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4",
34
34
  children: /* @__PURE__ */ u("div", {
35
- className: "bg-card border border-border rounded-xl shadow-lg max-w-sm w-full p-6 text-center space-y-4",
35
+ className: "bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-w-sm w-full p-6 text-center space-y-4",
36
36
  children: [
37
37
  /* @__PURE__ */ l("div", {
38
38
  className: `inline-flex p-3 rounded-full ${e.success.bg} ${e.success.text}`,
39
39
  children: /* @__PURE__ */ l(a, { className: "size-8" })
40
40
  }),
41
41
  /* @__PURE__ */ l("h2", {
42
- className: "text-lg font-semibold text-foreground",
42
+ className: "text-lg font-semibold text-text-primary",
43
43
  children: "Quota assigned!"
44
44
  }),
45
45
  /* @__PURE__ */ u("p", {
46
- className: "text-sm text-muted-foreground",
46
+ className: "text-sm text-text-secondary",
47
47
  children: [
48
48
  p.length,
49
49
  " quota",
@@ -51,7 +51,7 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
51
51
  " been assigned to",
52
52
  " ",
53
53
  /* @__PURE__ */ l("span", {
54
- className: "font-medium text-foreground",
54
+ className: "font-medium text-text-primary",
55
55
  children: m.find((e) => e.id === v)?.name ?? v
56
56
  }),
57
57
  "."
@@ -60,22 +60,22 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
60
60
  ]
61
61
  })
62
62
  }) : /* @__PURE__ */ l("div", {
63
- className: "fixed inset-0 bg-background/80 backdrop-blur-sm z-50 flex items-center justify-center p-4",
63
+ className: "fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4",
64
64
  children: /* @__PURE__ */ u("div", {
65
- className: "bg-card border border-border rounded-xl shadow-lg max-w-md w-full",
65
+ className: "bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-w-md w-full",
66
66
  children: [
67
67
  /* @__PURE__ */ u("div", {
68
- className: "flex items-center justify-between px-6 py-4 border-b border-border",
68
+ className: "flex items-center justify-between px-6 py-4 border-b border-border-subtle",
69
69
  children: [/* @__PURE__ */ u("div", {
70
70
  className: "flex items-center gap-2",
71
- children: [/* @__PURE__ */ l(o, { className: "size-5 text-primary" }), /* @__PURE__ */ l("h2", {
72
- className: "text-base font-semibold text-foreground",
71
+ children: [/* @__PURE__ */ l(o, { className: "size-5 text-text-link" }), /* @__PURE__ */ l("h2", {
72
+ className: "text-base font-semibold text-text-primary",
73
73
  children: "Assign to a Workspace"
74
74
  })]
75
75
  }), /* @__PURE__ */ l("button", {
76
76
  type: "button",
77
77
  onClick: _,
78
- className: "p-1 rounded hover:bg-muted/50 transition-colors text-muted-foreground",
78
+ className: "p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary",
79
79
  "aria-label": "Skip",
80
80
  children: /* @__PURE__ */ l(c, { className: "size-4" })
81
81
  })]
@@ -84,7 +84,7 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
84
84
  className: "px-6 py-5 space-y-5",
85
85
  children: [
86
86
  /* @__PURE__ */ u("p", {
87
- className: "text-sm text-muted-foreground",
87
+ className: "text-sm text-text-secondary",
88
88
  children: [
89
89
  "Select which workspace should receive the quota",
90
90
  p.length > 1 ? "s" : "",
@@ -92,17 +92,17 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
92
92
  ]
93
93
  }),
94
94
  /* @__PURE__ */ u("div", {
95
- className: "bg-muted/30 rounded-lg p-3 space-y-2",
95
+ className: "bg-bg-sunken rounded-lg p-3 space-y-2",
96
96
  children: [/* @__PURE__ */ l("p", {
97
- className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
97
+ className: "text-xs font-medium text-text-secondary uppercase tracking-wide",
98
98
  children: "Included quotas"
99
99
  }), p.map((e) => /* @__PURE__ */ u("div", {
100
100
  className: "flex items-center justify-between text-sm",
101
101
  children: [/* @__PURE__ */ l("span", {
102
- className: "text-foreground",
102
+ className: "text-text-primary",
103
103
  children: e.quotaName
104
104
  }), /* @__PURE__ */ l("span", {
105
- className: "text-muted-foreground",
105
+ className: "text-text-secondary",
106
106
  children: e.noLimit ? "Unlimited" : e.limit == null ? "—" : `${e.limit} max`
107
107
  })]
108
108
  }, e.quotaName))]
@@ -111,25 +111,25 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
111
111
  className: "space-y-1.5",
112
112
  children: [/* @__PURE__ */ l("label", {
113
113
  htmlFor: "ws-selector",
114
- className: "block text-sm font-medium text-foreground",
114
+ className: "block text-sm font-medium text-text-primary",
115
115
  children: "Workspace"
116
116
  }), /* @__PURE__ */ l("select", {
117
117
  id: "ws-selector",
118
118
  value: v,
119
119
  onChange: (e) => y(e.target.value),
120
- className: "w-full px-3 py-2 text-sm border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary",
120
+ className: "w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
121
121
  children: m.map((e) => /* @__PURE__ */ l("option", {
122
122
  value: e.id,
123
123
  children: e.name
124
124
  }, e.id))
125
125
  })]
126
126
  }) : /* @__PURE__ */ u("div", {
127
- className: "bg-muted/30 rounded-lg p-3 text-sm text-muted-foreground",
127
+ className: "bg-bg-sunken rounded-lg p-3 text-sm text-text-secondary",
128
128
  children: [
129
129
  "Will be assigned to",
130
130
  " ",
131
131
  /* @__PURE__ */ l("span", {
132
- className: "font-medium text-foreground",
132
+ className: "font-medium text-text-primary",
133
133
  children: m[0]?.name ?? h
134
134
  })
135
135
  ]
@@ -141,17 +141,17 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
141
141
  ]
142
142
  }),
143
143
  /* @__PURE__ */ u("div", {
144
- className: "flex items-center justify-end gap-3 px-6 py-4 border-t border-border",
144
+ className: "flex items-center justify-end gap-3 px-6 py-4 border-t border-border-subtle",
145
145
  children: [/* @__PURE__ */ l("button", {
146
146
  type: "button",
147
147
  onClick: _,
148
- className: "px-4 py-2 text-sm border border-border rounded-md hover:bg-muted/50 transition-colors text-muted-foreground",
148
+ className: "px-4 py-2 text-sm border border-border-subtle rounded-button hover:bg-bg-sunken transition-colors text-text-secondary",
149
149
  children: "Skip for now"
150
150
  }), /* @__PURE__ */ u("button", {
151
151
  type: "button",
152
152
  onClick: () => void E(),
153
153
  disabled: !v || T,
154
- className: "flex items-center gap-2 px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50",
154
+ className: "flex items-center gap-2 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50",
155
155
  children: [T && /* @__PURE__ */ l(s, { className: "size-3.5 animate-spin" }), "Assign quota"]
156
156
  })]
157
157
  })
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceQuotaAssignmentModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.tsx"],"sourcesContent":["/**\n * WorkspaceQuotaAssignmentModal\n *\n * Shown after a successful plan / add-on purchase to let the user assign\n * workspace-level quotas (agents, sandboxes, sessions, runtime, etc.) to a\n * specific workspace.\n *\n * Spec rules:\n * - Free plan or single workspace → auto-assign; no modal shown.\n * - Multiple workspaces → require selection before activating the quota.\n *\n * This component handles both cases: the parent calls autoAssignOrShow() and\n * will either receive an immediate assignment result (auto-assign path) or\n * render this modal for user selection.\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { CheckCircle, Layers, AlertTriangle, Loader2, X } from 'lucide-react';\nimport { useActivateSubscriptionForWorkspace } from '../../usage/hooks/useUsage';\nimport { statusTokens } from '../../../shared/utils/tokens';\n\nexport interface WorkspaceOption {\n id: string;\n name: string;\n}\n\nexport interface WorkspaceQuotaToAssign {\n quotaName: string;\n productId: string;\n limit: number | null;\n noLimit: boolean;\n resetPeriod: string;\n quotaMode: 'dedicated' | 'pooled';\n}\n\ninterface WorkspaceQuotaAssignmentModalProps {\n billingAccountId: string;\n subscriptionId: string;\n quotas: WorkspaceQuotaToAssign[];\n workspaces: WorkspaceOption[];\n /**\n * Current workspace — used as the pre-selected value.\n * Pass the workspace from the app shell token context.\n */\n currentWorkspaceId: string;\n onAssigned: (workspaceId: string) => void;\n onSkip: () => void;\n}\n\n/**\n * Derives which quotas in a plan are workspace-scoped (not global).\n * Global quotas: org_count, workspace_count, project_count.\n * All others are workspace-scoped.\n */\nconst GLOBAL_QUOTA_NAMES = new Set(['org_count', 'workspace_count', 'project_count']);\n\nexport function isWorkspaceScopedQuota(quotaName: string): boolean {\n return !GLOBAL_QUOTA_NAMES.has(quotaName);\n}\n\nexport const WorkspaceQuotaAssignmentModal: FC<WorkspaceQuotaAssignmentModalProps> = ({\n billingAccountId,\n subscriptionId,\n quotas,\n workspaces,\n currentWorkspaceId,\n onAssigned,\n onSkip,\n}) => {\n const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(currentWorkspaceId);\n const [isDone, setIsDone] = useState(false);\n const [assignError, setAssignError] = useState<string | null>(null);\n\n const { activate, isLoading } = useActivateSubscriptionForWorkspace();\n\n const handleAssign = useCallback(async () => {\n if (!selectedWorkspaceId) return;\n setAssignError(null);\n\n try {\n await activate(subscriptionId, billingAccountId, selectedWorkspaceId);\n setIsDone(true);\n onAssigned(selectedWorkspaceId);\n } catch (err) {\n setAssignError(err instanceof Error ? err.message : 'Failed to assign quota to workspace.');\n }\n }, [selectedWorkspaceId, billingAccountId, subscriptionId, activate, onAssigned]);\n\n if (isDone) {\n return (\n <div className=\"fixed inset-0 bg-background/80 backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-card border border-border rounded-xl shadow-lg max-w-sm w-full p-6 text-center space-y-4\">\n <div\n className={`inline-flex p-3 rounded-full ${statusTokens.success.bg} ${statusTokens.success.text}`}\n >\n <CheckCircle className=\"size-8\" />\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Quota assigned!</h2>\n <p className=\"text-sm text-muted-foreground\">\n {quotas.length} quota{quotas.length !== 1 ? 's have' : ' has'} been assigned to{' '}\n <span className=\"font-medium text-foreground\">\n {workspaces.find((w) => w.id === selectedWorkspaceId)?.name ?? selectedWorkspaceId}\n </span>\n .\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"fixed inset-0 bg-background/80 backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-card border border-border rounded-xl shadow-lg max-w-md w-full\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border\">\n <div className=\"flex items-center gap-2\">\n <Layers className=\"size-5 text-primary\" />\n <h2 className=\"text-base font-semibold text-foreground\">Assign to a Workspace</h2>\n </div>\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"p-1 rounded hover:bg-muted/50 transition-colors text-muted-foreground\"\n aria-label=\"Skip\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-5 space-y-5\">\n <p className=\"text-sm text-muted-foreground\">\n Select which workspace should receive the quota{quotas.length > 1 ? 's' : ''} included\n in your purchase.\n </p>\n\n {/* Quotas list */}\n <div className=\"bg-muted/30 rounded-lg p-3 space-y-2\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Included quotas\n </p>\n {quotas.map((q) => (\n <div key={q.quotaName} className=\"flex items-center justify-between text-sm\">\n <span className=\"text-foreground\">{q.quotaName}</span>\n <span className=\"text-muted-foreground\">\n {q.noLimit ? 'Unlimited' : q.limit != null ? `${q.limit} max` : '—'}\n </span>\n </div>\n ))}\n </div>\n\n {/* Workspace selector */}\n {workspaces.length > 1 ? (\n <div className=\"space-y-1.5\">\n <label htmlFor=\"ws-selector\" className=\"block text-sm font-medium text-foreground\">\n Workspace\n </label>\n <select\n id=\"ws-selector\"\n value={selectedWorkspaceId}\n onChange={(e) => setSelectedWorkspaceId(e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n {workspaces.map((ws) => (\n <option key={ws.id} value={ws.id}>\n {ws.name}\n </option>\n ))}\n </select>\n </div>\n ) : (\n <div className=\"bg-muted/30 rounded-lg p-3 text-sm text-muted-foreground\">\n Will be assigned to{' '}\n <span className=\"font-medium text-foreground\">\n {workspaces[0]?.name ?? currentWorkspaceId}\n </span>\n </div>\n )}\n\n {/* Error */}\n {assignError && (\n <div\n className={`flex items-start gap-2 text-sm ${statusTokens.error.text} ${statusTokens.error.bg} rounded-lg p-3`}\n >\n <AlertTriangle className=\"size-4 shrink-0 mt-0.5\" />\n <span>{assignError}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border\">\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"px-4 py-2 text-sm border border-border rounded-md hover:bg-muted/50 transition-colors text-muted-foreground\"\n >\n Skip for now\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAssign()}\n disabled={!selectedWorkspaceId || isLoading}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isLoading && <Loader2 className=\"size-3.5 animate-spin\" />}\n Assign quota\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkspaceQuotaAssignmentModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.tsx"],"sourcesContent":["/**\n * WorkspaceQuotaAssignmentModal\n *\n * Shown after a successful plan / add-on purchase to let the user assign\n * workspace-level quotas (agents, sandboxes, sessions, runtime, etc.) to a\n * specific workspace.\n *\n * Spec rules:\n * - Free plan or single workspace → auto-assign; no modal shown.\n * - Multiple workspaces → require selection before activating the quota.\n *\n * This component handles both cases: the parent calls autoAssignOrShow() and\n * will either receive an immediate assignment result (auto-assign path) or\n * render this modal for user selection.\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { CheckCircle, Layers, AlertTriangle, Loader2, X } from 'lucide-react';\nimport { useActivateSubscriptionForWorkspace } from '../../usage/hooks/useUsage';\nimport { statusTokens } from '../../../shared/utils/tokens';\n\nexport interface WorkspaceOption {\n id: string;\n name: string;\n}\n\nexport interface WorkspaceQuotaToAssign {\n quotaName: string;\n productId: string;\n limit: number | null;\n noLimit: boolean;\n resetPeriod: string;\n quotaMode: 'dedicated' | 'pooled';\n}\n\ninterface WorkspaceQuotaAssignmentModalProps {\n billingAccountId: string;\n subscriptionId: string;\n quotas: WorkspaceQuotaToAssign[];\n workspaces: WorkspaceOption[];\n /**\n * Current workspace — used as the pre-selected value.\n * Pass the workspace from the app shell token context.\n */\n currentWorkspaceId: string;\n onAssigned: (workspaceId: string) => void;\n onSkip: () => void;\n}\n\n/**\n * Derives which quotas in a plan are workspace-scoped (not global).\n * Global quotas: org_count, workspace_count, project_count.\n * All others are workspace-scoped.\n */\nconst GLOBAL_QUOTA_NAMES = new Set(['org_count', 'workspace_count', 'project_count']);\n\nexport function isWorkspaceScopedQuota(quotaName: string): boolean {\n return !GLOBAL_QUOTA_NAMES.has(quotaName);\n}\n\nexport const WorkspaceQuotaAssignmentModal: FC<WorkspaceQuotaAssignmentModalProps> = ({\n billingAccountId,\n subscriptionId,\n quotas,\n workspaces,\n currentWorkspaceId,\n onAssigned,\n onSkip,\n}) => {\n const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(currentWorkspaceId);\n const [isDone, setIsDone] = useState(false);\n const [assignError, setAssignError] = useState<string | null>(null);\n\n const { activate, isLoading } = useActivateSubscriptionForWorkspace();\n\n const handleAssign = useCallback(async () => {\n if (!selectedWorkspaceId) return;\n setAssignError(null);\n\n try {\n await activate(subscriptionId, billingAccountId, selectedWorkspaceId);\n setIsDone(true);\n onAssigned(selectedWorkspaceId);\n } catch (err) {\n setAssignError(err instanceof Error ? err.message : 'Failed to assign quota to workspace.');\n }\n }, [selectedWorkspaceId, billingAccountId, subscriptionId, activate, onAssigned]);\n\n if (isDone) {\n return (\n <div className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-w-sm w-full p-6 text-center space-y-4\">\n <div\n className={`inline-flex p-3 rounded-full ${statusTokens.success.bg} ${statusTokens.success.text}`}\n >\n <CheckCircle className=\"size-8\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Quota assigned!</h2>\n <p className=\"text-sm text-text-secondary\">\n {quotas.length} quota{quotas.length !== 1 ? 's have' : ' has'} been assigned to{' '}\n <span className=\"font-medium text-text-primary\">\n {workspaces.find((w) => w.id === selectedWorkspaceId)?.name ?? selectedWorkspaceId}\n </span>\n .\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-w-md w-full\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-subtle\">\n <div className=\"flex items-center gap-2\">\n <Layers className=\"size-5 text-text-link\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Assign to a Workspace</h2>\n </div>\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n aria-label=\"Skip\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-5 space-y-5\">\n <p className=\"text-sm text-text-secondary\">\n Select which workspace should receive the quota{quotas.length > 1 ? 's' : ''} included\n in your purchase.\n </p>\n\n {/* Quotas list */}\n <div className=\"bg-bg-sunken rounded-lg p-3 space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Included quotas\n </p>\n {quotas.map((q) => (\n <div key={q.quotaName} className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-primary\">{q.quotaName}</span>\n <span className=\"text-text-secondary\">\n {q.noLimit ? 'Unlimited' : q.limit != null ? `${q.limit} max` : '—'}\n </span>\n </div>\n ))}\n </div>\n\n {/* Workspace selector */}\n {workspaces.length > 1 ? (\n <div className=\"space-y-1.5\">\n <label htmlFor=\"ws-selector\" className=\"block text-sm font-medium text-text-primary\">\n Workspace\n </label>\n <select\n id=\"ws-selector\"\n value={selectedWorkspaceId}\n onChange={(e) => setSelectedWorkspaceId(e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {workspaces.map((ws) => (\n <option key={ws.id} value={ws.id}>\n {ws.name}\n </option>\n ))}\n </select>\n </div>\n ) : (\n <div className=\"bg-bg-sunken rounded-lg p-3 text-sm text-text-secondary\">\n Will be assigned to{' '}\n <span className=\"font-medium text-text-primary\">\n {workspaces[0]?.name ?? currentWorkspaceId}\n </span>\n </div>\n )}\n\n {/* Error */}\n {assignError && (\n <div\n className={`flex items-start gap-2 text-sm ${statusTokens.error.text} ${statusTokens.error.bg} rounded-lg p-3`}\n >\n <AlertTriangle className=\"size-4 shrink-0 mt-0.5\" />\n <span>{assignError}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"px-4 py-2 text-sm border border-border-subtle rounded-button hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n Skip for now\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAssign()}\n disabled={!selectedWorkspaceId || isLoading}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isLoading && <Loader2 className=\"size-3.5 animate-spin\" />}\n Assign quota\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}