@burdenoff/microfe-billing 2026.624.1 → 2026.624.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/modules/affiliate/pages/AffiliateDashboardPage.js +125 -150
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +146 -153
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +164 -179
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +229 -291
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +69 -74
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +142 -148
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +125 -117
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +78 -66
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +68 -83
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/shared/ui/MetricCard.js +1 -1
- package/dist/billing/shared/ui/MetricCard.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsPage.js","names":[],"sources":["../../../../../src/billing/modules/settings/pages/SettingsPage.tsx"],"sourcesContent":["/**\n * Settings Module - Settings Page\n * Billing account settings and configuration\n */\n\nimport { useState, useMemo, useEffect, useCallback, type FC } from 'react';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { OptionalFields } from '@burdenoff/fe-libs/ui';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport {\n useGetBillingAccountSettingsQuery,\n useGetBillingAccountsSettingsQuery,\n useGetDefaultBillingAccountSettingsQuery,\n useUpdateBillingAccountSettingsMutation,\n useSetDefaultBillingAccountSettingsMutation,\n ArchiveBillingAccountSettingsDocument,\n} from '../../../../generated/global-operations';\nimport type { BillingAccount } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// Helper to get billing account ID from URL (for navigation from Overview page)\nconst getInitialBillingAccountId = (): string | undefined => {\n if (typeof window === 'undefined') return undefined;\n const urlParams = new URLSearchParams(window.location.search);\n return urlParams.get('billingAccountId') || undefined;\n};\n\nexport const SettingsPage: 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 permissions = useBillingPermissions();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const { emit } = useBillingEventEmitter();\n\n // State - initialize with URL param if present\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string | undefined>(\n getInitialBillingAccountId\n );\n const [isAccountSelectorOpen, setIsAccountSelectorOpen] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [isArchiving, setIsArchiving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saveSuccess, setSaveSuccess] = useState(false);\n\n // Set default confirmation modal state\n const [isSetDefaultModalOpen, setIsSetDefaultModalOpen] = useState(false);\n const [pendingSave, setPendingSave] = useState(false);\n\n // Form state - includes isDefault toggle\n const [formData, setFormData] = useState({\n name: '',\n email: '',\n billingEmail: '',\n currency: 'USD',\n taxId: '',\n isDefault: false,\n });\n\n // Fetch all billing accounts for the selector (lightweight query without subscriptions)\n const {\n data: accountsData,\n loading: isLoadingAccounts,\n refetch: refetchAccounts,\n } = useGetBillingAccountsSettingsQuery({});\n const billingAccounts = useMemo<BillingAccount[]>(\n () => accountsData?.getBillingAccountsByOrg || [],\n [accountsData?.getBillingAccountsByOrg]\n );\n\n // Fetch default billing account (without subscriptions)\n const {\n data: defaultData,\n loading: isLoadingDefault,\n refetch: refetchDefault,\n } = useGetDefaultBillingAccountSettingsQuery({});\n const defaultBillingAccount: BillingAccount | null =\n defaultData?.getDefaultBillingAccount || null;\n\n // Always fetch selected billing account if we have an ID from URL (without subscriptions)\n const {\n data: selectedData,\n loading: isLoadingSelected,\n refetch: refetchSelected,\n } = useGetBillingAccountSettingsQuery({\n variables: { id: selectedBillingAccountId! },\n skip: !selectedBillingAccountId,\n });\n const selectedAccountData: BillingAccount | null = selectedData?.getBillingAccount || null;\n\n // Mutation for updating billing account\n const [updateBillingAccountMutation] = useUpdateBillingAccountSettingsMutation();\n\n // Mutation for setting default billing account\n const [setDefaultBillingAccountMutation] = useSetDefaultBillingAccountSettingsMutation();\n\n // Determine which account to use - prioritize URL param\n const billingAccount = useMemo(() => {\n // If we have a selected account ID from URL\n if (selectedBillingAccountId) {\n // First try to use the fetched selected account data\n if (selectedAccountData) {\n return selectedAccountData;\n }\n // While loading, try to find from the accounts list\n const fromList = billingAccounts.find((a) => a.id === selectedBillingAccountId);\n if (fromList) {\n return fromList;\n }\n // If still loading and not in list, return null to show loading state\n if (isLoadingSelected) {\n return null;\n }\n }\n // No URL param - use default account\n return defaultBillingAccount;\n }, [\n selectedBillingAccountId,\n selectedAccountData,\n billingAccounts,\n isLoadingSelected,\n defaultBillingAccount,\n ]);\n\n const isLoading =\n isLoadingAccounts || isLoadingDefault || (selectedBillingAccountId && isLoadingSelected);\n\n // Update form when billing account changes\n useEffect(() => {\n if (billingAccount) {\n setFormData({\n name: billingAccount.name || '',\n email: billingAccount.email || '',\n billingEmail: billingAccount.billingEmail || '',\n currency: billingAccount.currency || 'USD',\n taxId: billingAccount.taxId || '',\n isDefault: billingAccount.isDefault || false,\n });\n }\n }, [billingAccount]);\n\n // Handle account selection\n const handleSelectAccount = (account: BillingAccount) => {\n setSelectedBillingAccountId(account.id);\n setIsAccountSelectorOpen(false);\n setSaveSuccess(false);\n setSaveError(null);\n };\n\n // Handle form input changes\n const handleInputChange = (field: keyof typeof formData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n setSaveSuccess(false);\n setSaveError(null);\n };\n\n // Find the current default account\n const currentDefaultAccount = useMemo(() => {\n return billingAccounts.find((a) => a.isDefault) || null;\n }, [billingAccounts]);\n\n // Check if form has changes\n const hasChanges = useMemo(() => {\n if (!billingAccount) return false;\n return (\n formData.name !== (billingAccount.name || '') ||\n formData.email !== (billingAccount.email || '') ||\n formData.billingEmail !== (billingAccount.billingEmail || '') ||\n formData.currency !== (billingAccount.currency || 'USD') ||\n formData.taxId !== (billingAccount.taxId || '') ||\n formData.isDefault !== (billingAccount.isDefault || false)\n );\n }, [formData, billingAccount]);\n\n // Check if trying to change default status to another account\n const isChangingDefault = formData.isDefault && !billingAccount?.isDefault;\n const needsDefaultConfirmation =\n isChangingDefault && currentDefaultAccount && currentDefaultAccount.id !== billingAccount?.id;\n\n // Perform the actual save operation\n const performSave = useCallback(async () => {\n if (!billingAccount?.id) return;\n\n setIsSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n setIsSetDefaultModalOpen(false);\n setPendingSave(false);\n\n try {\n // Update billing account details\n await updateBillingAccountMutation({\n variables: {\n input: {\n id: billingAccount.id,\n name: formData.name,\n email: formData.email,\n billingEmail: formData.billingEmail || null,\n currency: formData.currency,\n taxId: formData.taxId || null,\n },\n },\n });\n emit('billing.billing_account.updated', {\n route: '/billing/settings',\n entityId: billingAccount.id,\n source: 'settings',\n });\n\n // If setting as default, call the separate mutation\n if (isChangingDefault) {\n await setDefaultBillingAccountMutation({\n variables: { id: billingAccount.id },\n refetchQueries: ['GetBillingAccountsByTenant', 'GetDefaultBillingAccount'],\n });\n emit('billing.billing_account.updated', {\n route: '/billing/settings',\n entityId: billingAccount.id,\n source: 'settings.default',\n });\n }\n\n // Refetch to update cache\n if (selectedBillingAccountId) {\n await refetchSelected();\n } else {\n await refetchDefault();\n }\n\n setSaveSuccess(true);\n // Clear success message after 3 seconds\n setTimeout(() => setSaveSuccess(false), 3000);\n } catch (error) {\n setSaveError(error instanceof Error ? error.message : 'Failed to save changes');\n } finally {\n setIsSaving(false);\n }\n }, [\n billingAccount?.id,\n formData,\n isChangingDefault,\n updateBillingAccountMutation,\n setDefaultBillingAccountMutation,\n selectedBillingAccountId,\n refetchSelected,\n refetchDefault,\n emit,\n ]);\n\n // Handle save button click\n const handleSave = useCallback(() => {\n if (!billingAccount?.id) return;\n\n // If changing default and there's an existing default, show confirmation\n if (needsDefaultConfirmation) {\n setPendingSave(true);\n setIsSetDefaultModalOpen(true);\n } else {\n performSave();\n }\n }, [billingAccount?.id, needsDefaultConfirmation, performSave]);\n\n const handleArchiveAccount = useCallback(async () => {\n if (!billingAccount?.id || !billingAccount.isActive) {\n return;\n }\n\n const replacementDefaultAccount = billingAccounts.find(\n (account) => account.id !== billingAccount.id && account.isActive\n );\n\n const confirmed = window.confirm(\n billingAccount.isDefault\n ? `Archive ${billingAccount.name}? ${replacementDefaultAccount ? `${replacementDefaultAccount.name} will become the default account.` : 'No replacement default account is available.'}`\n : `Archive ${billingAccount.name}?`\n );\n\n if (!confirmed) {\n return;\n }\n\n setIsArchiving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n await directBillingGraphqlRequest<\n { archiveBillingAccount: { id: string; isActive: boolean; isDefault: boolean } },\n { id: string; reason: string; replacementDefaultBillingAccountId: string | null }\n >({\n apiGatewayUrl,\n authToken,\n orgId,\n query: ArchiveBillingAccountSettingsDocument,\n variables: {\n id: billingAccount.id,\n reason: 'Archived from billing settings',\n replacementDefaultBillingAccountId: billingAccount.isDefault\n ? (replacementDefaultAccount?.id ?? null)\n : null,\n },\n });\n emit('billing.billing_account.archived', {\n route: '/billing/settings',\n entityId: billingAccount.id,\n source: 'settings',\n });\n\n await Promise.all([refetchAccounts(), refetchDefault()]);\n setSelectedBillingAccountId(replacementDefaultAccount?.id);\n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 3000);\n } catch (error) {\n setSaveError(error instanceof Error ? error.message : 'Failed to archive billing account');\n } finally {\n setIsArchiving(false);\n }\n }, [\n apiGatewayUrl,\n authToken,\n billingAccount,\n billingAccounts,\n orgId,\n refetchAccounts,\n refetchDefault,\n emit,\n ]);\n\n if (!permissions.canManageBillingAccount) {\n return <AccessDenied message=\"You don't have permission to manage billing settings.\" />;\n }\n\n if (isLoading && !billingAccount) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-10 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.settings.billingSettings', 'Billing Settings')}\n description={tr(\n 'billing.settings.description',\n 'Manage your billing account configuration'\n )}\n />\n\n {/* Account Selector (if multiple accounts) */}\n {billingAccounts.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsAccountSelectorOpen(!isAccountSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-subtle rounded-lg bg-bg-surface hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-lg bg-action-primary-bg/10 flex items-center justify-center\">\n <svg\n className=\"size-4 text-primary\"\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=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {billingAccount?.name || tr('billing.settings.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-text-secondary\">{billingAccount?.email}</p>\n </div>\n </div>\n <svg\n className={`size-4 text-text-secondary transition-transform ${isAccountSelectorOpen ? 'rotate-180' : ''}`}\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=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </button>\n\n {isAccountSelectorOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsAccountSelectorOpen(false)} />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border-subtle rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto\">\n {billingAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => handleSelectAccount(account)}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-8 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <svg\n className=\"size-4 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{account.email}</p>\n </div>\n </div>\n {billingAccount?.id === account.id && (\n <svg\n className=\"size-4 text-primary flex-shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Account Settings */}\n <div className=\"border border-border-subtle rounded-lg bg-bg-surface\">\n <div className=\"p-6 border-b border-border-subtle\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.settings.accountInfo', 'Account Information')}\n </h2>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.settings.accountInfoDescription',\n 'Update your billing contact details and tax information'\n )}\n </p>\n </div>\n {billingAccount?.isDefault && (\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium bg-action-primary-bg/10 text-primary rounded-full\">\n <svg className=\"size-3\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {tr('billing.settings.defaultAccount', 'Default Account')}\n </span>\n )}\n </div>\n </div>\n <div className=\"p-6 space-y-6\">\n {/* Account Status Summary */}\n {billingAccount && (\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4 text-sm pb-4 border-b border-border-subtle\">\n <div>\n <p className=\"text-text-secondary\">{tr('billing.settings.status', 'Status')}</p>\n <p\n className={`font-medium ${billingAccount.isActive ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {billingAccount.isActive\n ? tr('billing.settings.active', 'Active')\n : tr('billing.settings.inactive', 'Inactive')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('billing.settings.creditBalance', 'Credit Balance')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {(billingAccount.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('billing.settings.accountId', 'Account ID')}\n </p>\n <p className=\"font-mono text-xs text-text-primary truncate\" title={billingAccount.id}>\n {billingAccount.id}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('billing.settings.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {billingAccount.createdAt\n ? new Date(billingAccount.createdAt).toLocaleDateString()\n : tr('billing.settings.notAvailable', 'N/A')}\n </p>\n </div>\n </div>\n )}\n\n {/* Contact Details Section — Required fields above-the-fold */}\n <div>\n <h3 className=\"text-sm font-medium text-text-primary mb-4\">\n {tr('billing.settings.contactDetails', 'Contact Details')}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"billing-account-name\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.accountName', 'Account Name')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"billing-account-name\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleInputChange('name', e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n <div>\n <label\n htmlFor=\"billing-account-email\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.email', 'Email')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"billing-account-email\"\n type=\"email\"\n value={formData.email}\n onChange={(e) => handleInputChange('email', e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n </div>\n </div>\n\n {/* Advanced billing settings — currency / billing email / tax ID. Most accounts keep defaults. */}\n <OptionalFields\n title={tr('billing.settings.advancedTitle', 'Advanced settings')}\n description={tr(\n 'billing.settings.advancedDescription',\n 'Billing email override, currency, and tax identification.'\n )}\n >\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"billing-account-billing-email\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.billingEmail', 'Billing Email')}\n </label>\n <input\n id=\"billing-account-billing-email\"\n type=\"email\"\n value={formData.billingEmail}\n onChange={(e) => handleInputChange('billingEmail', e.target.value)}\n placeholder={tr(\n 'billing.settings.billingEmailPlaceholder',\n 'Optional - defaults to account email'\n )}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'billing.settings.billingEmailHelp',\n 'Invoices will be sent to this email if provided'\n )}\n </p>\n </div>\n <div>\n <label\n htmlFor=\"billing-account-currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.currency', 'Currency')}\n </label>\n <select\n id=\"billing-account-currency\"\n value={formData.currency}\n onChange={(e) => handleInputChange('currency', e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n >\n <option value=\"USD\">USD - US Dollar</option>\n <option value=\"EUR\">EUR - Euro</option>\n <option value=\"GBP\">GBP - British Pound</option>\n <option value=\"INR\">INR - Indian Rupee</option>\n </select>\n </div>\n <div className=\"sm:col-span-2 max-w-md\">\n <label\n htmlFor=\"billing-account-tax-id\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.taxId', 'Tax ID / VAT Number')}\n </label>\n <input\n id=\"billing-account-tax-id\"\n type=\"text\"\n value={formData.taxId}\n onChange={(e) => handleInputChange('taxId', e.target.value)}\n placeholder={tr(\n 'billing.settings.taxIdPlaceholder',\n 'Enter your tax identification number'\n )}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'billing.settings.taxIdHelp',\n 'Used for invoices and tax compliance (e.g., GST, VAT number)'\n )}\n </p>\n </div>\n </div>\n </OptionalFields>\n\n {/* Default Account Setting */}\n {billingAccount?.isActive && billingAccounts.length > 1 && (\n <div className=\"pt-4 border-t border-border-subtle\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex items-center h-6\">\n <input\n type=\"checkbox\"\n id=\"isDefault\"\n checked={formData.isDefault}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, isDefault: e.target.checked }))\n }\n disabled={billingAccount?.isDefault}\n className=\"size-4 rounded border-border-subtle text-primary focus:ring-primary focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n </div>\n <div className=\"flex-1\">\n <label\n htmlFor=\"isDefault\"\n className=\"text-sm font-medium text-text-primary cursor-pointer\"\n >\n {tr('billing.settings.setDefault', 'Set as default billing account')}\n </label>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {billingAccount?.isDefault\n ? tr(\n 'billing.settings.currentDefaultDescription',\n 'This is currently your default billing account'\n )\n : tr(\n 'billing.settings.makePrimaryDescription',\n 'Make this your primary billing account for new subscriptions'\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Save Button and Status */}\n <div className=\"pt-4 border-t border-border-subtle flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {saveSuccess && (\n <div className=\"flex items-center gap-2 text-sm text-status-success-text\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n {tr('billing.settings.saveSuccess', 'Changes saved successfully')}\n </div>\n )}\n {saveError && (\n <div className=\"flex items-center gap-2 text-sm text-status-error-text\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\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 {saveError}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-3\">\n {permissions.canArchiveBillingAccount && billingAccount?.isActive ? (\n <button\n type=\"button\"\n onClick={handleArchiveAccount}\n disabled={isSaving || isArchiving}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-status-error-text rounded-md hover:bg-status-error-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isArchiving ? 'Archiving...' : 'Archive Account'}\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={\n !hasChanges || isSaving || isArchiving || !formData.name || !formData.email\n }\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center gap-2\"\n >\n {isSaving && (\n <svg className=\"size-4 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n )}\n {isSaving ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Set as Default Confirmation Modal */}\n {isSetDefaultModalOpen && pendingSave && (\n <>\n <div\n className=\"fixed inset-0 bg-overlay-scrim z-40\"\n onClick={() => {\n if (!isSaving) {\n setIsSetDefaultModalOpen(false);\n setPendingSave(false);\n // Reset the checkbox if cancelled\n setFormData((prev) => ({ ...prev, isDefault: billingAccount?.isDefault || false }));\n }\n }}\n />\n <div className=\"fixed inset-0 flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg shadow-xl max-w-md w-full p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-2\">\n Confirm Default Account Change\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n You are about to set{' '}\n <strong className=\"text-text-primary\">{billingAccount?.name}</strong> as your default\n billing account and save your changes.\n </p>\n\n {currentDefaultAccount && currentDefaultAccount.id !== billingAccount?.id && (\n <div className=\"mb-4 p-3 bg-status-warning-bg-subtle border border-border-subtle rounded-md\">\n <div className=\"flex items-start gap-2\">\n <svg\n className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\"\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 className=\"text-sm\">\n <p className=\"font-medium text-status-warning-text\">\n Current default will change\n </p>\n <p className=\"text-status-warning-text mt-0.5\">\n <strong>{currentDefaultAccount.name}</strong> will no longer be the default\n account.\n </p>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setIsSetDefaultModalOpen(false);\n setPendingSave(false);\n // Reset the checkbox if cancelled\n setFormData((prev) => ({\n ...prev,\n isDefault: billingAccount?.isDefault || false,\n }));\n }}\n disabled={isSaving}\n className=\"px-4 py-2 text-sm font-medium text-text-primary border border-border-subtle rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={performSave}\n disabled={isSaving}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 disabled:opacity-50 transition-colors flex items-center gap-2\"\n >\n {isSaving && (\n <svg className=\"size-4 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n )}\n {isSaving ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </div>\n </div>\n </>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAwBA,IAAM,UAAuD;AACvD,cAAO,SAAW,KAEtB,QADkB,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,mBAAmB,IAAI,KAAA;GAGjC,UAAyB;CACpC,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,IAAc,IAAuB,EACrC,EAAE,kBAAe,cAAW,aAAU,GAAY,EAClD,EAAE,YAAS,IAAwB,EAGnC,CAAC,GAA0B,KAA+B,EAC9D,EACD,EACK,CAAC,GAAuB,KAA4B,EAAS,GAAM,EACnE,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,IAAa,KAAkB,EAAS,GAAM,EAG/C,CAAC,IAAuB,KAA4B,EAAS,GAAM,EACnE,CAAC,IAAa,KAAkB,EAAS,GAAM,EAG/C,CAAC,GAAU,KAAe,EAAS;EACvC,MAAM;EACN,OAAO;EACP,cAAc;EACd,UAAU;EACV,OAAO;EACP,WAAW;EACZ,CAAC,EAGI,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,GAAmC,EAAE,CAAC,EACpC,IAAkB,QAChB,GAAc,2BAA2B,EAAE,EACjD,CAAC,GAAc,wBAAwB,CACxC,EAGK,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,GAAyC,EAAE,CAAC,EAC1C,IACJ,GAAa,4BAA4B,MAGrC,EACJ,MAAM,IACN,SAAS,GACT,SAAS,MACP,GAAkC;EACpC,WAAW,EAAE,IAAI,GAA2B;EAC5C,MAAM,CAAC;EACR,CAAC,EACI,IAA6C,IAAc,qBAAqB,MAGhF,CAAC,KAAgC,IAAyC,EAG1E,CAAC,KAAoC,IAA6C,EAGlF,IAAiB,QAAc;AAEnC,MAAI,GAA0B;AAE5B,OAAI,EACF,QAAO;GAGT,IAAM,IAAW,EAAgB,MAAM,MAAM,EAAE,OAAO,EAAyB;AAC/E,OAAI,EACF,QAAO;AAGT,OAAI,EACF,QAAO;;AAIX,SAAO;IACN;EACD;EACA;EACA;EACA;EACA;EACD,CAAC,EAEI,KACJ,KAAqB,KAAqB,KAA4B;AAGxE,UAAgB;AACd,EAAI,KACF,EAAY;GACV,MAAM,EAAe,QAAQ;GAC7B,OAAO,EAAe,SAAS;GAC/B,cAAc,EAAe,gBAAgB;GAC7C,UAAU,EAAe,YAAY;GACrC,OAAO,EAAe,SAAS;GAC/B,WAAW,EAAe,aAAa;GACxC,CAAC;IAEH,CAAC,EAAe,CAAC;CAGpB,IAAM,MAAuB,MAA4B;AAIvD,EAHA,EAA4B,EAAQ,GAAG,EACvC,EAAyB,GAAM,EAC/B,EAAe,GAAM,EACrB,EAAa,KAAK;IAId,KAAqB,GAA8B,MAAkB;AAGzE,EAFA,GAAa,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAO,EAAE,EACpD,EAAe,GAAM,EACrB,EAAa,KAAK;IAId,IAAwB,QACrB,EAAgB,MAAM,MAAM,EAAE,UAAU,IAAI,MAClD,CAAC,EAAgB,CAAC,EAGf,KAAa,QACZ,IAEH,EAAS,UAAU,EAAe,QAAQ,OAC1C,EAAS,WAAW,EAAe,SAAS,OAC5C,EAAS,kBAAkB,EAAe,gBAAgB,OAC1D,EAAS,cAAc,EAAe,YAAY,UAClD,EAAS,WAAW,EAAe,SAAS,OAC5C,EAAS,eAAe,EAAe,aAAa,MAP1B,IAS3B,CAAC,GAAU,EAAe,CAAC,EAGxB,IAAoB,EAAS,aAAa,CAAC,GAAgB,WAC3D,IACJ,KAAqB,KAAyB,EAAsB,OAAO,GAAgB,IAGvF,IAAc,EAAY,YAAY;AACrC,SAAgB,IAMrB;GAJA,EAAY,GAAK,EACjB,EAAa,KAAK,EAClB,EAAe,GAAM,EACrB,EAAyB,GAAM,EAC/B,EAAe,GAAM;AAErB,OAAI;AA0CF,IAxCA,MAAM,EAA6B,EACjC,WAAW,EACT,OAAO;KACL,IAAI,EAAe;KACnB,MAAM,EAAS;KACf,OAAO,EAAS;KAChB,cAAc,EAAS,gBAAgB;KACvC,UAAU,EAAS;KACnB,OAAO,EAAS,SAAS;KAC1B,EACF,EACF,CAAC,EACF,EAAK,mCAAmC;KACtC,OAAO;KACP,UAAU,EAAe;KACzB,QAAQ;KACT,CAAC,EAGE,MACF,MAAM,EAAiC;KACrC,WAAW,EAAE,IAAI,EAAe,IAAI;KACpC,gBAAgB,CAAC,8BAA8B,2BAA2B;KAC3E,CAAC,EACF,EAAK,mCAAmC;KACtC,OAAO;KACP,UAAU,EAAe;KACzB,QAAQ;KACT,CAAC,GAIA,IACF,MAAM,GAAiB,GAEvB,MAAM,GAAgB,EAGxB,EAAe,GAAK,EAEpB,iBAAiB,EAAe,GAAM,EAAE,IAAK;YACtC,GAAO;AACd,MAAa,aAAiB,QAAQ,EAAM,UAAU,yBAAyB;aACvE;AACR,MAAY,GAAM;;;IAEnB;EACD,GAAgB;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAGI,IAAa,QAAkB;AAC9B,KAAgB,OAGjB,KACF,EAAe,GAAK,EACpB,EAAyB,GAAK,IAE9B,GAAa;IAEd;EAAC,GAAgB;EAAI;EAA0B;EAAY,CAAC,EAEzD,KAAuB,EAAY,YAAY;AACnD,MAAI,CAAC,GAAgB,MAAM,CAAC,EAAe,SACzC;EAGF,IAAM,IAA4B,EAAgB,MAC/C,MAAY,EAAQ,OAAO,EAAe,MAAM,EAAQ,SAC1D;AAEiB,aAAO,QACvB,EAAe,YACX,WAAW,EAAe,KAAK,IAAI,IAA4B,GAAG,EAA0B,KAAK,qCAAqC,mDACtI,WAAW,EAAe,KAAK,GACpC,EAQD;GAFA,EAAe,GAAK,EACpB,EAAa,KAAK,EAClB,EAAe,GAAM;AAErB,OAAI;AA0BF,IAzBA,MAAM,GAGJ;KACA;KACA;KACA;KACA,OAAO;KACP,WAAW;MACT,IAAI,EAAe;MACnB,QAAQ;MACR,oCAAoC,EAAe,YAC9C,GAA2B,MAAM,OAClC;MACL;KACF,CAAC,EACF,EAAK,oCAAoC;KACvC,OAAO;KACP,UAAU,EAAe;KACzB,QAAQ;KACT,CAAC,EAEF,MAAM,QAAQ,IAAI,CAAC,GAAiB,EAAE,GAAgB,CAAC,CAAC,EACxD,EAA4B,GAA2B,GAAG,EAC1D,EAAe,GAAK,EACpB,iBAAiB,EAAe,GAAM,EAAE,IAAK;YACtC,GAAO;AACd,MAAa,aAAiB,QAAQ,EAAM,UAAU,oCAAoC;aAClF;AACR,MAAe,GAAM;;;IAEtB;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAmBF,QAjBK,EAAY,0BAIb,MAAa,CAAC,IAEd,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,EAC/D,kBAAC,OAAD;GAAK,WAAU;aACZ;IAAC;IAAG;IAAG;IAAE,CAAC,KAAK,MACd,kBAAC,OAAD,EAAa,WAAU,2CAA4C,EAAzD,EAAyD,CACnE;GACE,CAAA,CACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,oCAAoC,mBAAmB;IACjE,aAAa,EACX,gCACA,4CACD;IACD,CAAA;GAGD,EAAgB,SAAS,KACxB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAyB,CAAC,EAAsB;KAC/D,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,MAAK;QACL,SAAQ;QACR,QAAO;kBAEP,kBAAC,QAAD;SACE,eAAc;SACd,gBAAe;SACf,aAAa;SACb,GAAE;SACF,CAAA;QACE,CAAA;OACF,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,GAAgB,QAAQ,EAAG,kCAAkC,iBAAiB;QAC7E,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAA+B,GAAgB;QAAU,CAAA,CAClE;SACF;SACN,kBAAC,OAAD;MACE,WAAW,mDAAmD,IAAwB,eAAe;MACrG,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,CACC;QAER,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAqB,eAAe,EAAyB,GAAM;KAAI,CAAA,EACtF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAgB,KAAK,MACpB,kBAAC,UAAD;MACE,MAAK;MAEL,eAAe,GAAoB,EAAQ;MAC3C,WAAU;gBAJZ,CAME,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,SAAQ;SACR,QAAO;mBAEP,kBAAC,QAAD;UACE,eAAc;UACd,gBAAe;UACf,aAAa;UACb,GAAE;UACF,CAAA;SACE,CAAA;QACF,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAQ;SACP,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAwC,EAAQ;SAAU,CAAA,CACnE;UACF;UACL,GAAgB,OAAO,EAAQ,MAC9B,kBAAC,OAAD;OACE,WAAU;OACV,MAAK;OACL,SAAQ;OACR,QAAO;iBAEP,kBAAC,QAAD;QACE,eAAc;QACd,gBAAe;QACf,aAAa;QACb,GAAE;QACF,CAAA;OACE,CAAA,CAED;QA1CF,EAAQ,GA0CN,CACT;KACE,CAAA,CACL,EAAA,CAAA,CAED;;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,gCAAgC,sBAAsB;OACvD,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,2CACA,0DACD;OACC,CAAA,CACA,EAAA,CAAA,EACL,GAAgB,aACf,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,OAAD;QAAK,WAAU;QAAS,MAAK;QAAe,SAAQ;kBAClD,kBAAC,QAAD;SACE,UAAS;SACT,GAAE;SACF,UAAS;SACT,CAAA;QACE,CAAA,EACL,EAAG,mCAAmC,kBAAkB,CACpD;SAEL;;KACF,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAuB,EAAG,2BAA2B,SAAS;SAAK,CAAA,EAChF,kBAAC,KAAD;SACE,WAAW,eAAe,EAAe,WAAW,6BAA6B;mBAEhF,EAAe,WACZ,EAAG,2BAA2B,SAAS,GACvC,EAAG,6BAA6B,WAAW;SAC7C,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,kCAAkC,iBAAiB;SACrD,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb,EACI,EAAe,gBAAgB,GAAG,gBAAgB,EAAC,WACnD;WACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,8BAA8B,aAAa;SAC7C,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;SAA+C,OAAO,EAAe;mBAC/E,EAAe;SACd,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAuB,EAAG,4BAA4B,UAAU;SAAK,CAAA,EAClF,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAe,YACZ,IAAI,KAAK,EAAe,UAAU,CAAC,oBAAoB,GACvD,EAAG,iCAAiC,MAAM;SAC5C,CAAA,CACA,EAAA,CAAA;QACF;;MAIR,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,mCAAmC,kBAAkB;OACtD,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAFZ;SAIG,EAAG,gCAAgC,eAAe;SAAE;SACrD,kBAAC,QAAD;UAAM,WAAU;oBAAyB;UAAQ,CAAA;SAC3C;WACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO,EAAS;QAChB,WAAW,MAAM,EAAkB,QAAQ,EAAE,OAAO,MAAM;QAC1D,WAAU;QACV,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAFZ;SAIG,EAAG,0BAA0B,QAAQ;SAAE;SACxC,kBAAC,QAAD;UAAM,WAAU;oBAAyB;UAAQ,CAAA;SAC3C;WACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO,EAAS;QAChB,WAAW,MAAM,EAAkB,SAAS,EAAE,OAAO,MAAM;QAC3D,WAAU;QACV,CAAA,CACE,EAAA,CAAA,CACF;SACF,EAAA,CAAA;MAGN,kBAAC,GAAD;OACE,OAAO,EAAG,kCAAkC,oBAAoB;OAChE,aAAa,EACX,wCACA,4DACD;iBAED,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WACE,SAAQ;WACR,WAAU;qBAET,EAAG,iCAAiC,gBAAgB;WAC/C,CAAA;UACR,kBAAC,SAAD;WACE,IAAG;WACH,MAAK;WACL,OAAO,EAAS;WAChB,WAAW,MAAM,EAAkB,gBAAgB,EAAE,OAAO,MAAM;WAClE,aAAa,EACX,4CACA,uCACD;WACD,WAAU;WACV,CAAA;UACF,kBAAC,KAAD;WAAG,WAAU;qBACV,EACC,qCACA,kDACD;WACC,CAAA;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UACE,SAAQ;UACR,WAAU;oBAET,EAAG,6BAA6B,WAAW;UACtC,CAAA,EACR,kBAAC,UAAD;UACE,IAAG;UACH,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,YAAY,EAAE,OAAO,MAAM;UAC9D,WAAU;oBAJZ;WAME,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAAwB,CAAA;WAC5C,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAAmB,CAAA;WACvC,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAA4B,CAAA;WAChD,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAA2B,CAAA;WACxC;YACL,EAAA,CAAA;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,SAAD;YACE,SAAQ;YACR,WAAU;sBAET,EAAG,0BAA0B,sBAAsB;YAC9C,CAAA;WACR,kBAAC,SAAD;YACE,IAAG;YACH,MAAK;YACL,OAAO,EAAS;YAChB,WAAW,MAAM,EAAkB,SAAS,EAAE,OAAO,MAAM;YAC3D,aAAa,EACX,qCACA,uCACD;YACD,WAAU;YACV,CAAA;WACF,kBAAC,KAAD;YAAG,WAAU;sBACV,EACC,8BACA,+DACD;YACC,CAAA;WACA;;SACF;;OACS,CAAA;MAGhB,GAAgB,YAAY,EAAgB,SAAS,KACpD,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,SAAD;UACE,MAAK;UACL,IAAG;UACH,SAAS,EAAS;UAClB,WAAW,MACT,GAAa,OAAU;WAAE,GAAG;WAAM,WAAW,EAAE,OAAO;WAAS,EAAE;UAEnE,UAAU,GAAgB;UAC1B,WAAU;UACV,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,SAAD;UACE,SAAQ;UACR,WAAU;oBAET,EAAG,+BAA+B,iCAAiC;UAC9D,CAAA,EACR,kBAAC,KAAD;UAAG,WAAU;oBACV,GAAgB,YACb,EACE,8CACA,iDACD,GACD,EACE,2CACA,+DACD;UACH,CAAA,CACA;WACF;;OACF,CAAA;MAIR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,MACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;UAAS,MAAK;UAAO,SAAQ;UAAY,QAAO;oBAC7D,kBAAC,QAAD;WACE,eAAc;WACd,gBAAe;WACf,aAAa;WACb,GAAE;WACF,CAAA;UACE,CAAA,EACL,EAAG,gCAAgC,6BAA6B,CAC7D;YAEP,KACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;UAAS,MAAK;UAAO,SAAQ;UAAY,QAAO;oBAC7D,kBAAC,QAAD;WACE,eAAc;WACd,gBAAe;WACf,aAAa;WACb,GAAE;WACF,CAAA;UACE,CAAA,EACL,EACG;WAEJ;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,EAAY,4BAA4B,GAAgB,WACvD,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,UAAU,KAAY;SACtB,WAAU;mBAET,IAAc,iBAAiB;SACzB,CAAA,GACP,MACJ,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,UACE,CAAC,MAAc,KAAY,KAAe,CAAC,EAAS,QAAQ,CAAC,EAAS;SAExE,WAAU;mBANZ,CAQG,KACC,kBAAC,OAAD;UAAK,WAAU;UAAsB,MAAK;UAAO,SAAQ;oBAAzD,CACE,kBAAC,UAAD;WACE,WAAU;WACV,IAAG;WACH,IAAG;WACH,GAAE;WACF,QAAO;WACP,aAAY;WACZ,CAAA,EACF,kBAAC,QAAD;WACE,WAAU;WACV,MAAK;WACL,GAAE;WACF,CAAA,CACE;aAEP,IAAW,cAAc,eACnB;WACL;UACF;;MACF;OACF;;GAGL,MAAyB,MACxB,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IACE,WAAU;IACV,eAAe;AACb,KAAK,MACH,EAAyB,GAAM,EAC/B,EAAe,GAAM,EAErB,GAAa,OAAU;MAAE,GAAG;MAAM,WAAW,GAAgB,aAAa;MAAO,EAAE;;IAGvF,CAAA,EACF,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAExD,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAgD;QACzB;QACrB,kBAAC,UAAD;SAAQ,WAAU;mBAAqB,GAAgB;SAAc,CAAA;;QAEnE;;MAEH,KAAyB,EAAsB,OAAO,GAAgB,MACrE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,SAAQ;SACR,QAAO;mBAEP,kBAAC,QAAD;UACE,eAAc;UACd,gBAAe;UACf,aAAa;UACb,GAAE;UACF,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAuC;UAEhD,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBAAb,CACE,kBAAC,UAAD,EAAA,UAAS,EAAsB,MAAc,CAAA,EAAA,0CAE3C;YACA;WACF;;OACF,CAAA;MAGR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe;AAIb,SAHA,EAAyB,GAAM,EAC/B,EAAe,GAAM,EAErB,GAAa,OAAU;UACrB,GAAG;UACH,WAAW,GAAgB,aAAa;UACzC,EAAE;;QAEL,UAAU;QACV,WAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU;QACV,WAAU;kBAJZ,CAMG,KACC,kBAAC,OAAD;SAAK,WAAU;SAAsB,MAAK;SAAO,SAAQ;mBAAzD,CACE,kBAAC,UAAD;UACE,WAAU;UACV,IAAG;UACH,IAAG;UACH,GAAE;UACF,QAAO;UACP,aAAY;UACZ,CAAA,EACF,kBAAC,QAAD;UACE,WAAU;UACV,MAAK;UACL,GAAE;UACF,CAAA,CACE;YAEP,IAAW,cAAc,eACnB;UACL;;MACF;;IACF,CAAA,CACL,EAAA,CAAA;GAED;MAthBC,kBAAC,GAAD,EAAc,SAAQ,yDAA0D,CAAA"}
|
|
1
|
+
{"version":3,"file":"SettingsPage.js","names":[],"sources":["../../../../../src/billing/modules/settings/pages/SettingsPage.tsx"],"sourcesContent":["/**\n * Settings Module - Settings Page\n * Billing account settings and configuration\n */\n\nimport { useState, useMemo, useEffect, useCallback, type FC } from 'react';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n OptionalFields,\n} from '@burdenoff/fe-libs/ui';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport {\n useGetBillingAccountSettingsQuery,\n useGetBillingAccountsSettingsQuery,\n useGetDefaultBillingAccountSettingsQuery,\n useUpdateBillingAccountSettingsMutation,\n useSetDefaultBillingAccountSettingsMutation,\n ArchiveBillingAccountSettingsDocument,\n} from '../../../../generated/global-operations';\nimport type { BillingAccount } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// Helper to get billing account ID from URL (for navigation from Overview page)\nconst getInitialBillingAccountId = (): string | undefined => {\n if (typeof window === 'undefined') return undefined;\n const urlParams = new URLSearchParams(window.location.search);\n return urlParams.get('billingAccountId') || undefined;\n};\n\nexport const SettingsPage: 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 permissions = useBillingPermissions();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const { emit } = useBillingEventEmitter();\n\n // State - initialize with URL param if present\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string | undefined>(\n getInitialBillingAccountId\n );\n const [isAccountSelectorOpen, setIsAccountSelectorOpen] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [isArchiving, setIsArchiving] = useState(false);\n const [isArchiveConfirmOpen, setIsArchiveConfirmOpen] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [saveSuccess, setSaveSuccess] = useState(false);\n\n // Set default confirmation modal state\n const [isSetDefaultModalOpen, setIsSetDefaultModalOpen] = useState(false);\n const [pendingSave, setPendingSave] = useState(false);\n\n // Form state - includes isDefault toggle\n const [formData, setFormData] = useState({\n name: '',\n email: '',\n billingEmail: '',\n currency: 'USD',\n taxId: '',\n isDefault: false,\n });\n\n // Fetch all billing accounts for the selector (lightweight query without subscriptions)\n const {\n data: accountsData,\n loading: isLoadingAccounts,\n refetch: refetchAccounts,\n } = useGetBillingAccountsSettingsQuery({});\n const billingAccounts = useMemo<BillingAccount[]>(\n () => accountsData?.getBillingAccountsByOrg || [],\n [accountsData?.getBillingAccountsByOrg]\n );\n\n // Fetch default billing account (without subscriptions)\n const {\n data: defaultData,\n loading: isLoadingDefault,\n refetch: refetchDefault,\n } = useGetDefaultBillingAccountSettingsQuery({});\n const defaultBillingAccount: BillingAccount | null =\n defaultData?.getDefaultBillingAccount || null;\n\n // Always fetch selected billing account if we have an ID from URL (without subscriptions)\n const {\n data: selectedData,\n loading: isLoadingSelected,\n refetch: refetchSelected,\n } = useGetBillingAccountSettingsQuery({\n variables: { id: selectedBillingAccountId! },\n skip: !selectedBillingAccountId,\n });\n const selectedAccountData: BillingAccount | null = selectedData?.getBillingAccount || null;\n\n // Mutation for updating billing account\n const [updateBillingAccountMutation] = useUpdateBillingAccountSettingsMutation();\n\n // Mutation for setting default billing account\n const [setDefaultBillingAccountMutation] = useSetDefaultBillingAccountSettingsMutation();\n\n // Determine which account to use - prioritize URL param\n const billingAccount = useMemo(() => {\n // If we have a selected account ID from URL\n if (selectedBillingAccountId) {\n // First try to use the fetched selected account data\n if (selectedAccountData) {\n return selectedAccountData;\n }\n // While loading, try to find from the accounts list\n const fromList = billingAccounts.find((a) => a.id === selectedBillingAccountId);\n if (fromList) {\n return fromList;\n }\n // If still loading and not in list, return null to show loading state\n if (isLoadingSelected) {\n return null;\n }\n }\n // No URL param - use default account\n return defaultBillingAccount;\n }, [\n selectedBillingAccountId,\n selectedAccountData,\n billingAccounts,\n isLoadingSelected,\n defaultBillingAccount,\n ]);\n\n const isLoading =\n isLoadingAccounts || isLoadingDefault || (selectedBillingAccountId && isLoadingSelected);\n\n // Update form when billing account changes\n useEffect(() => {\n if (billingAccount) {\n setFormData({\n name: billingAccount.name || '',\n email: billingAccount.email || '',\n billingEmail: billingAccount.billingEmail || '',\n currency: billingAccount.currency || 'USD',\n taxId: billingAccount.taxId || '',\n isDefault: billingAccount.isDefault || false,\n });\n }\n }, [billingAccount]);\n\n // Handle account selection\n const handleSelectAccount = (account: BillingAccount) => {\n setSelectedBillingAccountId(account.id);\n setIsAccountSelectorOpen(false);\n setSaveSuccess(false);\n setSaveError(null);\n };\n\n // Handle form input changes\n const handleInputChange = (field: keyof typeof formData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n setSaveSuccess(false);\n setSaveError(null);\n };\n\n // Find the current default account\n const currentDefaultAccount = useMemo(() => {\n return billingAccounts.find((a) => a.isDefault) || null;\n }, [billingAccounts]);\n\n // Check if form has changes\n const hasChanges = useMemo(() => {\n if (!billingAccount) return false;\n return (\n formData.name !== (billingAccount.name || '') ||\n formData.email !== (billingAccount.email || '') ||\n formData.billingEmail !== (billingAccount.billingEmail || '') ||\n formData.currency !== (billingAccount.currency || 'USD') ||\n formData.taxId !== (billingAccount.taxId || '') ||\n formData.isDefault !== (billingAccount.isDefault || false)\n );\n }, [formData, billingAccount]);\n\n // Check if trying to change default status to another account\n const isChangingDefault = formData.isDefault && !billingAccount?.isDefault;\n const needsDefaultConfirmation =\n isChangingDefault && currentDefaultAccount && currentDefaultAccount.id !== billingAccount?.id;\n\n // Perform the actual save operation\n const performSave = useCallback(async () => {\n if (!billingAccount?.id) return;\n\n setIsSaving(true);\n setSaveError(null);\n setSaveSuccess(false);\n setIsSetDefaultModalOpen(false);\n setPendingSave(false);\n\n try {\n // Update billing account details\n await updateBillingAccountMutation({\n variables: {\n input: {\n id: billingAccount.id,\n name: formData.name,\n email: formData.email,\n billingEmail: formData.billingEmail || null,\n currency: formData.currency,\n taxId: formData.taxId || null,\n },\n },\n });\n emit('billing.billing_account.updated', {\n route: '/billing/settings',\n entityId: billingAccount.id,\n source: 'settings',\n });\n\n // If setting as default, call the separate mutation\n if (isChangingDefault) {\n await setDefaultBillingAccountMutation({\n variables: { id: billingAccount.id },\n refetchQueries: ['GetBillingAccountsByTenant', 'GetDefaultBillingAccount'],\n });\n emit('billing.billing_account.updated', {\n route: '/billing/settings',\n entityId: billingAccount.id,\n source: 'settings.default',\n });\n }\n\n // Refetch to update cache\n if (selectedBillingAccountId) {\n await refetchSelected();\n } else {\n await refetchDefault();\n }\n\n setSaveSuccess(true);\n // Clear success message after 3 seconds\n setTimeout(() => setSaveSuccess(false), 3000);\n } catch (error) {\n setSaveError(error instanceof Error ? error.message : 'Failed to save changes');\n } finally {\n setIsSaving(false);\n }\n }, [\n billingAccount?.id,\n formData,\n isChangingDefault,\n updateBillingAccountMutation,\n setDefaultBillingAccountMutation,\n selectedBillingAccountId,\n refetchSelected,\n refetchDefault,\n emit,\n ]);\n\n // Handle save button click\n const handleSave = useCallback(() => {\n if (!billingAccount?.id) return;\n\n // If changing default and there's an existing default, show confirmation\n if (needsDefaultConfirmation) {\n setPendingSave(true);\n setIsSetDefaultModalOpen(true);\n } else {\n performSave();\n }\n }, [billingAccount?.id, needsDefaultConfirmation, performSave]);\n\n const replacementDefaultAccount = useMemo(\n () =>\n billingAccount\n ? billingAccounts.find(\n (account) => account.id !== billingAccount.id && account.isActive\n )\n : undefined,\n [billingAccount, billingAccounts]\n );\n\n const archiveConfirmMessage = billingAccount\n ? billingAccount.isDefault\n ? `Archive ${billingAccount.name}? ${replacementDefaultAccount ? `${replacementDefaultAccount.name} will become the default account.` : 'No replacement default account is available.'}`\n : `Archive ${billingAccount.name}?`\n : '';\n\n const handleArchiveAccount = useCallback(async () => {\n if (!billingAccount?.id || !billingAccount.isActive) {\n return;\n }\n\n setIsArchiving(true);\n setSaveError(null);\n setSaveSuccess(false);\n\n try {\n await directBillingGraphqlRequest<\n { archiveBillingAccount: { id: string; isActive: boolean; isDefault: boolean } },\n { id: string; reason: string; replacementDefaultBillingAccountId: string | null }\n >({\n apiGatewayUrl,\n authToken,\n orgId,\n query: ArchiveBillingAccountSettingsDocument,\n variables: {\n id: billingAccount.id,\n reason: 'Archived from billing settings',\n replacementDefaultBillingAccountId: billingAccount.isDefault\n ? (replacementDefaultAccount?.id ?? null)\n : null,\n },\n });\n emit('billing.billing_account.archived', {\n route: '/billing/settings',\n entityId: billingAccount.id,\n source: 'settings',\n });\n\n await Promise.all([refetchAccounts(), refetchDefault()]);\n setSelectedBillingAccountId(replacementDefaultAccount?.id);\n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 3000);\n } catch (error) {\n setSaveError(error instanceof Error ? error.message : 'Failed to archive billing account');\n } finally {\n setIsArchiving(false);\n }\n }, [\n apiGatewayUrl,\n authToken,\n billingAccount,\n replacementDefaultAccount,\n orgId,\n refetchAccounts,\n refetchDefault,\n emit,\n ]);\n\n if (!permissions.canManageBillingAccount) {\n return <AccessDenied message=\"You don't have permission to manage billing settings.\" />;\n }\n\n if (isLoading && !billingAccount) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-10 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.settings.billingSettings', 'Billing Settings')}\n description={tr(\n 'billing.settings.description',\n 'Manage your billing account configuration'\n )}\n />\n\n {/* Account Selector (if multiple accounts) */}\n {billingAccounts.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsAccountSelectorOpen(!isAccountSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-subtle rounded-lg bg-bg-surface hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-lg bg-action-primary-bg/10 flex items-center justify-center\">\n <svg\n className=\"size-4 text-primary\"\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=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {billingAccount?.name || tr('billing.settings.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-text-secondary\">{billingAccount?.email}</p>\n </div>\n </div>\n <svg\n className={`size-4 text-text-secondary transition-transform ${isAccountSelectorOpen ? 'rotate-180' : ''}`}\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=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </button>\n\n {isAccountSelectorOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsAccountSelectorOpen(false)} />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-popover border border-border-subtle rounded-lg shadow-lg z-20 py-1 max-h-60 overflow-y-auto\">\n {billingAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => handleSelectAccount(account)}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-8 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <svg\n className=\"size-4 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"\n />\n </svg>\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{account.email}</p>\n </div>\n </div>\n {billingAccount?.id === account.id && (\n <svg\n className=\"size-4 text-primary flex-shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Account Settings */}\n <div className=\"border border-border-subtle rounded-lg bg-bg-surface\">\n <div className=\"p-6 border-b border-border-subtle\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.settings.accountInfo', 'Account Information')}\n </h2>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.settings.accountInfoDescription',\n 'Update your billing contact details and tax information'\n )}\n </p>\n </div>\n {billingAccount?.isDefault && (\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium bg-action-primary-bg/10 text-primary rounded-full\">\n <svg className=\"size-3\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\"\n clipRule=\"evenodd\"\n />\n </svg>\n {tr('billing.settings.defaultAccount', 'Default Account')}\n </span>\n )}\n </div>\n </div>\n <div className=\"p-6 space-y-6\">\n {/* Account Status Summary */}\n {billingAccount && (\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4 text-sm pb-4 border-b border-border-subtle\">\n <div>\n <p className=\"text-text-secondary\">{tr('billing.settings.status', 'Status')}</p>\n <p\n className={`font-medium ${billingAccount.isActive ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {billingAccount.isActive\n ? tr('billing.settings.active', 'Active')\n : tr('billing.settings.inactive', 'Inactive')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('billing.settings.creditBalance', 'Credit Balance')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {(billingAccount.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('billing.settings.accountId', 'Account ID')}\n </p>\n <p className=\"font-mono text-xs text-text-primary truncate\" title={billingAccount.id}>\n {billingAccount.id}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('billing.settings.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {billingAccount.createdAt\n ? new Date(billingAccount.createdAt).toLocaleDateString()\n : tr('billing.settings.notAvailable', 'N/A')}\n </p>\n </div>\n </div>\n )}\n\n {/* Contact Details Section — Required fields above-the-fold */}\n <div>\n <h3 className=\"text-sm font-medium text-text-primary mb-4\">\n {tr('billing.settings.contactDetails', 'Contact Details')}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"billing-account-name\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.accountName', 'Account Name')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"billing-account-name\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleInputChange('name', e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n <div>\n <label\n htmlFor=\"billing-account-email\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.email', 'Email')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"billing-account-email\"\n type=\"email\"\n value={formData.email}\n onChange={(e) => handleInputChange('email', e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n </div>\n </div>\n\n {/* Advanced billing settings — currency / billing email / tax ID. Most accounts keep defaults. */}\n <OptionalFields\n title={tr('billing.settings.advancedTitle', 'Advanced settings')}\n description={tr(\n 'billing.settings.advancedDescription',\n 'Billing email override, currency, and tax identification.'\n )}\n >\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"billing-account-billing-email\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.billingEmail', 'Billing Email')}\n </label>\n <input\n id=\"billing-account-billing-email\"\n type=\"email\"\n value={formData.billingEmail}\n onChange={(e) => handleInputChange('billingEmail', e.target.value)}\n placeholder={tr(\n 'billing.settings.billingEmailPlaceholder',\n 'Optional - defaults to account email'\n )}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'billing.settings.billingEmailHelp',\n 'Invoices will be sent to this email if provided'\n )}\n </p>\n </div>\n <div>\n <label\n htmlFor=\"billing-account-currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.currency', 'Currency')}\n </label>\n <select\n id=\"billing-account-currency\"\n value={formData.currency}\n onChange={(e) => handleInputChange('currency', e.target.value)}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n >\n <option value=\"USD\">USD - US Dollar</option>\n <option value=\"EUR\">EUR - Euro</option>\n <option value=\"GBP\">GBP - British Pound</option>\n <option value=\"INR\">INR - Indian Rupee</option>\n </select>\n </div>\n <div className=\"sm:col-span-2 max-w-md\">\n <label\n htmlFor=\"billing-account-tax-id\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.settings.taxId', 'Tax ID / VAT Number')}\n </label>\n <input\n id=\"billing-account-tax-id\"\n type=\"text\"\n value={formData.taxId}\n onChange={(e) => handleInputChange('taxId', e.target.value)}\n placeholder={tr(\n 'billing.settings.taxIdPlaceholder',\n 'Enter your tax identification number'\n )}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-md bg-bg-surface text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'billing.settings.taxIdHelp',\n 'Used for invoices and tax compliance (e.g., GST, VAT number)'\n )}\n </p>\n </div>\n </div>\n </OptionalFields>\n\n {/* Default Account Setting */}\n {billingAccount?.isActive && billingAccounts.length > 1 && (\n <div className=\"pt-4 border-t border-border-subtle\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex items-center h-6\">\n <input\n type=\"checkbox\"\n id=\"isDefault\"\n checked={formData.isDefault}\n onChange={(e) =>\n setFormData((prev) => ({ ...prev, isDefault: e.target.checked }))\n }\n disabled={billingAccount?.isDefault}\n className=\"size-4 rounded border-border-subtle text-primary focus:ring-primary focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n </div>\n <div className=\"flex-1\">\n <label\n htmlFor=\"isDefault\"\n className=\"text-sm font-medium text-text-primary cursor-pointer\"\n >\n {tr('billing.settings.setDefault', 'Set as default billing account')}\n </label>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {billingAccount?.isDefault\n ? tr(\n 'billing.settings.currentDefaultDescription',\n 'This is currently your default billing account'\n )\n : tr(\n 'billing.settings.makePrimaryDescription',\n 'Make this your primary billing account for new subscriptions'\n )}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Save Button and Status */}\n <div className=\"pt-4 border-t border-border-subtle flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {saveSuccess && (\n <div className=\"flex items-center gap-2 text-sm text-status-success-text\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n {tr('billing.settings.saveSuccess', 'Changes saved successfully')}\n </div>\n )}\n {saveError && (\n <div className=\"flex items-center gap-2 text-sm text-status-error-text\">\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\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 {saveError}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-3\">\n {permissions.canArchiveBillingAccount && billingAccount?.isActive ? (\n <button\n type=\"button\"\n onClick={() => setIsArchiveConfirmOpen(true)}\n disabled={isSaving || isArchiving}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-status-error-text rounded-md hover:bg-status-error-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isArchiving ? 'Archiving...' : 'Archive Account'}\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={\n !hasChanges || isSaving || isArchiving || !formData.name || !formData.email\n }\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center gap-2\"\n >\n {isSaving && (\n <svg className=\"size-4 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n )}\n {isSaving ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Set as Default Confirmation Modal */}\n {isSetDefaultModalOpen && pendingSave && (\n <>\n <div\n className=\"fixed inset-0 bg-overlay-scrim z-40\"\n onClick={() => {\n if (!isSaving) {\n setIsSetDefaultModalOpen(false);\n setPendingSave(false);\n // Reset the checkbox if cancelled\n setFormData((prev) => ({ ...prev, isDefault: billingAccount?.isDefault || false }));\n }\n }}\n />\n <div className=\"fixed inset-0 flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg shadow-xl max-w-md w-full p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-2\">\n Confirm Default Account Change\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n You are about to set{' '}\n <strong className=\"text-text-primary\">{billingAccount?.name}</strong> as your default\n billing account and save your changes.\n </p>\n\n {currentDefaultAccount && currentDefaultAccount.id !== billingAccount?.id && (\n <div className=\"mb-4 p-3 bg-status-warning-bg-subtle border border-border-subtle rounded-md\">\n <div className=\"flex items-start gap-2\">\n <svg\n className=\"size-5 text-status-warning-text flex-shrink-0 mt-0.5\"\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 className=\"text-sm\">\n <p className=\"font-medium text-status-warning-text\">\n Current default will change\n </p>\n <p className=\"text-status-warning-text mt-0.5\">\n <strong>{currentDefaultAccount.name}</strong> will no longer be the default\n account.\n </p>\n </div>\n </div>\n </div>\n )}\n\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setIsSetDefaultModalOpen(false);\n setPendingSave(false);\n // Reset the checkbox if cancelled\n setFormData((prev) => ({\n ...prev,\n isDefault: billingAccount?.isDefault || false,\n }));\n }}\n disabled={isSaving}\n className=\"px-4 py-2 text-sm font-medium text-text-primary border border-border-subtle rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={performSave}\n disabled={isSaving}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 disabled:opacity-50 transition-colors flex items-center gap-2\"\n >\n {isSaving && (\n <svg className=\"size-4 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n )}\n {isSaving ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </div>\n </div>\n </>\n )}\n\n <AlertDialog open={isArchiveConfirmOpen} onOpenChange={setIsArchiveConfirmOpen}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Archive billing account?</AlertDialogTitle>\n <AlertDialogDescription>{archiveConfirmMessage}</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={() => {\n setIsArchiveConfirmOpen(false);\n void handleArchiveAccount();\n }}\n >\n Archive Account\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAkCA,IAAM,WAAuD;AACvD,cAAO,SAAW,KAEtB,QADkB,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,mBAAmB,IAAI,KAAA;GAGjC,UAAyB;CACpC,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,IAAc,IAAuB,EACrC,EAAE,kBAAe,cAAW,aAAU,GAAY,EAClD,EAAE,YAAS,IAAwB,EAGnC,CAAC,GAA0B,KAA+B,EAC9D,GACD,EACK,CAAC,GAAuB,KAA4B,EAAS,GAAM,EACnE,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,IAAsB,KAA2B,EAAS,GAAM,EACjE,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,IAAa,KAAkB,EAAS,GAAM,EAG/C,CAAC,GAAuB,KAA4B,EAAS,GAAM,EACnE,CAAC,GAAa,KAAkB,EAAS,GAAM,EAG/C,CAAC,GAAU,KAAe,EAAS;EACvC,MAAM;EACN,OAAO;EACP,cAAc;EACd,UAAU;EACV,OAAO;EACP,WAAW;EACZ,CAAC,EAGI,EACJ,MAAM,GACN,SAAS,IACT,SAAS,MACP,GAAmC,EAAE,CAAC,EACpC,IAAkB,QAChB,GAAc,2BAA2B,EAAE,EACjD,CAAC,GAAc,wBAAwB,CACxC,EAGK,EACJ,MAAM,IACN,SAAS,IACT,SAAS,MACP,GAAyC,EAAE,CAAC,EAC1C,IACJ,IAAa,4BAA4B,MAGrC,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,GAAkC;EACpC,WAAW,EAAE,IAAI,GAA2B;EAC5C,MAAM,CAAC;EACR,CAAC,EACI,IAA6C,GAAc,qBAAqB,MAGhF,CAAC,KAAgC,IAAyC,EAG1E,CAAC,KAAoC,IAA6C,EAGlF,IAAiB,QAAc;AAEnC,MAAI,GAA0B;AAE5B,OAAI,EACF,QAAO;GAGT,IAAM,IAAW,EAAgB,MAAM,MAAM,EAAE,OAAO,EAAyB;AAC/E,OAAI,EACF,QAAO;AAGT,OAAI,EACF,QAAO;;AAIX,SAAO;IACN;EACD;EACA;EACA;EACA;EACA;EACD,CAAC,EAEI,KACJ,MAAqB,MAAqB,KAA4B;AAGxE,UAAgB;AACd,EAAI,KACF,EAAY;GACV,MAAM,EAAe,QAAQ;GAC7B,OAAO,EAAe,SAAS;GAC/B,cAAc,EAAe,gBAAgB;GAC7C,UAAU,EAAe,YAAY;GACrC,OAAO,EAAe,SAAS;GAC/B,WAAW,EAAe,aAAa;GACxC,CAAC;IAEH,CAAC,EAAe,CAAC;CAGpB,IAAM,MAAuB,MAA4B;AAIvD,EAHA,EAA4B,EAAQ,GAAG,EACvC,EAAyB,GAAM,EAC/B,EAAe,GAAM,EACrB,EAAa,KAAK;IAId,KAAqB,GAA8B,MAAkB;AAGzE,EAFA,GAAa,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAO,EAAE,EACpD,EAAe,GAAM,EACrB,EAAa,KAAK;IAId,IAAwB,QACrB,EAAgB,MAAM,MAAM,EAAE,UAAU,IAAI,MAClD,CAAC,EAAgB,CAAC,EAGf,KAAa,QACZ,IAEH,EAAS,UAAU,EAAe,QAAQ,OAC1C,EAAS,WAAW,EAAe,SAAS,OAC5C,EAAS,kBAAkB,EAAe,gBAAgB,OAC1D,EAAS,cAAc,EAAe,YAAY,UAClD,EAAS,WAAW,EAAe,SAAS,OAC5C,EAAS,eAAe,EAAe,aAAa,MAP1B,IAS3B,CAAC,GAAU,EAAe,CAAC,EAGxB,IAAoB,EAAS,aAAa,CAAC,GAAgB,WAC3D,IACJ,KAAqB,KAAyB,EAAsB,OAAO,GAAgB,IAGvF,IAAc,EAAY,YAAY;AACrC,SAAgB,IAMrB;GAJA,EAAY,GAAK,EACjB,EAAa,KAAK,EAClB,EAAe,GAAM,EACrB,EAAyB,GAAM,EAC/B,EAAe,GAAM;AAErB,OAAI;AA0CF,IAxCA,MAAM,EAA6B,EACjC,WAAW,EACT,OAAO;KACL,IAAI,EAAe;KACnB,MAAM,EAAS;KACf,OAAO,EAAS;KAChB,cAAc,EAAS,gBAAgB;KACvC,UAAU,EAAS;KACnB,OAAO,EAAS,SAAS;KAC1B,EACF,EACF,CAAC,EACF,EAAK,mCAAmC;KACtC,OAAO;KACP,UAAU,EAAe;KACzB,QAAQ;KACT,CAAC,EAGE,MACF,MAAM,EAAiC;KACrC,WAAW,EAAE,IAAI,EAAe,IAAI;KACpC,gBAAgB,CAAC,8BAA8B,2BAA2B;KAC3E,CAAC,EACF,EAAK,mCAAmC;KACtC,OAAO;KACP,UAAU,EAAe;KACzB,QAAQ;KACT,CAAC,GAIA,IACF,MAAM,GAAiB,GAEvB,MAAM,GAAgB,EAGxB,EAAe,GAAK,EAEpB,iBAAiB,EAAe,GAAM,EAAE,IAAK;YACtC,GAAO;AACd,MAAa,aAAiB,QAAQ,EAAM,UAAU,yBAAyB;aACvE;AACR,MAAY,GAAM;;;IAEnB;EACD,GAAgB;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAGI,KAAa,QAAkB;AAC9B,KAAgB,OAGjB,KACF,EAAe,GAAK,EACpB,EAAyB,GAAK,IAE9B,GAAa;IAEd;EAAC,GAAgB;EAAI;EAA0B;EAAY,CAAC,EAEzD,IAA4B,QAE9B,IACI,EAAgB,MACb,MAAY,EAAQ,OAAO,EAAe,MAAM,EAAQ,SAC1D,GACD,KAAA,GACN,CAAC,GAAgB,EAAgB,CAClC,EAEK,KAAwB,IAC1B,EAAe,YACb,WAAW,EAAe,KAAK,IAAI,IAA4B,GAAG,EAA0B,KAAK,qCAAqC,mDACtI,WAAW,EAAe,KAAK,KACjC,IAEE,KAAuB,EAAY,YAAY;AAC/C,SAAC,GAAgB,MAAM,CAAC,EAAe,WAM3C;GAFA,EAAe,GAAK,EACpB,EAAa,KAAK,EAClB,EAAe,GAAM;AAErB,OAAI;AA0BF,IAzBA,MAAM,GAGJ;KACA;KACA;KACA;KACA,OAAO;KACP,WAAW;MACT,IAAI,EAAe;MACnB,QAAQ;MACR,oCAAoC,EAAe,YAC9C,GAA2B,MAAM,OAClC;MACL;KACF,CAAC,EACF,EAAK,oCAAoC;KACvC,OAAO;KACP,UAAU,EAAe;KACzB,QAAQ;KACT,CAAC,EAEF,MAAM,QAAQ,IAAI,CAAC,GAAiB,EAAE,GAAgB,CAAC,CAAC,EACxD,EAA4B,GAA2B,GAAG,EAC1D,EAAe,GAAK,EACpB,iBAAiB,EAAe,GAAM,EAAE,IAAK;YACtC,GAAO;AACd,MAAa,aAAiB,QAAQ,EAAM,UAAU,oCAAoC;aAClF;AACR,MAAe,GAAM;;;IAEtB;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAmBF,QAjBK,EAAY,0BAIb,MAAa,CAAC,IAEd,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,+CAAgD,CAAA,EAC/D,kBAAC,OAAD;GAAK,WAAU;aACZ;IAAC;IAAG;IAAG;IAAE,CAAC,KAAK,MACd,kBAAC,OAAD,EAAa,WAAU,2CAA4C,EAAzD,EAAyD,CACnE;GACE,CAAA,CACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,oCAAoC,mBAAmB;IACjE,aAAa,EACX,gCACA,4CACD;IACD,CAAA;GAGD,EAAgB,SAAS,KACxB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAyB,CAAC,EAAsB;KAC/D,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,MAAK;QACL,SAAQ;QACR,QAAO;kBAEP,kBAAC,QAAD;SACE,eAAc;SACd,gBAAe;SACf,aAAa;SACb,GAAE;SACF,CAAA;QACE,CAAA;OACF,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,GAAgB,QAAQ,EAAG,kCAAkC,iBAAiB;QAC7E,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAA+B,GAAgB;QAAU,CAAA,CAClE;SACF;SACN,kBAAC,OAAD;MACE,WAAW,mDAAmD,IAAwB,eAAe;MACrG,MAAK;MACL,SAAQ;MACR,QAAO;gBAEP,kBAAC,QAAD;OACE,eAAc;OACd,gBAAe;OACf,aAAa;OACb,GAAE;OACF,CAAA;MACE,CAAA,CACC;QAER,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAqB,eAAe,EAAyB,GAAM;KAAI,CAAA,EACtF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAgB,KAAK,MACpB,kBAAC,UAAD;MACE,MAAK;MAEL,eAAe,GAAoB,EAAQ;MAC3C,WAAU;gBAJZ,CAME,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,SAAQ;SACR,QAAO;mBAEP,kBAAC,QAAD;UACE,eAAc;UACd,gBAAe;UACf,aAAa;UACb,GAAE;UACF,CAAA;SACE,CAAA;QACF,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAQ;SACP,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAwC,EAAQ;SAAU,CAAA,CACnE;UACF;UACL,GAAgB,OAAO,EAAQ,MAC9B,kBAAC,OAAD;OACE,WAAU;OACV,MAAK;OACL,SAAQ;OACR,QAAO;iBAEP,kBAAC,QAAD;QACE,eAAc;QACd,gBAAe;QACf,aAAa;QACb,GAAE;QACF,CAAA;OACE,CAAA,CAED;QA1CF,EAAQ,GA0CN,CACT;KACE,CAAA,CACL,EAAA,CAAA,CAED;;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,gCAAgC,sBAAsB;OACvD,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,2CACA,0DACD;OACC,CAAA,CACA,EAAA,CAAA,EACL,GAAgB,aACf,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,OAAD;QAAK,WAAU;QAAS,MAAK;QAAe,SAAQ;kBAClD,kBAAC,QAAD;SACE,UAAS;SACT,GAAE;SACF,UAAS;SACT,CAAA;QACE,CAAA,EACL,EAAG,mCAAmC,kBAAkB,CACpD;SAEL;;KACF,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAuB,EAAG,2BAA2B,SAAS;SAAK,CAAA,EAChF,kBAAC,KAAD;SACE,WAAW,eAAe,EAAe,WAAW,6BAA6B;mBAEhF,EAAe,WACZ,EAAG,2BAA2B,SAAS,GACvC,EAAG,6BAA6B,WAAW;SAC7C,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,kCAAkC,iBAAiB;SACrD,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb,EACI,EAAe,gBAAgB,GAAG,gBAAgB,EAAC,WACnD;WACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,8BAA8B,aAAa;SAC7C,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;SAA+C,OAAO,EAAe;mBAC/E,EAAe;SACd,CAAA,CACA,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAuB,EAAG,4BAA4B,UAAU;SAAK,CAAA,EAClF,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAe,YACZ,IAAI,KAAK,EAAe,UAAU,CAAC,oBAAoB,GACvD,EAAG,iCAAiC,MAAM;SAC5C,CAAA,CACA,EAAA,CAAA;QACF;;MAIR,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,mCAAmC,kBAAkB;OACtD,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAFZ;SAIG,EAAG,gCAAgC,eAAe;SAAE;SACrD,kBAAC,QAAD;UAAM,WAAU;oBAAyB;UAAQ,CAAA;SAC3C;WACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO,EAAS;QAChB,WAAW,MAAM,EAAkB,QAAQ,EAAE,OAAO,MAAM;QAC1D,WAAU;QACV,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBAFZ;SAIG,EAAG,0BAA0B,QAAQ;SAAE;SACxC,kBAAC,QAAD;UAAM,WAAU;oBAAyB;UAAQ,CAAA;SAC3C;WACR,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO,EAAS;QAChB,WAAW,MAAM,EAAkB,SAAS,EAAE,OAAO,MAAM;QAC3D,WAAU;QACV,CAAA,CACE,EAAA,CAAA,CACF;SACF,EAAA,CAAA;MAGN,kBAAC,IAAD;OACE,OAAO,EAAG,kCAAkC,oBAAoB;OAChE,aAAa,EACX,wCACA,4DACD;iBAED,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WACE,SAAQ;WACR,WAAU;qBAET,EAAG,iCAAiC,gBAAgB;WAC/C,CAAA;UACR,kBAAC,SAAD;WACE,IAAG;WACH,MAAK;WACL,OAAO,EAAS;WAChB,WAAW,MAAM,EAAkB,gBAAgB,EAAE,OAAO,MAAM;WAClE,aAAa,EACX,4CACA,uCACD;WACD,WAAU;WACV,CAAA;UACF,kBAAC,KAAD;WAAG,WAAU;qBACV,EACC,qCACA,kDACD;WACC,CAAA;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UACE,SAAQ;UACR,WAAU;oBAET,EAAG,6BAA6B,WAAW;UACtC,CAAA,EACR,kBAAC,UAAD;UACE,IAAG;UACH,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,YAAY,EAAE,OAAO,MAAM;UAC9D,WAAU;oBAJZ;WAME,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAAwB,CAAA;WAC5C,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAAmB,CAAA;WACvC,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAA4B,CAAA;WAChD,kBAAC,UAAD;YAAQ,OAAM;sBAAM;YAA2B,CAAA;WACxC;YACL,EAAA,CAAA;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,SAAD;YACE,SAAQ;YACR,WAAU;sBAET,EAAG,0BAA0B,sBAAsB;YAC9C,CAAA;WACR,kBAAC,SAAD;YACE,IAAG;YACH,MAAK;YACL,OAAO,EAAS;YAChB,WAAW,MAAM,EAAkB,SAAS,EAAE,OAAO,MAAM;YAC3D,aAAa,EACX,qCACA,uCACD;YACD,WAAU;YACV,CAAA;WACF,kBAAC,KAAD;YAAG,WAAU;sBACV,EACC,8BACA,+DACD;YACC,CAAA;WACA;;SACF;;OACS,CAAA;MAGhB,GAAgB,YAAY,EAAgB,SAAS,KACpD,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,SAAD;UACE,MAAK;UACL,IAAG;UACH,SAAS,EAAS;UAClB,WAAW,MACT,GAAa,OAAU;WAAE,GAAG;WAAM,WAAW,EAAE,OAAO;WAAS,EAAE;UAEnE,UAAU,GAAgB;UAC1B,WAAU;UACV,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,SAAD;UACE,SAAQ;UACR,WAAU;oBAET,EAAG,+BAA+B,iCAAiC;UAC9D,CAAA,EACR,kBAAC,KAAD;UAAG,WAAU;oBACV,GAAgB,YACb,EACE,8CACA,iDACD,GACD,EACE,2CACA,+DACD;UACH,CAAA,CACA;WACF;;OACF,CAAA;MAIR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,MACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;UAAS,MAAK;UAAO,SAAQ;UAAY,QAAO;oBAC7D,kBAAC,QAAD;WACE,eAAc;WACd,gBAAe;WACf,aAAa;WACb,GAAE;WACF,CAAA;UACE,CAAA,EACL,EAAG,gCAAgC,6BAA6B,CAC7D;YAEP,KACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;UAAS,MAAK;UAAO,SAAQ;UAAY,QAAO;oBAC7D,kBAAC,QAAD;WACE,eAAc;WACd,gBAAe;WACf,aAAa;WACb,GAAE;WACF,CAAA;UACE,CAAA,EACL,EACG;WAEJ;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,EAAY,4BAA4B,GAAgB,WACvD,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAwB,GAAK;SAC5C,UAAU,KAAY;SACtB,WAAU;mBAET,IAAc,iBAAiB;SACzB,CAAA,GACP,MACJ,kBAAC,UAAD;SACE,MAAK;SACL,SAAS;SACT,UACE,CAAC,MAAc,KAAY,KAAe,CAAC,EAAS,QAAQ,CAAC,EAAS;SAExE,WAAU;mBANZ,CAQG,KACC,kBAAC,OAAD;UAAK,WAAU;UAAsB,MAAK;UAAO,SAAQ;oBAAzD,CACE,kBAAC,UAAD;WACE,WAAU;WACV,IAAG;WACH,IAAG;WACH,GAAE;WACF,QAAO;WACP,aAAY;WACZ,CAAA,EACF,kBAAC,QAAD;WACE,WAAU;WACV,MAAK;WACL,GAAE;WACF,CAAA,CACE;aAEP,IAAW,cAAc,eACnB;WACL;UACF;;MACF;OACF;;GAGL,KAAyB,KACxB,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;IACE,WAAU;IACV,eAAe;AACb,KAAK,MACH,EAAyB,GAAM,EAC/B,EAAe,GAAM,EAErB,GAAa,OAAU;MAAE,GAAG;MAAM,WAAW,GAAgB,aAAa;MAAO,EAAE;;IAGvF,CAAA,EACF,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAExD,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAgD;QACzB;QACrB,kBAAC,UAAD;SAAQ,WAAU;mBAAqB,GAAgB;SAAc,CAAA;;QAEnE;;MAEH,KAAyB,EAAsB,OAAO,GAAgB,MACrE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SACE,WAAU;SACV,MAAK;SACL,SAAQ;SACR,QAAO;mBAEP,kBAAC,QAAD;UACE,eAAc;UACd,gBAAe;UACf,aAAa;UACb,GAAE;UACF,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAuC;UAEhD,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBAAb,CACE,kBAAC,UAAD,EAAA,UAAS,EAAsB,MAAc,CAAA,EAAA,0CAE3C;YACA;WACF;;OACF,CAAA;MAGR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe;AAIb,SAHA,EAAyB,GAAM,EAC/B,EAAe,GAAM,EAErB,GAAa,OAAU;UACrB,GAAG;UACH,WAAW,GAAgB,aAAa;UACzC,EAAE;;QAEL,UAAU;QACV,WAAU;kBACX;QAEQ,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU;QACV,WAAU;kBAJZ,CAMG,KACC,kBAAC,OAAD;SAAK,WAAU;SAAsB,MAAK;SAAO,SAAQ;mBAAzD,CACE,kBAAC,UAAD;UACE,WAAU;UACV,IAAG;UACH,IAAG;UACH,GAAE;UACF,QAAO;UACP,aAAY;UACZ,CAAA,EACF,kBAAC,QAAD;UACE,WAAU;UACV,MAAK;UACL,GAAE;UACF,CAAA,CACE;YAEP,IAAW,cAAc,eACnB;UACL;;MACF;;IACF,CAAA,CACL,EAAA,CAAA;GAGL,kBAAC,GAAD;IAAa,MAAM;IAAsB,cAAc;cACrD,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAkB,4BAA2C,CAAA,EAC7D,kBAAC,IAAD,EAAA,UAAyB,IAA+C,CAAA,CACtD,EAAA,CAAA,EACpB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAmB,UAA0B,CAAA,EAC7C,kBAAC,IAAD;KACE,eAAe;AAER,MADL,EAAwB,GAAM,EACzB,IAAsB;;eAE9B;KAEmB,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;IACT,CAAA;GACV;MA1iBC,kBAAC,GAAD,EAAc,SAAQ,yDAA0D,CAAA"}
|
|
@@ -12,78 +12,77 @@ import { useActorProfiles as c } from "../../../shared/hooks/useActorProfiles.js
|
|
|
12
12
|
import "../../../shared/hooks/index.js";
|
|
13
13
|
import { useCallback as l, useEffect as u, useMemo as d, useState as f } from "react";
|
|
14
14
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
15
|
+
import { AlertDialog as h, AlertDialogAction as g, AlertDialogCancel as _, AlertDialogContent as v, AlertDialogDescription as y, AlertDialogFooter as b, AlertDialogHeader as x, AlertDialogTitle as S } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/billing/modules/settings/pages/TeamPermissionsPage.tsx
|
|
16
|
-
var
|
|
17
|
-
let { apiGatewayUrl:
|
|
18
|
-
orgId:
|
|
19
|
-
billingAccounts:
|
|
17
|
+
var C = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, w = "\n query BillingScopeRoles($scopeId: String!) {\n roles(scopeId: $scopeId) {\n id\n name\n description\n priority\n isSystem\n }\n }\n", T = "\n query BillingScopeActors($scopeId: String!) {\n actors(scopeId: $scopeId) {\n actorId\n actorType\n roleAssignments {\n id\n actorId\n actorType\n roleId\n scopeId\n isActive\n expiresAt\n role { id name priority }\n }\n }\n }\n", E = "\n mutation AssignBillingRole($input: AssignRoleInput!) {\n assignRoleToActor(input: $input) {\n id\n actorId\n roleId\n scopeId\n }\n }\n", D = "\n mutation RemoveBillingRole($assignmentId: String!) {\n removeRoleFromActor(assignmentId: $assignmentId)\n }\n", O = () => {
|
|
18
|
+
let { apiGatewayUrl: O, authToken: k, orgId: A } = e(), { billingAccounts: j } = s(), { selectedAccountId: M } = i({
|
|
19
|
+
orgId: A,
|
|
20
|
+
billingAccounts: j,
|
|
20
21
|
syncFromUrl: !0,
|
|
21
22
|
urlParamName: "billingAccountId"
|
|
22
|
-
}),
|
|
23
|
-
apiGatewayUrl:
|
|
24
|
-
authToken:
|
|
25
|
-
orgId:
|
|
23
|
+
}), N = M ?? void 0, P = o({ billingAccountIdOverride: N }), [F, I] = f([]), [L, R] = f([]), [z, B] = f(!1), [V, H] = f(null), [U, W] = f(""), [G, K] = f(""), [q, J] = f(!1), [Y, X] = f(null), Z = l(async (e, t) => a({
|
|
24
|
+
apiGatewayUrl: O,
|
|
25
|
+
authToken: k,
|
|
26
|
+
orgId: A,
|
|
26
27
|
query: e,
|
|
27
28
|
variables: t
|
|
28
29
|
}), [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
]),
|
|
33
|
-
if (
|
|
34
|
-
|
|
30
|
+
O,
|
|
31
|
+
k,
|
|
32
|
+
A
|
|
33
|
+
]), Q = l(async () => {
|
|
34
|
+
if (N) {
|
|
35
|
+
B(!0), H(null);
|
|
35
36
|
try {
|
|
36
|
-
let [e, t] = await Promise.all([
|
|
37
|
-
|
|
37
|
+
let [e, t] = await Promise.all([Z(w, { scopeId: N }), Z(T, { scopeId: N })]);
|
|
38
|
+
I(e.roles ?? []), R(t.actors ?? []), K((e.roles ?? []).find((e) => /viewer/i.test(e.name))?.id ?? e.roles?.[0]?.id ?? "");
|
|
38
39
|
} catch (e) {
|
|
39
|
-
|
|
40
|
+
H(e instanceof Error ? e.message : "Failed to load team data");
|
|
40
41
|
} finally {
|
|
41
|
-
|
|
42
|
+
B(!1);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
}, [
|
|
45
|
+
}, [N, Z]);
|
|
45
46
|
u(() => {
|
|
46
|
-
|
|
47
|
-
}, [
|
|
48
|
-
let
|
|
47
|
+
Q();
|
|
48
|
+
}, [Q]);
|
|
49
|
+
let $ = d(() => [...L].sort((e, t) => {
|
|
49
50
|
let n = Math.max(...e.roleAssignments.map((e) => e.role?.priority ?? 0), 0);
|
|
50
51
|
return Math.max(...t.roleAssignments.map((e) => e.role?.priority ?? 0), 0) - n;
|
|
51
|
-
}), [
|
|
52
|
+
}), [L]), ee = c(d(() => $.map((e) => ({
|
|
52
53
|
actorId: e.actorId,
|
|
53
54
|
actorType: e.actorType
|
|
54
|
-
})), [
|
|
55
|
-
if (
|
|
56
|
-
}, [
|
|
57
|
-
if (e.preventDefault(), !
|
|
58
|
-
let t =
|
|
59
|
-
if (!
|
|
60
|
-
|
|
55
|
+
})), [$])), te = d(() => {
|
|
56
|
+
if (N) return j.find((e) => e.id === N)?.name ?? void 0;
|
|
57
|
+
}, [N, j]), ne = async (e) => {
|
|
58
|
+
if (e.preventDefault(), !N || !U.trim() || !G) return;
|
|
59
|
+
let t = U.trim();
|
|
60
|
+
if (!C.test(t)) {
|
|
61
|
+
H("User ID must be a UUID (e.g. 1a2b3c4d-...). Get it from the user profile page.");
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
J(!0), H(null);
|
|
64
65
|
try {
|
|
65
|
-
await
|
|
66
|
+
await Z(E, { input: {
|
|
66
67
|
actorId: t,
|
|
67
68
|
actorType: "USER",
|
|
68
|
-
roleId:
|
|
69
|
-
scopeId:
|
|
70
|
-
} }),
|
|
69
|
+
roleId: G,
|
|
70
|
+
scopeId: N
|
|
71
|
+
} }), W(""), await Q();
|
|
71
72
|
} catch (e) {
|
|
72
|
-
|
|
73
|
+
H(e instanceof Error ? e.message : "Failed to assign role");
|
|
73
74
|
} finally {
|
|
74
|
-
|
|
75
|
+
J(!1);
|
|
75
76
|
}
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
P(e instanceof Error ? e.message : "Failed to remove role");
|
|
83
|
-
}
|
|
77
|
+
}, re = async (e) => {
|
|
78
|
+
H(null);
|
|
79
|
+
try {
|
|
80
|
+
await Z(D, { assignmentId: e }), await Q();
|
|
81
|
+
} catch (e) {
|
|
82
|
+
H(e instanceof Error ? e.message : "Failed to remove role");
|
|
84
83
|
}
|
|
85
84
|
};
|
|
86
|
-
return
|
|
85
|
+
return P.canManageBillingAccount ? N ? /* @__PURE__ */ m("div", {
|
|
87
86
|
className: "space-y-6",
|
|
88
87
|
children: [
|
|
89
88
|
/* @__PURE__ */ p(n, {
|
|
@@ -100,26 +99,26 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
100
99
|
" ",
|
|
101
100
|
/* @__PURE__ */ p("span", {
|
|
102
101
|
className: "text-text-primary",
|
|
103
|
-
children:
|
|
102
|
+
children: te ?? "Unnamed account"
|
|
104
103
|
}),
|
|
105
|
-
|
|
104
|
+
N ? /* @__PURE__ */ m("details", {
|
|
106
105
|
className: "mt-2 text-xs text-text-secondary",
|
|
107
106
|
children: [/* @__PURE__ */ p("summary", {
|
|
108
107
|
className: "cursor-pointer",
|
|
109
108
|
children: "Account ID"
|
|
110
109
|
}), /* @__PURE__ */ p("span", {
|
|
111
110
|
className: "mt-1 inline-block font-mono",
|
|
112
|
-
children:
|
|
111
|
+
children: N
|
|
113
112
|
})]
|
|
114
113
|
}) : null
|
|
115
114
|
]
|
|
116
115
|
}),
|
|
117
|
-
|
|
116
|
+
V && /* @__PURE__ */ p("div", {
|
|
118
117
|
className: "rounded-lg border border-status-error-border/30 bg-status-error-bg/5 p-4 text-sm text-status-error-text",
|
|
119
|
-
children:
|
|
118
|
+
children: V
|
|
120
119
|
}),
|
|
121
120
|
/* @__PURE__ */ m("form", {
|
|
122
|
-
onSubmit: (e) => void
|
|
121
|
+
onSubmit: (e) => void ne(e),
|
|
123
122
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 space-y-3",
|
|
124
123
|
children: [
|
|
125
124
|
/* @__PURE__ */ p("h3", {
|
|
@@ -139,8 +138,8 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
139
138
|
/* @__PURE__ */ p("input", {
|
|
140
139
|
id: "newActorId",
|
|
141
140
|
type: "text",
|
|
142
|
-
value:
|
|
143
|
-
onChange: (e) =>
|
|
141
|
+
value: U,
|
|
142
|
+
onChange: (e) => W(e.target.value),
|
|
144
143
|
placeholder: "Paste the user ID from their profile page",
|
|
145
144
|
className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm",
|
|
146
145
|
required: !0
|
|
@@ -156,11 +155,11 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
156
155
|
children: "Role"
|
|
157
156
|
}), /* @__PURE__ */ p("select", {
|
|
158
157
|
id: "newRoleId",
|
|
159
|
-
value:
|
|
160
|
-
onChange: (e) =>
|
|
158
|
+
value: G,
|
|
159
|
+
onChange: (e) => K(e.target.value),
|
|
161
160
|
className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm",
|
|
162
161
|
required: !0,
|
|
163
|
-
children:
|
|
162
|
+
children: F.map((e) => /* @__PURE__ */ p("option", {
|
|
164
163
|
value: e.id,
|
|
165
164
|
children: e.name
|
|
166
165
|
}, e.id))
|
|
@@ -168,9 +167,9 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
168
167
|
}),
|
|
169
168
|
/* @__PURE__ */ p("button", {
|
|
170
169
|
type: "submit",
|
|
171
|
-
disabled:
|
|
170
|
+
disabled: q || !U.trim() || !G,
|
|
172
171
|
className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text disabled:opacity-50",
|
|
173
|
-
children:
|
|
172
|
+
children: q ? "Adding…" : "Add member"
|
|
174
173
|
})
|
|
175
174
|
]
|
|
176
175
|
}),
|
|
@@ -185,10 +184,10 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
185
184
|
className: "text-xs text-text-secondary",
|
|
186
185
|
children: "Each member's role applies only to this billing account."
|
|
187
186
|
})]
|
|
188
|
-
}),
|
|
187
|
+
}), z ? /* @__PURE__ */ p("div", {
|
|
189
188
|
className: "p-6 text-sm text-text-secondary",
|
|
190
189
|
children: "Loading…"
|
|
191
|
-
}) :
|
|
190
|
+
}) : $.length === 0 ? /* @__PURE__ */ p("div", {
|
|
192
191
|
className: "p-6 text-sm text-text-secondary",
|
|
193
192
|
children: "No members assigned yet."
|
|
194
193
|
}) : /* @__PURE__ */ m("table", {
|
|
@@ -213,13 +212,13 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
213
212
|
children: "Actions"
|
|
214
213
|
})
|
|
215
214
|
]
|
|
216
|
-
}) }), /* @__PURE__ */ p("tbody", { children:
|
|
215
|
+
}) }), /* @__PURE__ */ p("tbody", { children: $.map((e) => /* @__PURE__ */ m("tr", {
|
|
217
216
|
className: "border-b border-border-subtle last:border-0",
|
|
218
217
|
children: [
|
|
219
218
|
/* @__PURE__ */ p("td", {
|
|
220
219
|
className: "px-4 py-3",
|
|
221
220
|
children: /* @__PURE__ */ p(r, {
|
|
222
|
-
profile:
|
|
221
|
+
profile: ee[e.actorId] ?? {
|
|
223
222
|
actorId: e.actorId,
|
|
224
223
|
actorType: e.actorType,
|
|
225
224
|
displayName: e.actorType.toLowerCase() === "user" ? `User ${e.actorId.slice(0, 8)}…` : e.actorType,
|
|
@@ -241,7 +240,7 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
241
240
|
className: "inline-flex items-center gap-1 rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs",
|
|
242
241
|
children: [e.role?.name ?? e.roleId, /* @__PURE__ */ p("button", {
|
|
243
242
|
type: "button",
|
|
244
|
-
onClick: () =>
|
|
243
|
+
onClick: () => X(e.id),
|
|
245
244
|
className: "text-text-secondary hover:text-status-error-text",
|
|
246
245
|
"aria-label": `Remove ${e.role?.name ?? "role"}`,
|
|
247
246
|
children: "×"
|
|
@@ -273,7 +272,7 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
273
272
|
}),
|
|
274
273
|
/* @__PURE__ */ p("ul", {
|
|
275
274
|
className: "mt-3 space-y-2",
|
|
276
|
-
children:
|
|
275
|
+
children: F.map((e) => /* @__PURE__ */ m("li", {
|
|
277
276
|
className: "flex items-start gap-3 rounded-md border border-border-subtle bg-bg-surface p-3",
|
|
278
277
|
children: [/* @__PURE__ */ m("div", {
|
|
279
278
|
className: "flex-1",
|
|
@@ -291,6 +290,19 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
291
290
|
}, e.id))
|
|
292
291
|
})
|
|
293
292
|
]
|
|
293
|
+
}),
|
|
294
|
+
/* @__PURE__ */ p(h, {
|
|
295
|
+
open: Y !== null,
|
|
296
|
+
onOpenChange: (e) => {
|
|
297
|
+
e || X(null);
|
|
298
|
+
},
|
|
299
|
+
children: /* @__PURE__ */ m(v, { children: [/* @__PURE__ */ m(x, { children: [/* @__PURE__ */ p(S, { children: "Remove role assignment?" }), /* @__PURE__ */ p(y, { children: "Remove this role assignment? The member will lose the access this role grants for this billing account." })] }), /* @__PURE__ */ m(b, { children: [/* @__PURE__ */ p(_, { children: "Cancel" }), /* @__PURE__ */ p(g, {
|
|
300
|
+
onClick: () => {
|
|
301
|
+
let e = Y;
|
|
302
|
+
X(null), e && re(e);
|
|
303
|
+
},
|
|
304
|
+
children: "Remove"
|
|
305
|
+
})] })] })
|
|
294
306
|
})
|
|
295
307
|
]
|
|
296
308
|
}) : /* @__PURE__ */ m("div", {
|
|
@@ -305,6 +317,6 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
|
|
|
305
317
|
}) : /* @__PURE__ */ p(t, { message: "You don't have permission to manage team and permissions for this billing account." });
|
|
306
318
|
};
|
|
307
319
|
//#endregion
|
|
308
|
-
export {
|
|
320
|
+
export { O as TeamPermissionsPage };
|
|
309
321
|
|
|
310
322
|
//# sourceMappingURL=TeamPermissionsPage.js.map
|