@burdenoff/microfe-billing 2026.821.1 → 2026.825.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  2. package/dist/billing/BillingRoot.js.map +1 -1
  3. package/dist/billing/BillingUserRoutes.js.map +1 -1
  4. package/dist/billing/hooks/useBillingAccountSelection.js.map +1 -1
  5. package/dist/billing/hooks/useBillingEventEmitter.js.map +1 -1
  6. package/dist/billing/hooks/useBillingNavigate.js.map +1 -1
  7. package/dist/billing/hooks/useBillingPermissions.js.map +1 -1
  8. package/dist/billing/hooks/useDefaultBillingCurrency.js.map +1 -1
  9. package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
  10. package/dist/billing/modules/addons/hooks/useAddons.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  12. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  13. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  14. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  15. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  16. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  17. package/dist/billing/modules/addons/store/addonCartStore.js.map +1 -1
  18. package/dist/billing/modules/affiliate/api.js.map +1 -1
  19. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +107 -107
  20. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  21. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +80 -81
  22. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/hooks/useInvoices.js.map +1 -1
  24. package/dist/billing/modules/billing/hooks/useRefunds.js.map +1 -1
  25. package/dist/billing/modules/billing/hooks/useTransactions.js.map +1 -1
  26. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  27. package/dist/billing/modules/billing/pages/InvoicesListPage.js +1 -1
  28. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  29. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  30. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  31. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  32. package/dist/billing/modules/billing/pages/UserRefundsPage.js +127 -127
  33. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  34. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  35. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
  36. package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
  37. package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
  38. package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
  39. package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
  40. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
  41. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
  42. package/dist/billing/modules/checkout/components/StoreShippingSection.js.map +1 -1
  43. package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
  44. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  45. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
  46. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
  47. package/dist/billing/modules/checkout/hooks/useCheckout.js +0 -1
  48. package/dist/billing/modules/checkout/hooks/useCheckout.js.map +1 -1
  49. package/dist/billing/modules/checkout/hooks/useNativeCheckout.js.map +1 -1
  50. package/dist/billing/modules/checkout/hooks/usePaymentProviders.js.map +1 -1
  51. package/dist/billing/modules/checkout/hooks/usePaymentStatusPolling.js.map +1 -1
  52. package/dist/billing/modules/checkout/hooks/useStoreShipping.js.map +1 -1
  53. package/dist/billing/modules/checkout/pages/CheckoutPage.js +412 -395
  54. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  55. package/dist/billing/modules/checkout/utils/storeOrder.js.map +1 -1
  56. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  57. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  58. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  59. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  60. package/dist/billing/modules/credits/hooks/useCredits.js.map +1 -1
  61. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  62. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  63. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  64. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  65. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  66. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  67. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  68. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  69. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  70. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  71. package/dist/billing/modules/dashboard/hooks/useBillingOverviewData.js.map +1 -1
  72. package/dist/billing/modules/dashboard/hooks/useDashboard.js.map +1 -1
  73. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  74. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  75. package/dist/billing/modules/earnings/api.js.map +1 -1
  76. package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js +145 -145
  77. package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js.map +1 -1
  78. package/dist/billing/modules/earnings/pages/EarningsPage.js +133 -133
  79. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  80. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +7 -9
  81. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  82. package/dist/billing/modules/earnings/pages/PayoutsPage.js +95 -95
  83. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  84. package/dist/billing/modules/plans/constants/featuredQuotas.js.map +1 -1
  85. package/dist/billing/modules/plans/hooks/usePlans.js.map +1 -1
  86. package/dist/billing/modules/plans/hooks/useQuotas.js.map +1 -1
  87. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  88. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  89. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  90. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  91. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  92. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  93. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  94. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  95. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  96. package/dist/billing/modules/quota/hooks/useQuotaTemplates.js.map +1 -1
  97. package/dist/billing/modules/quota/pages/QuotaAssignFreePage.js.map +1 -1
  98. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  99. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  100. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  101. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  102. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  103. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  104. package/dist/billing/modules/store/pages/StoreCheckoutPage.js +5 -6
  105. package/dist/billing/modules/store/pages/StoreCheckoutPage.js.map +1 -1
  106. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  107. package/dist/billing/modules/subscriptions/hooks/useSubscriptions.js.map +1 -1
  108. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  109. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  110. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  111. package/dist/billing/modules/usage/hooks/useUsage.js +14 -9
  112. package/dist/billing/modules/usage/hooks/useUsage.js.map +1 -1
  113. package/dist/billing/modules/usage/pages/UsagePage.js +481 -453
  114. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  115. package/dist/billing/providers/BillingProvider.js.map +1 -1
  116. package/dist/billing/shared/components/AccessDenied.js.map +1 -1
  117. package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
  118. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
  119. package/dist/billing/shared/components/EmptyState.js.map +1 -1
  120. package/dist/billing/shared/components/PageHeader.js.map +1 -1
  121. package/dist/billing/shared/components/ServerError.js.map +1 -1
  122. package/dist/billing/shared/components/StatCard.js.map +1 -1
  123. package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -1
  124. package/dist/billing/shared/types/graphql.js.map +1 -1
  125. package/dist/billing/shared/ui/Card.js.map +1 -1
  126. package/dist/billing/shared/ui/MetricCard.js.map +1 -1
  127. package/dist/billing/shared/ui/ProgressBar.js.map +1 -1
  128. package/dist/billing/shared/ui/StatusPill.js.map +1 -1
  129. package/dist/billing/shared/utils/directBillingGraphql.js.map +1 -1
  130. package/dist/billing/shared/utils/error.js.map +1 -1
  131. package/dist/billing/shared/utils/format.js.map +1 -1
  132. package/dist/billing/shared/utils/gatewayGraphql.js.map +1 -1
  133. package/dist/billing/shared/utils/navigation.js.map +1 -1
  134. package/dist/billing/shared/utils/paymentTheme.js.map +1 -1
  135. package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -1
  136. package/dist/billing/shared/utils/status.js.map +1 -1
  137. package/dist/billing/shared/utils/tokens.js.map +1 -1
  138. package/dist/generated/global-operations.js +5 -1
  139. package/dist/generated/global-operations.js.map +1 -1
  140. package/dist/generated/global-types.js.map +1 -1
  141. package/dist/generated/wspace-operations.js +1 -1
  142. package/dist/generated/wspace-operations.js.map +1 -1
  143. package/dist/node_modules/@capacitor/core/dist/index.js +1 -1
  144. package/dist/node_modules/@capacitor/core/dist/index.js.map +1 -1
  145. package/package.json +13 -10
