@burdenoff/microfe-billing 2026.526.1 → 2026.528.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingAdminRoutes.js +1 -1
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/BillingUserRoutes.js +1 -1
- package/dist/billing/BillingUserRoutes.js.map +1 -1
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js +15 -8
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +13 -6
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +8 -4
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +3 -2
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +44 -23
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +60 -31
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +9 -9
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +8 -8
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +16 -10
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +24 -16
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +5 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +46 -29
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +34 -22
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +10 -10
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js +3 -3
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/NativeCheckoutView.js +7 -0
- package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js +13 -13
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/PromoCodeInput.js +5 -5
- package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +4 -4
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
- package/dist/billing/modules/checkout/components/StripePaymentForm.js +11 -11
- package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +3 -3
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +8 -8
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +61 -52
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +22 -15
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +30 -21
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +9 -8
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +11 -8
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +22 -17
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +15 -8
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +17 -11
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +15 -11
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +21 -17
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +14 -11
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +32 -26
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +14 -10
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +18 -11
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +44 -32
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +35 -26
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +15 -15
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +11 -11
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +5 -5
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +9 -4
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +13 -8
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +3 -2
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +14 -7
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +23 -9
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js +11 -7
- package/dist/billing/modules/quota/pages/QuotaPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +3 -3
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +6 -2
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +5 -4
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +26 -12
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +356 -349
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +157 -123
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +13 -8
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +15 -7
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +30 -20
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +25 -16
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +50 -34
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/components/AccessDenied.js +2 -2
- package/dist/billing/shared/components/AccessDenied.js.map +1 -1
- package/dist/billing/shared/components/ActorIdentity.js +67 -0
- package/dist/billing/shared/components/ActorIdentity.js.map +1 -0
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +6 -2
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
- package/dist/billing/shared/components/EmptyState.js +7 -21
- package/dist/billing/shared/components/EmptyState.js.map +1 -1
- package/dist/billing/shared/components/PageHeader.js +7 -19
- package/dist/billing/shared/components/PageHeader.js.map +1 -1
- package/dist/billing/shared/components/ServerError.js +5 -4
- package/dist/billing/shared/components/ServerError.js.map +1 -1
- package/dist/billing/shared/components/index.js +1 -0
- package/dist/billing/shared/hooks/index.js +1 -0
- package/dist/billing/shared/hooks/useActorProfiles.js +113 -0
- package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"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';\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 <div className=\"fixed inset-0 bg-overlay-scrim flex items-center justify-center z-50 p-4\">\n <div\n className=\"bg-background border border-border rounded-lg shadow-lg w-full max-w-md\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"manage-seats-title\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border\">\n <div className=\"flex items-center gap-2\">\n <Users className=\"w-5 h-5 text-primary\" />\n <h2 id=\"manage-seats-title\" className=\"text-lg font-semibold text-foreground\">\n {tr('billing.subscriptions.manageSeats', 'Manage Seats')}\n </h2>\n </div>\n <button\n onClick={onClose}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Close manage seats dialog\"\n >\n ✕\n </button>\n </div>\n\n {/* Body */}\n <div className=\"p-6 space-y-6\">\n {/* Current vs new */}\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>Current seats</span>\n <span className=\"font-medium text-foreground\">{currentSeats}</span>\n </div>\n\n {/* Seat selector */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium text-foreground\">New seat count</label>\n <div className=\"flex items-center gap-3\">\n <button\n onClick={decrement}\n disabled={newSeatCount <= minSeats || isLoading}\n className=\"w-9 h-9 rounded-md border border-border flex items-center justify-center hover:bg-accent disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n aria-label=\"Decrease seat count\"\n >\n <Minus className=\"w-4 h-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 rounded-md bg-background text-foreground text-sm focus:outline-none focus:ring-2 focus:ring-primary disabled:opacity-50\"\n />\n <button\n onClick={increment}\n disabled={newSeatCount >= maxSeats || isLoading}\n className=\"w-9 h-9 rounded-md border border-border flex items-center justify-center hover:bg-accent disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n aria-label=\"Increase seat count\"\n >\n <Plus className=\"w-4 h-4\" />\n </button>\n <span className=\"text-sm text-muted-foreground\">\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-muted/50 border border-border p-4 space-y-2 text-sm\">\n <div className=\"flex justify-between text-muted-foreground\">\n <span>Per seat</span>\n <span>{formatCurrency(pricePerSeat, currency)} / month</span>\n </div>\n <div className=\"flex justify-between text-muted-foreground\">\n <span>Current total</span>\n <span>{formatCurrency(currentMonthlyTotal, currency)} / month</span>\n </div>\n <div className=\"border-t border-border pt-2 flex justify-between font-medium text-foreground\">\n <span>New total</span>\n <span\n className={\n seatDelta > 0\n ? 'text-destructive'\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-muted-foreground\">\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-md bg-status-warning-bg-subtle border border-status-warning-border text-sm text-status-warning-text\">\n <AlertCircle className=\"w-4 h-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-md bg-destructive/10 border border-destructive/30 text-sm text-destructive\">\n <AlertCircle className=\"w-4 h-4 mt-0.5 shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 p-6 border-t border-border\">\n <button\n onClick={onClose}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm rounded-md border border-border hover:bg-accent text-foreground transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <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-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n {isLoading && <Loader2 className=\"w-4 h-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 </div>\n </div>\n </div>\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';\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 <div className=\"fixed inset-0 bg-overlay-scrim flex items-center justify-center z-50 p-4\">\n <div\n className=\"bg-background border border-border rounded-lg shadow-lg w-full max-w-md\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"manage-seats-title\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border\">\n <div className=\"flex items-center gap-2\">\n <Users className=\"size-5 text-primary\" />\n <h2 id=\"manage-seats-title\" className=\"text-lg font-semibold text-foreground\">\n {tr('billing.subscriptions.manageSeats', 'Manage Seats')}\n </h2>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Close manage seats dialog\"\n >\n ✕\n </button>\n </div>\n\n {/* Body */}\n <div className=\"p-6 space-y-6\">\n {/* Current vs new */}\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>Current seats</span>\n <span className=\"font-medium text-foreground\">{currentSeats}</span>\n </div>\n\n {/* Seat selector */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium text-foreground\">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-md border border-border flex items-center justify-center hover:bg-accent 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 rounded-md bg-background text-foreground text-sm focus:outline-none focus:ring-2 focus:ring-primary disabled:opacity-50\"\n />\n <button\n type=\"button\"\n onClick={increment}\n disabled={newSeatCount >= maxSeats || isLoading}\n className=\"size-9 rounded-md border border-border flex items-center justify-center hover:bg-accent 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-muted-foreground\">\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-muted/50 border border-border p-4 space-y-2 text-sm\">\n <div className=\"flex justify-between text-muted-foreground\">\n <span>Per seat</span>\n <span>{formatCurrency(pricePerSeat, currency)} / month</span>\n </div>\n <div className=\"flex justify-between text-muted-foreground\">\n <span>Current total</span>\n <span>{formatCurrency(currentMonthlyTotal, currency)} / month</span>\n </div>\n <div className=\"border-t border-border pt-2 flex justify-between font-medium text-foreground\">\n <span>New total</span>\n <span\n className={\n seatDelta > 0\n ? 'text-destructive'\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-muted-foreground\">\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-md 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-md bg-destructive/10 border border-destructive/30 text-sm text-destructive\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <span>{error}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 p-6 border-t border-border\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm rounded-md border border-border hover:bg-accent text-foreground 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-md bg-primary text-primary-foreground hover:bg-primary/90 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 </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -49,9 +49,9 @@ var x = () => {
|
|
|
49
49
|
className: "text-center space-y-2",
|
|
50
50
|
children: [
|
|
51
51
|
/* @__PURE__ */ v("div", {
|
|
52
|
-
className: "
|
|
52
|
+
className: "size-12 mx-auto rounded-full bg-muted flex items-center justify-center",
|
|
53
53
|
children: /* @__PURE__ */ v("svg", {
|
|
54
|
-
className: "
|
|
54
|
+
className: "size-6 text-muted-foreground",
|
|
55
55
|
fill: "none",
|
|
56
56
|
viewBox: "0 0 24 24",
|
|
57
57
|
stroke: "currentColor",
|
|
@@ -78,7 +78,7 @@ var x = () => {
|
|
|
78
78
|
className: "space-y-6 p-6",
|
|
79
79
|
children: [/* @__PURE__ */ y("div", {
|
|
80
80
|
className: "flex items-center gap-4",
|
|
81
|
-
children: [/* @__PURE__ */ v("div", { className: "
|
|
81
|
+
children: [/* @__PURE__ */ v("div", { className: "size-10 bg-muted animate-pulse rounded" }), /* @__PURE__ */ v("div", { className: "h-8 w-48 bg-muted animate-pulse rounded" })]
|
|
82
82
|
}), /* @__PURE__ */ v("div", {
|
|
83
83
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
84
84
|
children: /* @__PURE__ */ v("div", {
|
|
@@ -96,9 +96,9 @@ var x = () => {
|
|
|
96
96
|
className: "text-center space-y-4",
|
|
97
97
|
children: [
|
|
98
98
|
/* @__PURE__ */ v("div", {
|
|
99
|
-
className: "
|
|
99
|
+
className: "size-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center",
|
|
100
100
|
children: /* @__PURE__ */ v("svg", {
|
|
101
|
-
className: "
|
|
101
|
+
className: "size-6 text-destructive",
|
|
102
102
|
fill: "none",
|
|
103
103
|
viewBox: "0 0 24 24",
|
|
104
104
|
stroke: "currentColor",
|
|
@@ -119,6 +119,7 @@ var x = () => {
|
|
|
119
119
|
children: A?.message || "The subscription you are looking for does not exist."
|
|
120
120
|
}),
|
|
121
121
|
/* @__PURE__ */ v("button", {
|
|
122
|
+
type: "button",
|
|
122
123
|
onClick: () => E(p("/subscriptions", O)),
|
|
123
124
|
className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
124
125
|
children: "Back to Subscriptions"
|
|
@@ -135,10 +136,11 @@ var x = () => {
|
|
|
135
136
|
children: [/* @__PURE__ */ y("div", {
|
|
136
137
|
className: "flex items-start gap-4",
|
|
137
138
|
children: [/* @__PURE__ */ v("button", {
|
|
139
|
+
type: "button",
|
|
138
140
|
onClick: () => E(p("/subscriptions", O)),
|
|
139
141
|
className: "p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors",
|
|
140
142
|
children: /* @__PURE__ */ v("svg", {
|
|
141
|
-
className: "
|
|
143
|
+
className: "size-5",
|
|
142
144
|
fill: "none",
|
|
143
145
|
viewBox: "0 0 24 24",
|
|
144
146
|
stroke: "currentColor",
|
|
@@ -166,16 +168,19 @@ var x = () => {
|
|
|
166
168
|
className: "flex items-center gap-2",
|
|
167
169
|
children: [
|
|
168
170
|
X && D.canUpdateSubscription && G?.pricingModel === r.PER_SEAT && /* @__PURE__ */ v("button", {
|
|
171
|
+
type: "button",
|
|
169
172
|
onClick: () => z(!0),
|
|
170
173
|
className: "px-4 py-2 text-sm font-medium border border-border rounded-md hover:bg-accent text-foreground transition-colors",
|
|
171
174
|
children: "Manage Seats"
|
|
172
175
|
}),
|
|
173
176
|
X && D.canUpdateSubscription && /* @__PURE__ */ v("button", {
|
|
177
|
+
type: "button",
|
|
174
178
|
onClick: () => E(p(`/subscriptions/${k.id}/upgrade`, O ?? k.billingAccountId)),
|
|
175
179
|
className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
176
180
|
children: "Change Plan"
|
|
177
181
|
}),
|
|
178
182
|
X && D.canCancelSubscription && !k.canceledAt && /* @__PURE__ */ v("button", {
|
|
183
|
+
type: "button",
|
|
179
184
|
onClick: () => L(!0),
|
|
180
185
|
disabled: N,
|
|
181
186
|
className: "px-4 py-2 text-sm font-medium border border-border text-destructive rounded-md hover:bg-destructive/10 transition-colors disabled:opacity-50",
|
|
@@ -278,6 +283,7 @@ var x = () => {
|
|
|
278
283
|
J.length > 5 && /* @__PURE__ */ v("div", {
|
|
279
284
|
className: "p-4 text-center border-t border-border",
|
|
280
285
|
children: /* @__PURE__ */ v("button", {
|
|
286
|
+
type: "button",
|
|
281
287
|
className: "text-sm text-primary hover:underline",
|
|
282
288
|
children: "View all transactions"
|
|
283
289
|
})
|
|
@@ -436,12 +442,14 @@ var x = () => {
|
|
|
436
442
|
/* @__PURE__ */ y("div", {
|
|
437
443
|
className: "flex items-center justify-end gap-3",
|
|
438
444
|
children: [/* @__PURE__ */ v("button", {
|
|
445
|
+
type: "button",
|
|
439
446
|
onClick: () => {
|
|
440
447
|
L(!1), V(""), U(null);
|
|
441
448
|
},
|
|
442
449
|
className: "px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors",
|
|
443
450
|
children: "Keep Subscription"
|
|
444
451
|
}), /* @__PURE__ */ v("button", {
|
|
452
|
+
type: "button",
|
|
445
453
|
onClick: W,
|
|
446
454
|
disabled: N,
|
|
447
455
|
className: "px-4 py-2 text-sm font-medium bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors disabled:opacity-50",
|
|
@@ -520,7 +528,7 @@ var x = () => {
|
|
|
520
528
|
})]
|
|
521
529
|
}), T = ({ item: e }) => /* @__PURE__ */ y("div", {
|
|
522
530
|
className: "flex items-start gap-3",
|
|
523
|
-
children: [/* @__PURE__ */ v("div", { className: "
|
|
531
|
+
children: [/* @__PURE__ */ v("div", { className: "size-2 rounded-full bg-muted-foreground mt-2 shrink-0" }), /* @__PURE__ */ y("div", { children: [
|
|
524
532
|
/* @__PURE__ */ y("p", {
|
|
525
533
|
className: "text-sm text-foreground",
|
|
526
534
|
children: [
|
|
@@ -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-dom';\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 {\n formatCurrency,\n formatDate,\n formatPlanDuration,\n formatSubscriptionStatus,\n formatPercentage,\n} from '../../../shared/utils/format';\nimport {\n getSubscriptionStatusColor,\n getStatusBadgeClasses,\n getQuotaProgressColor,\n} from '../../../shared/utils/status';\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 { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\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 // Modal state\n const [showCancelModal, setShowCancelModal] = useState(false);\n const [showManageSeatsModal, setShowManageSeatsModal] = 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=\"w-12 h-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-muted-foreground\"\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-foreground\">Access Denied</h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n You don'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 p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"h-10 w-10 bg-muted animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-muted 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 rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-muted 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=\"w-12 h-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-destructive\"\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-foreground\">\n {tr('billing.subscriptions.notFound', 'Subscription not found')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {error?.message || 'The subscription you are looking for does not exist.'}\n </p>\n <button\n onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\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 // 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 return (\n <div className=\"space-y-6 p-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 onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n <svg className=\"w-5 h-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\">\n <h1 className=\"text-2xl font-bold text-foreground\">{plan?.name || 'Subscription'}</h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatSubscriptionStatus(subscription.status)}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground 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 </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {isActive &&\n permissions.canUpdateSubscription &&\n plan?.pricingModel === PricingModel.PER_SEAT && (\n <button\n onClick={() => setShowManageSeatsModal(true)}\n className=\"px-4 py-2 text-sm font-medium border border-border rounded-md hover:bg-accent text-foreground transition-colors\"\n >\n Manage Seats\n </button>\n )}\n {isActive && permissions.canUpdateSubscription && (\n <button\n onClick={() =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${subscription.id}/upgrade`,\n billingAccountId ?? subscription.billingAccountId\n )\n )\n }\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Change Plan\n </button>\n )}\n {isActive && permissions.canCancelSubscription && !subscription.canceledAt && (\n <button\n onClick={() => setShowCancelModal(true)}\n disabled={isCanceling}\n className=\"px-4 py-2 text-sm font-medium border border-border text-destructive rounded-md hover:bg-destructive/10 transition-colors disabled:opacity-50\"\n >\n {isCanceling ? 'Canceling...' : 'Cancel Subscription'}\n </button>\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-md\">\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 */}\n {isActive && (\n <div className=\"border border-border rounded-lg bg-card p-6\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">Current Period</h2>\n <span className=\"text-sm text-muted-foreground\">\n {daysRemaining} days remaining\n </span>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary transition-all duration-300\"\n style={{ width: `${progressPercentage}%` }}\n />\n </div>\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>{formatDate(subscription.startDate, 'short')}</span>\n <span>{formatDate(subscription.endDate, 'short')}</span>\n </div>\n </div>\n </div>\n )}\n\n {/* Quotas */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n Quota Usage ({quotas.length})\n </h2>\n </div>\n <div className=\"p-6\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n No quotas assigned to this subscription.\n </p>\n ) : (\n <div className=\"space-y-4\">\n {quotas.map((quota) => (\n <QuotaUsageItem key={quota.id} quota={quota} />\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Addons */}\n {addons.length > 0 && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">Addons ({addons.length})</h2>\n </div>\n <div className=\"p-6\">\n <div className=\"space-y-3\">\n {addons.map((addonSub) => (\n <AddonItem key={addonSub.id} addonSubscription={addonSub} />\n ))}\n </div>\n </div>\n </div>\n )}\n\n {/* Recent Transactions */}\n {transactions.length > 0 && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">Recent Transactions</h2>\n </div>\n <div className=\"divide-y divide-border\">\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\">\n <button className=\"text-sm text-primary hover:underline\">\n View all transactions\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Subscription Details */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">Details</h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-muted-foreground\">Subscription ID</p>\n <p className=\"font-mono text-sm text-foreground mt-0.5 break-all\">\n {subscription.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Plan</p>\n <p className=\"text-sm text-foreground mt-0.5\">{plan?.name || 'Unknown'}</p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Billing Account</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {subscription.billingAccount?.name || 'Unknown'}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Start Date</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {formatDate(subscription.startDate)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">End Date</p>\n <p className=\"text-sm text-foreground mt-0.5\">{formatDate(subscription.endDate)}</p>\n </div>\n {subscription.nextBillingDate && (\n <div>\n <p className=\"text-sm text-muted-foreground\">Next Billing</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-muted-foreground\">Auto Renewal</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n {subscription.paymentGateway && (\n <div>\n <p className=\"text-sm text-muted-foreground\">Payment Gateway</p>\n <p className=\"text-sm text-foreground mt-0.5 capitalize\">\n {subscription.paymentGateway}\n </p>\n </div>\n )}\n </div>\n </div>\n\n {/* History */}\n {history.length > 0 && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">History</h2>\n </div>\n <div className=\"p-6\">\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 </div>\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-card border border-border rounded-lg shadow-lg 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-foreground mb-2\"\n >\n Cancel Subscription\n </h3>\n <p className=\"text-sm text-muted-foreground 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-foreground\">\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-foreground 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 rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent resize-none\"\n rows={3}\n />\n </div>\n\n {cancelError && (\n <div className=\"mb-4 p-3 bg-destructive/10 border border-destructive/20 rounded-md\">\n <p className=\"text-sm text-destructive\">{cancelError}</p>\n </div>\n )}\n\n <div className=\"flex items-center justify-end gap-3\">\n <button\n onClick={() => {\n setShowCancelModal(false);\n setCancelReason('');\n setCancelError(null);\n }}\n className=\"px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n Keep Subscription\n </button>\n <button\n onClick={handleCancelSubscription}\n disabled={isCanceling}\n className=\"px-4 py-2 text-sm font-medium bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors 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 limits = quota.limits as { value?: number; type?: string } | 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\n return (\n <div className=\"p-4 rounded-lg bg-muted/30\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"font-medium text-foreground\">{quota.name}</span>\n <span className=\"text-sm text-muted-foreground\">\n {used} / {limit > 0 ? limit : '∞'}\n </span>\n </div>\n {limit > 0 && (\n <div className=\"h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full transition-all duration-300 ${getQuotaProgressColor(percentage)}`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n )}\n <div className=\"flex items-center justify-between mt-2 text-xs text-muted-foreground\">\n <span>{quota.quotaType}</span>\n <span>{formatPercentage(percentage)} used</span>\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-lg bg-muted/30\">\n <div>\n <p className=\"font-medium text-foreground\">{addon?.name || 'Unknown Addon'}</p>\n <p className=\"text-sm text-muted-foreground\">Qty: {addonSubscription.quantity}</p>\n </div>\n {addon && (\n <p className=\"text-sm font-medium text-foreground\">\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-6 py-4\">\n <div>\n <p className=\"font-medium text-foreground capitalize\">{transaction.type}</p>\n <p className=\"text-sm text-muted-foreground\">\n {formatDate(transaction.createdAt, 'short')}\n </p>\n </div>\n <div className=\"text-right\">\n <p\n className={`font-medium ${transaction.type === 'refund' ? 'text-status-error-text' : 'text-foreground'}`}\n >\n {transaction.type === 'refund' ? '-' : ''}\n {formatCurrency(transaction.amount, transaction.currency)}\n </p>\n <p className=\"text-sm text-muted-foreground 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=\"w-2 h-2 rounded-full bg-muted-foreground mt-2 shrink-0\" />\n <div>\n <p className=\"text-sm text-foreground\">\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-muted-foreground mt-0.5\">{item.reason}</p>}\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {formatDate(item.createdAt, 'short')}\n </p>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsCA,IAAa,UAAmC;CAC9C,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,EAAE,sBAAmB,IAAuC,EAC5D,IAAa,GAAoB,EACjC,EAAE,aAAS,GAAwB,EACnC,EAAE,cAAU,GAAY,EACxB,IAAc,IAAuB,EACrC,EAAE,0BAAsB,EAA2B;EACvD;EACA,aAAa;EACb,cAAc;EACf,CAAC,EACI,EAAE,MAAM,OAA8B,EAA0C,EACpF,aAAa,eACd,CAAC,EACI,IACJ,MAAqB,IAA2B,0BAA0B,MAAM,KAAA,GAC5E,EAAE,iBAAc,eAAW,UAAO,eAAY,EAClD,GACA,EACD,EACK,EAAE,uBAAoB,gBAAa,4BAAyB,uBAChE,GAA0B,EAGtB,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAsB,KAA2B,EAAS,GAAM,EACjE,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAG7D,IAA2B,YAAY;AACtC,SAEL;KAAe,KAAK;AACpB,OAAI;AAcF,IAbA,MAAM,EAAmB;KACvB,kBAAkB,KAAoB,EAAa;KACnD,gBAAgB,EAAa;KAC7B,QAAQ,KAAgB;KACzB,CAAC,EACF,GAAK,kCAAkC;KACrC,OAAO;KACP,UAAU,EAAa;KACvB,QAAQ;KACR,QAAQ,KAAgB;KACzB,CAAC,EACF,EAAmB,GAAM,EACzB,EAAgB,GAAG,EACnB,MAAM,GAAS;YACR,GAAK;AACZ,MAAe,aAAe,QAAQ,EAAI,UAAU,gCAAgC;;;;AAKxF,KAAI,CAAC,EAAY,qBACf,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eAAwC;KAAkB,CAAA;IACxE,kBAAC,KAAD;KAAG,WAAU;eAAyC;KAElD,CAAA;IACA;;EACF,CAAA;AAKV,KAAI,GACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAK,WAAU,4CAA6C,CAAA,EAC5D,kBAAC,OAAD,EAAK,WAAU,2CAA4C,CAAA,CACvD;MACN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAK,WAAU,2CAA4C,CAAA,EAC3D,kBAAC,OAAD,EAAK,WAAU,6CAA8C,CAAA,CACzD;;IACF,CAAA;GACF,CAAA,CACF;;AAKV,KAAI,KAAS,CAAC,EACZ,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,kCAAkC,yBAAyB;KAC5D,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,GAAO,WAAW;KACjB,CAAA;IACJ,kBAAC,UAAD;KACE,eAAe,EAAW,EAAqB,kBAAkB,EAAiB,CAAC;KACnF,WAAU;eACX;KAEQ,CAAA;IACL;;EACF,CAAA;CAIV,IAAM,IAAO,EAAa,MACpB,IAAS,EAAa,UAAU,EAAE,EAClC,IAAS,EAAa,sBAAsB,EAAE,EAC9C,IAAe,EAAa,gBAAgB,EAAE,EAC9C,IAAU,EAAa,WAAW,EAAE,EACpC,KAAc,EAA2B,EAAa,OAAO,EAC7D,IAAW,EAAa,WAAY,UAGpC,IAAU,IAAI,KAAK,EAAa,QAAQ,EACxC,KAAY,IAAI,KAAK,EAAa,UAAU,EAC5C,qBAAM,IAAI,MAAM,EAChB,IAAY,KAAK,MAAM,EAAQ,SAAS,GAAG,GAAU,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,EACxF,IAAgB,KAAK,IACzB,GACA,KAAK,MAAM,EAAQ,SAAS,GAAG,GAAI,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,CACvE,EACK,KAAqB,KAAK,OAAQ,IAAY,KAAiB,IAAa,IAAI;AAEtF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,eAAe,EAAW,EAAqB,kBAAkB,EAAiB,CAAC;MACnF,WAAU;gBAEV,kBAAC,OAAD;OAAK,WAAU;OAAU,MAAK;OAAO,SAAQ;OAAY,QAAO;iBAC9D,kBAAC,QAAD;QACE,eAAc;QACd,gBAAe;QACf,aAAa;QACb,GAAE;QACF,CAAA;OACE,CAAA;MACC,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAsC,GAAM,QAAQ;OAAoB,CAAA,EACtF,kBAAC,QAAD;OACE,WAAW,yCAAyC,EAAsB,GAAY;iBAErF,GAAyB,EAAa,OAAO;OACzC,CAAA,CACH;SACN,kBAAC,KAAD;MAAG,WAAU;gBACV,GAAM,iBAAiB,EAAa,YAAY,EAAK,eAClD,GAAG,EAAe,EAAK,cAAc,EAAK,SAAS,CAAC,YAAY,EAAmB,EAAK,SAAS,CAAC,KAAK,EAAa,aAAa,EAAK,YAAY,EAAE,WAAW,EAAe,EAAK,gBAAgB,EAAa,aAAa,EAAK,YAAY,IAAI,EAAK,SAAS,CAAC,KAAK,EAAmB,EAAK,SAAS,KACvS,IACE,GAAG,EAAe,EAAK,OAAO,EAAK,SAAS,CAAC,KAAK,EAAmB,EAAK,SAAS,KACnF;MACJ,CAAA,CACA,EAAA,CAAA,CACF;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,EAAY,yBACZ,GAAM,iBAAiB,EAAa,YAClC,kBAAC,UAAD;OACE,eAAe,EAAwB,GAAK;OAC5C,WAAU;iBACX;OAEQ,CAAA;MAEZ,KAAY,EAAY,yBACvB,kBAAC,UAAD;OACE,eACE,EACE,EACE,kBAAkB,EAAa,GAAG,WAClC,KAAoB,EAAa,iBAClC,CACF;OAEH,WAAU;iBACX;OAEQ,CAAA;MAEV,KAAY,EAAY,yBAAyB,CAAC,EAAa,cAC9D,kBAAC,UAAD;OACE,eAAe,EAAmB,GAAK;OACvC,UAAU;OACV,WAAU;iBAET,IAAc,iBAAiB;OACzB,CAAA;MAEV,EAAa,cACZ,kBAAC,QAAD;OAAM,WAAU;iBAAgG;OAEzG,CAAA;MAEL;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAwC;SAAmB,CAAA,EACzE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,GAAc,kBACV;WACH;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UACE,WAAU;UACV,OAAO,EAAE,OAAO,GAAG,GAAmB,IAAI;UAC1C,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAa,WAAW,QAAQ,EAAQ,CAAA,EAC1D,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAa,SAAS,QAAQ,EAAQ,CAAA,CACpD;WACF;UACF;;MAIR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,MAAD;SAAI,WAAU;mBAAd;UAAsD;UACtC,EAAO;UAAO;UACzB;;QACD,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAO,WAAW,IACjB,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAEzC,CAAA,GAEJ,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAAsC,UAAS,EAA1B,EAAM,GAAoB,CAC/C;SACE,CAAA;QAEJ,CAAA,CACF;;MAGL,EAAO,SAAS,KACf,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,MAAD;SAAI,WAAU;mBAAd;UAAsD;UAAS,EAAO;UAAO;UAAM;;QAC/E,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAA6B,mBAAmB,GAAY,EAA5C,EAAS,GAAmC,CAC5D;SACE,CAAA;QACF,CAAA,CACF;;MAIP,EAAa,SAAS,KACrB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,MAAD;UAAI,WAAU;oBAAwC;UAAwB,CAAA;SAC1E,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAa,MAAM,GAAG,EAAE,CAAC,KAAK,MAC7B,kBAAC,GAAD,EAAmD,gBAAe,EAA5C,EAAY,GAAgC,CAClE;SACE,CAAA;QACL,EAAa,SAAS,KACrB,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,UAAD;UAAQ,WAAU;oBAAuC;UAEhD,CAAA;SACL,CAAA;QAEJ;;MAEJ;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,MAAD;QAAI,WAAU;kBAAwC;QAAY,CAAA;OAC9D,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa;SACZ,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAQ,CAAA,EACrD,kBAAC,KAAD;SAAG,WAAU;mBAAkC,GAAM,QAAQ;SAAc,CAAA,CACvE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa,gBAAgB,QAAQ;SACpC,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAc,CAAA,EAC3D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAW,EAAa,UAAU;SACjC,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAY,CAAA,EACzD,kBAAC,KAAD;SAAG,WAAU;mBAAkC,EAAW,EAAa,QAAQ;SAAK,CAAA,CAChF,EAAA,CAAA;QACL,EAAa,mBACZ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAgB,CAAA,EAC7D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAW,EAAa,gBAAgB;SACvC,CAAA,CACA,EAAA,CAAA;QAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAgB,CAAA,EAC7D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa,cAAc,YAAY;SACtC,CAAA,CACA,EAAA,CAAA;QACL,EAAa,kBACZ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa;SACZ,CAAA,CACA,EAAA,CAAA;QAEJ;SACF;SAGL,EAAQ,SAAS,KAChB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,MAAD;QAAI,WAAU;kBAAwC;QAAY,CAAA;OAC9D,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,MACxB,kBAAC,GAAD,EAAiC,SAAQ,EAAvB,EAAK,GAAkB,CACzC;QACE,CAAA;OACF,CAAA,CACF;QAEJ;OACF;;GAGL,KAAwB,KACvB,kBAAC,GAAD;IACgB;IACd,eAAe,EAAwB,GAAM;IAC7C,WAAW,OAAO,MAAc;AAE9B,KADA,MAAM,EAAwB;MAAE,gBAAgB,EAAa;MAAI;MAAW,CAAC,EAC7E,MAAM,GAAS;;IAEjB,WAAW;IACX,CAAA;GAIH,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAU;KACV,eAAe,EAAmB,GAAM;KACxC,CAAA,EACF,kBAAC,OAAD;KACE,WAAU;KACV,MAAK;KACL,cAAW;KACX,mBAAgB;eAJlB;MAME,kBAAC,MAAD;OACE,IAAG;OACH,WAAU;iBACX;OAEI,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAkD;QAEnC;QACb,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAW,EAAa,QAAQ;SAC5B,CAAA;;QAEL;;MAEJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAiD;QAE1D,CAAA,EACR,kBAAC,YAAD;QACE,OAAO;QACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;QAChD,aAAY;QACZ,WAAU;QACV,MAAM;QACN,CAAA,CACE;;MAEL,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBAA4B;QAAgB,CAAA;OACrD,CAAA;MAGR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,eAAe;AAGb,SAFA,EAAmB,GAAM,EACzB,EAAgB,GAAG,EACnB,EAAe,KAAK;;QAEtB,WAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,SAAS;QACT,UAAU;QACV,WAAU;kBAET,IAAc,iBAAiB;QACzB,CAAA,CACL;;MACF;OACF;;GAEJ;;GAQJ,KAAkD,EAAE,eAAY;CAEpE,IAAM,IADS,EAAM,QACC,SAAS,GACzB,IAAO,EAAM,mBAAmB,GAChC,IAAa,IAAQ,IAAI,KAAK,IAAI,KAAM,IAAO,IAAS,IAAI,GAAG;AAErE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAA+B,EAAM;KAAY,CAAA,EACjE,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG;MAAK;MAAI,IAAQ,IAAI,IAAQ;MACzB;OACH;;GACL,IAAQ,KACP,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KACE,WAAW,sCAAsC,EAAsB,EAAW;KAClF,OAAO,EAAE,OAAO,GAAG,EAAW,IAAI;KAClC,CAAA;IACE,CAAA;GAER,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAM,WAAiB,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAiB,EAAW,EAAC,QAAY,EAAA,CAAA,CAC5C;;GACF;;GAIJ,KAA2D,EAAE,2BAAwB;CACzF,IAAM,IAAQ,EAAkB;AAChC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;GAAG,WAAU;aAA+B,GAAO,QAAQ;GAAoB,CAAA,EAC/E,kBAAC,KAAD;GAAG,WAAU;aAAb,CAA6C,SAAM,EAAkB,SAAa;KAC9E,EAAA,CAAA,EACL,KACC,kBAAC,KAAD;GAAG,WAAU;aACV,EAAe,EAAM,QAAQ,EAAkB,UAAU,EAAM,SAAS;GACvE,CAAA,CAEF;;GAIJ,KAAqD,EAAE,qBAEzD,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;EAAG,WAAU;YAA0C,EAAY;EAAS,CAAA,EAC5E,kBAAC,KAAD;EAAG,WAAU;YACV,EAAW,EAAY,WAAW,QAAQ;EACzC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,KAAD;GACE,WAAW,eAAe,EAAY,SAAS,WAAW,2BAA2B;aADvF,CAGG,EAAY,SAAS,WAAW,MAAM,IACtC,EAAe,EAAY,QAAQ,EAAY,SAAS,CACvD;MACJ,kBAAC,KAAD;GAAG,WAAU;aAA4C,EAAY;GAAW,CAAA,CAC5E;IACF;IAIJ,KAAkD,EAAE,cAEtD,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD,EAAK,WAAU,0DAA2D,CAAA,EAC1E,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,KAAD;GAAG,WAAU;aAAb;IACE,kBAAC,QAAD;KAAM,WAAU;eAAc,EAAK;KAAkB,CAAA;IACpD;IACD,kBAAC,QAAD;KAAM,WAAU;eAA0B,EAAK;KAAgB,CAAA;IAC7D;;EACH,EAAK,UAAU,kBAAC,KAAD;GAAG,WAAU;aAAwC,EAAK;GAAW,CAAA;EACrF,kBAAC,KAAD;GAAG,WAAU;aACV,EAAW,EAAK,WAAW,QAAQ;GAClC,CAAA;EACA,EAAA,CAAA,CACF"}
|
|
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-dom';\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 {\n formatCurrency,\n formatDate,\n formatPlanDuration,\n formatSubscriptionStatus,\n formatPercentage,\n} from '../../../shared/utils/format';\nimport {\n getSubscriptionStatusColor,\n getStatusBadgeClasses,\n getQuotaProgressColor,\n} from '../../../shared/utils/status';\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 { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\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 // Modal state\n const [showCancelModal, setShowCancelModal] = useState(false);\n const [showManageSeatsModal, setShowManageSeatsModal] = 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-muted flex items-center justify-center\">\n <svg\n className=\"size-6 text-muted-foreground\"\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-foreground\">Access Denied</h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n You don'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 p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-muted animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-muted 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 rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-muted 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-destructive/10 flex items-center justify-center\">\n <svg\n className=\"size-6 text-destructive\"\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-foreground\">\n {tr('billing.subscriptions.notFound', 'Subscription not found')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\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-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\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 // 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 return (\n <div className=\"space-y-6 p-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 onClick={() => navigateTo(withBillingAccountId('/subscriptions', billingAccountId))}\n className=\"p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\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\">\n <h1 className=\"text-2xl font-bold text-foreground\">{plan?.name || 'Subscription'}</h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatSubscriptionStatus(subscription.status)}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground 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 </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {isActive &&\n permissions.canUpdateSubscription &&\n plan?.pricingModel === PricingModel.PER_SEAT && (\n <button\n type=\"button\"\n onClick={() => setShowManageSeatsModal(true)}\n className=\"px-4 py-2 text-sm font-medium border border-border rounded-md hover:bg-accent text-foreground transition-colors\"\n >\n Manage Seats\n </button>\n )}\n {isActive && permissions.canUpdateSubscription && (\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${subscription.id}/upgrade`,\n billingAccountId ?? subscription.billingAccountId\n )\n )\n }\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Change Plan\n </button>\n )}\n {isActive && permissions.canCancelSubscription && !subscription.canceledAt && (\n <button\n type=\"button\"\n onClick={() => setShowCancelModal(true)}\n disabled={isCanceling}\n className=\"px-4 py-2 text-sm font-medium border border-border text-destructive rounded-md hover:bg-destructive/10 transition-colors disabled:opacity-50\"\n >\n {isCanceling ? 'Canceling...' : 'Cancel Subscription'}\n </button>\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-md\">\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 */}\n {isActive && (\n <div className=\"border border-border rounded-lg bg-card p-6\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">Current Period</h2>\n <span className=\"text-sm text-muted-foreground\">\n {daysRemaining} days remaining\n </span>\n </div>\n <div className=\"space-y-2\">\n <div className=\"h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary transition-all duration-300\"\n style={{ width: `${progressPercentage}%` }}\n />\n </div>\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>{formatDate(subscription.startDate, 'short')}</span>\n <span>{formatDate(subscription.endDate, 'short')}</span>\n </div>\n </div>\n </div>\n )}\n\n {/* Quotas */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n Quota Usage ({quotas.length})\n </h2>\n </div>\n <div className=\"p-6\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n No quotas assigned to this subscription.\n </p>\n ) : (\n <div className=\"space-y-4\">\n {quotas.map((quota) => (\n <QuotaUsageItem key={quota.id} quota={quota} />\n ))}\n </div>\n )}\n </div>\n </div>\n\n {/* Addons */}\n {addons.length > 0 && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">Addons ({addons.length})</h2>\n </div>\n <div className=\"p-6\">\n <div className=\"space-y-3\">\n {addons.map((addonSub) => (\n <AddonItem key={addonSub.id} addonSubscription={addonSub} />\n ))}\n </div>\n </div>\n </div>\n )}\n\n {/* Recent Transactions */}\n {transactions.length > 0 && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">Recent Transactions</h2>\n </div>\n <div className=\"divide-y divide-border\">\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\">\n <button type=\"button\" className=\"text-sm text-primary hover:underline\">\n View all transactions\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Subscription Details */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">Details</h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-muted-foreground\">Subscription ID</p>\n <p className=\"font-mono text-sm text-foreground mt-0.5 break-all\">\n {subscription.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Plan</p>\n <p className=\"text-sm text-foreground mt-0.5\">{plan?.name || 'Unknown'}</p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Billing Account</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {subscription.billingAccount?.name || 'Unknown'}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Start Date</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {formatDate(subscription.startDate)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">End Date</p>\n <p className=\"text-sm text-foreground mt-0.5\">{formatDate(subscription.endDate)}</p>\n </div>\n {subscription.nextBillingDate && (\n <div>\n <p className=\"text-sm text-muted-foreground\">Next Billing</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-muted-foreground\">Auto Renewal</p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n {subscription.paymentGateway && (\n <div>\n <p className=\"text-sm text-muted-foreground\">Payment Gateway</p>\n <p className=\"text-sm text-foreground mt-0.5 capitalize\">\n {subscription.paymentGateway}\n </p>\n </div>\n )}\n </div>\n </div>\n\n {/* History */}\n {history.length > 0 && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">History</h2>\n </div>\n <div className=\"p-6\">\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 </div>\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-card border border-border rounded-lg shadow-lg 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-foreground mb-2\"\n >\n Cancel Subscription\n </h3>\n <p className=\"text-sm text-muted-foreground 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-foreground\">\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-foreground 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 rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent resize-none\"\n rows={3}\n />\n </div>\n\n {cancelError && (\n <div className=\"mb-4 p-3 bg-destructive/10 border border-destructive/20 rounded-md\">\n <p className=\"text-sm text-destructive\">{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-muted-foreground hover:text-foreground 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-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors 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 limits = quota.limits as { value?: number; type?: string } | 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\n return (\n <div className=\"p-4 rounded-lg bg-muted/30\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"font-medium text-foreground\">{quota.name}</span>\n <span className=\"text-sm text-muted-foreground\">\n {used} / {limit > 0 ? limit : '∞'}\n </span>\n </div>\n {limit > 0 && (\n <div className=\"h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full transition-all duration-300 ${getQuotaProgressColor(percentage)}`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n )}\n <div className=\"flex items-center justify-between mt-2 text-xs text-muted-foreground\">\n <span>{quota.quotaType}</span>\n <span>{formatPercentage(percentage)} used</span>\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-lg bg-muted/30\">\n <div>\n <p className=\"font-medium text-foreground\">{addon?.name || 'Unknown Addon'}</p>\n <p className=\"text-sm text-muted-foreground\">Qty: {addonSubscription.quantity}</p>\n </div>\n {addon && (\n <p className=\"text-sm font-medium text-foreground\">\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-6 py-4\">\n <div>\n <p className=\"font-medium text-foreground capitalize\">{transaction.type}</p>\n <p className=\"text-sm text-muted-foreground\">\n {formatDate(transaction.createdAt, 'short')}\n </p>\n </div>\n <div className=\"text-right\">\n <p\n className={`font-medium ${transaction.type === 'refund' ? 'text-status-error-text' : 'text-foreground'}`}\n >\n {transaction.type === 'refund' ? '-' : ''}\n {formatCurrency(transaction.amount, transaction.currency)}\n </p>\n <p className=\"text-sm text-muted-foreground 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-muted-foreground mt-2 shrink-0\" />\n <div>\n <p className=\"text-sm text-foreground\">\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-muted-foreground mt-0.5\">{item.reason}</p>}\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {formatDate(item.createdAt, 'short')}\n </p>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsCA,IAAa,UAAmC;CAC9C,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,EAAE,sBAAmB,IAAuC,EAC5D,IAAa,GAAoB,EACjC,EAAE,aAAS,GAAwB,EACnC,EAAE,cAAU,GAAY,EACxB,IAAc,IAAuB,EACrC,EAAE,0BAAsB,EAA2B;EACvD;EACA,aAAa;EACb,cAAc;EACf,CAAC,EACI,EAAE,MAAM,OAA8B,EAA0C,EACpF,aAAa,eACd,CAAC,EACI,IACJ,MAAqB,IAA2B,0BAA0B,MAAM,KAAA,GAC5E,EAAE,iBAAc,eAAW,UAAO,eAAY,EAClD,GACA,EACD,EACK,EAAE,uBAAoB,gBAAa,4BAAyB,uBAChE,GAA0B,EAGtB,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAsB,KAA2B,EAAS,GAAM,EACjE,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAG7D,IAA2B,YAAY;AACtC,SAEL;KAAe,KAAK;AACpB,OAAI;AAcF,IAbA,MAAM,EAAmB;KACvB,kBAAkB,KAAoB,EAAa;KACnD,gBAAgB,EAAa;KAC7B,QAAQ,KAAgB;KACzB,CAAC,EACF,GAAK,kCAAkC;KACrC,OAAO;KACP,UAAU,EAAa;KACvB,QAAQ;KACR,QAAQ,KAAgB;KACzB,CAAC,EACF,EAAmB,GAAM,EACzB,EAAgB,GAAG,EACnB,MAAM,GAAS;YACR,GAAK;AACZ,MAAe,aAAe,QAAQ,EAAI,UAAU,gCAAgC;;;;AAKxF,KAAI,CAAC,EAAY,qBACf,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eAAwC;KAAkB,CAAA;IACxE,kBAAC,KAAD;KAAG,WAAU;eAAyC;KAElD,CAAA;IACA;;EACF,CAAA;AAKV,KAAI,GACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAK,WAAU,0CAA2C,CAAA,EAC1D,kBAAC,OAAD,EAAK,WAAU,2CAA4C,CAAA,CACvD;MACN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAK,WAAU,2CAA4C,CAAA,EAC3D,kBAAC,OAAD,EAAK,WAAU,6CAA8C,CAAA,CACzD;;IACF,CAAA;GACF,CAAA,CACF;;AAKV,KAAI,KAAS,CAAC,EACZ,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA;KACF,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,kCAAkC,yBAAyB;KAC5D,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,GAAO,WAAW;KACjB,CAAA;IACJ,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,EAAqB,kBAAkB,EAAiB,CAAC;KACnF,WAAU;eACX;KAEQ,CAAA;IACL;;EACF,CAAA;CAIV,IAAM,IAAO,EAAa,MACpB,IAAS,EAAa,UAAU,EAAE,EAClC,IAAS,EAAa,sBAAsB,EAAE,EAC9C,IAAe,EAAa,gBAAgB,EAAE,EAC9C,IAAU,EAAa,WAAW,EAAE,EACpC,KAAc,EAA2B,EAAa,OAAO,EAC7D,IAAW,EAAa,WAAY,UAGpC,IAAU,IAAI,KAAK,EAAa,QAAQ,EACxC,KAAY,IAAI,KAAK,EAAa,UAAU,EAC5C,qBAAM,IAAI,MAAM,EAChB,IAAY,KAAK,MAAM,EAAQ,SAAS,GAAG,GAAU,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,EACxF,IAAgB,KAAK,IACzB,GACA,KAAK,MAAM,EAAQ,SAAS,GAAG,GAAI,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,CACvE,EACK,KAAqB,KAAK,OAAQ,IAAY,KAAiB,IAAa,IAAI;AAEtF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,EAAqB,kBAAkB,EAAiB,CAAC;MACnF,WAAU;gBAEV,kBAAC,OAAD;OAAK,WAAU;OAAS,MAAK;OAAO,SAAQ;OAAY,QAAO;iBAC7D,kBAAC,QAAD;QACE,eAAc;QACd,gBAAe;QACf,aAAa;QACb,GAAE;QACF,CAAA;OACE,CAAA;MACC,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAsC,GAAM,QAAQ;OAAoB,CAAA,EACtF,kBAAC,QAAD;OACE,WAAW,yCAAyC,EAAsB,GAAY;iBAErF,GAAyB,EAAa,OAAO;OACzC,CAAA,CACH;SACN,kBAAC,KAAD;MAAG,WAAU;gBACV,GAAM,iBAAiB,EAAa,YAAY,EAAK,eAClD,GAAG,EAAe,EAAK,cAAc,EAAK,SAAS,CAAC,YAAY,EAAmB,EAAK,SAAS,CAAC,KAAK,EAAa,aAAa,EAAK,YAAY,EAAE,WAAW,EAAe,EAAK,gBAAgB,EAAa,aAAa,EAAK,YAAY,IAAI,EAAK,SAAS,CAAC,KAAK,EAAmB,EAAK,SAAS,KACvS,IACE,GAAG,EAAe,EAAK,OAAO,EAAK,SAAS,CAAC,KAAK,EAAmB,EAAK,SAAS,KACnF;MACJ,CAAA,CACA,EAAA,CAAA,CACF;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,EAAY,yBACZ,GAAM,iBAAiB,EAAa,YAClC,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAwB,GAAK;OAC5C,WAAU;iBACX;OAEQ,CAAA;MAEZ,KAAY,EAAY,yBACvB,kBAAC,UAAD;OACE,MAAK;OACL,eACE,EACE,EACE,kBAAkB,EAAa,GAAG,WAClC,KAAoB,EAAa,iBAClC,CACF;OAEH,WAAU;iBACX;OAEQ,CAAA;MAEV,KAAY,EAAY,yBAAyB,CAAC,EAAa,cAC9D,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAmB,GAAK;OACvC,UAAU;OACV,WAAU;iBAET,IAAc,iBAAiB;OACzB,CAAA;MAEV,EAAa,cACZ,kBAAC,QAAD;OAAM,WAAU;iBAAgG;OAEzG,CAAA;MAEL;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAwC;SAAmB,CAAA,EACzE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,GAAc,kBACV;WACH;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UACE,WAAU;UACV,OAAO,EAAE,OAAO,GAAG,GAAmB,IAAI;UAC1C,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAa,WAAW,QAAQ,EAAQ,CAAA,EAC1D,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAa,SAAS,QAAQ,EAAQ,CAAA,CACpD;WACF;UACF;;MAIR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,MAAD;SAAI,WAAU;mBAAd;UAAsD;UACtC,EAAO;UAAO;UACzB;;QACD,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAO,WAAW,IACjB,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAEzC,CAAA,GAEJ,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAAsC,UAAS,EAA1B,EAAM,GAAoB,CAC/C;SACE,CAAA;QAEJ,CAAA,CACF;;MAGL,EAAO,SAAS,KACf,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,MAAD;SAAI,WAAU;mBAAd;UAAsD;UAAS,EAAO;UAAO;UAAM;;QAC/E,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAA6B,mBAAmB,GAAY,EAA5C,EAAS,GAAmC,CAC5D;SACE,CAAA;QACF,CAAA,CACF;;MAIP,EAAa,SAAS,KACrB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,MAAD;UAAI,WAAU;oBAAwC;UAAwB,CAAA;SAC1E,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAa,MAAM,GAAG,EAAE,CAAC,KAAK,MAC7B,kBAAC,GAAD,EAAmD,gBAAe,EAA5C,EAAY,GAAgC,CAClE;SACE,CAAA;QACL,EAAa,SAAS,KACrB,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,UAAD;UAAQ,MAAK;UAAS,WAAU;oBAAuC;UAE9D,CAAA;SACL,CAAA;QAEJ;;MAEJ;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,MAAD;QAAI,WAAU;kBAAwC;QAAY,CAAA;OAC9D,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa;SACZ,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAQ,CAAA,EACrD,kBAAC,KAAD;SAAG,WAAU;mBAAkC,GAAM,QAAQ;SAAc,CAAA,CACvE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa,gBAAgB,QAAQ;SACpC,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAc,CAAA,EAC3D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAW,EAAa,UAAU;SACjC,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAY,CAAA,EACzD,kBAAC,KAAD;SAAG,WAAU;mBAAkC,EAAW,EAAa,QAAQ;SAAK,CAAA,CAChF,EAAA,CAAA;QACL,EAAa,mBACZ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAgB,CAAA,EAC7D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAW,EAAa,gBAAgB;SACvC,CAAA,CACA,EAAA,CAAA;QAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAgB,CAAA,EAC7D,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa,cAAc,YAAY;SACtC,CAAA,CACA,EAAA,CAAA;QACL,EAAa,kBACZ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAmB,CAAA,EAChE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAa;SACZ,CAAA,CACA,EAAA,CAAA;QAEJ;SACF;SAGL,EAAQ,SAAS,KAChB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,MAAD;QAAI,WAAU;kBAAwC;QAAY,CAAA;OAC9D,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAQ,MAAM,GAAG,EAAE,CAAC,KAAK,MACxB,kBAAC,GAAD,EAAiC,SAAQ,EAAvB,EAAK,GAAkB,CACzC;QACE,CAAA;OACF,CAAA,CACF;QAEJ;OACF;;GAGL,KAAwB,KACvB,kBAAC,GAAD;IACgB;IACd,eAAe,EAAwB,GAAM;IAC7C,WAAW,OAAO,MAAc;AAE9B,KADA,MAAM,EAAwB;MAAE,gBAAgB,EAAa;MAAI;MAAW,CAAC,EAC7E,MAAM,GAAS;;IAEjB,WAAW;IACX,CAAA;GAIH,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KACE,WAAU;KACV,eAAe,EAAmB,GAAM;KACxC,CAAA,EACF,kBAAC,OAAD;KACE,WAAU;KACV,MAAK;KACL,cAAW;KACX,mBAAgB;eAJlB;MAME,kBAAC,MAAD;OACE,IAAG;OACH,WAAU;iBACX;OAEI,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAkD;QAEnC;QACb,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAW,EAAa,QAAQ;SAC5B,CAAA;;QAEL;;MAEJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAiD;QAE1D,CAAA,EACR,kBAAC,YAAD;QACE,OAAO;QACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;QAChD,aAAY;QACZ,WAAU;QACV,MAAM;QACN,CAAA,CACE;;MAEL,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBAA4B;QAAgB,CAAA;OACrD,CAAA;MAGR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe;AAGb,SAFA,EAAmB,GAAM,EACzB,EAAgB,GAAG,EACnB,EAAe,KAAK;;QAEtB,WAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU;QACV,WAAU;kBAET,IAAc,iBAAiB;QACzB,CAAA,CACL;;MACF;OACF;;GAEJ;;GAQJ,KAAkD,EAAE,eAAY;CAEpE,IAAM,IADS,EAAM,QACC,SAAS,GACzB,IAAO,EAAM,mBAAmB,GAChC,IAAa,IAAQ,IAAI,KAAK,IAAI,KAAM,IAAO,IAAS,IAAI,GAAG;AAErE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAA+B,EAAM;KAAY,CAAA,EACjE,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG;MAAK;MAAI,IAAQ,IAAI,IAAQ;MACzB;OACH;;GACL,IAAQ,KACP,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KACE,WAAW,sCAAsC,EAAsB,EAAW;KAClF,OAAO,EAAE,OAAO,GAAG,EAAW,IAAI;KAClC,CAAA;IACE,CAAA;GAER,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAM,WAAiB,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAiB,EAAW,EAAC,QAAY,EAAA,CAAA,CAC5C;;GACF;;GAIJ,KAA2D,EAAE,2BAAwB;CACzF,IAAM,IAAQ,EAAkB;AAChC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;GAAG,WAAU;aAA+B,GAAO,QAAQ;GAAoB,CAAA,EAC/E,kBAAC,KAAD;GAAG,WAAU;aAAb,CAA6C,SAAM,EAAkB,SAAa;KAC9E,EAAA,CAAA,EACL,KACC,kBAAC,KAAD;GAAG,WAAU;aACV,EAAe,EAAM,QAAQ,EAAkB,UAAU,EAAM,SAAS;GACvE,CAAA,CAEF;;GAIJ,KAAqD,EAAE,qBAEzD,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;EAAG,WAAU;YAA0C,EAAY;EAAS,CAAA,EAC5E,kBAAC,KAAD;EAAG,WAAU;YACV,EAAW,EAAY,WAAW,QAAQ;EACzC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,KAAD;GACE,WAAW,eAAe,EAAY,SAAS,WAAW,2BAA2B;aADvF,CAGG,EAAY,SAAS,WAAW,MAAM,IACtC,EAAe,EAAY,QAAQ,EAAY,SAAS,CACvD;MACJ,kBAAC,KAAD;GAAG,WAAU;aAA4C,EAAY;GAAW,CAAA,CAC5E;IACF;IAIJ,KAAkD,EAAE,cAEtD,kBAAC,OAAD;CAAK,WAAU;WAAf,CACE,kBAAC,OAAD,EAAK,WAAU,yDAA0D,CAAA,EACzE,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,KAAD;GAAG,WAAU;aAAb;IACE,kBAAC,QAAD;KAAM,WAAU;eAAc,EAAK;KAAkB,CAAA;IACpD;IACD,kBAAC,QAAD;KAAM,WAAU;eAA0B,EAAK;KAAgB,CAAA;IAC7D;;EACH,EAAK,UAAU,kBAAC,KAAD;GAAG,WAAU;aAAwC,EAAK;GAAW,CAAA;EACrF,kBAAC,KAAD;GAAG,WAAU;aACV,EAAW,EAAK,WAAW,QAAQ;GAClC,CAAA;EACA,EAAA,CAAA,CACF"}
|
|
@@ -41,7 +41,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
41
41
|
className: "border border-border rounded-lg p-6",
|
|
42
42
|
children: /* @__PURE__ */ v("div", {
|
|
43
43
|
className: "flex items-center gap-4",
|
|
44
|
-
children: [/* @__PURE__ */ _("div", { className: "
|
|
44
|
+
children: [/* @__PURE__ */ _("div", { className: "size-12 bg-muted animate-pulse rounded" }), /* @__PURE__ */ v("div", {
|
|
45
45
|
className: "flex-1 space-y-2",
|
|
46
46
|
children: [/* @__PURE__ */ _("div", { className: "h-5 w-32 bg-muted animate-pulse rounded" }), /* @__PURE__ */ _("div", { className: "h-4 w-48 bg-muted animate-pulse rounded" })]
|
|
47
47
|
})]
|
|
@@ -54,9 +54,9 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
54
54
|
className: "text-center space-y-4",
|
|
55
55
|
children: [
|
|
56
56
|
/* @__PURE__ */ _("div", {
|
|
57
|
-
className: "
|
|
57
|
+
className: "size-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center",
|
|
58
58
|
children: /* @__PURE__ */ _("svg", {
|
|
59
|
-
className: "
|
|
59
|
+
className: "size-6 text-destructive",
|
|
60
60
|
fill: "none",
|
|
61
61
|
viewBox: "0 0 24 24",
|
|
62
62
|
stroke: "currentColor",
|
|
@@ -77,6 +77,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
77
77
|
children: H.message
|
|
78
78
|
}),
|
|
79
79
|
/* @__PURE__ */ _("button", {
|
|
80
|
+
type: "button",
|
|
80
81
|
onClick: () => U(),
|
|
81
82
|
className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
82
83
|
children: "Try Again"
|
|
@@ -95,10 +96,11 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
95
96
|
className: "text-sm text-muted-foreground mt-1",
|
|
96
97
|
children: r("billing.subscriptions.subtitle", "Manage your active and past subscriptions")
|
|
97
98
|
})] }), a.canCreateSubscription && /* @__PURE__ */ v("button", {
|
|
99
|
+
type: "button",
|
|
98
100
|
onClick: () => i("/plans"),
|
|
99
101
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
100
102
|
children: [/* @__PURE__ */ _("svg", {
|
|
101
|
-
className: "
|
|
103
|
+
className: "size-4",
|
|
102
104
|
fill: "none",
|
|
103
105
|
viewBox: "0 0 24 24",
|
|
104
106
|
stroke: "currentColor",
|
|
@@ -114,14 +116,15 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
114
116
|
s.length > 1 && /* @__PURE__ */ v("div", {
|
|
115
117
|
className: "relative",
|
|
116
118
|
children: [/* @__PURE__ */ v("button", {
|
|
119
|
+
type: "button",
|
|
117
120
|
onClick: () => D(!E),
|
|
118
121
|
className: "w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border rounded-lg bg-card hover:bg-muted/50 transition-colors",
|
|
119
122
|
children: [/* @__PURE__ */ v("div", {
|
|
120
123
|
className: "flex items-center gap-3",
|
|
121
124
|
children: [/* @__PURE__ */ _("div", {
|
|
122
|
-
className: "
|
|
125
|
+
className: "size-8 rounded-lg bg-primary/10 flex items-center justify-center",
|
|
123
126
|
children: /* @__PURE__ */ _("svg", {
|
|
124
|
-
className: "
|
|
127
|
+
className: "size-4 text-primary",
|
|
125
128
|
fill: "none",
|
|
126
129
|
viewBox: "0 0 24 24",
|
|
127
130
|
stroke: "currentColor",
|
|
@@ -143,7 +146,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
143
146
|
})]
|
|
144
147
|
})]
|
|
145
148
|
}), /* @__PURE__ */ _("svg", {
|
|
146
|
-
className: `
|
|
149
|
+
className: `size-4 text-muted-foreground transition-transform ${E ? "rotate-180" : ""}`,
|
|
147
150
|
fill: "none",
|
|
148
151
|
viewBox: "0 0 24 24",
|
|
149
152
|
stroke: "currentColor",
|
|
@@ -160,14 +163,15 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
160
163
|
}), /* @__PURE__ */ _("div", {
|
|
161
164
|
className: "absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto",
|
|
162
165
|
children: s.map((e) => /* @__PURE__ */ v("button", {
|
|
166
|
+
type: "button",
|
|
163
167
|
onClick: () => G(e),
|
|
164
168
|
className: "w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-muted transition-colors",
|
|
165
169
|
children: [/* @__PURE__ */ v("div", {
|
|
166
170
|
className: "flex items-center gap-3 min-w-0",
|
|
167
171
|
children: [/* @__PURE__ */ _("div", {
|
|
168
|
-
className: "
|
|
172
|
+
className: "size-8 rounded-lg bg-muted flex items-center justify-center flex-shrink-0",
|
|
169
173
|
children: /* @__PURE__ */ _("svg", {
|
|
170
|
-
className: "
|
|
174
|
+
className: "size-4 text-muted-foreground",
|
|
171
175
|
fill: "none",
|
|
172
176
|
viewBox: "0 0 24 24",
|
|
173
177
|
stroke: "currentColor",
|
|
@@ -189,7 +193,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
189
193
|
})]
|
|
190
194
|
})]
|
|
191
195
|
}), W?.id === e.id && /* @__PURE__ */ _("svg", {
|
|
192
|
-
className: "
|
|
196
|
+
className: "size-4 text-primary flex-shrink-0",
|
|
193
197
|
fill: "none",
|
|
194
198
|
viewBox: "0 0 24 24",
|
|
195
199
|
stroke: "currentColor",
|
|
@@ -249,6 +253,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
249
253
|
"canceled",
|
|
250
254
|
"inactive"
|
|
251
255
|
].map((e) => /* @__PURE__ */ _("button", {
|
|
256
|
+
type: "button",
|
|
252
257
|
onClick: () => k(e),
|
|
253
258
|
className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors capitalize ${O === e ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
254
259
|
children: e === "inactive" ? "Inactive" : e
|
|
@@ -257,7 +262,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
257
262
|
/* @__PURE__ */ v("div", {
|
|
258
263
|
className: "relative flex-1 min-w-0 w-full sm:max-w-md",
|
|
259
264
|
children: [/* @__PURE__ */ _("svg", {
|
|
260
|
-
className: "absolute left-3 top-1/2 -translate-y-1/2
|
|
265
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground",
|
|
261
266
|
fill: "none",
|
|
262
267
|
viewBox: "0 0 24 24",
|
|
263
268
|
stroke: "currentColor",
|
|
@@ -290,9 +295,9 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
290
295
|
className: "flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg",
|
|
291
296
|
children: [
|
|
292
297
|
/* @__PURE__ */ _("div", {
|
|
293
|
-
className: "
|
|
298
|
+
className: "size-12 rounded-full bg-muted flex items-center justify-center mb-4",
|
|
294
299
|
children: /* @__PURE__ */ _("svg", {
|
|
295
|
-
className: "
|
|
300
|
+
className: "size-6 text-muted-foreground",
|
|
296
301
|
fill: "none",
|
|
297
302
|
viewBox: "0 0 24 24",
|
|
298
303
|
stroke: "currentColor",
|
|
@@ -313,6 +318,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
313
318
|
children: O !== "all" || M ? "No subscriptions match your filters." : "You don't have any subscriptions yet."
|
|
314
319
|
}),
|
|
315
320
|
a.canCreateSubscription && O === "all" && !M && /* @__PURE__ */ _("button", {
|
|
321
|
+
type: "button",
|
|
316
322
|
onClick: () => i("/plans"),
|
|
317
323
|
className: "mt-4 px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
|
|
318
324
|
children: "Browse Plans"
|
|
@@ -330,11 +336,12 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
330
336
|
className: "flex items-center justify-center gap-2 pt-4",
|
|
331
337
|
children: [
|
|
332
338
|
/* @__PURE__ */ _("button", {
|
|
339
|
+
type: "button",
|
|
333
340
|
onClick: () => K(P - 1),
|
|
334
341
|
disabled: P === 1,
|
|
335
342
|
className: "px-3 py-1.5 text-sm border border-border rounded-md disabled:opacity-50 disabled:cursor-not-allowed hover:bg-muted transition-colors",
|
|
336
343
|
children: /* @__PURE__ */ _("svg", {
|
|
337
|
-
className: "
|
|
344
|
+
className: "size-4",
|
|
338
345
|
fill: "none",
|
|
339
346
|
viewBox: "0 0 24 24",
|
|
340
347
|
stroke: "currentColor",
|
|
@@ -354,6 +361,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
354
361
|
className: "px-2 text-muted-foreground",
|
|
355
362
|
children: "..."
|
|
356
363
|
}, e) : t ? /* @__PURE__ */ _("button", {
|
|
364
|
+
type: "button",
|
|
357
365
|
onClick: () => K(e),
|
|
358
366
|
className: `px-3 py-1.5 text-sm rounded-md transition-colors ${e === P ? "bg-primary text-primary-foreground" : "border border-border hover:bg-muted"}`,
|
|
359
367
|
children: e
|
|
@@ -361,11 +369,12 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
361
369
|
})
|
|
362
370
|
}),
|
|
363
371
|
/* @__PURE__ */ _("button", {
|
|
372
|
+
type: "button",
|
|
364
373
|
onClick: () => K(P + 1),
|
|
365
374
|
disabled: P === B,
|
|
366
375
|
className: "px-3 py-1.5 text-sm border border-border rounded-md disabled:opacity-50 disabled:cursor-not-allowed hover:bg-muted transition-colors",
|
|
367
376
|
children: /* @__PURE__ */ _("svg", {
|
|
368
|
-
className: "
|
|
377
|
+
className: "size-4",
|
|
369
378
|
fill: "none",
|
|
370
379
|
viewBox: "0 0 24 24",
|
|
371
380
|
stroke: "currentColor",
|
|
@@ -386,9 +395,9 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
386
395
|
className: "text-center space-y-2",
|
|
387
396
|
children: [
|
|
388
397
|
/* @__PURE__ */ _("div", {
|
|
389
|
-
className: "
|
|
398
|
+
className: "size-12 mx-auto rounded-full bg-muted flex items-center justify-center",
|
|
390
399
|
children: /* @__PURE__ */ _("svg", {
|
|
391
|
-
className: "
|
|
400
|
+
className: "size-6 text-muted-foreground",
|
|
392
401
|
fill: "none",
|
|
393
402
|
viewBox: "0 0 24 24",
|
|
394
403
|
stroke: "currentColor",
|
|
@@ -423,9 +432,9 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
423
432
|
className: "flex flex-col sm:flex-row sm:items-center gap-4",
|
|
424
433
|
children: [
|
|
425
434
|
/* @__PURE__ */ _("div", {
|
|
426
|
-
className: "
|
|
435
|
+
className: "size-12 rounded-lg bg-primary/10 flex items-center justify-center shrink-0",
|
|
427
436
|
children: /* @__PURE__ */ _("svg", {
|
|
428
|
-
className: "
|
|
437
|
+
className: "size-6 text-primary",
|
|
429
438
|
fill: "none",
|
|
430
439
|
viewBox: "0 0 24 24",
|
|
431
440
|
stroke: "currentColor",
|
|
@@ -480,6 +489,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
480
489
|
children: "remaining"
|
|
481
490
|
})]
|
|
482
491
|
}), /* @__PURE__ */ _("button", {
|
|
492
|
+
type: "button",
|
|
483
493
|
onClick: (e) => {
|
|
484
494
|
e.stopPropagation(), t();
|
|
485
495
|
},
|
|
@@ -508,7 +518,7 @@ var b = ["expired", "upgraded"], x = 10, S = () => {
|
|
|
508
518
|
children: [
|
|
509
519
|
"Canceled on ",
|
|
510
520
|
r(e.canceledAt),
|
|
511
|
-
e.cancellationReason && /* @__PURE__ */ v("span", { children: ["
|
|
521
|
+
e.cancellationReason && /* @__PURE__ */ v("span", { children: [": ", e.cancellationReason] })
|
|
512
522
|
]
|
|
513
523
|
})
|
|
514
524
|
})
|