@burdenoff/microfe-billing 2026.624.3 → 2026.625.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingAdminRoutes.js +1 -1
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +2 -2
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +4 -4
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +2 -2
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +98 -99
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +88 -85
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +6 -6
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +2 -2
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +360 -370
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +2 -2
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +2 -2
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +102 -104
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +5 -5
- package/dist/billing/modules/coupons/pages/CouponsListPage.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 +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +29 -28
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +2 -2
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +3 -3
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +2 -2
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +139 -142
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +6 -6
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +4 -4
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +142 -133
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +2 -2
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +4 -4
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +2 -2
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +4 -4
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +2 -2
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +2 -2
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +2 -2
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +5 -5
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +2 -2
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -265
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +7 -7
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +13 -13
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/ui/Card.js +1 -1
- package/dist/billing/shared/ui/Card.js.map +1 -1
- package/dist/billing/shared/ui/MetricCard.js +28 -21
- package/dist/billing/shared/ui/MetricCard.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BillingAccountSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/BillingAccountSelector.tsx"],"sourcesContent":["/**\n * Billing Account Selector Component\n * Dropdown selector for choosing billing account in checkout\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { Building2, CreditCard, ChevronDown, Check, Lock } from 'lucide-react';\nimport type { CheckoutBillingAccount } from '../types';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountSelectorProps {\n accounts: CheckoutBillingAccount[];\n selectedAccountId: string | null;\n onSelect: (account: CheckoutBillingAccount) => void;\n isLoading?: boolean;\n disabled?: boolean;\n disabledMessage?: string;\n}\n\nexport const BillingAccountSelector: FC<BillingAccountSelectorProps> = ({\n accounts,\n selectedAccountId,\n onSelect,\n isLoading = false,\n disabled = false,\n disabledMessage,\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 [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Close on escape key\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }\n }, [isOpen]);\n\n const selectedAccount = accounts.find((a) => a.id === selectedAccountId);\n\n if (isLoading) {\n return (\n <div className=\"space-y-2\">\n <div className=\"h-4 w-28 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-12 bg-bg-sunken animate-pulse rounded-lg\" />\n </div>\n );\n }\n\n if (accounts.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed border-border-subtle p-4 text-center\">\n <Building2 className=\"mx-auto size-6 text-text-secondary\" />\n <p className=\"mt-2 text-sm text-text-secondary\">No billing accounts found</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2 relative\" ref={dropdownRef}>\n <div className=\"flex items-center justify-between\">\n <label className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </label>\n {disabled && (\n <span className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Lock className=\"size-3\" />\n Locked\n </span>\n )}\n </div>\n\n {/* Dropdown trigger */}\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n className={`\n w-full flex items-center justify-between gap-3 p-3 rounded-lg border\n transition-colors text-left\n ${\n disabled\n ? 'border-border-subtle bg-bg-sunken cursor-not-allowed opacity-80'\n : isOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong'\n }\n `}\n >\n {selectedAccount ? (\n <div className=\"flex items-center gap-3 min-w-0 flex-1\">\n <div\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ${\n disabled\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-text-link'\n }`}\n >\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary truncate\">\n {selectedAccount.name}\n </span>\n {!selectedAccount.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-status-error-bg-subtle text-status-error-text shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary truncate\">{selectedAccount.email}</p>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-text-primary\">\n <CreditCard className=\"size-3.5 text-text-secondary\" />\n {formatCurrency(selectedAccount.creditAmount, 'CREDITS')}\n </div>\n </div>\n </div>\n ) : (\n <span className=\"text-text-secondary\">Select billing account</span>\n )}\n {disabled ? (\n <Lock className=\"size-4 shrink-0 text-text-secondary\" />\n ) : (\n <ChevronDown\n className={`size-4 shrink-0 text-text-secondary transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n )}\n </button>\n\n {/* Disabled message */}\n {disabled && disabledMessage && (\n <p className=\"text-xs text-text-secondary\">{disabledMessage}</p>\n )}\n\n {/* Dropdown menu */}\n {isOpen && !disabled && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-64 overflow-auto rounded-card border border-border-subtle bg-bg-elevated shadow-elevation-3\">\n {accounts.map((account) => {\n const isSelected = account.id === selectedAccountId;\n return (\n <button\n key={account.id}\n type=\"button\"\n onClick={() => {\n onSelect(account);\n setIsOpen(false);\n }}\n className={`\n w-full flex items-center gap-3 p-3 text-left\n transition-colors\n ${isSelected ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}\n `}\n >\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-link\">\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary truncate\">{account.name}</span>\n {!account.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-status-error-bg-subtle text-status-error-text shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary truncate\">{account.email}</p>\n </div>\n <div className=\"shrink-0 flex items-center gap-2\">\n <div className=\"text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-text-primary\">\n <CreditCard className=\"size-3.5 text-text-secondary\" />\n {formatCurrency(account.creditAmount, 'CREDITS')}\n </div>\n </div>\n {isSelected && <Check className=\"size-4 text-text-link\" />}\n </div>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BillingAccountSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/BillingAccountSelector.tsx"],"sourcesContent":["/**\n * Billing Account Selector Component\n * Dropdown selector for choosing billing account in checkout\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { Building2, CreditCard, ChevronDown, Check, Lock } from 'lucide-react';\nimport type { CheckoutBillingAccount } from '../types';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountSelectorProps {\n accounts: CheckoutBillingAccount[];\n selectedAccountId: string | null;\n onSelect: (account: CheckoutBillingAccount) => void;\n isLoading?: boolean;\n disabled?: boolean;\n disabledMessage?: string;\n}\n\nexport const BillingAccountSelector: FC<BillingAccountSelectorProps> = ({\n accounts,\n selectedAccountId,\n onSelect,\n isLoading = false,\n disabled = false,\n disabledMessage,\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 [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Close on escape key\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }\n }, [isOpen]);\n\n const selectedAccount = accounts.find((a) => a.id === selectedAccountId);\n\n if (isLoading) {\n return (\n <div className=\"space-y-2\">\n <div className=\"h-4 w-28 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-12 bg-bg-sunken animate-pulse rounded-lg\" />\n </div>\n );\n }\n\n if (accounts.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed border-border-subtle p-4 text-center\">\n <Building2 className=\"mx-auto size-6 text-text-secondary\" />\n <p className=\"mt-2 text-sm text-text-secondary\">No billing accounts found</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2 relative\" ref={dropdownRef}>\n <div className=\"flex items-center justify-between\">\n <label className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </label>\n {disabled && (\n <span className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Lock className=\"size-3\" />\n Locked\n </span>\n )}\n </div>\n\n {/* Dropdown trigger */}\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n className={`\n w-full flex items-center justify-between gap-3 p-3 rounded-lg border\n transition-colors text-left\n ${\n disabled\n ? 'border-border-subtle bg-bg-sunken cursor-not-allowed opacity-80'\n : isOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong'\n }\n `}\n >\n {selectedAccount ? (\n <div className=\"flex items-center gap-3 min-w-0 flex-1\">\n <div\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ${\n disabled\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-text-link'\n }`}\n >\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary truncate\">\n {selectedAccount.name}\n </span>\n {!selectedAccount.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-status-error-bg-subtle text-status-error-text shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary truncate\">{selectedAccount.email}</p>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-text-primary\">\n <CreditCard className=\"size-3.5 text-text-secondary\" />\n {formatCurrency(selectedAccount.creditAmount, 'CREDITS')}\n </div>\n </div>\n </div>\n ) : (\n <span className=\"text-text-secondary\">Select billing account</span>\n )}\n {disabled ? (\n <Lock className=\"size-4 shrink-0 text-text-secondary\" />\n ) : (\n <ChevronDown\n className={`size-4 shrink-0 text-text-secondary transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n )}\n </button>\n\n {/* Disabled message */}\n {disabled && disabledMessage && (\n <p className=\"text-xs text-text-secondary\">{disabledMessage}</p>\n )}\n\n {/* Dropdown menu */}\n {isOpen && !disabled && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-64 overflow-auto rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3\">\n {accounts.map((account) => {\n const isSelected = account.id === selectedAccountId;\n return (\n <button\n key={account.id}\n type=\"button\"\n onClick={() => {\n onSelect(account);\n setIsOpen(false);\n }}\n className={`\n w-full flex items-center gap-3 p-3 text-left\n transition-colors\n ${isSelected ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}\n `}\n >\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-link\">\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary truncate\">{account.name}</span>\n {!account.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-status-error-bg-subtle text-status-error-text shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary truncate\">{account.email}</p>\n </div>\n <div className=\"shrink-0 flex items-center gap-2\">\n <div className=\"text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-text-primary\">\n <CreditCard className=\"size-3.5 text-text-secondary\" />\n {formatCurrency(account.creditAmount, 'CREDITS')}\n </div>\n </div>\n {isSelected && <Check className=\"size-4 text-text-link\" />}\n </div>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -64,7 +64,7 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
64
64
|
return /* @__PURE__ */ s("div", {
|
|
65
65
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4",
|
|
66
66
|
children: /* @__PURE__ */ c("div", {
|
|
67
|
-
className: "bg-bg-surface border border-border-
|
|
67
|
+
className: "bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-lg max-h-[90vh] overflow-y-auto",
|
|
68
68
|
children: [
|
|
69
69
|
/* @__PURE__ */ c("div", {
|
|
70
70
|
className: "flex items-start justify-between p-6 border-b border-border-subtle",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompleteBillingAccountModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/CompleteBillingAccountModal.tsx"],"sourcesContent":["/**\n * Modal shown at checkout when the selected billing account is missing\n * a billing address or contact number — fields that can't be auto-filled\n * during account creation but are required by payment providers.\n */\n\nimport { type FC, useState } from 'react';\nimport { MapPin, Phone, Loader2, X } from 'lucide-react';\nimport {\n useSetBillingAddressMutation,\n useUpdateBillingAccountMutation,\n} from '../../../../generated/global-operations';\n\nconst COUNTRY_OPTIONS = [\n { value: 'IN', label: 'India' },\n { value: 'US', label: 'United States' },\n { value: 'GB', label: 'United Kingdom' },\n { value: 'AU', label: 'Australia' },\n { value: 'CA', label: 'Canada' },\n { value: 'SG', label: 'Singapore' },\n { value: 'DE', label: 'Germany' },\n { value: 'FR', label: 'France' },\n { value: 'AE', label: 'UAE' },\n];\n\ninterface FormState {\n contactNumber: string;\n line1: string;\n line2: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n}\n\ninterface FormErrors {\n contactNumber?: string;\n line1?: string;\n city?: string;\n postalCode?: string;\n country?: string;\n}\n\ninterface Props {\n billingAccountId: string;\n /** Whether contactNumber is already filled on the account */\n hasPhone: boolean;\n /** Whether a billing address already exists (with line1 + postalCode) */\n hasAddress: boolean;\n onCompleted: () => void;\n onCancel: () => void;\n}\n\n/** Returns true when a billing account has everything required for checkout. */\nexport function isBillingAccountComplete(account: {\n contactNumber?: string | null;\n billingAddresses?: Array<{\n line1?: string | null;\n city?: string | null;\n postalCode?: string | null;\n country?: string | null;\n }> | null;\n}): boolean {\n const hasPhone = Boolean(account.contactNumber?.trim());\n const primaryAddress = account.billingAddresses?.[0];\n const hasAddress = Boolean(\n primaryAddress?.line1?.trim() &&\n primaryAddress?.city?.trim() &&\n primaryAddress?.postalCode?.trim() &&\n primaryAddress?.country?.trim()\n );\n return hasPhone && hasAddress;\n}\n\nexport const CompleteBillingAccountModal: FC<Props> = ({\n billingAccountId,\n hasPhone,\n hasAddress,\n onCompleted,\n onCancel,\n}) => {\n const [form, setForm] = useState<FormState>({\n contactNumber: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n country: 'IN',\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [submitting, setSubmitting] = useState(false);\n const [serverError, setServerError] = useState<string | null>(null);\n\n const [setBillingAddress] = useSetBillingAddressMutation();\n const [updateBillingAccount] = useUpdateBillingAccountMutation();\n\n const set = (field: keyof FormState, value: string) =>\n setForm((prev) => ({ ...prev, [field]: value }));\n\n const validate = (): boolean => {\n const e: FormErrors = {};\n if (!hasPhone && !form.contactNumber.trim()) {\n e.contactNumber = 'Phone number is required for payment processing';\n }\n if (!hasAddress) {\n if (!form.line1.trim()) e.line1 = 'Street address is required';\n if (!form.city.trim()) e.city = 'City is required';\n if (!form.postalCode.trim()) e.postalCode = 'Postal code is required';\n if (!form.country.trim()) e.country = 'Country is required';\n }\n setErrors(e);\n return Object.keys(e).length === 0;\n };\n\n const handleSubmit = async () => {\n if (!validate()) return;\n setSubmitting(true);\n setServerError(null);\n\n try {\n const ops: Promise<unknown>[] = [];\n\n if (!hasPhone && form.contactNumber.trim()) {\n ops.push(\n updateBillingAccount({\n variables: {\n input: {\n id: billingAccountId,\n contactNumber: form.contactNumber.trim(),\n },\n },\n })\n );\n }\n\n if (!hasAddress) {\n ops.push(\n setBillingAddress({\n variables: {\n input: {\n billingAccountId,\n line1: form.line1.trim(),\n ...(form.line2.trim() ? { line2: form.line2.trim() } : {}),\n city: form.city.trim(),\n ...(form.state.trim() ? { state: form.state.trim() } : {}),\n postalCode: form.postalCode.trim(),\n country: form.country,\n },\n },\n })\n );\n }\n\n await Promise.all(ops);\n onCompleted();\n } catch {\n setServerError('Failed to save billing details. Please try again.');\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n {/* Header */}\n <div className=\"flex items-start justify-between p-6 border-b border-border-subtle\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Complete billing details</h2>\n <p className=\"text-sm text-text-secondary mt-1\">\n We need a few more details before processing your payment.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-2 text-text-secondary hover:text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"p-6 space-y-6\">\n {serverError && (\n <div className=\"p-3 rounded-lg bg-status-error-bg-subtle border border-status-error-border text-sm text-status-error-text\">\n {serverError}\n </div>\n )}\n\n {/* Phone */}\n {!hasPhone && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Phone className=\"size-4 text-text-secondary\" />\n Contact number\n </div>\n <div>\n <input\n type=\"tel\"\n value={form.contactNumber}\n onChange={(e) => set('contactNumber', e.target.value)}\n placeholder=\"+91 98765 43210\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.contactNumber ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.contactNumber && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.contactNumber}</p>\n )}\n </div>\n </section>\n )}\n\n {/* Address */}\n {!hasAddress && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <MapPin className=\"size-4 text-text-secondary\" />\n Billing address\n </div>\n\n <div className=\"space-y-3\">\n {/* Country */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Country</label>\n <select\n value={form.country}\n onChange={(e) => set('country', e.target.value)}\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.country ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n >\n {COUNTRY_OPTIONS.map((c) => (\n <option key={c.value} value={c.value}>\n {c.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Line 1 */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Street address</label>\n <input\n type=\"text\"\n value={form.line1}\n onChange={(e) => set('line1', e.target.value)}\n placeholder=\"123 Main St\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.line1 ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.line1 && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.line1}</p>\n )}\n </div>\n\n {/* Line 2 */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Apartment, suite, etc. <span className=\"text-text-muted\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.line2}\n onChange={(e) => set('line2', e.target.value)}\n placeholder=\"Apt 4B\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* City + State */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">City</label>\n <input\n type=\"text\"\n value={form.city}\n onChange={(e) => set('city', e.target.value)}\n placeholder=\"Mumbai\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.city ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.city && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.city}</p>\n )}\n </div>\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n State <span className=\"text-text-muted\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.state}\n onChange={(e) => set('state', e.target.value)}\n placeholder=\"Maharashtra\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n {/* Postal code */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Postal code</label>\n <input\n type=\"text\"\n value={form.postalCode}\n onChange={(e) => set('postalCode', e.target.value)}\n placeholder=\"400001\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.postalCode ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.postalCode && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.postalCode}</p>\n )}\n </div>\n </div>\n </section>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 p-6 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={submitting}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={submitting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover rounded-lg transition-colors disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"size-4 animate-spin\" />}\n Save and continue\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompleteBillingAccountModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/CompleteBillingAccountModal.tsx"],"sourcesContent":["/**\n * Modal shown at checkout when the selected billing account is missing\n * a billing address or contact number — fields that can't be auto-filled\n * during account creation but are required by payment providers.\n */\n\nimport { type FC, useState } from 'react';\nimport { MapPin, Phone, Loader2, X } from 'lucide-react';\nimport {\n useSetBillingAddressMutation,\n useUpdateBillingAccountMutation,\n} from '../../../../generated/global-operations';\n\nconst COUNTRY_OPTIONS = [\n { value: 'IN', label: 'India' },\n { value: 'US', label: 'United States' },\n { value: 'GB', label: 'United Kingdom' },\n { value: 'AU', label: 'Australia' },\n { value: 'CA', label: 'Canada' },\n { value: 'SG', label: 'Singapore' },\n { value: 'DE', label: 'Germany' },\n { value: 'FR', label: 'France' },\n { value: 'AE', label: 'UAE' },\n];\n\ninterface FormState {\n contactNumber: string;\n line1: string;\n line2: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n}\n\ninterface FormErrors {\n contactNumber?: string;\n line1?: string;\n city?: string;\n postalCode?: string;\n country?: string;\n}\n\ninterface Props {\n billingAccountId: string;\n /** Whether contactNumber is already filled on the account */\n hasPhone: boolean;\n /** Whether a billing address already exists (with line1 + postalCode) */\n hasAddress: boolean;\n onCompleted: () => void;\n onCancel: () => void;\n}\n\n/** Returns true when a billing account has everything required for checkout. */\nexport function isBillingAccountComplete(account: {\n contactNumber?: string | null;\n billingAddresses?: Array<{\n line1?: string | null;\n city?: string | null;\n postalCode?: string | null;\n country?: string | null;\n }> | null;\n}): boolean {\n const hasPhone = Boolean(account.contactNumber?.trim());\n const primaryAddress = account.billingAddresses?.[0];\n const hasAddress = Boolean(\n primaryAddress?.line1?.trim() &&\n primaryAddress?.city?.trim() &&\n primaryAddress?.postalCode?.trim() &&\n primaryAddress?.country?.trim()\n );\n return hasPhone && hasAddress;\n}\n\nexport const CompleteBillingAccountModal: FC<Props> = ({\n billingAccountId,\n hasPhone,\n hasAddress,\n onCompleted,\n onCancel,\n}) => {\n const [form, setForm] = useState<FormState>({\n contactNumber: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n country: 'IN',\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [submitting, setSubmitting] = useState(false);\n const [serverError, setServerError] = useState<string | null>(null);\n\n const [setBillingAddress] = useSetBillingAddressMutation();\n const [updateBillingAccount] = useUpdateBillingAccountMutation();\n\n const set = (field: keyof FormState, value: string) =>\n setForm((prev) => ({ ...prev, [field]: value }));\n\n const validate = (): boolean => {\n const e: FormErrors = {};\n if (!hasPhone && !form.contactNumber.trim()) {\n e.contactNumber = 'Phone number is required for payment processing';\n }\n if (!hasAddress) {\n if (!form.line1.trim()) e.line1 = 'Street address is required';\n if (!form.city.trim()) e.city = 'City is required';\n if (!form.postalCode.trim()) e.postalCode = 'Postal code is required';\n if (!form.country.trim()) e.country = 'Country is required';\n }\n setErrors(e);\n return Object.keys(e).length === 0;\n };\n\n const handleSubmit = async () => {\n if (!validate()) return;\n setSubmitting(true);\n setServerError(null);\n\n try {\n const ops: Promise<unknown>[] = [];\n\n if (!hasPhone && form.contactNumber.trim()) {\n ops.push(\n updateBillingAccount({\n variables: {\n input: {\n id: billingAccountId,\n contactNumber: form.contactNumber.trim(),\n },\n },\n })\n );\n }\n\n if (!hasAddress) {\n ops.push(\n setBillingAddress({\n variables: {\n input: {\n billingAccountId,\n line1: form.line1.trim(),\n ...(form.line2.trim() ? { line2: form.line2.trim() } : {}),\n city: form.city.trim(),\n ...(form.state.trim() ? { state: form.state.trim() } : {}),\n postalCode: form.postalCode.trim(),\n country: form.country,\n },\n },\n })\n );\n }\n\n await Promise.all(ops);\n onCompleted();\n } catch {\n setServerError('Failed to save billing details. Please try again.');\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4\">\n <div className=\"bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n {/* Header */}\n <div className=\"flex items-start justify-between p-6 border-b border-border-subtle\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Complete billing details</h2>\n <p className=\"text-sm text-text-secondary mt-1\">\n We need a few more details before processing your payment.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-2 text-text-secondary hover:text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"p-6 space-y-6\">\n {serverError && (\n <div className=\"p-3 rounded-lg bg-status-error-bg-subtle border border-status-error-border text-sm text-status-error-text\">\n {serverError}\n </div>\n )}\n\n {/* Phone */}\n {!hasPhone && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Phone className=\"size-4 text-text-secondary\" />\n Contact number\n </div>\n <div>\n <input\n type=\"tel\"\n value={form.contactNumber}\n onChange={(e) => set('contactNumber', e.target.value)}\n placeholder=\"+91 98765 43210\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.contactNumber ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.contactNumber && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.contactNumber}</p>\n )}\n </div>\n </section>\n )}\n\n {/* Address */}\n {!hasAddress && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <MapPin className=\"size-4 text-text-secondary\" />\n Billing address\n </div>\n\n <div className=\"space-y-3\">\n {/* Country */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Country</label>\n <select\n value={form.country}\n onChange={(e) => set('country', e.target.value)}\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.country ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n >\n {COUNTRY_OPTIONS.map((c) => (\n <option key={c.value} value={c.value}>\n {c.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Line 1 */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Street address</label>\n <input\n type=\"text\"\n value={form.line1}\n onChange={(e) => set('line1', e.target.value)}\n placeholder=\"123 Main St\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.line1 ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.line1 && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.line1}</p>\n )}\n </div>\n\n {/* Line 2 */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Apartment, suite, etc. <span className=\"text-text-muted\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.line2}\n onChange={(e) => set('line2', e.target.value)}\n placeholder=\"Apt 4B\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* City + State */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">City</label>\n <input\n type=\"text\"\n value={form.city}\n onChange={(e) => set('city', e.target.value)}\n placeholder=\"Mumbai\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.city ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.city && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.city}</p>\n )}\n </div>\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n State <span className=\"text-text-muted\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.state}\n onChange={(e) => set('state', e.target.value)}\n placeholder=\"Maharashtra\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n {/* Postal code */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Postal code</label>\n <input\n type=\"text\"\n value={form.postalCode}\n onChange={(e) => set('postalCode', e.target.value)}\n placeholder=\"400001\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.postalCode ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.postalCode && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.postalCode}</p>\n )}\n </div>\n </div>\n </section>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 p-6 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={submitting}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={submitting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover rounded-lg transition-colors disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"size-4 animate-spin\" />}\n Save and continue\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -56,7 +56,7 @@ var u = ({ subscriptions: u, selectedSubscriptionId: d, onSelect: f, isLoading:
|
|
|
56
56
|
}), /* @__PURE__ */ s(o, { className: `size-3.5 shrink-0 text-text-secondary transition-transform ${g ? "rotate-180" : ""}` })]
|
|
57
57
|
})]
|
|
58
58
|
}), g && /* @__PURE__ */ s("div", {
|
|
59
|
-
className: "absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-card border border-border-
|
|
59
|
+
className: "absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3",
|
|
60
60
|
children: u.map((e) => {
|
|
61
61
|
let t = e.id === d;
|
|
62
62
|
return /* @__PURE__ */ c("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/SubscriptionSelector.tsx"],"sourcesContent":["/**\n * Subscription Selector Component\n * Compact inline dropdown for choosing subscription when purchasing addons\n * Only shows when multiple subscriptions exist - single subscription auto-selects silently\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { ChevronDown, Check, AlertCircle } from 'lucide-react';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface Subscription {\n id: string;\n status: string;\n startDate: string;\n endDate: string;\n plan?: {\n id: string;\n name: string;\n productID?: string;\n product?: {\n id: string;\n name: string;\n } | null;\n } | null;\n}\n\ninterface SubscriptionSelectorProps {\n subscriptions: Subscription[];\n selectedSubscriptionId: string | null;\n onSelect: (subscriptionId: string | null) => void;\n isLoading?: boolean;\n}\n\nexport const SubscriptionSelector: FC<SubscriptionSelectorProps> = ({\n subscriptions,\n selectedSubscriptionId,\n onSelect,\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 [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Auto-select if only one subscription (silently, no UI shown)\n useEffect(() => {\n if (subscriptions.length === 1 && !selectedSubscriptionId) {\n onSelect(subscriptions[0].id);\n }\n }, [subscriptions, selectedSubscriptionId, onSelect]);\n\n const selectedSubscription = subscriptions.find((s) => s.id === selectedSubscriptionId);\n\n // Format subscription display name: \"Product - Plan\" or just \"Plan\"\n const getDisplayName = (subscription: Subscription) => {\n const planName = subscription.plan?.name || 'Subscription';\n const productName = subscription.plan?.product?.name;\n return productName ? `${productName} - ${planName}` : planName;\n };\n\n // Loading state - compact\n if (isLoading) {\n return <div className=\"h-9 bg-bg-sunken animate-pulse rounded-button\" />;\n }\n\n // No subscriptions - compact warning\n if (subscriptions.length === 0) {\n return (\n <div\n className={`flex items-center gap-2 p-2 rounded-button ${statusTokens.warning.bg} border ${statusTokens.warning.border}`}\n >\n <AlertCircle className={`size-4 ${statusTokens.warning.icon} shrink-0`} />\n <span className={`text-sm ${statusTokens.warning.text}`}>\n No active subscription. Subscribe to a plan first.\n </span>\n </div>\n );\n }\n\n // Only one subscription - don't show anything, auto-select happens silently\n if (subscriptions.length === 1) {\n return null;\n }\n\n // Multiple subscriptions - compact dropdown\n return (\n <div className=\"relative\" ref={dropdownRef}>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary shrink-0\">\n {tr('billing.checkout.attachTo', 'Attach to:')}\n </span>\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n className={`\n flex-1 flex items-center justify-between gap-2 px-3 py-1.5 rounded-button border text-sm\n transition-colors text-left\n ${\n isOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : selectedSubscriptionId\n ? 'border-border-subtle hover:border-border-strong bg-bg-surface'\n : `${statusTokens.warning.border} ${statusTokens.warning.bg}`\n }\n `}\n >\n {selectedSubscription ? (\n <span className=\"font-medium text-text-primary truncate\">\n {getDisplayName(selectedSubscription)}\n </span>\n ) : (\n <span className={statusTokens.warning.text}>\n {tr('billing.checkout.selectSubscription', 'Select subscription')}\n </span>\n )}\n <ChevronDown\n className={`size-3.5 shrink-0 text-text-secondary transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n </button>\n </div>\n\n {/* Dropdown menu */}\n {isOpen && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-card border border-border-
|
|
1
|
+
{"version":3,"file":"SubscriptionSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/SubscriptionSelector.tsx"],"sourcesContent":["/**\n * Subscription Selector Component\n * Compact inline dropdown for choosing subscription when purchasing addons\n * Only shows when multiple subscriptions exist - single subscription auto-selects silently\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { ChevronDown, Check, AlertCircle } from 'lucide-react';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface Subscription {\n id: string;\n status: string;\n startDate: string;\n endDate: string;\n plan?: {\n id: string;\n name: string;\n productID?: string;\n product?: {\n id: string;\n name: string;\n } | null;\n } | null;\n}\n\ninterface SubscriptionSelectorProps {\n subscriptions: Subscription[];\n selectedSubscriptionId: string | null;\n onSelect: (subscriptionId: string | null) => void;\n isLoading?: boolean;\n}\n\nexport const SubscriptionSelector: FC<SubscriptionSelectorProps> = ({\n subscriptions,\n selectedSubscriptionId,\n onSelect,\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 [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Auto-select if only one subscription (silently, no UI shown)\n useEffect(() => {\n if (subscriptions.length === 1 && !selectedSubscriptionId) {\n onSelect(subscriptions[0].id);\n }\n }, [subscriptions, selectedSubscriptionId, onSelect]);\n\n const selectedSubscription = subscriptions.find((s) => s.id === selectedSubscriptionId);\n\n // Format subscription display name: \"Product - Plan\" or just \"Plan\"\n const getDisplayName = (subscription: Subscription) => {\n const planName = subscription.plan?.name || 'Subscription';\n const productName = subscription.plan?.product?.name;\n return productName ? `${productName} - ${planName}` : planName;\n };\n\n // Loading state - compact\n if (isLoading) {\n return <div className=\"h-9 bg-bg-sunken animate-pulse rounded-button\" />;\n }\n\n // No subscriptions - compact warning\n if (subscriptions.length === 0) {\n return (\n <div\n className={`flex items-center gap-2 p-2 rounded-button ${statusTokens.warning.bg} border ${statusTokens.warning.border}`}\n >\n <AlertCircle className={`size-4 ${statusTokens.warning.icon} shrink-0`} />\n <span className={`text-sm ${statusTokens.warning.text}`}>\n No active subscription. Subscribe to a plan first.\n </span>\n </div>\n );\n }\n\n // Only one subscription - don't show anything, auto-select happens silently\n if (subscriptions.length === 1) {\n return null;\n }\n\n // Multiple subscriptions - compact dropdown\n return (\n <div className=\"relative\" ref={dropdownRef}>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary shrink-0\">\n {tr('billing.checkout.attachTo', 'Attach to:')}\n </span>\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n className={`\n flex-1 flex items-center justify-between gap-2 px-3 py-1.5 rounded-button border text-sm\n transition-colors text-left\n ${\n isOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : selectedSubscriptionId\n ? 'border-border-subtle hover:border-border-strong bg-bg-surface'\n : `${statusTokens.warning.border} ${statusTokens.warning.bg}`\n }\n `}\n >\n {selectedSubscription ? (\n <span className=\"font-medium text-text-primary truncate\">\n {getDisplayName(selectedSubscription)}\n </span>\n ) : (\n <span className={statusTokens.warning.text}>\n {tr('billing.checkout.selectSubscription', 'Select subscription')}\n </span>\n )}\n <ChevronDown\n className={`size-3.5 shrink-0 text-text-secondary transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n </button>\n </div>\n\n {/* Dropdown menu */}\n {isOpen && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-48 overflow-auto rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3\">\n {subscriptions.map((subscription) => {\n const isSelected = subscription.id === selectedSubscriptionId;\n return (\n <button\n key={subscription.id}\n type=\"button\"\n onClick={() => {\n onSelect(subscription.id);\n setIsOpen(false);\n }}\n className={`\n w-full flex items-center justify-between gap-2 px-3 py-2 text-sm text-left\n transition-colors\n ${isSelected ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}\n `}\n >\n <span className=\"font-medium text-text-primary truncate\">\n {getDisplayName(subscription)}\n </span>\n {isSelected && <Check className=\"size-3.5 text-text-link shrink-0\" />}\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;AAkCA,IAAa,KAAuD,EAClE,kBACA,2BACA,aACA,eAAY,SACR;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,IAAc,EAAuB,KAAK;AAehD,CAZA,QAAgB;EACd,IAAM,KAAsB,MAAsB;AAChD,GAAI,EAAY,WAAW,CAAC,EAAY,QAAQ,SAAS,EAAM,OAAe,IAC5E,EAAU,GAAM;;AAKpB,SADA,SAAS,iBAAiB,aAAa,EAAmB,QAC7C,SAAS,oBAAoB,aAAa,EAAmB;IACzE,EAAE,CAAC,EAGN,QAAgB;AACd,EAAI,EAAc,WAAW,KAAK,CAAC,KACjC,EAAS,EAAc,GAAG,GAAG;IAE9B;EAAC;EAAe;EAAwB;EAAS,CAAC;CAErD,IAAM,IAAuB,EAAc,MAAM,MAAM,EAAE,OAAO,EAAuB,EAGjF,KAAkB,MAA+B;EACrD,IAAM,IAAW,EAAa,MAAM,QAAQ,gBACtC,IAAc,EAAa,MAAM,SAAS;AAChD,SAAO,IAAc,GAAG,EAAY,KAAK,MAAa;;AA4BxD,QAxBI,IACK,kBAAC,OAAD,EAAK,WAAU,iDAAkD,CAAA,GAItE,EAAc,WAAW,IAEzB,kBAAC,OAAD;EACE,WAAW,8CAA8C,EAAa,QAAQ,GAAG,UAAU,EAAa,QAAQ;YADlH,CAGE,kBAAC,GAAD,EAAa,WAAW,UAAU,EAAa,QAAQ,KAAK,YAAc,CAAA,EAC1E,kBAAC,QAAD;GAAM,WAAW,WAAW,EAAa,QAAQ;aAAQ;GAElD,CAAA,CACH;MAKN,EAAc,WAAW,IACpB,OAKP,kBAAC,OAAD;EAAK,WAAU;EAAW,KAAK;YAA/B,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cACb,EAAG,6BAA6B,aAAa;IACzC,CAAA,EACP,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAU,CAAC,EAAO;IACjC,WAAW;;;cAIP,IACI,+DACA,IACE,kEACA,GAAG,EAAa,QAAQ,OAAO,GAAG,EAAa,QAAQ,KAC9D;;cAZL,CAeG,IACC,kBAAC,QAAD;KAAM,WAAU;eACb,EAAe,EAAqB;KAChC,CAAA,GAEP,kBAAC,QAAD;KAAM,WAAW,EAAa,QAAQ;eACnC,EAAG,uCAAuC,sBAAsB;KAC5D,CAAA,EAET,kBAAC,GAAD,EACE,WAAW,8DACT,IAAS,eAAe,MAE1B,CAAA,CACK;MACL;MAGL,KACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAc,KAAK,MAAiB;IACnC,IAAM,IAAa,EAAa,OAAO;AACvC,WACE,kBAAC,UAAD;KAEE,MAAK;KACL,eAAe;AAEb,MADA,EAAS,EAAa,GAAG,EACzB,EAAU,GAAM;;KAElB,WAAW;;;oBAGP,IAAa,kCAAkC,qBAAqB;;eAV1E,CAaE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAe,EAAa;MACxB,CAAA,EACN,KAAc,kBAAC,GAAD,EAAO,WAAU,oCAAqC,CAAA,CAC9D;OAhBF,EAAa,GAgBX;KAEX;GACE,CAAA,CAEJ"}
|
|
@@ -32,7 +32,7 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
|
|
|
32
32
|
return b ? /* @__PURE__ */ l("div", {
|
|
33
33
|
className: "fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4",
|
|
34
34
|
children: /* @__PURE__ */ u("div", {
|
|
35
|
-
className: "bg-bg-surface border border-border-
|
|
35
|
+
className: "bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] max-w-sm w-full p-6 text-center space-y-4",
|
|
36
36
|
children: [
|
|
37
37
|
/* @__PURE__ */ l("div", {
|
|
38
38
|
className: `inline-flex p-3 rounded-full ${e.success.bg} ${e.success.text}`,
|
|
@@ -62,7 +62,7 @@ var p = ({ billingAccountId: d, subscriptionId: f, quotas: p, workspaces: m, cur
|
|
|
62
62
|
}) : /* @__PURE__ */ l("div", {
|
|
63
63
|
className: "fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4",
|
|
64
64
|
children: /* @__PURE__ */ u("div", {
|
|
65
|
-
className: "bg-bg-surface border border-border-
|
|
65
|
+
className: "bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] max-w-md w-full",
|
|
66
66
|
children: [
|
|
67
67
|
/* @__PURE__ */ u("div", {
|
|
68
68
|
className: "flex items-center justify-between px-6 py-4 border-b border-border-subtle",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspaceQuotaAssignmentModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.tsx"],"sourcesContent":["/**\n * WorkspaceQuotaAssignmentModal\n *\n * Shown after a successful plan / add-on purchase to let the user assign\n * workspace-level quotas (agents, sandboxes, sessions, runtime, etc.) to a\n * specific workspace.\n *\n * Spec rules:\n * - Free plan or single workspace → auto-assign; no modal shown.\n * - Multiple workspaces → require selection before activating the quota.\n *\n * This component handles both cases: the parent calls autoAssignOrShow() and\n * will either receive an immediate assignment result (auto-assign path) or\n * render this modal for user selection.\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { CheckCircle, Layers, AlertTriangle, Loader2, X } from 'lucide-react';\nimport { useActivateSubscriptionForWorkspace } from '../../usage/hooks/useUsage';\nimport { statusTokens } from '../../../shared/utils/tokens';\n\nexport interface WorkspaceOption {\n id: string;\n name: string;\n}\n\nexport interface WorkspaceQuotaToAssign {\n quotaName: string;\n productId: string;\n limit: number | null;\n noLimit: boolean;\n resetPeriod: string;\n quotaMode: 'dedicated' | 'pooled';\n}\n\ninterface WorkspaceQuotaAssignmentModalProps {\n billingAccountId: string;\n subscriptionId: string;\n quotas: WorkspaceQuotaToAssign[];\n workspaces: WorkspaceOption[];\n /**\n * Current workspace — used as the pre-selected value.\n * Pass the workspace from the app shell token context.\n */\n currentWorkspaceId: string;\n onAssigned: (workspaceId: string) => void;\n onSkip: () => void;\n}\n\n/**\n * Derives which quotas in a plan are workspace-scoped (not global).\n * Global quotas: org_count, workspace_count, project_count.\n * All others are workspace-scoped.\n */\nconst GLOBAL_QUOTA_NAMES = new Set(['org_count', 'workspace_count', 'project_count']);\n\nexport function isWorkspaceScopedQuota(quotaName: string): boolean {\n return !GLOBAL_QUOTA_NAMES.has(quotaName);\n}\n\nexport const WorkspaceQuotaAssignmentModal: FC<WorkspaceQuotaAssignmentModalProps> = ({\n billingAccountId,\n subscriptionId,\n quotas,\n workspaces,\n currentWorkspaceId,\n onAssigned,\n onSkip,\n}) => {\n const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(currentWorkspaceId);\n const [isDone, setIsDone] = useState(false);\n const [assignError, setAssignError] = useState<string | null>(null);\n\n const { activate, isLoading } = useActivateSubscriptionForWorkspace();\n\n const handleAssign = useCallback(async () => {\n if (!selectedWorkspaceId) return;\n setAssignError(null);\n\n try {\n await activate(subscriptionId, billingAccountId, selectedWorkspaceId);\n setIsDone(true);\n onAssigned(selectedWorkspaceId);\n } catch (err) {\n setAssignError(err instanceof Error ? err.message : 'Failed to assign quota to workspace.');\n }\n }, [selectedWorkspaceId, billingAccountId, subscriptionId, activate, onAssigned]);\n\n if (isDone) {\n return (\n <div className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-w-sm w-full p-6 text-center space-y-4\">\n <div\n className={`inline-flex p-3 rounded-full ${statusTokens.success.bg} ${statusTokens.success.text}`}\n >\n <CheckCircle className=\"size-8\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Quota assigned!</h2>\n <p className=\"text-sm text-text-secondary\">\n {quotas.length} quota{quotas.length !== 1 ? 's have' : ' has'} been assigned to{' '}\n <span className=\"font-medium text-text-primary\">\n {workspaces.find((w) => w.id === selectedWorkspaceId)?.name ?? selectedWorkspaceId}\n </span>\n .\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-w-md w-full\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-subtle\">\n <div className=\"flex items-center gap-2\">\n <Layers className=\"size-5 text-text-link\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Assign to a Workspace</h2>\n </div>\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n aria-label=\"Skip\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-5 space-y-5\">\n <p className=\"text-sm text-text-secondary\">\n Select which workspace should receive the quota{quotas.length > 1 ? 's' : ''} included\n in your purchase.\n </p>\n\n {/* Quotas list */}\n <div className=\"bg-bg-sunken rounded-lg p-3 space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Included quotas\n </p>\n {quotas.map((q) => (\n <div key={q.quotaName} className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-primary\">{q.quotaName}</span>\n <span className=\"text-text-secondary\">\n {q.noLimit ? 'Unlimited' : q.limit != null ? `${q.limit} max` : '—'}\n </span>\n </div>\n ))}\n </div>\n\n {/* Workspace selector */}\n {workspaces.length > 1 ? (\n <div className=\"space-y-1.5\">\n <label htmlFor=\"ws-selector\" className=\"block text-sm font-medium text-text-primary\">\n Workspace\n </label>\n <select\n id=\"ws-selector\"\n value={selectedWorkspaceId}\n onChange={(e) => setSelectedWorkspaceId(e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {workspaces.map((ws) => (\n <option key={ws.id} value={ws.id}>\n {ws.name}\n </option>\n ))}\n </select>\n </div>\n ) : (\n <div className=\"bg-bg-sunken rounded-lg p-3 text-sm text-text-secondary\">\n Will be assigned to{' '}\n <span className=\"font-medium text-text-primary\">\n {workspaces[0]?.name ?? currentWorkspaceId}\n </span>\n </div>\n )}\n\n {/* Error */}\n {assignError && (\n <div\n className={`flex items-start gap-2 text-sm ${statusTokens.error.text} ${statusTokens.error.bg} rounded-lg p-3`}\n >\n <AlertTriangle className=\"size-4 shrink-0 mt-0.5\" />\n <span>{assignError}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"px-4 py-2 text-sm border border-border-subtle rounded-button hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n Skip for now\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAssign()}\n disabled={!selectedWorkspaceId || isLoading}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isLoading && <Loader2 className=\"size-3.5 animate-spin\" />}\n Assign quota\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WorkspaceQuotaAssignmentModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.tsx"],"sourcesContent":["/**\n * WorkspaceQuotaAssignmentModal\n *\n * Shown after a successful plan / add-on purchase to let the user assign\n * workspace-level quotas (agents, sandboxes, sessions, runtime, etc.) to a\n * specific workspace.\n *\n * Spec rules:\n * - Free plan or single workspace → auto-assign; no modal shown.\n * - Multiple workspaces → require selection before activating the quota.\n *\n * This component handles both cases: the parent calls autoAssignOrShow() and\n * will either receive an immediate assignment result (auto-assign path) or\n * render this modal for user selection.\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { CheckCircle, Layers, AlertTriangle, Loader2, X } from 'lucide-react';\nimport { useActivateSubscriptionForWorkspace } from '../../usage/hooks/useUsage';\nimport { statusTokens } from '../../../shared/utils/tokens';\n\nexport interface WorkspaceOption {\n id: string;\n name: string;\n}\n\nexport interface WorkspaceQuotaToAssign {\n quotaName: string;\n productId: string;\n limit: number | null;\n noLimit: boolean;\n resetPeriod: string;\n quotaMode: 'dedicated' | 'pooled';\n}\n\ninterface WorkspaceQuotaAssignmentModalProps {\n billingAccountId: string;\n subscriptionId: string;\n quotas: WorkspaceQuotaToAssign[];\n workspaces: WorkspaceOption[];\n /**\n * Current workspace — used as the pre-selected value.\n * Pass the workspace from the app shell token context.\n */\n currentWorkspaceId: string;\n onAssigned: (workspaceId: string) => void;\n onSkip: () => void;\n}\n\n/**\n * Derives which quotas in a plan are workspace-scoped (not global).\n * Global quotas: org_count, workspace_count, project_count.\n * All others are workspace-scoped.\n */\nconst GLOBAL_QUOTA_NAMES = new Set(['org_count', 'workspace_count', 'project_count']);\n\nexport function isWorkspaceScopedQuota(quotaName: string): boolean {\n return !GLOBAL_QUOTA_NAMES.has(quotaName);\n}\n\nexport const WorkspaceQuotaAssignmentModal: FC<WorkspaceQuotaAssignmentModalProps> = ({\n billingAccountId,\n subscriptionId,\n quotas,\n workspaces,\n currentWorkspaceId,\n onAssigned,\n onSkip,\n}) => {\n const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(currentWorkspaceId);\n const [isDone, setIsDone] = useState(false);\n const [assignError, setAssignError] = useState<string | null>(null);\n\n const { activate, isLoading } = useActivateSubscriptionForWorkspace();\n\n const handleAssign = useCallback(async () => {\n if (!selectedWorkspaceId) return;\n setAssignError(null);\n\n try {\n await activate(subscriptionId, billingAccountId, selectedWorkspaceId);\n setIsDone(true);\n onAssigned(selectedWorkspaceId);\n } catch (err) {\n setAssignError(err instanceof Error ? err.message : 'Failed to assign quota to workspace.');\n }\n }, [selectedWorkspaceId, billingAccountId, subscriptionId, activate, onAssigned]);\n\n if (isDone) {\n return (\n <div className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] max-w-sm w-full p-6 text-center space-y-4\">\n <div\n className={`inline-flex p-3 rounded-full ${statusTokens.success.bg} ${statusTokens.success.text}`}\n >\n <CheckCircle className=\"size-8\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Quota assigned!</h2>\n <p className=\"text-sm text-text-secondary\">\n {quotas.length} quota{quotas.length !== 1 ? 's have' : ' has'} been assigned to{' '}\n <span className=\"font-medium text-text-primary\">\n {workspaces.find((w) => w.id === selectedWorkspaceId)?.name ?? selectedWorkspaceId}\n </span>\n .\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] max-w-md w-full\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-subtle\">\n <div className=\"flex items-center gap-2\">\n <Layers className=\"size-5 text-text-link\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Assign to a Workspace</h2>\n </div>\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n aria-label=\"Skip\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-5 space-y-5\">\n <p className=\"text-sm text-text-secondary\">\n Select which workspace should receive the quota{quotas.length > 1 ? 's' : ''} included\n in your purchase.\n </p>\n\n {/* Quotas list */}\n <div className=\"bg-bg-sunken rounded-lg p-3 space-y-2\">\n <p className=\"text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Included quotas\n </p>\n {quotas.map((q) => (\n <div key={q.quotaName} className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-primary\">{q.quotaName}</span>\n <span className=\"text-text-secondary\">\n {q.noLimit ? 'Unlimited' : q.limit != null ? `${q.limit} max` : '—'}\n </span>\n </div>\n ))}\n </div>\n\n {/* Workspace selector */}\n {workspaces.length > 1 ? (\n <div className=\"space-y-1.5\">\n <label htmlFor=\"ws-selector\" className=\"block text-sm font-medium text-text-primary\">\n Workspace\n </label>\n <select\n id=\"ws-selector\"\n value={selectedWorkspaceId}\n onChange={(e) => setSelectedWorkspaceId(e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {workspaces.map((ws) => (\n <option key={ws.id} value={ws.id}>\n {ws.name}\n </option>\n ))}\n </select>\n </div>\n ) : (\n <div className=\"bg-bg-sunken rounded-lg p-3 text-sm text-text-secondary\">\n Will be assigned to{' '}\n <span className=\"font-medium text-text-primary\">\n {workspaces[0]?.name ?? currentWorkspaceId}\n </span>\n </div>\n )}\n\n {/* Error */}\n {assignError && (\n <div\n className={`flex items-start gap-2 text-sm ${statusTokens.error.text} ${statusTokens.error.bg} rounded-lg p-3`}\n >\n <AlertTriangle className=\"size-4 shrink-0 mt-0.5\" />\n <span>{assignError}</span>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"px-4 py-2 text-sm border border-border-subtle rounded-button hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n Skip for now\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAssign()}\n disabled={!selectedWorkspaceId || isLoading}\n className=\"flex items-center gap-2 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isLoading && <Loader2 className=\"size-3.5 animate-spin\" />}\n Assign quota\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|