@@ -1 +1 @@
1
- {"version":3,"file":"ManageSeatsModal.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/components/ManageSeatsModal.tsx"],"sourcesContent":["/**\n * ManageSeatsModal\n * Lets users update the seat count on a PER_SEAT subscription.\n * Shows current seats, min/max bounds, and a proration notice for Razorpay.\n */\n\nimport { type FC, useState, useEffect } from 'react';\nimport { Minus, Plus, Users, AlertCircle, Loader2 } from 'lucide-react';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport type { BillingSubscription } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\ninterface ManageSeatsModalProps {\n subscription: BillingSubscription;\n onClose: () => void;\n onConfirm: (seatCount: number) => Promise<void>;\n isLoading?: boolean;\n}\n\nexport const ManageSeatsModal: FC<ManageSeatsModalProps> = ({\n subscription,\n onClose,\n onConfirm,\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 plan = subscription.plan;\n const minSeats = plan?.minSeats ?? 1;\n const maxSeats = plan?.maxSeats ?? 999;\n const pricePerSeat = plan?.pricePerSeat ?? 0;\n const currency = plan?.currency ?? 'USD';\n const currentSeats = subscription.seatCount ?? minSeats;\n\n const [newSeatCount, setNewSeatCount] = useState(currentSeats);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setNewSeatCount(currentSeats);\n }, [currentSeats]);\n\n const seatDelta = newSeatCount - currentSeats;\n const newMonthlyTotal = newSeatCount * pricePerSeat;\n const currentMonthlyTotal = currentSeats * pricePerSeat;\n const isRazorpay = subscription.paymentGateway === 'razorpay';\n\n function decrement() {\n setNewSeatCount((n) => Math.max(minSeats, n - 1));\n setError(null);\n }\n\n function increment() {\n setNewSeatCount((n) => Math.min(maxSeats, n + 1));\n setError(null);\n }\n\n function handleInputChange(value: string) {\n const parsed = parseInt(value, 10);\n if (isNaN(parsed)) return;\n if (parsed < minSeats) {\n setError(`Minimum ${minSeats} seat${minSeats !== 1 ? 's' : ''} required for this plan.`);\n setNewSeatCount(parsed);\n } else if (parsed > maxSeats) {\n setError(`Maximum ${maxSeats} seats allowed.`);\n setNewSeatCount(parsed);\n } else {\n setError(null);\n setNewSeatCount(parsed);\n }\n }\n\n async function handleConfirm() {\n if (newSeatCount < minSeats || newSeatCount > maxSeats) return;\n if (newSeatCount === currentSeats) {\n onClose();\n return;\n }\n setError(null);\n try {\n await onConfirm(newSeatCount);\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update seats.');\n }\n }\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !isLoading) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n {/* Header */}\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2\">\n <Users className=\"size-5 text-text-link\" />\n {tr('billing.subscriptions.manageSeats', 'Manage Seats')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n {/* Body */}\n <div className=\"space-y-6\">\n {/* Current vs new */}\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>Current seats</span>\n <span className=\"font-medium text-text-primary\">{currentSeats}</span>\n </div>\n\n {/* Seat selector */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium text-text-primary\">New seat count</label>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={decrement}\n disabled={newSeatCount <= minSeats || isLoading}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n aria-label=\"Decrease seat count\"\n >\n <Minus className=\"size-4\" />\n </button>\n <input\n type=\"number\"\n value={newSeatCount}\n min={minSeats}\n max={maxSeats}\n onChange={(e) => handleInputChange(e.target.value)}\n disabled={isLoading}\n className=\"w-20 h-9 text-center border border-border-subtle rounded-button bg-bg-surface text-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n />\n <button\n type=\"button\"\n onClick={increment}\n disabled={newSeatCount >= maxSeats || isLoading}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n aria-label=\"Increase seat count\"\n >\n <Plus className=\"size-4\" />\n </button>\n <span className=\"text-sm text-text-secondary\">\n {minSeats === maxSeats\n ? `(${minSeats} fixed)`\n : `(min ${minSeats}${maxSeats < 999 ? `, max ${maxSeats}` : ''})`}\n </span>\n </div>\n </div>\n\n {/* Pricing breakdown */}\n {pricePerSeat > 0 && (\n <div className=\"rounded-lg bg-bg-sunken border border-border-subtle p-4 space-y-2 text-sm\">\n <div className=\"flex justify-between text-text-secondary\">\n <span>Per seat</span>\n <span>{formatCurrency(pricePerSeat, currency)} / month</span>\n </div>\n <div className=\"flex justify-between text-text-secondary\">\n <span>Current total</span>\n <span>{formatCurrency(currentMonthlyTotal, currency)} / month</span>\n </div>\n <div className=\"border-t border-border-subtle pt-2 flex justify-between font-medium text-text-primary\">\n <span>New total</span>\n <span\n className={\n seatDelta > 0\n ? 'text-status-error-text'\n : seatDelta < 0\n ? 'text-status-success-text'\n : ''\n }\n >\n {formatCurrency(newMonthlyTotal, currency)} / month\n </span>\n </div>\n {seatDelta !== 0 && (\n <div className=\"text-xs text-text-secondary\">\n {seatDelta > 0\n ? `+${seatDelta} seat${seatDelta !== 1 ? 's' : ''} — +${formatCurrency(seatDelta * pricePerSeat, currency)}/month`\n : `${seatDelta} seat${Math.abs(seatDelta) !== 1 ? 's' : ''} — ${formatCurrency(seatDelta * pricePerSeat, currency)}/month`}\n </div>\n )}\n </div>\n )}\n\n {/* Razorpay proration notice */}\n {isRazorpay && seatDelta > 0 && (\n <div className=\"flex gap-2 p-3 rounded-button bg-status-warning-bg-subtle border border-status-warning-border text-sm text-status-warning-text\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <span>\n Seat additions apply from the next billing cycle. A separate prorated charge will be\n raised for the remaining days in the current cycle.\n </span>\n </div>\n )}\n\n {/* Error */}\n {error && (\n <div className=\"flex gap-2 p-3 rounded-button bg-status-error-bg-subtle border border-status-error-border text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm rounded-button border border-border-subtle hover:bg-action-ghost-bgHover text-text-primary transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleConfirm}\n disabled={\n isLoading ||\n newSeatCount === currentSeats ||\n newSeatCount < minSeats ||\n newSeatCount > maxSeats\n }\n className=\"px-4 py-2 text-sm rounded-button bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n {isLoading && <Loader2 className=\"size-4 animate-spin\" />}\n {newSeatCount === currentSeats\n ? 'No change'\n : seatDelta > 0\n ? `Add ${seatDelta} seat${seatDelta !== 1 ? 's' : ''}`\n : `Remove ${Math.abs(seatDelta)} seat${Math.abs(seatDelta) !== 1 ? 's' : ''}`}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ManageSeatsModal.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/components/ManageSeatsModal.tsx"],"sourcesContent":["/**\n * ManageSeatsModal\n * Lets users update the seat count on a PER_SEAT subscription.\n * Shows current seats, min/max bounds, and a proration notice for Razorpay.\n */\n\nimport { type FC, useState, useEffect } from 'react';\nimport { Minus, Plus, Users, AlertCircle, Loader2 } from 'lucide-react';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport type { BillingSubscription } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\ninterface ManageSeatsModalProps {\n subscription: BillingSubscription;\n onClose: () => void;\n onConfirm: (seatCount: number) => Promise<void>;\n isLoading?: boolean;\n}\n\nexport const ManageSeatsModal: FC<ManageSeatsModalProps> = ({\n subscription,\n onClose,\n onConfirm,\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 plan = subscription.plan;\n const minSeats = plan?.minSeats ?? 1;\n const maxSeats = plan?.maxSeats ?? 999;\n const pricePerSeat = plan?.pricePerSeat ?? 0;\n const currency = plan?.currency ?? 'USD';\n const currentSeats = subscription.seatCount ?? minSeats;\n\n const [newSeatCount, setNewSeatCount] = useState(currentSeats);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setNewSeatCount(currentSeats);\n }, [currentSeats]);\n\n const seatDelta = newSeatCount - currentSeats;\n const newMonthlyTotal = newSeatCount * pricePerSeat;\n const currentMonthlyTotal = currentSeats * pricePerSeat;\n const isRazorpay = subscription.paymentGateway === 'razorpay';\n\n function decrement() {\n setNewSeatCount((n) => Math.max(minSeats, n - 1));\n setError(null);\n }\n\n function increment() {\n setNewSeatCount((n) => Math.min(maxSeats, n + 1));\n setError(null);\n }\n\n function handleInputChange(value: string) {\n const parsed = parseInt(value, 10);\n if (isNaN(parsed)) return;\n if (parsed < minSeats) {\n setError(`Minimum ${minSeats} seat${minSeats !== 1 ? 's' : ''} required for this plan.`);\n setNewSeatCount(parsed);\n } else if (parsed > maxSeats) {\n setError(`Maximum ${maxSeats} seats allowed.`);\n setNewSeatCount(parsed);\n } else {\n setError(null);\n setNewSeatCount(parsed);\n }\n }\n\n async function handleConfirm() {\n if (newSeatCount < minSeats || newSeatCount > maxSeats) return;\n if (newSeatCount === currentSeats) {\n onClose();\n return;\n }\n setError(null);\n try {\n await onConfirm(newSeatCount);\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update seats.');\n }\n }\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !isLoading) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n {/* Header */}\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2\">\n <Users className=\"size-5 text-text-link\" />\n {tr('billing.subscriptions.manageSeats', 'Manage Seats')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n {/* Body */}\n <div className=\"space-y-6\">\n {/* Current vs new */}\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>Current seats</span>\n <span className=\"font-medium text-text-primary\">{currentSeats}</span>\n </div>\n\n {/* Seat selector */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium text-text-primary\">New seat count</label>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={decrement}\n disabled={newSeatCount <= minSeats || isLoading}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n aria-label=\"Decrease seat count\"\n >\n <Minus className=\"size-4\" />\n </button>\n <input\n type=\"number\"\n value={newSeatCount}\n min={minSeats}\n max={maxSeats}\n onChange={(e) => handleInputChange(e.target.value)}\n disabled={isLoading}\n className=\"w-20 h-9 text-center border border-border-subtle rounded-button bg-bg-surface text-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n />\n <button\n type=\"button\"\n onClick={increment}\n disabled={newSeatCount >= maxSeats || isLoading}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n aria-label=\"Increase seat count\"\n >\n <Plus className=\"size-4\" />\n </button>\n <span className=\"text-sm text-text-secondary\">\n {minSeats === maxSeats\n ? `(${minSeats} fixed)`\n : `(min ${minSeats}${maxSeats < 999 ? `, max ${maxSeats}` : ''})`}\n </span>\n </div>\n </div>\n\n {/* Pricing breakdown */}\n {pricePerSeat > 0 && (\n <div className=\"rounded-lg bg-bg-sunken border border-border-subtle p-4 space-y-2 text-sm\">\n <div className=\"flex justify-between text-text-secondary\">\n <span>Per seat</span>\n <span>{formatCurrency(pricePerSeat, currency)} / month</span>\n </div>\n <div className=\"flex justify-between text-text-secondary\">\n <span>Current total</span>\n <span>{formatCurrency(currentMonthlyTotal, currency)} / month</span>\n </div>\n <div className=\"border-t border-border-subtle pt-2 flex justify-between font-medium text-text-primary\">\n <span>New total</span>\n <span\n className={\n seatDelta > 0\n ? 'text-status-error-text'\n : seatDelta < 0\n ? 'text-status-success-text'\n : ''\n }\n >\n {formatCurrency(newMonthlyTotal, currency)} / month\n </span>\n </div>\n {seatDelta !== 0 && (\n <div className=\"text-xs text-text-secondary\">\n {seatDelta > 0\n ? `+${seatDelta} seat${seatDelta !== 1 ? 's' : ''} — +${formatCurrency(seatDelta * pricePerSeat, currency)}/month`\n : `${seatDelta} seat${Math.abs(seatDelta) !== 1 ? 's' : ''} — ${formatCurrency(seatDelta * pricePerSeat, currency)}/month`}\n </div>\n )}\n </div>\n )}\n\n {/* Razorpay proration notice */}\n {isRazorpay && seatDelta > 0 && (\n <div className=\"flex gap-2 p-3 rounded-button bg-status-warning-bg-subtle border border-status-warning-border text-sm text-status-warning-text\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <span>\n Seat additions apply from the next billing cycle. A separate prorated charge will be\n raised for the remaining days in the current cycle.\n </span>\n </div>\n )}\n\n {/* Error */}\n {error && (\n <div className=\"flex gap-2 p-3 rounded-button bg-status-error-bg-subtle border border-status-error-border text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm rounded-button border border-border-subtle hover:bg-action-ghost-bgHover text-text-primary transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleConfirm}\n disabled={\n isLoading ||\n newSeatCount === currentSeats ||\n newSeatCount < minSeats ||\n newSeatCount > maxSeats\n }\n className=\"px-4 py-2 text-sm rounded-button bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n {isLoading && <Loader2 className=\"size-4 animate-spin\" />}\n {newSeatCount === currentSeats\n ? 'No change'\n : seatDelta > 0\n ? `Add ${seatDelta} seat${seatDelta !== 1 ? 's' : ''}`\n : `Remove ${Math.abs(seatDelta)} seat${Math.abs(seatDelta) !== 1 ? 's' : ''}`}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSubscriptions.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/hooks/useSubscriptions.ts"],"sourcesContent":["/**\n * Subscriptions Module - useSubscriptions Hook\n * Provides access to subscription data and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetSubscriptionQuery,\n useGetBillingAccountQuery,\n useGetDefaultBillingAccountQuery,\n useGetSubscriptionUsageOverviewQuery,\n useGetBillingAccountsForSubscriptionsQuery,\n useGetPaginatedSubscriptionsQuery,\n useCreateBillingAccountSubscriptionsMutation,\n useUpdateBillingAccountSubscriptionsMutation,\n useInitiatePaymentSubscriptionsMutation,\n useSpendCreditsSubscriptionsMutation,\n useCancelSubscriptionMutation,\n useUpdateSubscriptionSeatsMutation,\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n} from '../../../../generated/global-operations';\nimport type {\n BillingSubscription,\n BillingAccount,\n SubscriptionStatus,\n CreateBillingAccountInput,\n UpdateBillingAccountInput,\n InitiatePaymentInput,\n InitiatePaymentResponse,\n SpendCreditsInput,\n CancelSubscriptionInput,\n UpdateSubscriptionSeatsInput,\n} from '../../../shared/types';\n\ninterface UseSubscriptionResult {\n subscription: BillingSubscription | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseBillingAccountResult {\n billingAccount: BillingAccount | null;\n subscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionsListResult {\n subscriptions: BillingSubscription[];\n activeSubscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionMutationsResult {\n createBillingAccount: (input: CreateBillingAccountInput) => Promise<BillingAccount>;\n updateBillingAccount: (input: UpdateBillingAccountInput) => Promise<BillingAccount>;\n initiatePayment: (input: InitiatePaymentInput) => Promise<InitiatePaymentResponse>;\n spendCredits: (input: SpendCreditsInput) => Promise<{ creditTransactionID: string }>;\n cancelSubscription: (input: CancelSubscriptionInput) => Promise<BillingSubscription>;\n updateSubscriptionSeats: (input: UpdateSubscriptionSeatsInput) => Promise<BillingSubscription>;\n isCreating: boolean;\n isUpdating: boolean;\n isInitiatingPayment: boolean;\n isSpendingCredits: boolean;\n isCanceling: boolean;\n isUpdatingSeats: boolean;\n}\n\n/**\n * Hook for fetching a single subscription by ID\n */\nexport function useSubscription(\n id: string | undefined,\n billingAccountId: string | undefined\n): UseSubscriptionResult {\n const { data, loading, error, refetch } = useGetSubscriptionQuery({\n variables: { id: id!, billingAccountId: billingAccountId! },\n skip: !id || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscription: data?.getSubscription || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account by ID\n */\nexport function useBillingAccount(id: string | undefined): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetBillingAccountQuery({\n variables: { id: id! },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching default billing account for an org\n */\nexport function useDefaultBillingAccount(): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetDefaultBillingAccountQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getDefaultBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for listing subscriptions from a billing account\n */\nexport function useSubscriptionsList(\n billingAccountId: string | undefined\n): UseSubscriptionsListResult {\n const { subscriptions, isLoading, error, refetch } = useBillingAccount(billingAccountId);\n\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n isLoading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook for subscription usage overview\n */\nexport function useSubscriptionUsageOverview(\n billingAccountId: string | undefined,\n page = 1,\n pageSize = 10\n) {\n const { data, loading, error, refetch } = useGetSubscriptionUsageOverviewQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscriptionsWithQuotas: data?.getSubscriptionUsageOverview || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for subscription mutations\n */\nexport function useSubscriptionMutations(): UseSubscriptionMutationsResult {\n const client = useApolloClient();\n\n const [createBillingAccountMutation, { loading: isCreating }] =\n useCreateBillingAccountSubscriptionsMutation();\n const [updateBillingAccountMutation, { loading: isUpdating }] =\n useUpdateBillingAccountSubscriptionsMutation();\n const [initiatePaymentMutation, { loading: isInitiatingPayment }] =\n useInitiatePaymentSubscriptionsMutation();\n const [spendCreditsMutation, { loading: isSpendingCredits }] =\n useSpendCreditsSubscriptionsMutation();\n const [cancelSubscriptionMutation, { loading: isCanceling }] = useCancelSubscriptionMutation();\n const [updateSeatsMutation, { loading: isUpdatingSeats }] = useUpdateSubscriptionSeatsMutation();\n\n const createBillingAccount = useCallback(\n async (input: CreateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await createBillingAccountMutation({\n variables: { input },\n });\n return data.createBillingAccount;\n } catch (error) {\n console.error('Failed to create billing account:', error);\n throw error;\n }\n },\n [createBillingAccountMutation]\n );\n\n const updateBillingAccount = useCallback(\n async (input: UpdateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await updateBillingAccountMutation({\n variables: { input },\n });\n // Refetch billing account after update\n await client.refetchQueries({ include: [GetBillingAccountDocument] });\n return data.updateBillingAccount;\n } catch (error) {\n console.error('Failed to update billing account:', error);\n throw error;\n }\n },\n [updateBillingAccountMutation, client]\n );\n\n const initiatePayment = useCallback(\n async (input: InitiatePaymentInput): Promise<InitiatePaymentResponse> => {\n try {\n const { data } = await initiatePaymentMutation({\n variables: { input },\n });\n return data.initiatePayment;\n } catch (error) {\n console.error('Failed to initiate payment:', error);\n throw error;\n }\n },\n [initiatePaymentMutation]\n );\n\n const spendCredits = useCallback(\n async (input: SpendCreditsInput): Promise<{ creditTransactionID: string }> => {\n try {\n const { data } = await spendCreditsMutation({\n variables: { input },\n });\n // Refetch billing account after spending credits\n await client.refetchQueries({\n include: [GetBillingAccountDocument, GetDefaultBillingAccountDocument],\n });\n return data.spendCredits;\n } catch (error) {\n console.error('Failed to spend credits:', error);\n throw error;\n }\n },\n [spendCreditsMutation, client]\n );\n\n const cancelSubscription = useCallback(\n async (input: CancelSubscriptionInput): Promise<BillingSubscription> => {\n try {\n const { data } = await cancelSubscriptionMutation({\n variables: { input },\n });\n // Refetch subscription and billing account after cancellation\n await client.refetchQueries({\n include: [\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n ],\n });\n return data.cancelSubscription;\n } catch (error) {\n console.error('Failed to cancel subscription:', error);\n throw error;\n }\n },\n [cancelSubscriptionMutation, client]\n );\n\n const updateSubscriptionSeats = useCallback(\n async (input: UpdateSubscriptionSeatsInput): Promise<BillingSubscription> => {\n try {\n const { data } = await updateSeatsMutation({ variables: { input } });\n await client.refetchQueries({ include: [GetSubscriptionDocument] });\n return data.updateSubscriptionSeats;\n } catch (error) {\n console.error('Failed to update subscription seats:', error);\n throw error;\n }\n },\n [updateSeatsMutation, client]\n );\n\n return {\n createBillingAccount,\n updateBillingAccount,\n initiatePayment,\n spendCredits,\n cancelSubscription,\n updateSubscriptionSeats,\n isCreating,\n isUpdating,\n isInitiatingPayment,\n isSpendingCredits,\n isCanceling,\n isUpdatingSeats,\n };\n}\n\n/**\n * Hook for fetching all billing accounts for an org\n */\nexport function useBillingAccounts() {\n const { data, loading, error, refetch } = useGetBillingAccountsForSubscriptionsQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccounts: BillingAccount[] = data?.getBillingAccountsByOrg || [];\n\n return {\n billingAccounts,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching paginated subscriptions for a billing account\n */\nexport function usePaginatedSubscriptions(\n billingAccountId: string | undefined,\n page: number = 1,\n pageSize: number = 10,\n search?: string\n) {\n const { data, loading, error, refetch } = useGetPaginatedSubscriptionsQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize, search: search || undefined },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const result = data?.getPaginatedSubscriptions;\n const subscriptions = useMemo<BillingSubscription[]>(\n () => result?.subscriptions || [],\n [result?.subscriptions]\n );\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n totalCount: result?.totalCount || 0,\n page: result?.page || page,\n pageSize: result?.pageSize || pageSize,\n totalPages: result?.totalPages || 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useSubscriptions.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/hooks/useSubscriptions.ts"],"sourcesContent":["/**\n * Subscriptions Module - useSubscriptions Hook\n * Provides access to subscription data and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetSubscriptionQuery,\n useGetBillingAccountQuery,\n useGetDefaultBillingAccountQuery,\n useGetSubscriptionUsageOverviewQuery,\n useGetBillingAccountsForSubscriptionsQuery,\n useGetPaginatedSubscriptionsQuery,\n useCreateBillingAccountSubscriptionsMutation,\n useUpdateBillingAccountSubscriptionsMutation,\n useInitiatePaymentSubscriptionsMutation,\n useSpendCreditsSubscriptionsMutation,\n useCancelSubscriptionMutation,\n useUpdateSubscriptionSeatsMutation,\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n} from '../../../../generated/global-operations';\nimport type {\n BillingSubscription,\n BillingAccount,\n SubscriptionStatus,\n CreateBillingAccountInput,\n UpdateBillingAccountInput,\n InitiatePaymentInput,\n InitiatePaymentResponse,\n SpendCreditsInput,\n CancelSubscriptionInput,\n UpdateSubscriptionSeatsInput,\n} from '../../../shared/types';\n\ninterface UseSubscriptionResult {\n subscription: BillingSubscription | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseBillingAccountResult {\n billingAccount: BillingAccount | null;\n subscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionsListResult {\n subscriptions: BillingSubscription[];\n activeSubscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionMutationsResult {\n createBillingAccount: (input: CreateBillingAccountInput) => Promise<BillingAccount>;\n updateBillingAccount: (input: UpdateBillingAccountInput) => Promise<BillingAccount>;\n initiatePayment: (input: InitiatePaymentInput) => Promise<InitiatePaymentResponse>;\n spendCredits: (input: SpendCreditsInput) => Promise<{ creditTransactionID: string }>;\n cancelSubscription: (input: CancelSubscriptionInput) => Promise<BillingSubscription>;\n updateSubscriptionSeats: (input: UpdateSubscriptionSeatsInput) => Promise<BillingSubscription>;\n isCreating: boolean;\n isUpdating: boolean;\n isInitiatingPayment: boolean;\n isSpendingCredits: boolean;\n isCanceling: boolean;\n isUpdatingSeats: boolean;\n}\n\n/**\n * Hook for fetching a single subscription by ID\n */\nexport function useSubscription(\n id: string | undefined,\n billingAccountId: string | undefined\n): UseSubscriptionResult {\n const { data, loading, error, refetch } = useGetSubscriptionQuery({\n variables: { id: id!, billingAccountId: billingAccountId! },\n skip: !id || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscription: data?.getSubscription || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account by ID\n */\nexport function useBillingAccount(id: string | undefined): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetBillingAccountQuery({\n variables: { id: id! },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching default billing account for an org\n */\nexport function useDefaultBillingAccount(): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetDefaultBillingAccountQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getDefaultBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for listing subscriptions from a billing account\n */\nexport function useSubscriptionsList(\n billingAccountId: string | undefined\n): UseSubscriptionsListResult {\n const { subscriptions, isLoading, error, refetch } = useBillingAccount(billingAccountId);\n\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n isLoading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook for subscription usage overview\n */\nexport function useSubscriptionUsageOverview(\n billingAccountId: string | undefined,\n page = 1,\n pageSize = 10\n) {\n const { data, loading, error, refetch } = useGetSubscriptionUsageOverviewQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscriptionsWithQuotas: data?.getSubscriptionUsageOverview || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for subscription mutations\n */\nexport function useSubscriptionMutations(): UseSubscriptionMutationsResult {\n const client = useApolloClient();\n\n const [createBillingAccountMutation, { loading: isCreating }] =\n useCreateBillingAccountSubscriptionsMutation();\n const [updateBillingAccountMutation, { loading: isUpdating }] =\n useUpdateBillingAccountSubscriptionsMutation();\n const [initiatePaymentMutation, { loading: isInitiatingPayment }] =\n useInitiatePaymentSubscriptionsMutation();\n const [spendCreditsMutation, { loading: isSpendingCredits }] =\n useSpendCreditsSubscriptionsMutation();\n const [cancelSubscriptionMutation, { loading: isCanceling }] = useCancelSubscriptionMutation();\n const [updateSeatsMutation, { loading: isUpdatingSeats }] = useUpdateSubscriptionSeatsMutation();\n\n const createBillingAccount = useCallback(\n async (input: CreateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await createBillingAccountMutation({\n variables: { input },\n });\n return data.createBillingAccount;\n } catch (error) {\n console.error('Failed to create billing account:', error);\n throw error;\n }\n },\n [createBillingAccountMutation]\n );\n\n const updateBillingAccount = useCallback(\n async (input: UpdateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await updateBillingAccountMutation({\n variables: { input },\n });\n // Refetch billing account after update\n await client.refetchQueries({ include: [GetBillingAccountDocument] });\n return data.updateBillingAccount;\n } catch (error) {\n console.error('Failed to update billing account:', error);\n throw error;\n }\n },\n [updateBillingAccountMutation, client]\n );\n\n const initiatePayment = useCallback(\n async (input: InitiatePaymentInput): Promise<InitiatePaymentResponse> => {\n try {\n const { data } = await initiatePaymentMutation({\n variables: { input },\n });\n return data.initiatePayment;\n } catch (error) {\n console.error('Failed to initiate payment:', error);\n throw error;\n }\n },\n [initiatePaymentMutation]\n );\n\n const spendCredits = useCallback(\n async (input: SpendCreditsInput): Promise<{ creditTransactionID: string }> => {\n try {\n const { data } = await spendCreditsMutation({\n variables: { input },\n });\n // Refetch billing account after spending credits\n await client.refetchQueries({\n include: [GetBillingAccountDocument, GetDefaultBillingAccountDocument],\n });\n return data.spendCredits;\n } catch (error) {\n console.error('Failed to spend credits:', error);\n throw error;\n }\n },\n [spendCreditsMutation, client]\n );\n\n const cancelSubscription = useCallback(\n async (input: CancelSubscriptionInput): Promise<BillingSubscription> => {\n try {\n const { data } = await cancelSubscriptionMutation({\n variables: { input },\n });\n // Refetch subscription and billing account after cancellation\n await client.refetchQueries({\n include: [\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n ],\n });\n return data.cancelSubscription;\n } catch (error) {\n console.error('Failed to cancel subscription:', error);\n throw error;\n }\n },\n [cancelSubscriptionMutation, client]\n );\n\n const updateSubscriptionSeats = useCallback(\n async (input: UpdateSubscriptionSeatsInput): Promise<BillingSubscription> => {\n try {\n const { data } = await updateSeatsMutation({ variables: { input } });\n await client.refetchQueries({ include: [GetSubscriptionDocument] });\n return data.updateSubscriptionSeats;\n } catch (error) {\n console.error('Failed to update subscription seats:', error);\n throw error;\n }\n },\n [updateSeatsMutation, client]\n );\n\n return {\n createBillingAccount,\n updateBillingAccount,\n initiatePayment,\n spendCredits,\n cancelSubscription,\n updateSubscriptionSeats,\n isCreating,\n isUpdating,\n isInitiatingPayment,\n isSpendingCredits,\n isCanceling,\n isUpdatingSeats,\n };\n}\n\n/**\n * Hook for fetching all billing accounts for an org\n */\nexport function useBillingAccounts() {\n const { data, loading, error, refetch } = useGetBillingAccountsForSubscriptionsQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccounts: BillingAccount[] = data?.getBillingAccountsByOrg || [];\n\n return {\n billingAccounts,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching paginated subscriptions for a billing account\n */\nexport function usePaginatedSubscriptions(\n billingAccountId: string | undefined,\n page: number = 1,\n pageSize: number = 10,\n search?: string\n) {\n const { data, loading, error, refetch } = useGetPaginatedSubscriptionsQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize, search: search || undefined },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const result = data?.getPaginatedSubscriptions;\n const subscriptions = useMemo<BillingSubscription[]>(\n () => result?.subscriptions || [],\n [result?.subscriptions]\n );\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n totalCount: result?.totalCount || 0,\n page: result?.page || page,\n pageSize: result?.pageSize || pageSize,\n totalPages: result?.totalPages || 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/pages/SubscriptionDetailPage.tsx"],"sourcesContent":["/**\n * Subscriptions Module - Subscription Detail Page\n * Displays detailed information about a single subscription\n */\n\nimport { type FC, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport { useSubscription, useSubscriptionMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useGetDefaultBillingAccountDashboardQuery } from '../../../../generated/global-operations';\nimport { useWorkspaceQuotaOverview } from '../../usage/hooks/useUsage';\nimport {\n formatCurrency,\n formatDate,\n formatPlanDuration,\n formatSubscriptionStatus,\n formatPercentage,\n formatBytes,\n} from '../../../shared/utils/format';\nimport { getSubscriptionStatusColor, getQuotaProgressColor } from '../../../shared/utils/status';\nimport { Card, StatusPill, ProgressBar } from '../../../shared/ui';\nimport {\n CTAOverflowMenu,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n type CTAAction,\n type NextStep,\n} from '@burdenoff/fe-libs/ui';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport type {\n SubscriptionStatus,\n QuotaAssignment,\n AddonSubscription,\n SubscriptionHistory,\n Transaction,\n} from '../../../shared/types';\nimport { PricingModel } from '../../../shared/types';\nimport { ManageSeatsModal } from '../components/ManageSeatsModal';\nimport {\n WorkspaceQuotaAssignmentModal,\n type WorkspaceQuotaToAssign,\n} from '../../checkout/components/WorkspaceQuotaAssignmentModal';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { WorkspaceQuotaOverviewItem } from '../../usage/hooks/useUsage';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatQuotaDisplayName(name: string, context?: Record<string, unknown> | null): string {\n if (context?.displayName && typeof context.displayName === 'string') return context.displayName;\n if (context?.label && typeof context.label === 'string') return context.label;\n const parts = name.split('.');\n const meaningful = parts.length > 1 ? parts.slice(1) : parts;\n return meaningful\n .map((p) => p.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()))\n .join(' ');\n}\n\nfunction formatResetPeriod(period: string): string {\n if (period === 'DAILY') return 'Resets daily';\n if (period === 'MONTHLY') return 'Resets monthly';\n return '';\n}\n\n// ============================================================================\n// Page Component\n// ============================================================================\n\nexport const SubscriptionDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { subscriptionId } = useParams<{ subscriptionId: string }>();\n const navigateTo = useBillingNavigate();\n const { emit } = useBillingEventEmitter();\n const { orgId } = useBilling();\n const permissions = useBillingPermissions();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n const { data: defaultBillingAccountData } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-first',\n });\n const billingAccountId =\n selectedAccountId ?? defaultBillingAccountData?.getDefaultBillingAccount?.id ?? undefined;\n const { subscription, isLoading, error, refetch } = useSubscription(\n subscriptionId,\n billingAccountId\n );\n const { cancelSubscription, isCanceling, updateSubscriptionSeats, isUpdatingSeats } =\n useSubscriptionMutations();\n\n const { workspaceId } = useBilling();\n\n // Workspace quota overview — used to detect if quotas are already active and show live usage\n const { overview: wsQuotaOverview, refetch: refetchWsOverview } = useWorkspaceQuotaOverview(\n workspaceId ?? undefined\n );\n\n // Modal state\n const [showCancelModal, setShowCancelModal] = useState(false);\n const [showManageSeatsModal, setShowManageSeatsModal] = useState(false);\n const [showAssignWorkspaceModal, setShowAssignWorkspaceModal] = useState(false);\n const [cancelReason, setCancelReason] = useState('');\n const [cancelError, setCancelError] = useState<string | null>(null);\n\n // Handle cancel subscription\n const handleCancelSubscription = async () => {\n if (!subscription) return;\n\n setCancelError(null);\n try {\n await cancelSubscription({\n billingAccountId: billingAccountId ?? subscription.billingAccountId,\n subscriptionId: subscription.id,\n reason: cancelReason || 'User requested cancellation',\n });\n emit('billing.subscription.cancelled', {\n route: '/billing/subscriptions',\n entityId: subscription.id,\n source: 'subscription-detail',\n reason: cancelReason || 'User requested cancellation',\n });\n setShowCancelModal(false);\n setCancelReason('');\n await refetch();\n } catch (err) {\n setCancelError(err instanceof Error ? err.message : 'Failed to cancel subscription');\n }\n };\n\n // Permission check\n if (!permissions.canViewSubscriptions) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">\n You don&apos;t have permission to view this subscription.\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded-button\" />\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-5 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !subscription) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.subscriptions.notFound', 'Subscription not found')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {error?.message || 'The subscription you are looking for does not exist.'}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"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 duration-200\"\n >\n Back to Subscriptions\n </button>\n </div>\n </div>\n );\n }\n\n const plan = subscription.plan;\n const quotas = subscription.quotas || [];\n const addons = subscription.addonSubscriptions || [];\n const transactions = subscription.transactions || [];\n const history = subscription.history || [];\n const statusColor = getSubscriptionStatusColor(subscription.status);\n const isActive = subscription.status === ('active' as SubscriptionStatus);\n\n // Workspace quota assignments that belong to this subscription\n const wsAssignmentsForSub = wsQuotaOverview.filter(\n (item) => item.assignment.subscriptionId === subscription.id && item.assignment.isActive\n );\n // Assigned if subscription field is set OR workspace assignments already exist\n const isAssignedToWorkspace =\n !!subscription.assignedWorkspaceId || wsAssignmentsForSub.length > 0;\n\n // Calculate days remaining\n const endDate = new Date(subscription.endDate);\n const startDate = new Date(subscription.startDate);\n const now = new Date();\n const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));\n const daysRemaining = Math.max(\n 0,\n Math.ceil((endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))\n );\n const progressPercentage = Math.round(((totalDays - daysRemaining) / totalDays) * 100);\n\n // Recommended next actions for this subscription\n const nextSteps: NextStep[] = [\n ...(isActive && permissions.canUpdateSubscription\n ? [\n {\n id: 'change-plan',\n label: 'Review or change your plan',\n description: 'Upgrade, downgrade, or compare what each tier includes.',\n onClick: () =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${subscription.id}/upgrade`,\n billingAccountId ?? subscription.billingAccountId\n )\n ),\n },\n ]\n : []),\n ...(isActive && workspaceId && !isAssignedToWorkspace\n ? [\n {\n id: 'activate-workspace',\n label: 'Activate quotas for your workspace',\n description: 'Turn on the workspace-scoped limits included in this plan.',\n onClick: () => setShowAssignWorkspaceModal(true),\n },\n ]\n : []),\n {\n id: 'track-usage',\n label: 'Track your usage',\n description: 'See how much of your included quota you have consumed.',\n onClick: () =>\n navigateTo(\n withBillingAccountId('/usage', billingAccountId ?? subscription.billingAccountId)\n ),\n },\n {\n id: 'view-invoices',\n label: 'Review invoices & payments',\n description: 'Check what has been billed for this subscription.',\n onClick: () =>\n navigateTo(\n withBillingAccountId('/invoices', billingAccountId ?? subscription.billingAccountId)\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n aria-label=\"Back to subscriptions\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"p-2 -ml-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors duration-200\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <div className=\"flex items-center gap-3 flex-wrap\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {plan?.name || 'Subscription'}\n </h1>\n <StatusPill status={statusColor} dot pulse={isActive}>\n {formatSubscriptionStatus(subscription.status)}\n </StatusPill>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {plan?.pricingModel === PricingModel.PER_SEAT && plan.pricePerSeat\n ? `${formatCurrency(plan.pricePerSeat, plan.currency)} / seat / ${formatPlanDuration(plan.duration)} · ${subscription.seatCount ?? plan.minSeats ?? 1} seats = ${formatCurrency(plan.pricePerSeat * (subscription.seatCount ?? plan.minSeats ?? 1), plan.currency)} / ${formatPlanDuration(plan.duration)}`\n : plan\n ? `${formatCurrency(plan.price, plan.currency)} / ${formatPlanDuration(plan.duration)}`\n : 'Unknown plan'}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.subscriptions.detailPurpose',\n 'Everything about this subscription in one view — its billing period, the quotas and add-ons it includes, recent charges and its full status history. Use it to track what you are getting, activate workspace quotas, change your plan, or cancel.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <CTAOverflowMenu\n primary={\n isActive && permissions.canUpdateSubscription\n ? {\n label: 'Change Plan',\n onSelect: () =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${subscription.id}/upgrade`,\n billingAccountId ?? subscription.billingAccountId\n )\n ),\n }\n : undefined\n }\n actions={\n [\n ...(isActive &&\n permissions.canUpdateSubscription &&\n plan?.pricingModel === PricingModel.PER_SEAT\n ? [\n {\n label: 'Manage Seats',\n onSelect: () => setShowManageSeatsModal(true),\n },\n ]\n : []),\n ...(isActive && permissions.canCancelSubscription && !subscription.canceledAt\n ? [\n {\n label: isCanceling ? 'Canceling…' : 'Cancel Subscription',\n onSelect: () => setShowCancelModal(true),\n disabled: isCanceling,\n intent: 'destructive' as const,\n },\n ]\n : []),\n ] satisfies CTAAction[]\n }\n />\n {subscription.canceledAt && (\n <span className=\"px-4 py-2 text-sm font-medium text-status-warning-text bg-status-warning-bg-subtle rounded-button\">\n Cancellation Scheduled\n </span>\n )}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Period Progress — primary status emphasis zone */}\n {isActive && (\n <EmphasisPanel className=\"p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Current Period</h2>\n <span className=\"text-sm text-text-secondary tabular-nums\">\n {daysRemaining} days remaining\n </span>\n </div>\n <div className=\"space-y-2\">\n <ProgressBar\n value={progressPercentage}\n status=\"primary\"\n size=\"md\"\n aria-label=\"Billing period progress\"\n />\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>{formatDate(subscription.startDate, 'short')}</span>\n <span>{formatDate(subscription.endDate, 'short')}</span>\n </div>\n </div>\n </EmphasisPanel>\n )}\n\n {/* Plan Quotas */}\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Plan Quotas\n {quotas.length > 0 && (\n <span className=\"ml-2 text-sm font-normal text-text-muted tabular-nums\">\n ({quotas.length})\n </span>\n )}\n </h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">Limits included in your plan</p>\n </div>\n <div className=\"p-5\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No quotas assigned to this subscription.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {quotas.map((quota) => (\n <QuotaUsageItem key={quota.id} quota={quota} />\n ))}\n </div>\n )}\n </div>\n </Card>\n\n {/* Workspace Quotas */}\n {isActive && workspaceId && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle flex items-center justify-between gap-4\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Workspace Quotas</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Active limits for your workspace\n </p>\n </div>\n {isAssignedToWorkspace ? (\n <StatusPill status=\"success\" dot>\n Active\n </StatusPill>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowAssignWorkspaceModal(true)}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n Activate for Workspace\n </button>\n )}\n </div>\n\n <div className=\"p-5\">\n {isAssignedToWorkspace ? (\n wsAssignmentsForSub.length > 0 ? (\n <div className=\"space-y-3\">\n {wsAssignmentsForSub.map((item) => (\n <WorkspaceQuotaItem key={item.assignment.id} item={item} />\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Quotas are activated on workspace{' '}\n <span className=\"font-mono text-xs text-text-primary bg-bg-sunken/60 rounded px-1.5 py-0.5\">\n {subscription.assignedWorkspaceId}\n </span>\n . Usage data will appear here once quotas are consumed.\n </p>\n )\n ) : (\n <div className=\"flex items-start gap-3\">\n <div className=\"size-8 rounded-full bg-status-warning-bg-subtle flex items-center justify-center shrink-0 mt-0.5\">\n <svg\n className=\"size-4 text-status-warning-text\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Workspace quotas not activated yet\n </p>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Click{' '}\n <button\n type=\"button\"\n onClick={() => setShowAssignWorkspaceModal(true)}\n className=\"text-text-link hover:underline font-medium\"\n >\n Activate for Workspace\n </button>{' '}\n to enable the workspace-scoped limits included in this plan.\n </p>\n </div>\n </div>\n )}\n </div>\n\n {showAssignWorkspaceModal && (\n <WorkspaceQuotaAssignmentModal\n billingAccountId={billingAccountId ?? subscription.billingAccountId}\n subscriptionId={subscription.id}\n quotas={[] as WorkspaceQuotaToAssign[]}\n workspaces={[{ id: workspaceId, name: 'Current workspace' }]}\n currentWorkspaceId={workspaceId}\n onAssigned={() => {\n setShowAssignWorkspaceModal(false);\n void refetch();\n void refetchWsOverview();\n }}\n onSkip={() => setShowAssignWorkspaceModal(false)}\n />\n )}\n </Card>\n )}\n\n {/* Addons */}\n {addons.length > 0 && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Addons{' '}\n <span className=\"text-sm font-normal text-text-muted tabular-nums\">\n ({addons.length})\n </span>\n </h2>\n </div>\n <div className=\"p-5\">\n <div className=\"space-y-3\">\n {addons.map((addonSub) => (\n <AddonItem key={addonSub.id} addonSubscription={addonSub} />\n ))}\n </div>\n </div>\n </Card>\n )}\n\n {/* Recent Transactions */}\n {transactions.length > 0 && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">Recent Transactions</h2>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {transactions.slice(0, 5).map((transaction) => (\n <TransactionItem key={transaction.id} transaction={transaction} />\n ))}\n </div>\n {transactions.length > 5 && (\n <div className=\"p-4 text-center border-t border-border-subtle\">\n <button type=\"button\" className=\"text-sm text-text-link hover:underline\">\n View all transactions\n </button>\n </div>\n )}\n </Card>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6 lg:sticky lg:top-6 lg:self-start\">\n {/* Next steps guidance */}\n <NextSteps storageKey=\"billing-subscription-detail\" steps={nextSteps} />\n\n {/* Subscription Details */}\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">Details</h2>\n </div>\n <div className=\"p-5 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Subscription ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">\n {subscription.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Plan</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{plan?.name || 'Unknown'}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Billing Account</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {subscription.billingAccount?.name || 'Unknown'}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Start Date</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(subscription.startDate)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">End Date</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(subscription.endDate)}\n </p>\n </div>\n {subscription.nextBillingDate && (\n <div>\n <p className=\"text-sm text-text-secondary\">Next Billing</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-text-secondary\">Auto Renewal</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n {subscription.paymentGateway && (\n <div>\n <p className=\"text-sm text-text-secondary\">Payment Gateway</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {subscription.paymentGateway}\n </p>\n </div>\n )}\n </div>\n </Card>\n\n {/* History */}\n {history.length > 0 && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">History</h2>\n </div>\n <div className=\"p-5\">\n <div className=\"space-y-3\">\n {history.slice(0, 5).map((item) => (\n <HistoryItem key={item.id} item={item} />\n ))}\n </div>\n </div>\n </Card>\n )}\n </div>\n </div>\n\n {/* Manage Seats Modal */}\n {showManageSeatsModal && subscription && (\n <ManageSeatsModal\n subscription={subscription}\n onClose={() => setShowManageSeatsModal(false)}\n onConfirm={async (seatCount) => {\n await updateSubscriptionSeats({ subscriptionId: subscription.id, seatCount });\n await refetch();\n }}\n isLoading={isUpdatingSeats}\n />\n )}\n\n {/* Cancel Subscription Modal */}\n {showCancelModal && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div\n className=\"absolute inset-0 bg-overlay-scrim\"\n onClick={() => setShowCancelModal(false)}\n />\n <div\n className=\"relative bg-bg-elevated border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-md mx-4 p-6\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"cancel-subscription-title\"\n >\n <h3\n id=\"cancel-subscription-title\"\n className=\"text-lg font-semibold text-text-primary mb-2\"\n >\n Cancel Subscription\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Are you sure you want to cancel this subscription? Your subscription will remain\n active until{' '}\n <span className=\"font-medium text-text-primary\">\n {formatDate(subscription.endDate)}\n </span>\n , but will not renew automatically.\n </p>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Reason for cancellation (optional)\n </label>\n <textarea\n value={cancelReason}\n onChange={(e) => setCancelReason(e.target.value)}\n placeholder=\"Please share why you're canceling...\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent resize-none\"\n rows={3}\n />\n </div>\n\n {cancelError && (\n <div className=\"mb-4 p-3 bg-status-error-bg-subtle border border-status-error-border rounded-card\">\n <p className=\"text-sm text-status-error-text\">{cancelError}</p>\n </div>\n )}\n\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setShowCancelModal(false);\n setCancelReason('');\n setCancelError(null);\n }}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n Keep Subscription\n </button>\n <button\n type=\"button\"\n onClick={handleCancelSubscription}\n disabled={isCanceling}\n className=\"px-4 py-2 text-sm font-medium bg-action-danger-bg text-action-danger-text rounded-button hover:bg-action-danger-bgHover transition-colors duration-200 disabled:opacity-50\"\n >\n {isCanceling ? 'Canceling...' : 'Confirm Cancellation'}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\nconst QuotaUsageItem: FC<{ quota: QuotaAssignment }> = ({ quota }) => {\n const context = quota.context as\n { displayName?: string; label?: string; description?: string } | null | undefined;\n const displayName = formatQuotaDisplayName(quota.name, context as Record<string, unknown> | null);\n const description =\n context?.description && typeof context.description === 'string' ? context.description : null;\n const limits = quota.limits as { value?: number } | null;\n const limit = limits?.value ?? 0;\n const used = quota.currentUsageSum ?? 0;\n const percentage = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;\n const isBytes = quota.name.includes('bytes') || quota.name.includes('storage');\n const isPooled = quota.quotaType === 'POOLED';\n\n return (\n <div className=\"p-4 rounded-card border border-border-seam bg-bg-surface\">\n <div className=\"flex items-start justify-between gap-4 mb-2\">\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{displayName}</p>\n {description && (\n <p className=\"text-xs text-text-secondary mt-0.5 line-clamp-2\">{description}</p>\n )}\n </div>\n <span className=\"text-sm font-medium text-text-primary shrink-0 tabular-nums\">\n {isBytes ? formatBytes(used) : used.toLocaleString()}\n {' / '}\n {limit > 0 ? (isBytes ? formatBytes(limit) : limit.toLocaleString()) : '∞'}\n </span>\n </div>\n\n {limit > 0 && (\n <div className=\"h-1.5 rounded-full bg-bg-sunken overflow-hidden mb-2\">\n <div\n className={`h-full rounded-full transition-all duration-300 ${getQuotaProgressColor(percentage)}`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n )}\n\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span className=\"px-1.5 py-0.5 rounded bg-bg-sunken font-medium\">\n {isPooled ? 'Shared' : 'Per Workspace'}\n </span>\n <div className=\"flex items-center gap-3\">\n {quota.endtime && <span>Expires {formatDate(quota.endtime, 'short')}</span>}\n {limit > 0 && (\n <span\n className={\n percentage >= 90\n ? 'text-status-error-text font-medium'\n : percentage >= 70\n ? 'text-status-warning-text font-medium'\n : ''\n }\n >\n {formatPercentage(percentage)} used\n </span>\n )}\n </div>\n </div>\n </div>\n );\n};\n\nconst WorkspaceQuotaItem: FC<{ item: WorkspaceQuotaOverviewItem }> = ({ item }) => {\n const { assignment, effectiveUsed, usagePercentage } = item;\n const displayName = formatQuotaDisplayName(assignment.quotaName);\n const hasLimit = !assignment.noLimit && assignment.limit !== null;\n const limit = assignment.limit ?? 0;\n const isBytes =\n assignment.quotaName.includes('bytes') || assignment.quotaName.includes('storage');\n const resetLabel = formatResetPeriod(assignment.resetPeriod);\n\n return (\n <div className=\"p-4 rounded-card border border-border-seam bg-bg-surface\">\n <div className=\"flex items-start justify-between gap-4 mb-2\">\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{displayName}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {assignment.quotaMode === 'pooled' ? 'Pooled across workspaces' : 'Dedicated'}\n {resetLabel && ` · ${resetLabel}`}\n </p>\n </div>\n <span className=\"text-sm font-medium text-text-primary shrink-0 tabular-nums\">\n {isBytes ? formatBytes(effectiveUsed) : effectiveUsed.toLocaleString()}\n {' / '}\n {assignment.noLimit\n ? '∞'\n : hasLimit\n ? isBytes\n ? formatBytes(limit)\n : limit.toLocaleString()\n : '∞'}\n </span>\n </div>\n\n {hasLimit && (\n <div className=\"h-1.5 rounded-full bg-bg-sunken overflow-hidden mb-2\">\n <div\n className={`h-full rounded-full transition-all duration-300 ${getQuotaProgressColor(usagePercentage)}`}\n style={{ width: `${Math.min(100, usagePercentage)}%` }}\n />\n </div>\n )}\n\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>Since {formatDate(assignment.createdAt, 'short')}</span>\n {hasLimit && (\n <span\n className={\n usagePercentage >= 90\n ? 'text-status-error-text font-medium'\n : usagePercentage >= 70\n ? 'text-status-warning-text font-medium'\n : ''\n }\n >\n {formatPercentage(usagePercentage)} used\n </span>\n )}\n </div>\n </div>\n );\n};\n\nconst AddonItem: FC<{ addonSubscription: AddonSubscription }> = ({ addonSubscription }) => {\n const addon = addonSubscription.addon;\n return (\n <div className=\"flex items-center justify-between p-3 rounded-card border border-border-seam bg-bg-sunken\">\n <div>\n <p className=\"font-medium text-text-primary\">{addon?.name || 'Unknown Addon'}</p>\n <p className=\"text-sm text-text-secondary\">Qty: {addonSubscription.quantity}</p>\n </div>\n {addon && (\n <p className=\"text-sm font-medium text-text-primary tabular-nums\">\n {formatCurrency(addon.price * addonSubscription.quantity, addon.currency)}\n </p>\n )}\n </div>\n );\n};\n\nconst TransactionItem: FC<{ transaction: Transaction }> = ({ transaction }) => {\n return (\n <div className=\"flex items-center justify-between px-5 py-4 hover:bg-bg-sunken transition-colors\">\n <div>\n <p className=\"font-medium text-text-primary capitalize\">{transaction.type}</p>\n <p className=\"text-sm text-text-secondary\">{formatDate(transaction.createdAt, 'short')}</p>\n </div>\n <div className=\"text-right\">\n <p\n className={`font-medium tabular-nums ${transaction.type === 'refund' ? 'text-status-error-text' : 'text-text-primary'}`}\n >\n {transaction.type === 'refund' ? '-' : ''}\n {formatCurrency(transaction.amount, transaction.currency)}\n </p>\n <p className=\"text-sm text-text-secondary capitalize\">{transaction.status}</p>\n </div>\n </div>\n );\n};\n\nconst HistoryItem: FC<{ item: SubscriptionHistory }> = ({ item }) => {\n return (\n <div className=\"flex items-start gap-3\">\n <div className=\"size-2 rounded-full bg-text-muted mt-2 shrink-0\" />\n <div>\n <p className=\"text-sm text-text-primary\">\n <span className=\"capitalize\">{item.fromStatus}</span>\n {' → '}\n <span className=\"capitalize font-medium\">{item.toStatus}</span>\n </p>\n {item.reason && <p className=\"text-xs text-text-secondary mt-0.5\">{item.reason}</p>}\n <p className=\"text-xs text-text-secondary mt-0.5\">{formatDate(item.createdAt, 'short')}</p>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"SubscriptionDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/pages/SubscriptionDetailPage.tsx"],"sourcesContent":["/**\n * Subscriptions Module - Subscription Detail Page\n * Displays detailed information about a single subscription\n */\n\nimport { type FC, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport { useSubscription, useSubscriptionMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useGetDefaultBillingAccountDashboardQuery } from '../../../../generated/global-operations';\nimport { useWorkspaceQuotaOverview } from '../../usage/hooks/useUsage';\nimport {\n formatCurrency,\n formatDate,\n formatPlanDuration,\n formatSubscriptionStatus,\n formatPercentage,\n formatBytes,\n} from '../../../shared/utils/format';\nimport { getSubscriptionStatusColor, getQuotaProgressColor } from '../../../shared/utils/status';\nimport { Card, StatusPill, ProgressBar } from '../../../shared/ui';\nimport {\n CTAOverflowMenu,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n type CTAAction,\n type NextStep,\n} from '@burdenoff/fe-libs/ui';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport type {\n SubscriptionStatus,\n QuotaAssignment,\n AddonSubscription,\n SubscriptionHistory,\n Transaction,\n} from '../../../shared/types';\nimport { PricingModel } from '../../../shared/types';\nimport { ManageSeatsModal } from '../components/ManageSeatsModal';\nimport {\n WorkspaceQuotaAssignmentModal,\n type WorkspaceQuotaToAssign,\n} from '../../checkout/components/WorkspaceQuotaAssignmentModal';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { WorkspaceQuotaOverviewItem } from '../../usage/hooks/useUsage';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction formatQuotaDisplayName(name: string, context?: Record<string, unknown> | null): string {\n if (context?.displayName && typeof context.displayName === 'string') return context.displayName;\n if (context?.label && typeof context.label === 'string') return context.label;\n const parts = name.split('.');\n const meaningful = parts.length > 1 ? parts.slice(1) : parts;\n return meaningful\n .map((p) => p.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()))\n .join(' ');\n}\n\nfunction formatResetPeriod(period: string): string {\n if (period === 'DAILY') return 'Resets daily';\n if (period === 'MONTHLY') return 'Resets monthly';\n return '';\n}\n\n// ============================================================================\n// Page Component\n// ============================================================================\n\nexport const SubscriptionDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { subscriptionId } = useParams<{ subscriptionId: string }>();\n const navigateTo = useBillingNavigate();\n const { emit } = useBillingEventEmitter();\n const { orgId } = useBilling();\n const permissions = useBillingPermissions();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n const { data: defaultBillingAccountData } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-first',\n });\n const billingAccountId =\n selectedAccountId ?? defaultBillingAccountData?.getDefaultBillingAccount?.id ?? undefined;\n const { subscription, isLoading, error, refetch } = useSubscription(\n subscriptionId,\n billingAccountId\n );\n const { cancelSubscription, isCanceling, updateSubscriptionSeats, isUpdatingSeats } =\n useSubscriptionMutations();\n\n const { workspaceId } = useBilling();\n\n // Workspace quota overview — used to detect if quotas are already active and show live usage\n const { overview: wsQuotaOverview, refetch: refetchWsOverview } = useWorkspaceQuotaOverview(\n workspaceId ?? undefined\n );\n\n // Modal state\n const [showCancelModal, setShowCancelModal] = useState(false);\n const [showManageSeatsModal, setShowManageSeatsModal] = useState(false);\n const [showAssignWorkspaceModal, setShowAssignWorkspaceModal] = useState(false);\n const [cancelReason, setCancelReason] = useState('');\n const [cancelError, setCancelError] = useState<string | null>(null);\n\n // Handle cancel subscription\n const handleCancelSubscription = async () => {\n if (!subscription) return;\n\n setCancelError(null);\n try {\n await cancelSubscription({\n billingAccountId: billingAccountId ?? subscription.billingAccountId,\n subscriptionId: subscription.id,\n reason: cancelReason || 'User requested cancellation',\n });\n emit('billing.subscription.cancelled', {\n route: '/billing/subscriptions',\n entityId: subscription.id,\n source: 'subscription-detail',\n reason: cancelReason || 'User requested cancellation',\n });\n setShowCancelModal(false);\n setCancelReason('');\n await refetch();\n } catch (err) {\n setCancelError(err instanceof Error ? err.message : 'Failed to cancel subscription');\n }\n };\n\n // Permission check\n if (!permissions.canViewSubscriptions) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">\n You don&apos;t have permission to view this subscription.\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded-button\" />\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-5 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !subscription) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.subscriptions.notFound', 'Subscription not found')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {error?.message || 'The subscription you are looking for does not exist.'}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"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 duration-200\"\n >\n Back to Subscriptions\n </button>\n </div>\n </div>\n );\n }\n\n const plan = subscription.plan;\n const quotas = subscription.quotas || [];\n const addons = subscription.addonSubscriptions || [];\n const transactions = subscription.transactions || [];\n const history = subscription.history || [];\n const statusColor = getSubscriptionStatusColor(subscription.status);\n const isActive = subscription.status === ('active' as SubscriptionStatus);\n\n // Workspace quota assignments that belong to this subscription\n const wsAssignmentsForSub = wsQuotaOverview.filter(\n (item) => item.assignment.subscriptionId === subscription.id && item.assignment.isActive\n );\n // Assigned if subscription field is set OR workspace assignments already exist\n const isAssignedToWorkspace =\n !!subscription.assignedWorkspaceId || wsAssignmentsForSub.length > 0;\n\n // Calculate days remaining\n const endDate = new Date(subscription.endDate);\n const startDate = new Date(subscription.startDate);\n const now = new Date();\n const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));\n const daysRemaining = Math.max(\n 0,\n Math.ceil((endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))\n );\n const progressPercentage = Math.round(((totalDays - daysRemaining) / totalDays) * 100);\n\n // Recommended next actions for this subscription\n const nextSteps: NextStep[] = [\n ...(isActive && permissions.canUpdateSubscription\n ? [\n {\n id: 'change-plan',\n label: 'Review or change your plan',\n description: 'Upgrade, downgrade, or compare what each tier includes.',\n onClick: () =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${subscription.id}/upgrade`,\n billingAccountId ?? subscription.billingAccountId\n )\n ),\n },\n ]\n : []),\n ...(isActive && workspaceId && !isAssignedToWorkspace\n ? [\n {\n id: 'activate-workspace',\n label: 'Activate quotas for your workspace',\n description: 'Turn on the workspace-scoped limits included in this plan.',\n onClick: () => setShowAssignWorkspaceModal(true),\n },\n ]\n : []),\n {\n id: 'track-usage',\n label: 'Track your usage',\n description: 'See how much of your included quota you have consumed.',\n onClick: () =>\n navigateTo(\n withBillingAccountId('/usage', billingAccountId ?? subscription.billingAccountId)\n ),\n },\n {\n id: 'view-invoices',\n label: 'Review invoices & payments',\n description: 'Check what has been billed for this subscription.',\n onClick: () =>\n navigateTo(\n withBillingAccountId('/invoices', billingAccountId ?? subscription.billingAccountId)\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n aria-label=\"Back to subscriptions\"\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"p-2 -ml-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors duration-200\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <div className=\"flex items-center gap-3 flex-wrap\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {plan?.name || 'Subscription'}\n </h1>\n <StatusPill status={statusColor} dot pulse={isActive}>\n {formatSubscriptionStatus(subscription.status)}\n </StatusPill>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {plan?.pricingModel === PricingModel.PER_SEAT && plan.pricePerSeat\n ? `${formatCurrency(plan.pricePerSeat, plan.currency)} / seat / ${formatPlanDuration(plan.duration)} · ${subscription.seatCount ?? plan.minSeats ?? 1} seats = ${formatCurrency(plan.pricePerSeat * (subscription.seatCount ?? plan.minSeats ?? 1), plan.currency)} / ${formatPlanDuration(plan.duration)}`\n : plan\n ? `${formatCurrency(plan.price, plan.currency)} / ${formatPlanDuration(plan.duration)}`\n : 'Unknown plan'}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.subscriptions.detailPurpose',\n 'Everything about this subscription in one view — its billing period, the quotas and add-ons it includes, recent charges and its full status history. Use it to track what you are getting, activate workspace quotas, change your plan, or cancel.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <CTAOverflowMenu\n primary={\n isActive && permissions.canUpdateSubscription\n ? {\n label: 'Change Plan',\n onSelect: () =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${subscription.id}/upgrade`,\n billingAccountId ?? subscription.billingAccountId\n )\n ),\n }\n : undefined\n }\n actions={\n [\n ...(isActive &&\n permissions.canUpdateSubscription &&\n plan?.pricingModel === PricingModel.PER_SEAT\n ? [\n {\n label: 'Manage Seats',\n onSelect: () => setShowManageSeatsModal(true),\n },\n ]\n : []),\n ...(isActive && permissions.canCancelSubscription && !subscription.canceledAt\n ? [\n {\n label: isCanceling ? 'Canceling…' : 'Cancel Subscription',\n onSelect: () => setShowCancelModal(true),\n disabled: isCanceling,\n intent: 'destructive' as const,\n },\n ]\n : []),\n ] satisfies CTAAction[]\n }\n />\n {subscription.canceledAt && (\n <span className=\"px-4 py-2 text-sm font-medium text-status-warning-text bg-status-warning-bg-subtle rounded-button\">\n Cancellation Scheduled\n </span>\n )}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Period Progress — primary status emphasis zone */}\n {isActive && (\n <EmphasisPanel className=\"p-5\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Current Period</h2>\n <span className=\"text-sm text-text-secondary tabular-nums\">\n {daysRemaining} days remaining\n </span>\n </div>\n <div className=\"space-y-2\">\n <ProgressBar\n value={progressPercentage}\n status=\"primary\"\n size=\"md\"\n aria-label=\"Billing period progress\"\n />\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>{formatDate(subscription.startDate, 'short')}</span>\n <span>{formatDate(subscription.endDate, 'short')}</span>\n </div>\n </div>\n </EmphasisPanel>\n )}\n\n {/* Plan Quotas */}\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Plan Quotas\n {quotas.length > 0 && (\n <span className=\"ml-2 text-sm font-normal text-text-muted tabular-nums\">\n ({quotas.length})\n </span>\n )}\n </h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">Limits included in your plan</p>\n </div>\n <div className=\"p-5\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No quotas assigned to this subscription.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {quotas.map((quota) => (\n <QuotaUsageItem key={quota.id} quota={quota} />\n ))}\n </div>\n )}\n </div>\n </Card>\n\n {/* Workspace Quotas */}\n {isActive && workspaceId && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle flex items-center justify-between gap-4\">\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Workspace Quotas</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Active limits for your workspace\n </p>\n </div>\n {isAssignedToWorkspace ? (\n <StatusPill status=\"success\" dot>\n Active\n </StatusPill>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowAssignWorkspaceModal(true)}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n Activate for Workspace\n </button>\n )}\n </div>\n\n <div className=\"p-5\">\n {isAssignedToWorkspace ? (\n wsAssignmentsForSub.length > 0 ? (\n <div className=\"space-y-3\">\n {wsAssignmentsForSub.map((item) => (\n <WorkspaceQuotaItem key={item.assignment.id} item={item} />\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Quotas are activated on workspace{' '}\n <span className=\"font-mono text-xs text-text-primary bg-bg-sunken/60 rounded px-1.5 py-0.5\">\n {subscription.assignedWorkspaceId}\n </span>\n . Usage data will appear here once quotas are consumed.\n </p>\n )\n ) : (\n <div className=\"flex items-start gap-3\">\n <div className=\"size-8 rounded-full bg-status-warning-bg-subtle flex items-center justify-center shrink-0 mt-0.5\">\n <svg\n className=\"size-4 text-status-warning-text\"\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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Workspace quotas not activated yet\n </p>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Click{' '}\n <button\n type=\"button\"\n onClick={() => setShowAssignWorkspaceModal(true)}\n className=\"text-text-link hover:underline font-medium\"\n >\n Activate for Workspace\n </button>{' '}\n to enable the workspace-scoped limits included in this plan.\n </p>\n </div>\n </div>\n )}\n </div>\n\n {showAssignWorkspaceModal && (\n <WorkspaceQuotaAssignmentModal\n billingAccountId={billingAccountId ?? subscription.billingAccountId}\n subscriptionId={subscription.id}\n quotas={[] as WorkspaceQuotaToAssign[]}\n workspaces={[{ id: workspaceId, name: 'Current workspace' }]}\n currentWorkspaceId={workspaceId}\n onAssigned={() => {\n setShowAssignWorkspaceModal(false);\n void refetch();\n void refetchWsOverview();\n }}\n onSkip={() => setShowAssignWorkspaceModal(false)}\n />\n )}\n </Card>\n )}\n\n {/* Addons */}\n {addons.length > 0 && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Addons{' '}\n <span className=\"text-sm font-normal text-text-muted tabular-nums\">\n ({addons.length})\n </span>\n </h2>\n </div>\n <div className=\"p-5\">\n <div className=\"space-y-3\">\n {addons.map((addonSub) => (\n <AddonItem key={addonSub.id} addonSubscription={addonSub} />\n ))}\n </div>\n </div>\n </Card>\n )}\n\n {/* Recent Transactions */}\n {transactions.length > 0 && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">Recent Transactions</h2>\n </div>\n <div className=\"divide-y divide-border-subtle\">\n {transactions.slice(0, 5).map((transaction) => (\n <TransactionItem key={transaction.id} transaction={transaction} />\n ))}\n </div>\n {transactions.length > 5 && (\n <div className=\"p-4 text-center border-t border-border-subtle\">\n <button type=\"button\" className=\"text-sm text-text-link hover:underline\">\n View all transactions\n </button>\n </div>\n )}\n </Card>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6 lg:sticky lg:top-6 lg:self-start\">\n {/* Next steps guidance */}\n <NextSteps storageKey=\"billing-subscription-detail\" steps={nextSteps} />\n\n {/* Subscription Details */}\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">Details</h2>\n </div>\n <div className=\"p-5 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Subscription ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">\n {subscription.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Plan</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{plan?.name || 'Unknown'}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Billing Account</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {subscription.billingAccount?.name || 'Unknown'}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Start Date</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(subscription.startDate)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">End Date</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(subscription.endDate)}\n </p>\n </div>\n {subscription.nextBillingDate && (\n <div>\n <p className=\"text-sm text-text-secondary\">Next Billing</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-text-secondary\">Auto Renewal</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n {subscription.paymentGateway && (\n <div>\n <p className=\"text-sm text-text-secondary\">Payment Gateway</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {subscription.paymentGateway}\n </p>\n </div>\n )}\n </div>\n </Card>\n\n {/* History */}\n {history.length > 0 && (\n <Card className=\"overflow-hidden\">\n <div className=\"p-5 border-b border-border-subtle\">\n <h2 className=\"text-base font-semibold text-text-primary\">History</h2>\n </div>\n <div className=\"p-5\">\n <div className=\"space-y-3\">\n {history.slice(0, 5).map((item) => (\n <HistoryItem key={item.id} item={item} />\n ))}\n </div>\n </div>\n </Card>\n )}\n </div>\n </div>\n\n {/* Manage Seats Modal */}\n {showManageSeatsModal && subscription && (\n <ManageSeatsModal\n subscription={subscription}\n onClose={() => setShowManageSeatsModal(false)}\n onConfirm={async (seatCount) => {\n await updateSubscriptionSeats({ subscriptionId: subscription.id, seatCount });\n await refetch();\n }}\n isLoading={isUpdatingSeats}\n />\n )}\n\n {/* Cancel Subscription Modal */}\n {showCancelModal && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div\n className=\"absolute inset-0 bg-overlay-scrim\"\n onClick={() => setShowCancelModal(false)}\n />\n <div\n className=\"relative bg-bg-elevated border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-md mx-4 p-6\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"cancel-subscription-title\"\n >\n <h3\n id=\"cancel-subscription-title\"\n className=\"text-lg font-semibold text-text-primary mb-2\"\n >\n Cancel Subscription\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Are you sure you want to cancel this subscription? Your subscription will remain\n active until{' '}\n <span className=\"font-medium text-text-primary\">\n {formatDate(subscription.endDate)}\n </span>\n , but will not renew automatically.\n </p>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Reason for cancellation (optional)\n </label>\n <textarea\n value={cancelReason}\n onChange={(e) => setCancelReason(e.target.value)}\n placeholder=\"Please share why you're canceling...\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent resize-none\"\n rows={3}\n />\n </div>\n\n {cancelError && (\n <div className=\"mb-4 p-3 bg-status-error-bg-subtle border border-status-error-border rounded-card\">\n <p className=\"text-sm text-status-error-text\">{cancelError}</p>\n </div>\n )}\n\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setShowCancelModal(false);\n setCancelReason('');\n setCancelError(null);\n }}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n Keep Subscription\n </button>\n <button\n type=\"button\"\n onClick={handleCancelSubscription}\n disabled={isCanceling}\n className=\"px-4 py-2 text-sm font-medium bg-action-danger-bg text-action-danger-text rounded-button hover:bg-action-danger-bgHover transition-colors duration-200 disabled:opacity-50\"\n >\n {isCanceling ? 'Canceling...' : 'Confirm Cancellation'}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\nconst QuotaUsageItem: FC<{ quota: QuotaAssignment }> = ({ quota }) => {\n const context = quota.context as\n | { displayName?: string; label?: string; description?: string }\n | null\n | undefined;\n const displayName = formatQuotaDisplayName(quota.name, context as Record<string, unknown> | null);\n const description =\n context?.description && typeof context.description === 'string' ? context.description : null;\n const limits = quota.limits as { value?: number } | null;\n const limit = limits?.value ?? 0;\n const used = quota.currentUsageSum ?? 0;\n const percentage = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;\n const isBytes = quota.name.includes('bytes') || quota.name.includes('storage');\n const isPooled = quota.quotaType === 'POOLED';\n\n return (\n <div className=\"p-4 rounded-card border border-border-seam bg-bg-surface\">\n <div className=\"flex items-start justify-between gap-4 mb-2\">\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{displayName}</p>\n {description && (\n <p className=\"text-xs text-text-secondary mt-0.5 line-clamp-2\">{description}</p>\n )}\n </div>\n <span className=\"text-sm font-medium text-text-primary shrink-0 tabular-nums\">\n {isBytes ? formatBytes(used) : used.toLocaleString()}\n {' / '}\n {limit > 0 ? (isBytes ? formatBytes(limit) : limit.toLocaleString()) : '∞'}\n </span>\n </div>\n\n {limit > 0 && (\n <div className=\"h-1.5 rounded-full bg-bg-sunken overflow-hidden mb-2\">\n <div\n className={`h-full rounded-full transition-all duration-300 ${getQuotaProgressColor(percentage)}`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n )}\n\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span className=\"px-1.5 py-0.5 rounded bg-bg-sunken font-medium\">\n {isPooled ? 'Shared' : 'Per Workspace'}\n </span>\n <div className=\"flex items-center gap-3\">\n {quota.endtime && <span>Expires {formatDate(quota.endtime, 'short')}</span>}\n {limit > 0 && (\n <span\n className={\n percentage >= 90\n ? 'text-status-error-text font-medium'\n : percentage >= 70\n ? 'text-status-warning-text font-medium'\n : ''\n }\n >\n {formatPercentage(percentage)} used\n </span>\n )}\n </div>\n </div>\n </div>\n );\n};\n\nconst WorkspaceQuotaItem: FC<{ item: WorkspaceQuotaOverviewItem }> = ({ item }) => {\n const { assignment, effectiveUsed, usagePercentage } = item;\n const displayName = formatQuotaDisplayName(assignment.quotaName);\n const hasLimit = !assignment.noLimit && assignment.limit !== null;\n const limit = assignment.limit ?? 0;\n const isBytes =\n assignment.quotaName.includes('bytes') || assignment.quotaName.includes('storage');\n const resetLabel = formatResetPeriod(assignment.resetPeriod);\n\n return (\n <div className=\"p-4 rounded-card border border-border-seam bg-bg-surface\">\n <div className=\"flex items-start justify-between gap-4 mb-2\">\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{displayName}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {assignment.quotaMode === 'pooled' ? 'Pooled across workspaces' : 'Dedicated'}\n {resetLabel && ` · ${resetLabel}`}\n </p>\n </div>\n <span className=\"text-sm font-medium text-text-primary shrink-0 tabular-nums\">\n {isBytes ? formatBytes(effectiveUsed) : effectiveUsed.toLocaleString()}\n {' / '}\n {assignment.noLimit\n ? '∞'\n : hasLimit\n ? isBytes\n ? formatBytes(limit)\n : limit.toLocaleString()\n : '∞'}\n </span>\n </div>\n\n {hasLimit && (\n <div className=\"h-1.5 rounded-full bg-bg-sunken overflow-hidden mb-2\">\n <div\n className={`h-full rounded-full transition-all duration-300 ${getQuotaProgressColor(usagePercentage)}`}\n style={{ width: `${Math.min(100, usagePercentage)}%` }}\n />\n </div>\n )}\n\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>Since {formatDate(assignment.createdAt, 'short')}</span>\n {hasLimit && (\n <span\n className={\n usagePercentage >= 90\n ? 'text-status-error-text font-medium'\n : usagePercentage >= 70\n ? 'text-status-warning-text font-medium'\n : ''\n }\n >\n {formatPercentage(usagePercentage)} used\n </span>\n )}\n </div>\n </div>\n );\n};\n\nconst AddonItem: FC<{ addonSubscription: AddonSubscription }> = ({ addonSubscription }) => {\n const addon = addonSubscription.addon;\n return (\n <div className=\"flex items-center justify-between p-3 rounded-card border border-border-seam bg-bg-sunken\">\n <div>\n <p className=\"font-medium text-text-primary\">{addon?.name || 'Unknown Addon'}</p>\n <p className=\"text-sm text-text-secondary\">Qty: {addonSubscription.quantity}</p>\n </div>\n {addon && (\n <p className=\"text-sm font-medium text-text-primary tabular-nums\">\n {formatCurrency(addon.price * addonSubscription.quantity, addon.currency)}\n </p>\n )}\n </div>\n );\n};\n\nconst TransactionItem: FC<{ transaction: Transaction }> = ({ transaction }) => {\n return (\n <div className=\"flex items-center justify-between px-5 py-4 hover:bg-bg-sunken transition-colors\">\n <div>\n <p className=\"font-medium text-text-primary capitalize\">{transaction.type}</p>\n <p className=\"text-sm text-text-secondary\">{formatDate(transaction.createdAt, 'short')}</p>\n </div>\n <div className=\"text-right\">\n <p\n className={`font-medium tabular-nums ${transaction.type === 'refund' ? 'text-status-error-text' : 'text-text-primary'}`}\n >\n {transaction.type === 'refund' ? '-' : ''}\n {formatCurrency(transaction.amount, transaction.currency)}\n </p>\n <p className=\"text-sm text-text-secondary capitalize\">{transaction.status}</p>\n </div>\n </div>\n );\n};\n\nconst HistoryItem: FC<{ item: SubscriptionHistory }> = ({ item }) => {\n return (\n <div className=\"flex items-start gap-3\">\n <div className=\"size-2 rounded-full bg-text-muted mt-2 shrink-0\" />\n <div>\n <p className=\"text-sm text-text-primary\">\n <span className=\"capitalize\">{item.fromStatus}</span>\n {' → '}\n <span className=\"capitalize font-medium\">{item.toStatus}</span>\n </p>\n {item.reason && <p className=\"text-xs text-text-secondary mt-0.5\">{item.reason}</p>}\n <p className=\"text-xs text-text-secondary mt-0.5\">{formatDate(item.createdAt, 'short')}</p>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}