@burdenoff/microfe-billing 2026.624.3 → 2026.625.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingAdminRoutes.js +1 -1
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +2 -2
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +4 -4
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +2 -2
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +98 -99
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +88 -85
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +6 -6
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +2 -2
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +360 -370
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +2 -2
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +2 -2
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +102 -104
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +5 -5
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +29 -28
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +2 -2
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +3 -3
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +2 -2
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +139 -142
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +6 -6
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +4 -4
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +142 -133
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +2 -2
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +4 -4
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +2 -2
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +4 -4
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +2 -2
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +2 -2
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +2 -2
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +5 -5
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +2 -2
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -265
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +7 -7
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +13 -13
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/ui/Card.js +1 -1
- package/dist/billing/shared/ui/Card.js.map +1 -1
- package/dist/billing/shared/ui/MetricCard.js +28 -21
- package/dist/billing/shared/ui/MetricCard.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateBillingAccountModal.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/CreateBillingAccountModal.tsx"],"sourcesContent":["/**\n * Create Billing Account Modal Component\n * Modal for creating a new billing account\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport {\n X,\n Building2,\n Mail,\n FileText,\n Loader2,\n AlertCircle,\n ChevronDown,\n Search,\n Check,\n MapPin,\n} from 'lucide-react';\nimport type { CreateBillingAccountInput, SetBillingAddressInput } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CreateBillingAccountModalProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (\n input: CreateBillingAccountInput,\n address?: Omit<SetBillingAddressInput, 'billingAccountId'>\n ) => Promise<void>;\n isSubmitting?: boolean;\n error?: string | null;\n}\n\n// Form data includes both account and address fields\ninterface FormData extends CreateBillingAccountInput {\n // Address fields\n country: string;\n line1: string;\n line2: string;\n city: string;\n state: string;\n postalCode: string;\n}\n\n// Common countries (sorted by usage)\nconst COUNTRIES = [\n { code: 'IN', name: 'India' },\n { code: 'US', name: 'United States' },\n { code: 'GB', name: 'United Kingdom' },\n { code: 'CA', name: 'Canada' },\n { code: 'AU', name: 'Australia' },\n { code: 'DE', name: 'Germany' },\n { code: 'FR', name: 'France' },\n { code: 'SG', name: 'Singapore' },\n { code: 'AE', name: 'United Arab Emirates' },\n { code: 'JP', name: 'Japan' },\n { code: 'NL', name: 'Netherlands' },\n { code: 'CH', name: 'Switzerland' },\n { code: 'SE', name: 'Sweden' },\n { code: 'NO', name: 'Norway' },\n { code: 'DK', name: 'Denmark' },\n { code: 'NZ', name: 'New Zealand' },\n { code: 'IE', name: 'Ireland' },\n { code: 'IT', name: 'Italy' },\n { code: 'ES', name: 'Spain' },\n { code: 'BR', name: 'Brazil' },\n { code: 'MX', name: 'Mexico' },\n { code: 'ZA', name: 'South Africa' },\n { code: 'KR', name: 'South Korea' },\n { code: 'HK', name: 'Hong Kong' },\n { code: 'MY', name: 'Malaysia' },\n { code: 'ID', name: 'Indonesia' },\n { code: 'PH', name: 'Philippines' },\n { code: 'TH', name: 'Thailand' },\n { code: 'VN', name: 'Vietnam' },\n { code: 'PK', name: 'Pakistan' },\n { code: 'BD', name: 'Bangladesh' },\n { code: 'LK', name: 'Sri Lanka' },\n];\n\n// Indian states and union territories (for GST IGST vs CGST+SGST determination)\nconst INDIAN_STATES = [\n 'Andaman and Nicobar Islands',\n 'Andhra Pradesh',\n 'Arunachal Pradesh',\n 'Assam',\n 'Bihar',\n 'Chandigarh',\n 'Chhattisgarh',\n 'Dadra and Nagar Haveli and Daman and Diu',\n 'Delhi',\n 'Goa',\n 'Gujarat',\n 'Haryana',\n 'Himachal Pradesh',\n 'Jammu and Kashmir',\n 'Jharkhand',\n 'Karnataka',\n 'Kerala',\n 'Ladakh',\n 'Lakshadweep',\n 'Madhya Pradesh',\n 'Maharashtra',\n 'Manipur',\n 'Meghalaya',\n 'Mizoram',\n 'Nagaland',\n 'Odisha',\n 'Puducherry',\n 'Punjab',\n 'Rajasthan',\n 'Sikkim',\n 'Tamil Nadu',\n 'Telangana',\n 'Tripura',\n 'Uttar Pradesh',\n 'Uttarakhand',\n 'West Bengal',\n];\n\ninterface CurrencyOption {\n code: string;\n name: string;\n symbol: string;\n flag: string;\n}\n\n// All major currencies supported (sorted by popularity/usage)\nconst CURRENCY_OPTIONS: CurrencyOption[] = [\n // Most common\n { code: 'USD', name: 'US Dollar', symbol: '$', flag: 'US' },\n { code: 'EUR', name: 'Euro', symbol: '€', flag: 'EU' },\n { code: 'GBP', name: 'British Pound', symbol: '£', flag: 'GB' },\n { code: 'INR', name: 'Indian Rupee', symbol: '₹', flag: 'IN' },\n { code: 'CAD', name: 'Canadian Dollar', symbol: 'C$', flag: 'CA' },\n { code: 'AUD', name: 'Australian Dollar', symbol: 'A$', flag: 'AU' },\n { code: 'JPY', name: 'Japanese Yen', symbol: '¥', flag: 'JP' },\n { code: 'CNY', name: 'Chinese Yuan', symbol: '¥', flag: 'CN' },\n { code: 'CHF', name: 'Swiss Franc', symbol: 'CHF', flag: 'CH' },\n { code: 'SGD', name: 'Singapore Dollar', symbol: 'S$', flag: 'SG' },\n { code: 'HKD', name: 'Hong Kong Dollar', symbol: 'HK$', flag: 'HK' },\n { code: 'NZD', name: 'New Zealand Dollar', symbol: 'NZ$', flag: 'NZ' },\n { code: 'SEK', name: 'Swedish Krona', symbol: 'kr', flag: 'SE' },\n { code: 'NOK', name: 'Norwegian Krone', symbol: 'kr', flag: 'NO' },\n { code: 'DKK', name: 'Danish Krone', symbol: 'kr', flag: 'DK' },\n { code: 'MXN', name: 'Mexican Peso', symbol: '$', flag: 'MX' },\n { code: 'BRL', name: 'Brazilian Real', symbol: 'R$', flag: 'BR' },\n { code: 'KRW', name: 'South Korean Won', symbol: '₩', flag: 'KR' },\n { code: 'ZAR', name: 'South African Rand', symbol: 'R', flag: 'ZA' },\n { code: 'AED', name: 'UAE Dirham', symbol: 'د.إ', flag: 'AE' },\n { code: 'SAR', name: 'Saudi Riyal', symbol: '﷼', flag: 'SA' },\n { code: 'PLN', name: 'Polish Złoty', symbol: 'zł', flag: 'PL' },\n { code: 'THB', name: 'Thai Baht', symbol: '฿', flag: 'TH' },\n { code: 'MYR', name: 'Malaysian Ringgit', symbol: 'RM', flag: 'MY' },\n { code: 'IDR', name: 'Indonesian Rupiah', symbol: 'Rp', flag: 'ID' },\n { code: 'PHP', name: 'Philippine Peso', symbol: '₱', flag: 'PH' },\n { code: 'TWD', name: 'Taiwan Dollar', symbol: 'NT$', flag: 'TW' },\n { code: 'CZK', name: 'Czech Koruna', symbol: 'Kč', flag: 'CZ' },\n { code: 'HUF', name: 'Hungarian Forint', symbol: 'Ft', flag: 'HU' },\n { code: 'ILS', name: 'Israeli Shekel', symbol: '₪', flag: 'IL' },\n { code: 'CLP', name: 'Chilean Peso', symbol: '$', flag: 'CL' },\n { code: 'COP', name: 'Colombian Peso', symbol: '$', flag: 'CO' },\n { code: 'PEN', name: 'Peruvian Sol', symbol: 'S/', flag: 'PE' },\n { code: 'ARS', name: 'Argentine Peso', symbol: '$', flag: 'AR' },\n { code: 'VND', name: 'Vietnamese Dong', symbol: '₫', flag: 'VN' },\n { code: 'EGP', name: 'Egyptian Pound', symbol: 'E£', flag: 'EG' },\n { code: 'NGN', name: 'Nigerian Naira', symbol: '₦', flag: 'NG' },\n { code: 'KES', name: 'Kenyan Shilling', symbol: 'KSh', flag: 'KE' },\n { code: 'PKR', name: 'Pakistani Rupee', symbol: '₨', flag: 'PK' },\n { code: 'BDT', name: 'Bangladeshi Taka', symbol: '৳', flag: 'BD' },\n { code: 'LKR', name: 'Sri Lankan Rupee', symbol: 'Rs', flag: 'LK' },\n { code: 'RON', name: 'Romanian Leu', symbol: 'lei', flag: 'RO' },\n { code: 'BGN', name: 'Bulgarian Lev', symbol: 'лв', flag: 'BG' },\n { code: 'HRK', name: 'Croatian Kuna', symbol: 'kn', flag: 'HR' },\n { code: 'UAH', name: 'Ukrainian Hryvnia', symbol: '₴', flag: 'UA' },\n { code: 'TRY', name: 'Turkish Lira', symbol: '₺', flag: 'TR' },\n { code: 'RUB', name: 'Russian Ruble', symbol: '₽', flag: 'RU' },\n { code: 'QAR', name: 'Qatari Riyal', symbol: 'ر.ق', flag: 'QA' },\n { code: 'KWD', name: 'Kuwaiti Dinar', symbol: 'د.ك', flag: 'KW' },\n { code: 'BHD', name: 'Bahraini Dinar', symbol: '.د.ب', flag: 'BH' },\n { code: 'OMR', name: 'Omani Rial', symbol: 'ر.ع.', flag: 'OM' },\n];\n\n/**\n * Convert country/region code to flag emoji\n */\nfunction getFlagEmoji(countryCode: string): string {\n if (countryCode === 'EU') return '🇪🇺';\n const codePoints = countryCode\n .toUpperCase()\n .split('')\n .map((char) => 127397 + char.charCodeAt(0));\n return String.fromCodePoint(...codePoints);\n}\n\nexport const CreateBillingAccountModal: FC<CreateBillingAccountModalProps> = ({\n isOpen,\n onClose,\n onSubmit,\n isSubmitting = false,\n error = null,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [formData, setFormData] = useState<FormData>({\n name: '',\n email: '',\n billingEmail: '',\n currency: 'USD',\n taxId: '',\n // Address fields\n country: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n });\n const [validationErrors, setValidationErrors] = useState<Record<string, string>>({});\n const [currencyDropdownOpen, setCurrencyDropdownOpen] = useState(false);\n const [currencySearch, setCurrencySearch] = useState('');\n const modalRef = useRef<HTMLDivElement>(null);\n const firstInputRef = useRef<HTMLInputElement>(null);\n const currencyDropdownRef = useRef<HTMLDivElement>(null);\n const currencySearchInputRef = useRef<HTMLInputElement>(null);\n\n // Filter currencies by search query\n const filteredCurrencies = useMemo(() => {\n if (!currencySearch.trim()) return CURRENCY_OPTIONS;\n const query = currencySearch.toLowerCase();\n return CURRENCY_OPTIONS.filter(\n (c) =>\n c.code.toLowerCase().includes(query) ||\n c.name.toLowerCase().includes(query) ||\n c.symbol.includes(query)\n );\n }, [currencySearch]);\n\n // Get selected currency details\n const selectedCurrencyDetails = useMemo(() => {\n return CURRENCY_OPTIONS.find((c) => c.code === formData.currency) || CURRENCY_OPTIONS[0];\n }, [formData.currency]);\n\n // Reset form when modal opens\n useEffect(() => {\n if (isOpen) {\n setFormData({\n name: '',\n email: '',\n billingEmail: '',\n currency: 'USD',\n taxId: '',\n // Address fields\n country: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n });\n setValidationErrors({});\n setCurrencyDropdownOpen(false);\n setCurrencySearch('');\n const focusTimer = setTimeout(() => firstInputRef.current?.focus(), 100);\n return () => clearTimeout(focusTimer);\n }\n return undefined;\n }, [isOpen]);\n\n // Close currency dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n currencyDropdownRef.current &&\n !currencyDropdownRef.current.contains(event.target as Node)\n ) {\n setCurrencyDropdownOpen(false);\n setCurrencySearch('');\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Focus search input when currency dropdown opens\n useEffect(() => {\n if (currencyDropdownOpen && currencySearchInputRef.current) {\n currencySearchInputRef.current.focus();\n }\n }, [currencyDropdownOpen]);\n\n // Handle escape key\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isOpen && !isSubmitting) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, isSubmitting, onClose]);\n\n // Trap focus within modal\n useEffect(() => {\n if (!isOpen) return;\n\n const modal = modalRef.current;\n if (!modal) return;\n\n const focusableElements = modal.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n );\n const firstElement = focusableElements[0] as HTMLElement;\n const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;\n\n const handleTabKey = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n e.preventDefault();\n lastElement.focus();\n }\n } else {\n if (document.activeElement === lastElement) {\n e.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n modal.addEventListener('keydown', handleTabKey);\n return () => modal.removeEventListener('keydown', handleTabKey);\n }, [isOpen]);\n\n const validateForm = useCallback(() => {\n const errors: Record<string, string> = {};\n\n if (!formData.name.trim()) {\n errors.name = 'Account name is required';\n }\n\n if (!formData.email.trim()) {\n errors.email = 'Email is required';\n } else if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.email)) {\n errors.email = 'Please enter a valid email address';\n }\n\n if (formData.billingEmail && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.billingEmail)) {\n errors.billingEmail = 'Please enter a valid email address';\n }\n\n // Address validation (required for proper tax calculation)\n if (!formData.country) {\n errors.country = 'Country is required';\n }\n\n if (!formData.line1.trim()) {\n errors.line1 = 'Address line 1 is required';\n }\n\n if (!formData.city.trim()) {\n errors.city = 'City is required';\n }\n\n // State is required for India (for IGST vs CGST+SGST determination)\n if (formData.country === 'IN' && !formData.state) {\n errors.state = 'State is required for India';\n }\n\n if (!formData.postalCode.trim()) {\n errors.postalCode = 'Postal code is required';\n }\n\n setValidationErrors(errors);\n return Object.keys(errors).length === 0;\n }, [formData]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!validateForm()) return;\n\n const input: CreateBillingAccountInput = {\n name: formData.name.trim(),\n email: formData.email.trim(),\n currency: formData.currency,\n };\n\n if (formData.billingEmail?.trim()) {\n input.billingEmail = formData.billingEmail.trim();\n }\n if (formData.taxId?.trim()) {\n input.taxId = formData.taxId.trim();\n }\n\n // Build address data (without billingAccountId - will be added by parent)\n const address: Omit<SetBillingAddressInput, 'billingAccountId'> = {\n country: formData.country,\n line1: formData.line1.trim(),\n city: formData.city.trim(),\n postalCode: formData.postalCode.trim(),\n };\n\n if (formData.line2?.trim()) {\n address.line2 = formData.line2.trim();\n }\n if (formData.state) {\n address.state = formData.state;\n }\n\n await onSubmit(input, address);\n };\n\n const handleChange = (field: keyof FormData, value: string) => {\n setFormData((prev) => {\n const updated = { ...prev, [field]: value };\n // Auto-set currency to INR when India is selected\n if (field === 'country' && value === 'IN' && prev.currency !== 'INR') {\n updated.currency = 'INR';\n }\n // Clear state when country changes (to avoid invalid state for new country)\n if (field === 'country' && value !== prev.country) {\n updated.state = '';\n }\n return updated;\n });\n // Clear validation error when user types\n if (validationErrors[field]) {\n setValidationErrors((prev) => {\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n >\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-bg-overlay backdrop-blur-sm\"\n onClick={!isSubmitting ? onClose : undefined}\n aria-hidden=\"true\"\n />\n\n {/* Modal */}\n <div\n ref={modalRef}\n className=\"relative w-full max-w-2xl mx-4 bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] max-h-[90vh] flex flex-col overflow-hidden\"\n >\n {/* Header */}\n <header className=\"flex items-center justify-between p-4 border-b border-border-subtle shrink-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Building2 className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-text-primary\">\n Create Billing Account\n </h2>\n <p className=\"text-sm text-text-secondary\">\n Set up a new billing account for your organization\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isSubmitting}\n className=\"p-2 rounded-button hover:bg-bg-sunken text-text-secondary hover:text-text-primary transition-colors disabled:opacity-50\"\n aria-label=\"Close modal\"\n >\n <X className=\"size-5\" />\n </button>\n </header>\n\n {/* Form - Scrollable content area */}\n <form onSubmit={handleSubmit} className=\"p-4 space-y-4 overflow-y-auto flex-1\">\n {/* Error Message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle border border-status-error-border text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <p className=\"text-sm\">{error}</p>\n </div>\n )}\n\n {/* Account Name */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary\">\n Account Name <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <Building2 className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n ref={firstInputRef}\n type=\"text\"\n id=\"name\"\n value={formData.name}\n onChange={(e) => handleChange('name', e.target.value)}\n className={`\n w-full pl-[3.25rem] pr-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.name ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"e.g., Acme Corporation\"\n disabled={isSubmitting}\n />\n </div>\n {validationErrors.name && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.name}</p>\n )}\n </div>\n\n {/* Email Row - 2 columns on desktop */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {/* Email */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"email\" className=\"block text-sm font-medium text-text-primary\">\n Email <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <Mail className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"email\"\n id=\"email\"\n value={formData.email}\n onChange={(e) => handleChange('email', e.target.value)}\n className={`\n w-full pl-[3.25rem] pr-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.email ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"contact@company.com\"\n disabled={isSubmitting}\n />\n </div>\n {validationErrors.email && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.email}</p>\n )}\n </div>\n\n {/* Billing Email */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"billingEmail\" className=\"block text-sm font-medium text-text-primary\">\n Billing Email <span className=\"text-text-secondary text-xs\">(optional)</span>\n </label>\n <div className=\"relative\">\n <Mail className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"email\"\n id=\"billingEmail\"\n value={formData.billingEmail || ''}\n onChange={(e) => handleChange('billingEmail', e.target.value)}\n className={`\n w-full pl-[3.25rem] pr-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.billingEmail ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"billing@company.com\"\n disabled={isSubmitting}\n />\n </div>\n {validationErrors.billingEmail && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.billingEmail}</p>\n )}\n </div>\n </div>\n\n {/* Billing Address Section */}\n <div className=\"pt-3 border-t border-border-subtle\">\n <h3 className=\"text-sm font-medium text-text-primary mb-3 flex items-center gap-2\">\n <MapPin className=\"size-4 text-text-secondary\" />\n Billing Address\n </h3>\n\n {/* Country + Address Line 1 - 2 columns on desktop */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3 mb-3\">\n {/* Country */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"country\" className=\"block text-sm font-medium text-text-primary\">\n Country <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"country\"\n value={formData.country}\n onChange={(e) => handleChange('country', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.country ? 'border-status-error-border' : 'border-border-default'}\n `}\n disabled={isSubmitting}\n >\n <option value=\"\">\n {tr('billing.overview.selectCountry', 'Select a country')}\n </option>\n {COUNTRIES.map((c) => (\n <option key={c.code} value={c.code}>\n {c.name}\n </option>\n ))}\n </select>\n {validationErrors.country && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.country}</p>\n )}\n </div>\n\n {/* Address Line 1 */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"line1\" className=\"block text-sm font-medium text-text-primary\">\n Address Line 1 <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"line1\"\n value={formData.line1}\n onChange={(e) => handleChange('line1', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.line1 ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"Street address, P.O. box\"\n disabled={isSubmitting}\n />\n {validationErrors.line1 && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.line1}</p>\n )}\n </div>\n </div>\n\n {/* Address Line 2 */}\n <div className=\"space-y-1.5 mb-3\">\n <label htmlFor=\"line2\" className=\"block text-sm font-medium text-text-primary\">\n Address Line 2 <span className=\"text-text-secondary text-xs\">(optional)</span>\n </label>\n <input\n type=\"text\"\n id=\"line2\"\n value={formData.line2}\n onChange={(e) => handleChange('line2', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-default rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder=\"Apartment, suite, unit, building, floor\"\n disabled={isSubmitting}\n />\n </div>\n\n {/* City + State + Postal Code Row - 3 columns on desktop */}\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 mb-3\">\n {/* City */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"city\" className=\"block text-sm font-medium text-text-primary\">\n City <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"city\"\n value={formData.city}\n onChange={(e) => handleChange('city', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.city ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"City\"\n disabled={isSubmitting}\n />\n {validationErrors.city && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.city}</p>\n )}\n </div>\n\n {/* State - Dropdown for India, text input for others */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"state\" className=\"block text-sm font-medium text-text-primary\">\n State{' '}\n {formData.country === 'IN' && <span className=\"text-status-error-text\">*</span>}\n </label>\n {formData.country === 'IN' ? (\n <select\n id=\"state\"\n value={formData.state}\n onChange={(e) => handleChange('state', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.state ? 'border-status-error-border' : 'border-border-default'}\n `}\n disabled={isSubmitting}\n >\n <option value=\"\">Select state</option>\n {INDIAN_STATES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n ) : (\n <input\n type=\"text\"\n id=\"state\"\n value={formData.state}\n onChange={(e) => handleChange('state', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-default rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder=\"State/Province\"\n disabled={isSubmitting}\n />\n )}\n {validationErrors.state && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.state}</p>\n )}\n </div>\n\n {/* Postal Code */}\n <div className=\"space-y-1.5 col-span-2 sm:col-span-1\">\n <label htmlFor=\"postalCode\" className=\"block text-sm font-medium text-text-primary\">\n Postal Code <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"postalCode\"\n value={formData.postalCode}\n onChange={(e) => handleChange('postalCode', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.postalCode ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder={formData.country === 'IN' ? '600001' : 'ZIP/Postal code'}\n disabled={isSubmitting}\n />\n {validationErrors.postalCode && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.postalCode}</p>\n )}\n </div>\n </div>\n </div>\n\n {/* Currency & Tax ID - 2 columns on desktop */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 pt-3 border-t border-border-subtle\">\n {/* Currency - Searchable Dropdown */}\n <div className=\"space-y-1.5\">\n <label className=\"block text-sm font-medium text-text-primary\">\n {tr('billing.overview.currency', 'Currency')}\n </label>\n <div className=\"relative\" ref={currencyDropdownRef}>\n {/* Dropdown trigger */}\n <button\n type=\"button\"\n onClick={() => !isSubmitting && setCurrencyDropdownOpen(!currencyDropdownOpen)}\n disabled={isSubmitting}\n className={`\n w-full flex items-center justify-between gap-2 px-3 py-2 rounded-lg border\n transition-all duration-200 text-left\n disabled:opacity-50 disabled:cursor-not-allowed\n ${\n currencyDropdownOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-default hover:border-border-strong'\n }\n `}\n >\n <div className=\"flex items-center gap-2\">\n <span className=\"text-base\">{getFlagEmoji(selectedCurrencyDetails.flag)}</span>\n <span className=\"font-medium text-text-primary\">\n {selectedCurrencyDetails.code}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {selectedCurrencyDetails.symbol} · {selectedCurrencyDetails.name}\n </span>\n </div>\n <ChevronDown\n className={`size-4 text-text-secondary transition-transform ${currencyDropdownOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {/* Dropdown menu - Opens UPWARD to avoid form overflow clipping */}\n {currencyDropdownOpen && (\n <div\n className=\"absolute z-50 w-full bottom-full mb-1 bg-bg-elevated border border-border-subtle rounded-card shadow-elevation-3 overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n >\n {/* Currency list - at top when opening upward */}\n <div className=\"max-h-48 overflow-y-auto\">\n {filteredCurrencies.length === 0 ? (\n <div className=\"px-3 py-4 text-sm text-text-secondary text-center\">\n No currencies found\n </div>\n ) : (\n filteredCurrencies.map((currency) => {\n const isSelected = currency.code === formData.currency;\n return (\n <button\n key={currency.code}\n type=\"button\"\n onClick={() => {\n handleChange('currency', currency.code);\n setCurrencyDropdownOpen(false);\n setCurrencySearch('');\n }}\n className={`\n w-full flex items-center gap-3 px-3 py-2 text-left\n transition-colors\n ${\n isSelected\n ? 'bg-[var(--color-accent-soft)] text-text-primary'\n : 'hover:bg-bg-sunken text-text-primary'\n }\n `}\n >\n <span className=\"text-base shrink-0\">\n {getFlagEmoji(currency.flag)}\n </span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{currency.code}</span>\n <span className=\"text-xs text-text-secondary\">\n {currency.symbol}\n </span>\n </div>\n <p className=\"text-xs text-text-secondary truncate\">\n {currency.name}\n </p>\n </div>\n {isSelected && <Check className=\"size-4 text-text-link shrink-0\" />}\n </button>\n );\n })\n )}\n </div>\n\n {/* Search input - at bottom when opening upward */}\n <div className=\"p-2 border-t border-border-subtle\">\n <div className=\"relative\">\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n ref={currencySearchInputRef}\n type=\"text\"\n placeholder=\"Search currency...\"\n value={currencySearch}\n onChange={(e) => setCurrencySearch(e.target.value)}\n onMouseDown={(e) => e.stopPropagation()}\n className=\"w-full pl-8 pr-3 py-2 text-sm bg-bg-surface border border-border-subtle rounded-button focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Tax ID */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"taxId\" className=\"block text-sm font-medium text-text-primary\">\n Tax ID / VAT Number <span className=\"text-text-secondary text-xs\">(optional)</span>\n </label>\n <div className=\"relative\">\n <FileText className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"text\"\n id=\"taxId\"\n value={formData.taxId || ''}\n onChange={(e) => handleChange('taxId', e.target.value)}\n className=\"w-full pl-[3.25rem] pr-3 py-2 border border-border-default rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder={\n formData.country === 'IN' ? 'e.g., 22AAAAA0000A1Z5' : 'Tax ID / VAT Number'\n }\n disabled={isSubmitting}\n />\n </div>\n </div>\n </div>\n </form>\n\n {/* Footer - Fixed at bottom */}\n <footer className=\"flex items-center justify-end gap-3 p-4 border-t border-border-subtle bg-bg-sunken rounded-b-card shrink-0\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isSubmitting}\n className=\"px-4 py-2 text-sm font-medium text-text-primary border border-border-subtle rounded-button hover:bg-bg-sunken hover:border-border-strong transition-all duration-200 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 disabled:opacity-50\"\n >\n {isSubmitting ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Creating…\n </>\n ) : (\n 'Create Account'\n )}\n </button>\n </footer>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateBillingAccountModal.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/CreateBillingAccountModal.tsx"],"sourcesContent":["/**\n * Create Billing Account Modal Component\n * Modal for creating a new billing account\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport {\n X,\n Building2,\n Mail,\n FileText,\n Loader2,\n AlertCircle,\n ChevronDown,\n Search,\n Check,\n MapPin,\n} from 'lucide-react';\nimport type { CreateBillingAccountInput, SetBillingAddressInput } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CreateBillingAccountModalProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (\n input: CreateBillingAccountInput,\n address?: Omit<SetBillingAddressInput, 'billingAccountId'>\n ) => Promise<void>;\n isSubmitting?: boolean;\n error?: string | null;\n}\n\n// Form data includes both account and address fields\ninterface FormData extends CreateBillingAccountInput {\n // Address fields\n country: string;\n line1: string;\n line2: string;\n city: string;\n state: string;\n postalCode: string;\n}\n\n// Common countries (sorted by usage)\nconst COUNTRIES = [\n { code: 'IN', name: 'India' },\n { code: 'US', name: 'United States' },\n { code: 'GB', name: 'United Kingdom' },\n { code: 'CA', name: 'Canada' },\n { code: 'AU', name: 'Australia' },\n { code: 'DE', name: 'Germany' },\n { code: 'FR', name: 'France' },\n { code: 'SG', name: 'Singapore' },\n { code: 'AE', name: 'United Arab Emirates' },\n { code: 'JP', name: 'Japan' },\n { code: 'NL', name: 'Netherlands' },\n { code: 'CH', name: 'Switzerland' },\n { code: 'SE', name: 'Sweden' },\n { code: 'NO', name: 'Norway' },\n { code: 'DK', name: 'Denmark' },\n { code: 'NZ', name: 'New Zealand' },\n { code: 'IE', name: 'Ireland' },\n { code: 'IT', name: 'Italy' },\n { code: 'ES', name: 'Spain' },\n { code: 'BR', name: 'Brazil' },\n { code: 'MX', name: 'Mexico' },\n { code: 'ZA', name: 'South Africa' },\n { code: 'KR', name: 'South Korea' },\n { code: 'HK', name: 'Hong Kong' },\n { code: 'MY', name: 'Malaysia' },\n { code: 'ID', name: 'Indonesia' },\n { code: 'PH', name: 'Philippines' },\n { code: 'TH', name: 'Thailand' },\n { code: 'VN', name: 'Vietnam' },\n { code: 'PK', name: 'Pakistan' },\n { code: 'BD', name: 'Bangladesh' },\n { code: 'LK', name: 'Sri Lanka' },\n];\n\n// Indian states and union territories (for GST IGST vs CGST+SGST determination)\nconst INDIAN_STATES = [\n 'Andaman and Nicobar Islands',\n 'Andhra Pradesh',\n 'Arunachal Pradesh',\n 'Assam',\n 'Bihar',\n 'Chandigarh',\n 'Chhattisgarh',\n 'Dadra and Nagar Haveli and Daman and Diu',\n 'Delhi',\n 'Goa',\n 'Gujarat',\n 'Haryana',\n 'Himachal Pradesh',\n 'Jammu and Kashmir',\n 'Jharkhand',\n 'Karnataka',\n 'Kerala',\n 'Ladakh',\n 'Lakshadweep',\n 'Madhya Pradesh',\n 'Maharashtra',\n 'Manipur',\n 'Meghalaya',\n 'Mizoram',\n 'Nagaland',\n 'Odisha',\n 'Puducherry',\n 'Punjab',\n 'Rajasthan',\n 'Sikkim',\n 'Tamil Nadu',\n 'Telangana',\n 'Tripura',\n 'Uttar Pradesh',\n 'Uttarakhand',\n 'West Bengal',\n];\n\ninterface CurrencyOption {\n code: string;\n name: string;\n symbol: string;\n flag: string;\n}\n\n// All major currencies supported (sorted by popularity/usage)\nconst CURRENCY_OPTIONS: CurrencyOption[] = [\n // Most common\n { code: 'USD', name: 'US Dollar', symbol: '$', flag: 'US' },\n { code: 'EUR', name: 'Euro', symbol: '€', flag: 'EU' },\n { code: 'GBP', name: 'British Pound', symbol: '£', flag: 'GB' },\n { code: 'INR', name: 'Indian Rupee', symbol: '₹', flag: 'IN' },\n { code: 'CAD', name: 'Canadian Dollar', symbol: 'C$', flag: 'CA' },\n { code: 'AUD', name: 'Australian Dollar', symbol: 'A$', flag: 'AU' },\n { code: 'JPY', name: 'Japanese Yen', symbol: '¥', flag: 'JP' },\n { code: 'CNY', name: 'Chinese Yuan', symbol: '¥', flag: 'CN' },\n { code: 'CHF', name: 'Swiss Franc', symbol: 'CHF', flag: 'CH' },\n { code: 'SGD', name: 'Singapore Dollar', symbol: 'S$', flag: 'SG' },\n { code: 'HKD', name: 'Hong Kong Dollar', symbol: 'HK$', flag: 'HK' },\n { code: 'NZD', name: 'New Zealand Dollar', symbol: 'NZ$', flag: 'NZ' },\n { code: 'SEK', name: 'Swedish Krona', symbol: 'kr', flag: 'SE' },\n { code: 'NOK', name: 'Norwegian Krone', symbol: 'kr', flag: 'NO' },\n { code: 'DKK', name: 'Danish Krone', symbol: 'kr', flag: 'DK' },\n { code: 'MXN', name: 'Mexican Peso', symbol: '$', flag: 'MX' },\n { code: 'BRL', name: 'Brazilian Real', symbol: 'R$', flag: 'BR' },\n { code: 'KRW', name: 'South Korean Won', symbol: '₩', flag: 'KR' },\n { code: 'ZAR', name: 'South African Rand', symbol: 'R', flag: 'ZA' },\n { code: 'AED', name: 'UAE Dirham', symbol: 'د.إ', flag: 'AE' },\n { code: 'SAR', name: 'Saudi Riyal', symbol: '﷼', flag: 'SA' },\n { code: 'PLN', name: 'Polish Złoty', symbol: 'zł', flag: 'PL' },\n { code: 'THB', name: 'Thai Baht', symbol: '฿', flag: 'TH' },\n { code: 'MYR', name: 'Malaysian Ringgit', symbol: 'RM', flag: 'MY' },\n { code: 'IDR', name: 'Indonesian Rupiah', symbol: 'Rp', flag: 'ID' },\n { code: 'PHP', name: 'Philippine Peso', symbol: '₱', flag: 'PH' },\n { code: 'TWD', name: 'Taiwan Dollar', symbol: 'NT$', flag: 'TW' },\n { code: 'CZK', name: 'Czech Koruna', symbol: 'Kč', flag: 'CZ' },\n { code: 'HUF', name: 'Hungarian Forint', symbol: 'Ft', flag: 'HU' },\n { code: 'ILS', name: 'Israeli Shekel', symbol: '₪', flag: 'IL' },\n { code: 'CLP', name: 'Chilean Peso', symbol: '$', flag: 'CL' },\n { code: 'COP', name: 'Colombian Peso', symbol: '$', flag: 'CO' },\n { code: 'PEN', name: 'Peruvian Sol', symbol: 'S/', flag: 'PE' },\n { code: 'ARS', name: 'Argentine Peso', symbol: '$', flag: 'AR' },\n { code: 'VND', name: 'Vietnamese Dong', symbol: '₫', flag: 'VN' },\n { code: 'EGP', name: 'Egyptian Pound', symbol: 'E£', flag: 'EG' },\n { code: 'NGN', name: 'Nigerian Naira', symbol: '₦', flag: 'NG' },\n { code: 'KES', name: 'Kenyan Shilling', symbol: 'KSh', flag: 'KE' },\n { code: 'PKR', name: 'Pakistani Rupee', symbol: '₨', flag: 'PK' },\n { code: 'BDT', name: 'Bangladeshi Taka', symbol: '৳', flag: 'BD' },\n { code: 'LKR', name: 'Sri Lankan Rupee', symbol: 'Rs', flag: 'LK' },\n { code: 'RON', name: 'Romanian Leu', symbol: 'lei', flag: 'RO' },\n { code: 'BGN', name: 'Bulgarian Lev', symbol: 'лв', flag: 'BG' },\n { code: 'HRK', name: 'Croatian Kuna', symbol: 'kn', flag: 'HR' },\n { code: 'UAH', name: 'Ukrainian Hryvnia', symbol: '₴', flag: 'UA' },\n { code: 'TRY', name: 'Turkish Lira', symbol: '₺', flag: 'TR' },\n { code: 'RUB', name: 'Russian Ruble', symbol: '₽', flag: 'RU' },\n { code: 'QAR', name: 'Qatari Riyal', symbol: 'ر.ق', flag: 'QA' },\n { code: 'KWD', name: 'Kuwaiti Dinar', symbol: 'د.ك', flag: 'KW' },\n { code: 'BHD', name: 'Bahraini Dinar', symbol: '.د.ب', flag: 'BH' },\n { code: 'OMR', name: 'Omani Rial', symbol: 'ر.ع.', flag: 'OM' },\n];\n\n/**\n * Convert country/region code to flag emoji\n */\nfunction getFlagEmoji(countryCode: string): string {\n if (countryCode === 'EU') return '🇪🇺';\n const codePoints = countryCode\n .toUpperCase()\n .split('')\n .map((char) => 127397 + char.charCodeAt(0));\n return String.fromCodePoint(...codePoints);\n}\n\nexport const CreateBillingAccountModal: FC<CreateBillingAccountModalProps> = ({\n isOpen,\n onClose,\n onSubmit,\n isSubmitting = false,\n error = null,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [formData, setFormData] = useState<FormData>({\n name: '',\n email: '',\n billingEmail: '',\n currency: 'USD',\n taxId: '',\n // Address fields\n country: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n });\n const [validationErrors, setValidationErrors] = useState<Record<string, string>>({});\n const [currencyDropdownOpen, setCurrencyDropdownOpen] = useState(false);\n const [currencySearch, setCurrencySearch] = useState('');\n const modalRef = useRef<HTMLDivElement>(null);\n const firstInputRef = useRef<HTMLInputElement>(null);\n const currencyDropdownRef = useRef<HTMLDivElement>(null);\n const currencySearchInputRef = useRef<HTMLInputElement>(null);\n\n // Filter currencies by search query\n const filteredCurrencies = useMemo(() => {\n if (!currencySearch.trim()) return CURRENCY_OPTIONS;\n const query = currencySearch.toLowerCase();\n return CURRENCY_OPTIONS.filter(\n (c) =>\n c.code.toLowerCase().includes(query) ||\n c.name.toLowerCase().includes(query) ||\n c.symbol.includes(query)\n );\n }, [currencySearch]);\n\n // Get selected currency details\n const selectedCurrencyDetails = useMemo(() => {\n return CURRENCY_OPTIONS.find((c) => c.code === formData.currency) || CURRENCY_OPTIONS[0];\n }, [formData.currency]);\n\n // Reset form when modal opens\n useEffect(() => {\n if (isOpen) {\n setFormData({\n name: '',\n email: '',\n billingEmail: '',\n currency: 'USD',\n taxId: '',\n // Address fields\n country: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n });\n setValidationErrors({});\n setCurrencyDropdownOpen(false);\n setCurrencySearch('');\n const focusTimer = setTimeout(() => firstInputRef.current?.focus(), 100);\n return () => clearTimeout(focusTimer);\n }\n return undefined;\n }, [isOpen]);\n\n // Close currency dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n currencyDropdownRef.current &&\n !currencyDropdownRef.current.contains(event.target as Node)\n ) {\n setCurrencyDropdownOpen(false);\n setCurrencySearch('');\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Focus search input when currency dropdown opens\n useEffect(() => {\n if (currencyDropdownOpen && currencySearchInputRef.current) {\n currencySearchInputRef.current.focus();\n }\n }, [currencyDropdownOpen]);\n\n // Handle escape key\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isOpen && !isSubmitting) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, isSubmitting, onClose]);\n\n // Trap focus within modal\n useEffect(() => {\n if (!isOpen) return;\n\n const modal = modalRef.current;\n if (!modal) return;\n\n const focusableElements = modal.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n );\n const firstElement = focusableElements[0] as HTMLElement;\n const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;\n\n const handleTabKey = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n e.preventDefault();\n lastElement.focus();\n }\n } else {\n if (document.activeElement === lastElement) {\n e.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n modal.addEventListener('keydown', handleTabKey);\n return () => modal.removeEventListener('keydown', handleTabKey);\n }, [isOpen]);\n\n const validateForm = useCallback(() => {\n const errors: Record<string, string> = {};\n\n if (!formData.name.trim()) {\n errors.name = 'Account name is required';\n }\n\n if (!formData.email.trim()) {\n errors.email = 'Email is required';\n } else if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.email)) {\n errors.email = 'Please enter a valid email address';\n }\n\n if (formData.billingEmail && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.billingEmail)) {\n errors.billingEmail = 'Please enter a valid email address';\n }\n\n // Address validation (required for proper tax calculation)\n if (!formData.country) {\n errors.country = 'Country is required';\n }\n\n if (!formData.line1.trim()) {\n errors.line1 = 'Address line 1 is required';\n }\n\n if (!formData.city.trim()) {\n errors.city = 'City is required';\n }\n\n // State is required for India (for IGST vs CGST+SGST determination)\n if (formData.country === 'IN' && !formData.state) {\n errors.state = 'State is required for India';\n }\n\n if (!formData.postalCode.trim()) {\n errors.postalCode = 'Postal code is required';\n }\n\n setValidationErrors(errors);\n return Object.keys(errors).length === 0;\n }, [formData]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!validateForm()) return;\n\n const input: CreateBillingAccountInput = {\n name: formData.name.trim(),\n email: formData.email.trim(),\n currency: formData.currency,\n };\n\n if (formData.billingEmail?.trim()) {\n input.billingEmail = formData.billingEmail.trim();\n }\n if (formData.taxId?.trim()) {\n input.taxId = formData.taxId.trim();\n }\n\n // Build address data (without billingAccountId - will be added by parent)\n const address: Omit<SetBillingAddressInput, 'billingAccountId'> = {\n country: formData.country,\n line1: formData.line1.trim(),\n city: formData.city.trim(),\n postalCode: formData.postalCode.trim(),\n };\n\n if (formData.line2?.trim()) {\n address.line2 = formData.line2.trim();\n }\n if (formData.state) {\n address.state = formData.state;\n }\n\n await onSubmit(input, address);\n };\n\n const handleChange = (field: keyof FormData, value: string) => {\n setFormData((prev) => {\n const updated = { ...prev, [field]: value };\n // Auto-set currency to INR when India is selected\n if (field === 'country' && value === 'IN' && prev.currency !== 'INR') {\n updated.currency = 'INR';\n }\n // Clear state when country changes (to avoid invalid state for new country)\n if (field === 'country' && value !== prev.country) {\n updated.state = '';\n }\n return updated;\n });\n // Clear validation error when user types\n if (validationErrors[field]) {\n setValidationErrors((prev) => {\n const next = { ...prev };\n delete next[field];\n return next;\n });\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n >\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-bg-overlay backdrop-blur-sm\"\n onClick={!isSubmitting ? onClose : undefined}\n aria-hidden=\"true\"\n />\n\n {/* Modal */}\n <div\n ref={modalRef}\n className=\"relative w-full max-w-2xl mx-4 bg-bg-surface border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] max-h-[90vh] flex flex-col overflow-hidden\"\n >\n {/* Header */}\n <header className=\"flex items-center justify-between p-4 border-b border-border-subtle shrink-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Building2 className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-text-primary\">\n Create Billing Account\n </h2>\n <p className=\"text-sm text-text-secondary\">\n Set up a new billing account for your organization\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isSubmitting}\n className=\"p-2 rounded-button hover:bg-bg-sunken text-text-secondary hover:text-text-primary transition-colors disabled:opacity-50\"\n aria-label=\"Close modal\"\n >\n <X className=\"size-5\" />\n </button>\n </header>\n\n {/* Form - Scrollable content area */}\n <form onSubmit={handleSubmit} className=\"p-4 space-y-4 overflow-y-auto flex-1\">\n {/* Error Message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-status-error-bg-subtle border border-status-error-border text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <p className=\"text-sm\">{error}</p>\n </div>\n )}\n\n {/* Account Name */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary\">\n Account Name <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <Building2 className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n ref={firstInputRef}\n type=\"text\"\n id=\"name\"\n value={formData.name}\n onChange={(e) => handleChange('name', e.target.value)}\n className={`\n w-full pl-[3.25rem] pr-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.name ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"e.g., Acme Corporation\"\n disabled={isSubmitting}\n />\n </div>\n {validationErrors.name && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.name}</p>\n )}\n </div>\n\n {/* Email Row - 2 columns on desktop */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {/* Email */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"email\" className=\"block text-sm font-medium text-text-primary\">\n Email <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <Mail className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"email\"\n id=\"email\"\n value={formData.email}\n onChange={(e) => handleChange('email', e.target.value)}\n className={`\n w-full pl-[3.25rem] pr-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.email ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"contact@company.com\"\n disabled={isSubmitting}\n />\n </div>\n {validationErrors.email && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.email}</p>\n )}\n </div>\n\n {/* Billing Email */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"billingEmail\" className=\"block text-sm font-medium text-text-primary\">\n Billing Email <span className=\"text-text-secondary text-xs\">(optional)</span>\n </label>\n <div className=\"relative\">\n <Mail className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"email\"\n id=\"billingEmail\"\n value={formData.billingEmail || ''}\n onChange={(e) => handleChange('billingEmail', e.target.value)}\n className={`\n w-full pl-[3.25rem] pr-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.billingEmail ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"billing@company.com\"\n disabled={isSubmitting}\n />\n </div>\n {validationErrors.billingEmail && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.billingEmail}</p>\n )}\n </div>\n </div>\n\n {/* Billing Address Section */}\n <div className=\"pt-3 border-t border-border-subtle\">\n <h3 className=\"text-sm font-medium text-text-primary mb-3 flex items-center gap-2\">\n <MapPin className=\"size-4 text-text-secondary\" />\n Billing Address\n </h3>\n\n {/* Country + Address Line 1 - 2 columns on desktop */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3 mb-3\">\n {/* Country */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"country\" className=\"block text-sm font-medium text-text-primary\">\n Country <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"country\"\n value={formData.country}\n onChange={(e) => handleChange('country', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.country ? 'border-status-error-border' : 'border-border-default'}\n `}\n disabled={isSubmitting}\n >\n <option value=\"\">\n {tr('billing.overview.selectCountry', 'Select a country')}\n </option>\n {COUNTRIES.map((c) => (\n <option key={c.code} value={c.code}>\n {c.name}\n </option>\n ))}\n </select>\n {validationErrors.country && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.country}</p>\n )}\n </div>\n\n {/* Address Line 1 */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"line1\" className=\"block text-sm font-medium text-text-primary\">\n Address Line 1 <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"line1\"\n value={formData.line1}\n onChange={(e) => handleChange('line1', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.line1 ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"Street address, P.O. box\"\n disabled={isSubmitting}\n />\n {validationErrors.line1 && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.line1}</p>\n )}\n </div>\n </div>\n\n {/* Address Line 2 */}\n <div className=\"space-y-1.5 mb-3\">\n <label htmlFor=\"line2\" className=\"block text-sm font-medium text-text-primary\">\n Address Line 2 <span className=\"text-text-secondary text-xs\">(optional)</span>\n </label>\n <input\n type=\"text\"\n id=\"line2\"\n value={formData.line2}\n onChange={(e) => handleChange('line2', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-default rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder=\"Apartment, suite, unit, building, floor\"\n disabled={isSubmitting}\n />\n </div>\n\n {/* City + State + Postal Code Row - 3 columns on desktop */}\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 mb-3\">\n {/* City */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"city\" className=\"block text-sm font-medium text-text-primary\">\n City <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"city\"\n value={formData.city}\n onChange={(e) => handleChange('city', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.city ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder=\"City\"\n disabled={isSubmitting}\n />\n {validationErrors.city && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.city}</p>\n )}\n </div>\n\n {/* State - Dropdown for India, text input for others */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"state\" className=\"block text-sm font-medium text-text-primary\">\n State{' '}\n {formData.country === 'IN' && <span className=\"text-status-error-text\">*</span>}\n </label>\n {formData.country === 'IN' ? (\n <select\n id=\"state\"\n value={formData.state}\n onChange={(e) => handleChange('state', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.state ? 'border-status-error-border' : 'border-border-default'}\n `}\n disabled={isSubmitting}\n >\n <option value=\"\">Select state</option>\n {INDIAN_STATES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n ) : (\n <input\n type=\"text\"\n id=\"state\"\n value={formData.state}\n onChange={(e) => handleChange('state', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-default rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder=\"State/Province\"\n disabled={isSubmitting}\n />\n )}\n {validationErrors.state && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.state}</p>\n )}\n </div>\n\n {/* Postal Code */}\n <div className=\"space-y-1.5 col-span-2 sm:col-span-1\">\n <label htmlFor=\"postalCode\" className=\"block text-sm font-medium text-text-primary\">\n Postal Code <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"postalCode\"\n value={formData.postalCode}\n onChange={(e) => handleChange('postalCode', e.target.value)}\n className={`\n w-full px-3 py-2 border rounded-lg bg-bg-surface text-text-primary\n placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n ${validationErrors.postalCode ? 'border-status-error-border' : 'border-border-default'}\n `}\n placeholder={formData.country === 'IN' ? '600001' : 'ZIP/Postal code'}\n disabled={isSubmitting}\n />\n {validationErrors.postalCode && (\n <p className=\"text-sm text-status-error-text\">{validationErrors.postalCode}</p>\n )}\n </div>\n </div>\n </div>\n\n {/* Currency & Tax ID - 2 columns on desktop */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 pt-3 border-t border-border-subtle\">\n {/* Currency - Searchable Dropdown */}\n <div className=\"space-y-1.5\">\n <label className=\"block text-sm font-medium text-text-primary\">\n {tr('billing.overview.currency', 'Currency')}\n </label>\n <div className=\"relative\" ref={currencyDropdownRef}>\n {/* Dropdown trigger */}\n <button\n type=\"button\"\n onClick={() => !isSubmitting && setCurrencyDropdownOpen(!currencyDropdownOpen)}\n disabled={isSubmitting}\n className={`\n w-full flex items-center justify-between gap-2 px-3 py-2 rounded-lg border\n transition-all duration-200 text-left\n disabled:opacity-50 disabled:cursor-not-allowed\n ${\n currencyDropdownOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-default hover:border-border-strong'\n }\n `}\n >\n <div className=\"flex items-center gap-2\">\n <span className=\"text-base\">{getFlagEmoji(selectedCurrencyDetails.flag)}</span>\n <span className=\"font-medium text-text-primary\">\n {selectedCurrencyDetails.code}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {selectedCurrencyDetails.symbol} · {selectedCurrencyDetails.name}\n </span>\n </div>\n <ChevronDown\n className={`size-4 text-text-secondary transition-transform ${currencyDropdownOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {/* Dropdown menu - Opens UPWARD to avoid form overflow clipping */}\n {currencyDropdownOpen && (\n <div\n className=\"absolute z-50 w-full bottom-full mb-1 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 overflow-hidden\"\n onMouseDown={(e) => e.stopPropagation()}\n >\n {/* Currency list - at top when opening upward */}\n <div className=\"max-h-48 overflow-y-auto\">\n {filteredCurrencies.length === 0 ? (\n <div className=\"px-3 py-4 text-sm text-text-secondary text-center\">\n No currencies found\n </div>\n ) : (\n filteredCurrencies.map((currency) => {\n const isSelected = currency.code === formData.currency;\n return (\n <button\n key={currency.code}\n type=\"button\"\n onClick={() => {\n handleChange('currency', currency.code);\n setCurrencyDropdownOpen(false);\n setCurrencySearch('');\n }}\n className={`\n w-full flex items-center gap-3 px-3 py-2 text-left\n transition-colors\n ${\n isSelected\n ? 'bg-[var(--color-accent-soft)] text-text-primary'\n : 'hover:bg-bg-sunken text-text-primary'\n }\n `}\n >\n <span className=\"text-base shrink-0\">\n {getFlagEmoji(currency.flag)}\n </span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{currency.code}</span>\n <span className=\"text-xs text-text-secondary\">\n {currency.symbol}\n </span>\n </div>\n <p className=\"text-xs text-text-secondary truncate\">\n {currency.name}\n </p>\n </div>\n {isSelected && <Check className=\"size-4 text-text-link shrink-0\" />}\n </button>\n );\n })\n )}\n </div>\n\n {/* Search input - at bottom when opening upward */}\n <div className=\"p-2 border-t border-border-subtle\">\n <div className=\"relative\">\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n ref={currencySearchInputRef}\n type=\"text\"\n placeholder=\"Search currency...\"\n value={currencySearch}\n onChange={(e) => setCurrencySearch(e.target.value)}\n onMouseDown={(e) => e.stopPropagation()}\n className=\"w-full pl-8 pr-3 py-2 text-sm bg-bg-surface border border-border-subtle rounded-button focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Tax ID */}\n <div className=\"space-y-1.5\">\n <label htmlFor=\"taxId\" className=\"block text-sm font-medium text-text-primary\">\n Tax ID / VAT Number <span className=\"text-text-secondary text-xs\">(optional)</span>\n </label>\n <div className=\"relative\">\n <FileText className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n type=\"text\"\n id=\"taxId\"\n value={formData.taxId || ''}\n onChange={(e) => handleChange('taxId', e.target.value)}\n className=\"w-full pl-[3.25rem] pr-3 py-2 border border-border-default rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\"\n placeholder={\n formData.country === 'IN' ? 'e.g., 22AAAAA0000A1Z5' : 'Tax ID / VAT Number'\n }\n disabled={isSubmitting}\n />\n </div>\n </div>\n </div>\n </form>\n\n {/* Footer - Fixed at bottom */}\n <footer className=\"flex items-center justify-end gap-3 p-4 border-t border-border-subtle bg-bg-sunken rounded-b-card shrink-0\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isSubmitting}\n className=\"px-4 py-2 text-sm font-medium text-text-primary border border-border-subtle rounded-button hover:bg-bg-sunken hover:border-border-strong transition-all duration-200 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 disabled:opacity-50\"\n >\n {isSubmitting ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Creating…\n </>\n ) : (\n 'Create Account'\n )}\n </button>\n </footer>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -128,7 +128,7 @@ var h = ({ brand: e, className: t }) => {
|
|
|
128
128
|
className: "fixed inset-0 z-10",
|
|
129
129
|
onClick: () => _(!1)
|
|
130
130
|
}), /* @__PURE__ */ p("div", {
|
|
131
|
-
className: "absolute top-full right-0 mt-1 min-w-[140px] rounded-card border border-border-
|
|
131
|
+
className: "absolute top-full right-0 mt-1 min-w-[140px] rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3 z-20 overflow-hidden whitespace-nowrap",
|
|
132
132
|
children: [!a.isDefault && /* @__PURE__ */ p("button", {
|
|
133
133
|
type: "button",
|
|
134
134
|
onClick: w,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaymentMethodsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/PaymentMethodsSection.tsx"],"sourcesContent":["/**\n * Payment Methods Section Component\n * Displays and manages payment methods\n * Note: Provider config is fetched in the modal, not here\n */\n\nimport { type FC, useState } from 'react';\nimport { CreditCard, Plus, MoreVertical, Trash2, Star, Loader2, CheckCircle2 } from 'lucide-react';\nimport type { PaymentMethod, PaymentMethodType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\ninterface PaymentMethodsSectionProps {\n paymentMethods: PaymentMethod[];\n billingAccountId: string;\n onAddPaymentMethod: () => void;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\n// Card brand icons mapping\nconst CardBrandIcon: FC<{ brand?: string; className?: string }> = ({ brand, className }) => {\n const brandLower = brand?.toLowerCase() || '';\n\n // In a real app, you'd use actual brand SVGs/images\n const brandColors: Record<string, string> = {\n visa: 'text-accent-blue',\n mastercard: 'text-accent-orange',\n amex: 'text-accent-blue',\n discover: 'text-accent-orange',\n default: 'text-text-secondary',\n };\n\n const color = brandColors[brandLower] || brandColors.default;\n\n return (\n <div className={`w-10 h-6 rounded bg-bg-sunken flex items-center justify-center ${className}`}>\n <CreditCard className={`size-5 ${color}`} />\n </div>\n );\n};\n\nconst PaymentMethodCard: FC<{\n method: PaymentMethod;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n}> = ({ method, onSetDefault, onDelete }) => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [isSettingDefault, setIsSettingDefault] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);\n\n const handleSetDefault = async () => {\n setIsMenuOpen(false);\n setIsSettingDefault(true);\n try {\n await onSetDefault(method.id);\n } finally {\n setIsSettingDefault(false);\n }\n };\n\n const handleDeleteRequest = async () => {\n setIsMenuOpen(false);\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete payment method',\n message: 'Are you sure you want to remove this payment method?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === true) {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n return;\n }\n if (confirmed === null) {\n setIsConfirmingDelete(true);\n }\n };\n\n const handleDelete = async () => {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setIsConfirmingDelete(false);\n }\n };\n\n const getMethodTypeLabel = (type: PaymentMethodType) => {\n const labels: Record<PaymentMethodType, string> = {\n CREDIT_CARD: 'Credit Card',\n DEBIT_CARD: 'Debit Card',\n BANK_TRANSFER: 'UPI',\n PAYPAL: 'PayPal',\n CRYPTO: 'Crypto',\n WALLET: 'Wallet',\n NETBANKING: 'Netbanking',\n };\n return labels[type] || type;\n };\n\n const getExpiryText = () => {\n if (method.expiryMonth && method.expiryYear) {\n const monthStr = String(method.expiryMonth).padStart(2, '0');\n const yearStr = String(method.expiryYear).slice(-2);\n return `${monthStr}/${yearStr}`;\n }\n return null;\n };\n\n const isExpired = () => {\n if (!method.expiryMonth || !method.expiryYear) return false;\n const now = new Date();\n const expiry = new Date(method.expiryYear, method.expiryMonth, 0);\n return expiry < now;\n };\n\n const getProviderBadge = () => {\n if (method.stripePaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent-violet-subtle text-accent-violet\">\n Stripe\n </span>\n );\n }\n if (method.razorpayPaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-status-info-bg-subtle text-status-info-text\">\n Razorpay\n </span>\n );\n }\n return null;\n };\n\n return (\n <div\n className={`\n relative p-4 border rounded-lg transition-all overflow-visible\n ${method.isDefault ? 'border-border-strong bg-[var(--color-accent-soft)]' : 'border-border-subtle bg-bg-surface'}\n ${isDeleting ? 'opacity-50' : ''}\n `}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <CardBrandIcon brand={method.brand} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium text-text-primary\">\n {method.brand || getMethodTypeLabel(method.type)} •••• {method.last4 || '****'}\n </p>\n {method.isDefault && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-soft)] text-text-link\">\n <Star className=\"size-2.5 fill-current\" />\n Default\n </span>\n )}\n {getProviderBadge()}\n </div>\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n {getExpiryText() && (\n <>\n <span className={isExpired() ? 'text-status-error-text' : ''}>\n Expires {getExpiryText()}\n </span>\n {isExpired() && <span className=\"text-status-error-text text-xs\">(Expired)</span>}\n </>\n )}\n {method.isVerified && (\n <span className=\"flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"size-3\" />\n Verified\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Actions Menu */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"p-2 rounded-button hover:bg-bg-sunken text-text-secondary hover:text-text-primary transition-colors\"\n disabled={isSettingDefault || isDeleting}\n aria-label=\"Payment method options\"\n >\n {isSettingDefault || isDeleting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <MoreVertical className=\"size-4\" />\n )}\n </button>\n\n {isMenuOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsMenuOpen(false)} />\n <div className=\"absolute top-full right-0 mt-1 min-w-[140px] rounded-card border border-border-subtle bg-bg-elevated shadow-elevation-3 z-20 overflow-hidden whitespace-nowrap\">\n {!method.isDefault && (\n <button\n type=\"button\"\n onClick={handleSetDefault}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-text-primary hover:bg-bg-sunken transition-colors text-left\"\n >\n <Star className=\"size-4 flex-shrink-0\" />\n Set as default\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleDeleteRequest}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-status-error-text hover:bg-status-error-bg-subtle transition-colors text-left\"\n >\n <Trash2 className=\"size-4 flex-shrink-0\" />\n Remove\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n {isConfirmingDelete && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3\">\n <p className=\"text-sm font-medium text-status-error-text\">\n Remove this payment method from your billing account?\n </p>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsConfirmingDelete(false)}\n className=\"rounded-button border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"rounded-button bg-action-danger-bg px-3 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-danger-bgHover disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isDeleting ? 'Removing…' : 'Remove'}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const PaymentMethodsSection: FC<PaymentMethodsSectionProps> = ({\n paymentMethods,\n billingAccountId: _billingAccountId,\n onAddPaymentMethod,\n onSetDefault,\n onDelete,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n // billingAccountId is available for future use\n void _billingAccountId;\n\n const hasPaymentMethods = paymentMethods.length > 0;\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.paymentMethods', 'Payment Methods')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Manage your payment methods for subscriptions and purchases\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onAddPaymentMethod}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n <Plus className=\"size-4\" />\n Add Method\n </button>\n </header>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n ) : hasPaymentMethods ? (\n <div className=\"space-y-3 overflow-visible\">\n {paymentMethods\n .filter((method): method is PaymentMethod => method !== null && method !== undefined)\n .map((method) => (\n <PaymentMethodCard\n key={method.id}\n method={method}\n onSetDefault={onSetDefault}\n onDelete={onDelete}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-8 border border-dashed border-border-subtle rounded-lg\">\n <div className=\"size-12 rounded-full bg-bg-sunken flex items-center justify-center mb-4\">\n <CreditCard className=\"size-6 text-text-secondary\" />\n </div>\n <h4 className=\"text-base font-medium text-text-primary\">No payment methods</h4>\n <p className=\"text-sm text-text-secondary mt-1 text-center max-w-sm\">\n Add a payment method to enable automatic payments for your subscriptions\n </p>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PaymentMethodsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/PaymentMethodsSection.tsx"],"sourcesContent":["/**\n * Payment Methods Section Component\n * Displays and manages payment methods\n * Note: Provider config is fetched in the modal, not here\n */\n\nimport { type FC, useState } from 'react';\nimport { CreditCard, Plus, MoreVertical, Trash2, Star, Loader2, CheckCircle2 } from 'lucide-react';\nimport type { PaymentMethod, PaymentMethodType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\ninterface PaymentMethodsSectionProps {\n paymentMethods: PaymentMethod[];\n billingAccountId: string;\n onAddPaymentMethod: () => void;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\n// Card brand icons mapping\nconst CardBrandIcon: FC<{ brand?: string; className?: string }> = ({ brand, className }) => {\n const brandLower = brand?.toLowerCase() || '';\n\n // In a real app, you'd use actual brand SVGs/images\n const brandColors: Record<string, string> = {\n visa: 'text-accent-blue',\n mastercard: 'text-accent-orange',\n amex: 'text-accent-blue',\n discover: 'text-accent-orange',\n default: 'text-text-secondary',\n };\n\n const color = brandColors[brandLower] || brandColors.default;\n\n return (\n <div className={`w-10 h-6 rounded bg-bg-sunken flex items-center justify-center ${className}`}>\n <CreditCard className={`size-5 ${color}`} />\n </div>\n );\n};\n\nconst PaymentMethodCard: FC<{\n method: PaymentMethod;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n}> = ({ method, onSetDefault, onDelete }) => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [isSettingDefault, setIsSettingDefault] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);\n\n const handleSetDefault = async () => {\n setIsMenuOpen(false);\n setIsSettingDefault(true);\n try {\n await onSetDefault(method.id);\n } finally {\n setIsSettingDefault(false);\n }\n };\n\n const handleDeleteRequest = async () => {\n setIsMenuOpen(false);\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete payment method',\n message: 'Are you sure you want to remove this payment method?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === true) {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n return;\n }\n if (confirmed === null) {\n setIsConfirmingDelete(true);\n }\n };\n\n const handleDelete = async () => {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setIsConfirmingDelete(false);\n }\n };\n\n const getMethodTypeLabel = (type: PaymentMethodType) => {\n const labels: Record<PaymentMethodType, string> = {\n CREDIT_CARD: 'Credit Card',\n DEBIT_CARD: 'Debit Card',\n BANK_TRANSFER: 'UPI',\n PAYPAL: 'PayPal',\n CRYPTO: 'Crypto',\n WALLET: 'Wallet',\n NETBANKING: 'Netbanking',\n };\n return labels[type] || type;\n };\n\n const getExpiryText = () => {\n if (method.expiryMonth && method.expiryYear) {\n const monthStr = String(method.expiryMonth).padStart(2, '0');\n const yearStr = String(method.expiryYear).slice(-2);\n return `${monthStr}/${yearStr}`;\n }\n return null;\n };\n\n const isExpired = () => {\n if (!method.expiryMonth || !method.expiryYear) return false;\n const now = new Date();\n const expiry = new Date(method.expiryYear, method.expiryMonth, 0);\n return expiry < now;\n };\n\n const getProviderBadge = () => {\n if (method.stripePaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent-violet-subtle text-accent-violet\">\n Stripe\n </span>\n );\n }\n if (method.razorpayPaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-status-info-bg-subtle text-status-info-text\">\n Razorpay\n </span>\n );\n }\n return null;\n };\n\n return (\n <div\n className={`\n relative p-4 border rounded-lg transition-all overflow-visible\n ${method.isDefault ? 'border-border-strong bg-[var(--color-accent-soft)]' : 'border-border-subtle bg-bg-surface'}\n ${isDeleting ? 'opacity-50' : ''}\n `}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <CardBrandIcon brand={method.brand} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium text-text-primary\">\n {method.brand || getMethodTypeLabel(method.type)} •••• {method.last4 || '****'}\n </p>\n {method.isDefault && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-soft)] text-text-link\">\n <Star className=\"size-2.5 fill-current\" />\n Default\n </span>\n )}\n {getProviderBadge()}\n </div>\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n {getExpiryText() && (\n <>\n <span className={isExpired() ? 'text-status-error-text' : ''}>\n Expires {getExpiryText()}\n </span>\n {isExpired() && <span className=\"text-status-error-text text-xs\">(Expired)</span>}\n </>\n )}\n {method.isVerified && (\n <span className=\"flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"size-3\" />\n Verified\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Actions Menu */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"p-2 rounded-button hover:bg-bg-sunken text-text-secondary hover:text-text-primary transition-colors\"\n disabled={isSettingDefault || isDeleting}\n aria-label=\"Payment method options\"\n >\n {isSettingDefault || isDeleting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <MoreVertical className=\"size-4\" />\n )}\n </button>\n\n {isMenuOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsMenuOpen(false)} />\n <div className=\"absolute top-full right-0 mt-1 min-w-[140px] rounded-card border border-border-seam bg-bg-elevated shadow-elevation-3 z-20 overflow-hidden whitespace-nowrap\">\n {!method.isDefault && (\n <button\n type=\"button\"\n onClick={handleSetDefault}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-text-primary hover:bg-bg-sunken transition-colors text-left\"\n >\n <Star className=\"size-4 flex-shrink-0\" />\n Set as default\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleDeleteRequest}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-status-error-text hover:bg-status-error-bg-subtle transition-colors text-left\"\n >\n <Trash2 className=\"size-4 flex-shrink-0\" />\n Remove\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n {isConfirmingDelete && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3\">\n <p className=\"text-sm font-medium text-status-error-text\">\n Remove this payment method from your billing account?\n </p>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsConfirmingDelete(false)}\n className=\"rounded-button border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"rounded-button bg-action-danger-bg px-3 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-danger-bgHover disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isDeleting ? 'Removing…' : 'Remove'}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const PaymentMethodsSection: FC<PaymentMethodsSectionProps> = ({\n paymentMethods,\n billingAccountId: _billingAccountId,\n onAddPaymentMethod,\n onSetDefault,\n onDelete,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n // billingAccountId is available for future use\n void _billingAccountId;\n\n const hasPaymentMethods = paymentMethods.length > 0;\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.paymentMethods', 'Payment Methods')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Manage your payment methods for subscriptions and purchases\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onAddPaymentMethod}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n <Plus className=\"size-4\" />\n Add Method\n </button>\n </header>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n ) : hasPaymentMethods ? (\n <div className=\"space-y-3 overflow-visible\">\n {paymentMethods\n .filter((method): method is PaymentMethod => method !== null && method !== undefined)\n .map((method) => (\n <PaymentMethodCard\n key={method.id}\n method={method}\n onSetDefault={onSetDefault}\n onDelete={onDelete}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-8 border border-dashed border-border-subtle rounded-lg\">\n <div className=\"size-12 rounded-full bg-bg-sunken flex items-center justify-center mb-4\">\n <CreditCard className=\"size-6 text-text-secondary\" />\n </div>\n <h4 className=\"text-base font-medium text-text-primary\">No payment methods</h4>\n <p className=\"text-sm text-text-secondary mt-1 text-center max-w-sm\">\n Add a payment method to enable automatic payments for your subscriptions\n </p>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":";;;;;;AAyBA,IAAM,KAA6D,EAAE,UAAO,mBAAgB;CAC1F,IAAM,IAAa,GAAO,aAAa,IAAI,IAGrC,IAAsC;EAC1C,MAAM;EACN,YAAY;EACZ,MAAM;EACN,UAAU;EACV,SAAS;EACV,EAEK,IAAQ,EAAY,MAAe,EAAY;AAErD,QACE,kBAAC,OAAD;EAAK,WAAW,kEAAkE;YAChF,kBAAC,GAAD,EAAY,WAAW,UAAU,KAAW,CAAA;EACxC,CAAA;GAIJ,KAIA,EAAE,WAAQ,iBAAc,kBAAe;CAC3C,IAAM,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAkB,KAAuB,EAAS,GAAM,EACzD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAoB,KAAyB,EAAS,GAAM,EAE7D,IAAmB,YAAY;AAEnC,EADA,EAAc,GAAM,EACpB,EAAoB,GAAK;AACzB,MAAI;AACF,SAAM,EAAa,EAAO,GAAG;YACrB;AACR,KAAoB,GAAM;;IAIxB,IAAsB,YAAY;AAEjC,EADL,EAAc,GAAM,EACf,EAAa,SAAS;EAC3B,IAAM,IAAY,MAAM,EAAc;GACpC,OAAO;GACP,SAAS;GACT,eAAe;GACf,mBAAmB;GACpB,CAAC;AACF,MAAI,MAAc,IAAM;AACtB,KAAc,GAAK;AACnB,OAAI;AAEG,IADL,MAAM,EAAS,EAAO,GAAG,EACpB,EAAa,UAAU;WACtB;AACD,MAAa,QAAQ;aAClB;AACR,MAAc,GAAM;;AAEtB;;AAEF,EAAI,MAAc,QAChB,EAAsB,GAAK;IAIzB,IAAe,YAAY;AAC/B,IAAc,GAAK;AACnB,MAAI;AAEG,GADL,MAAM,EAAS,EAAO,GAAG,EACpB,EAAa,UAAU;UACtB;AACD,KAAa,QAAQ;YAClB;AAER,GADA,EAAc,GAAM,EACpB,EAAsB,GAAM;;IAI1B,KAAsB,OACwB;EAChD,aAAa;EACb,YAAY;EACZ,eAAe;EACf,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,YAAY;EACb,EACa,MAAS,GAGnB,UACA,EAAO,eAAe,EAAO,aAGxB,GAFU,OAAO,EAAO,YAAY,CAAC,SAAS,GAAG,IAAI,CAEzC,GADH,OAAO,EAAO,WAAW,CAAC,MAAM,GAAG,KAG9C,MAGH,UAAkB;AACtB,MAAI,CAAC,EAAO,eAAe,CAAC,EAAO,WAAY,QAAO;EACtD,IAAM,oBAAM,IAAI,MAAM;AAEtB,SADe,IAAI,KAAK,EAAO,YAAY,EAAO,aAAa,EAAE,GACjD;;AAqBlB,QACE,kBAAC,OAAD;EACE,WAAW;;UAEP,EAAO,YAAY,uDAAuD,qCAAqC;UAC/G,IAAa,eAAe,GAAG;;YAJrC,CAOE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAe,OAAO,EAAO,OAAS,CAAA,EACtC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAO,SAAS,EAAmB,EAAO,KAAK;SAAC;SAAO,EAAO,SAAS;SACtE;;OACH,EAAO,aACN,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,kBAAC,GAAD,EAAM,WAAU,yBAA0B,CAAA,EAAA,UAErC;;OArCf,EAAO,wBAEP,kBAAC,QAAD;QAAM,WAAU;kBAAoH;QAE7H,CAAA,GAGP,EAAO,0BAEP,kBAAC,QAAD;QAAM,WAAU;kBAAwH;QAEjI,CAAA,GAGJ;OA0BO;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,GAAe,IACd,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,QAAD;OAAM,WAAW,GAAW,GAAG,2BAA2B;iBAA1D,CAA8D,YACnD,GAAe,CACnB;UACN,GAAW,IAAI,kBAAC,QAAD;OAAM,WAAU;iBAAiC;OAAgB,CAAA,CAChF,EAAA,CAAA,EAEJ,EAAO,cACN,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,EAAA,WAE9B;SAEL;QACF;OACF;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAc,CAAC,EAAW;KACzC,WAAU;KACV,UAAU,KAAoB;KAC9B,cAAW;eAEV,KAAoB,IACnB,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,GAE3C,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA;KAE9B,CAAA,EAER,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAqB,eAAe,EAAc,GAAM;KAAI,CAAA,EAC3E,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,CAAC,EAAO,aACP,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,wBAAyB,CAAA,EAAA,iBAElC;SAEX,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAAA,SAEpC;QACL;OACL,EAAA,CAAA,CAED;MACF;MAEL,KACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA6C;IAEtD,CAAA,EACJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAsB,GAAM;KAC3C,WAAU;eACX;KAEQ,CAAA,EACT,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;eAET,IAAa,cAAc;KACrB,CAAA,CACL;MACF;KAEJ;;GAIG,KAAyD,EACpE,mBACA,kBAAkB,GAClB,uBACA,iBACA,aACA,eAAY,IACZ,eAAY,IACZ,kBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAKnC,IAAoB,EAAe,SAAS;AAElD,QACE,kBAAC,WAAD;EAAS,WAAW,aAAa;EAAa,aAAW;YAAzD,CAEE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,mCAAmC,kBAAkB;IACtD,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,aAEpB;MACF;MAGR,IACC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAS,WAAU,2CAA4C,CAAA;GAC3D,CAAA,GACJ,IACF,kBAAC,OAAD;GAAK,WAAU;aACZ,EACE,QAAQ,MAAoC,KAAW,KAA6B,CACpF,KAAK,MACJ,kBAAC,GAAD;IAEU;IACM;IACJ;IACV,EAJK,EAAO,GAIZ,CACF;GACA,CAAA,GAEN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAY,WAAU,8BAA+B,CAAA;KACjD,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAAuB,CAAA;IAC/E,kBAAC,KAAD;KAAG,WAAU;eAAwD;KAEjE,CAAA;IACA;KAEA"}
|
|
@@ -27,7 +27,7 @@ var v = ({ addon: e, onView: r, onPurchase: a, onAddToCart: o, isInCart: d = !1,
|
|
|
27
27
|
maximumFractionDigits: 2
|
|
28
28
|
}).format(e), w = (e) => e === "yearly" ? "year" : "month", T = t(() => _(e.features || []), [e.features]);
|
|
29
29
|
return /* @__PURE__ */ h("article", {
|
|
30
|
-
className: "flex flex-col border border-border-
|
|
30
|
+
className: "flex flex-col border border-border-seam rounded-card bg-bg-surface shadow-[var(--shadow-pop)] overflow-hidden hover:shadow-elevation-2 [@media(hover:none)]: transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)]",
|
|
31
31
|
children: [
|
|
32
32
|
/* @__PURE__ */ m("header", {
|
|
33
33
|
className: "p-4 border-b border-border-subtle",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecommendedAddonsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/RecommendedAddonsSection.tsx"],"sourcesContent":["/**\n * Recommended Addons Section Component\n * Displays recommended addons that users might want to purchase\n * Supports add to cart functionality\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport {\n Package,\n ArrowRight,\n Sparkles,\n ChevronRight,\n Loader2,\n ShoppingCart,\n Check,\n Plus,\n Minus,\n Trash2,\n} from 'lucide-react';\nimport type { Addon, PlanDuration, SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n * e.g., 10 bots + 10 bots = 20 bots (shown as one feature)\n */\nfunction combineQuotas(features: SubscriptionFeatures[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\ninterface RecommendedAddonsSectionProps {\n addons: Addon[];\n hasSubscription: boolean;\n onViewAddon: (addonId: string) => void;\n onPurchaseAddon: (addonId: string) => void;\n onViewAllAddons: () => void;\n /** Handler for adding addon to cart */\n onAddToCart?: (addon: Addon) => void;\n /** Check if addon is in cart */\n isInCart?: (addonId: string) => boolean;\n /** Get cart quantity for addon */\n getCartQuantity?: (addonId: string) => number;\n /** Update quantity in cart */\n onUpdateQuantity?: (addonId: string, quantity: number) => void;\n /** Remove from cart */\n onRemoveFromCart?: (addonId: string) => void;\n /** Maximum number of addons to show (default: 3) */\n maxAddons?: number;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\nconst AddonCard: FC<{\n addon: Addon;\n onView: () => void;\n onPurchase: () => void;\n onAddToCart?: () => void;\n isInCart?: boolean;\n cartQuantity?: number;\n onUpdateQuantity?: (quantity: number) => void;\n onRemoveFromCart?: () => void;\n}> = ({\n addon,\n onView,\n onPurchase,\n onAddToCart,\n isInCart = false,\n cartQuantity = 0,\n onUpdateQuantity,\n onRemoveFromCart,\n}) => {\n const [justAdded, setJustAdded] = useState(false);\n\n const handleAddToCart = () => {\n if (onAddToCart) {\n onAddToCart();\n setJustAdded(true);\n setTimeout(() => setJustAdded(false), 2000);\n } else {\n onPurchase();\n }\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n };\n\n const getDurationLabel = (duration: PlanDuration) => {\n return duration === 'yearly' ? 'year' : 'month';\n };\n\n // Combine duplicate quotas (e.g., 10 bots + 10 bots = 20 bots)\n const combinedQuotas = useMemo(() => {\n const features = addon.features || [];\n return combineQuotas(features as SubscriptionFeatures[]);\n }, [addon.features]);\n\n return (\n <article className=\"flex flex-col border border-border-subtle rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden hover:border-border-strong hover:shadow-elevation-2 transition-all duration-200\">\n {/* Header */}\n <header className=\"p-4 border-b border-border-subtle\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Package className=\"size-5 text-status-success-text\" />\n </div>\n <div>\n <h4 className=\"font-semibold text-text-primary\">{addon.name}</h4>\n <p className=\"text-sm text-text-secondary\">\n {formatCurrency(addon.price, addon.currency)}/{getDurationLabel(addon.duration)}\n </p>\n </div>\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 flex-1\">\n {combinedQuotas.length > 0 ? (\n <ul className=\"space-y-2\">\n {combinedQuotas.slice(0, 2).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"size-1.5 rounded-full bg-status-success-bg\" />\n <span className=\"text-text-secondary\">{quota.name}</span>\n </div>\n <span className=\"font-medium text-text-primary\">\n {quota.totalValue.toLocaleString()}\n </span>\n </li>\n ))}\n {combinedQuotas.length > 2 && (\n <li className=\"text-xs text-text-secondary\">\n +{combinedQuotas.length - 2} more quotas\n </li>\n )}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with additional features\n </p>\n )}\n </div>\n\n {/* Footer */}\n <footer className=\"p-4 border-t border-border-subtle bg-bg-sunken flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"flex-1 px-3 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors text-center\"\n >\n View Details\n </button>\n {isInCart && onUpdateQuantity ? (\n // Quantity controls when in cart\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onRemoveFromCart}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-button transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity - 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium text-text-primary\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity + 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n ) : (\n // Add to cart button\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={justAdded}\n className={`flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-center flex items-center justify-center gap-1.5 ${\n justAdded\n ? 'bg-status-success-bg text-action-primary-text hover:bg-status-success-bg/90'\n : 'text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover'\n }`}\n >\n {justAdded ? (\n <>\n <Check className=\"size-4\" />\n Added\n </>\n ) : (\n <>\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </>\n )}\n </button>\n )}\n </footer>\n </article>\n );\n};\n\nexport const RecommendedAddonsSection: FC<RecommendedAddonsSectionProps> = ({\n addons,\n hasSubscription,\n onViewAddon,\n onPurchaseAddon,\n onViewAllAddons,\n onAddToCart,\n isInCart,\n getCartQuantity,\n onUpdateQuantity,\n onRemoveFromCart,\n maxAddons = 3,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n // Only show if user has a subscription\n if (!hasSubscription) {\n return null;\n }\n\n // Limit addons to maxAddons\n const displayAddons = addons.slice(0, maxAddons);\n\n if (isLoading) {\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n <header>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.recommendedAddons', 'Recommended Add-ons')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </header>\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n </section>\n );\n }\n\n if (displayAddons.length === 0) {\n return null;\n }\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Sparkles className=\"size-5 text-status-warning-text\" />\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Recommended Add-ons</h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-link hover:text-text-link transition-colors\"\n >\n View All\n <ChevronRight className=\"size-4\" />\n </button>\n </header>\n\n {/* Addons Grid - Limited to maxAddons */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {displayAddons.map((addon) => (\n <AddonCard\n key={addon.id}\n addon={addon}\n onView={() => onViewAddon(addon.id)}\n onPurchase={() => onPurchaseAddon(addon.id)}\n onAddToCart={onAddToCart ? () => onAddToCart(addon) : undefined}\n isInCart={isInCart ? isInCart(addon.id) : false}\n cartQuantity={getCartQuantity ? getCartQuantity(addon.id) : 0}\n onUpdateQuantity={\n onUpdateQuantity ? (qty) => onUpdateQuantity(addon.id, qty) : undefined\n }\n onRemoveFromCart={onRemoveFromCart ? () => onRemoveFromCart(addon.id) : undefined}\n />\n ))}\n </div>\n\n {/* See More Link - show if there are more addons */}\n {addons.length > maxAddons && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-2 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n View all {addons.length} add-ons\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":";;;;;;AAoCA,SAAS,EAAc,GAAmD;CACxE,IAAM,oBAAW,IAAI,KAA4B;AAEjD,MAAK,IAAM,KAAW,GAAU;AAC9B,MAAI,CAAC,EAAQ,MAAO;EAEpB,IAAM,IAAU,EAAQ,MAAM,IACxB,IAAa,EAAuB,EAAQ,EAE5C,IAAW,EAAS,IAAI,EAAQ;AACtC,EAAI,IACF,EAAS,cAAc,IAEvB,EAAS,IAAI,GAAS;GACpB;GACA,MAAM,EAAQ,MAAM,QAAQ;GAC5B,YAAY;GACb,CAAC;;AAIN,QAAO,MAAM,KAAK,EAAS,QAAQ,CAAC;;AA2BtC,IAAM,KASA,EACJ,UACA,WACA,eACA,gBACA,cAAW,IACX,kBAAe,GACf,qBACA,0BACI;CACJ,IAAM,CAAC,GAAW,KAAgB,EAAS,GAAM,EAE3C,UAAwB;AAC5B,EAAI,KACF,GAAa,EACb,EAAa,GAAK,EAClB,iBAAiB,EAAa,GAAM,EAAE,IAAK,IAE3C,GAAY;IAIV,KAAkB,GAAgB,MAC/B,IAAI,KAAK,aAAa,SAAS;EACpC,OAAO;EACP,UAAU,KAAY;EACtB,uBAAuB;EACvB,uBAAuB;EACxB,CAAC,CAAC,OAAO,EAAO,EAGb,KAAoB,MACjB,MAAa,WAAW,SAAS,SAIpC,IAAiB,QAEd,EADU,EAAM,YAAY,EAAE,CACmB,EACvD,CAAC,EAAM,SAAS,CAAC;AAEpB,QACE,kBAAC,WAAD;EAAS,WAAU;YAAnB;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAChB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,mCAAoC,CAAA;OACnD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAmC,EAAM;OAAU,CAAA,EACjE,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAe,EAAM,OAAO,EAAM,SAAS;QAAC;QAAE,EAAiB,EAAM,SAAS;QAC7E;SACA,EAAA,CAAA,CACF;;KACF,CAAA;IACC,CAAA;GAGT,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAe,SAAS,IACvB,kBAAC,MAAD;KAAI,WAAU;eAAd,CACG,EAAe,MAAM,GAAG,EAAE,CAAC,KAAK,MAC/B,kBAAC,MAAD;MAAwB,WAAU;gBAAlC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD,EAAM,WAAU,8CAA+C,CAAA,EAC/D,kBAAC,QAAD;QAAM,WAAU;kBAAuB,EAAM;QAAY,CAAA,CACrD;UACN,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAM,WAAW,gBAAgB;OAC7B,CAAA,CACJ;QARI,EAAM,QAQV,CACL,EACD,EAAe,SAAS,KACvB,kBAAC,MAAD;MAAI,WAAU;gBAAd;OAA4C;OACxC,EAAe,SAAS;OAAE;OACzB;QAEJ;SAEL,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAEvC,CAAA;IAEF,CAAA;GAGN,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eACX;KAEQ,CAAA,EACR,KAAY,IAEX,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;OACtB,CAAA;MACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAiB,IAAe,EAAE;OACjD,WAAU;iBAEV,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;OACrB,CAAA;MACT,kBAAC,QAAD;OAAM,WAAU;iBAAiD;OAAoB,CAAA;MACrF,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAiB,IAAe,EAAE;OACjD,WAAU;iBAEV,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;OACpB,CAAA;MACL;SAGN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAW,0HACT,IACI,gFACA;eAGL,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAAA,QAE3B,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,EAAA,cAElC,EAAA,CAAA;KAEE,CAAA,CAEJ;;GACD;;GAID,KAA+D,EAC1E,WACA,oBACA,gBACA,oBACA,oBACA,gBACA,aACA,oBACA,qBACA,qBACA,eAAY,GACZ,eAAY,IACZ,eAAY,IACZ,kBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;;AAGzC,KAAI,CAAC,EACH,QAAO;CAIT,IAAM,IAAgB,EAAO,MAAM,GAAG,EAAU;AAwBhD,QAtBI,IAEA,kBAAC,WAAD;EAAS,WAAW,aAAa;EAAa,aAAW;YAAzD,CACE,kBAAC,UAAD,EAAA,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAG,sCAAsC,sBAAsB;GAC7D,CAAA,EACL,kBAAC,KAAD;GAAG,WAAU;aAA8B;GAEvC,CAAA,CACG,EAAA,CAAA,EACT,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAS,WAAU,2CAA4C,CAAA;GAC3D,CAAA,CACE;MAIV,EAAc,WAAW,IACpB,OAIP,kBAAC,WAAD;EAAS,WAAW,aAAa;EAAa,aAAW;YAAzD;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAU,WAAU,mCAAoC,CAAA,EACxD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAwB,CAAA,EAChF,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA,CACA,EAAA,CAAA,CACF;QACN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAIC,YAEC,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,CAC5B;OACF;;GAGT,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,GAAD;KAES;KACP,cAAc,EAAY,EAAM,GAAG;KACnC,kBAAkB,EAAgB,EAAM,GAAG;KAC3C,aAAa,UAAoB,EAAY,EAAM,GAAG,KAAA;KACtD,UAAU,IAAW,EAAS,EAAM,GAAG,GAAG;KAC1C,cAAc,IAAkB,EAAgB,EAAM,GAAG,GAAG;KAC5D,kBACE,KAAoB,MAAQ,EAAiB,EAAM,IAAI,EAAI,GAAG,KAAA;KAEhE,kBAAkB,UAAyB,EAAiB,EAAM,GAAG,GAAG,KAAA;KACxE,EAXK,EAAM,GAWX,CACF;IACE,CAAA;GAGL,EAAO,SAAS,KACf,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ;MAIC;MACW,EAAO;MAAO;MACxB,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;MAC1B;;IACL,CAAA;GAEA"}
|
|
1
|
+
{"version":3,"file":"RecommendedAddonsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/RecommendedAddonsSection.tsx"],"sourcesContent":["/**\n * Recommended Addons Section Component\n * Displays recommended addons that users might want to purchase\n * Supports add to cart functionality\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport {\n Package,\n ArrowRight,\n Sparkles,\n ChevronRight,\n Loader2,\n ShoppingCart,\n Check,\n Plus,\n Minus,\n Trash2,\n} from 'lucide-react';\nimport type { Addon, PlanDuration, SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n * e.g., 10 bots + 10 bots = 20 bots (shown as one feature)\n */\nfunction combineQuotas(features: SubscriptionFeatures[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\n const quotaId = feature.quota.id;\n const limitValue = getPlanFeatureQuantity(feature);\n\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\ninterface RecommendedAddonsSectionProps {\n addons: Addon[];\n hasSubscription: boolean;\n onViewAddon: (addonId: string) => void;\n onPurchaseAddon: (addonId: string) => void;\n onViewAllAddons: () => void;\n /** Handler for adding addon to cart */\n onAddToCart?: (addon: Addon) => void;\n /** Check if addon is in cart */\n isInCart?: (addonId: string) => boolean;\n /** Get cart quantity for addon */\n getCartQuantity?: (addonId: string) => number;\n /** Update quantity in cart */\n onUpdateQuantity?: (addonId: string, quantity: number) => void;\n /** Remove from cart */\n onRemoveFromCart?: (addonId: string) => void;\n /** Maximum number of addons to show (default: 3) */\n maxAddons?: number;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\nconst AddonCard: FC<{\n addon: Addon;\n onView: () => void;\n onPurchase: () => void;\n onAddToCart?: () => void;\n isInCart?: boolean;\n cartQuantity?: number;\n onUpdateQuantity?: (quantity: number) => void;\n onRemoveFromCart?: () => void;\n}> = ({\n addon,\n onView,\n onPurchase,\n onAddToCart,\n isInCart = false,\n cartQuantity = 0,\n onUpdateQuantity,\n onRemoveFromCart,\n}) => {\n const [justAdded, setJustAdded] = useState(false);\n\n const handleAddToCart = () => {\n if (onAddToCart) {\n onAddToCart();\n setJustAdded(true);\n setTimeout(() => setJustAdded(false), 2000);\n } else {\n onPurchase();\n }\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n };\n\n const getDurationLabel = (duration: PlanDuration) => {\n return duration === 'yearly' ? 'year' : 'month';\n };\n\n // Combine duplicate quotas (e.g., 10 bots + 10 bots = 20 bots)\n const combinedQuotas = useMemo(() => {\n const features = addon.features || [];\n return combineQuotas(features as SubscriptionFeatures[]);\n }, [addon.features]);\n\n return (\n <article className=\"flex flex-col border border-border-seam rounded-card bg-bg-surface shadow-[var(--shadow-pop)] overflow-hidden hover:shadow-elevation-2 [@media(hover:none)]: transition-[box-shadow,transform] duration-200 ease-[var(--motion-easing-out)]\">\n {/* Header */}\n <header className=\"p-4 border-b border-border-subtle\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Package className=\"size-5 text-status-success-text\" />\n </div>\n <div>\n <h4 className=\"font-semibold text-text-primary\">{addon.name}</h4>\n <p className=\"text-sm text-text-secondary\">\n {formatCurrency(addon.price, addon.currency)}/{getDurationLabel(addon.duration)}\n </p>\n </div>\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 flex-1\">\n {combinedQuotas.length > 0 ? (\n <ul className=\"space-y-2\">\n {combinedQuotas.slice(0, 2).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"size-1.5 rounded-full bg-status-success-bg\" />\n <span className=\"text-text-secondary\">{quota.name}</span>\n </div>\n <span className=\"font-medium text-text-primary\">\n {quota.totalValue.toLocaleString()}\n </span>\n </li>\n ))}\n {combinedQuotas.length > 2 && (\n <li className=\"text-xs text-text-secondary\">\n +{combinedQuotas.length - 2} more quotas\n </li>\n )}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with additional features\n </p>\n )}\n </div>\n\n {/* Footer */}\n <footer className=\"p-4 border-t border-border-subtle bg-bg-sunken flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"flex-1 px-3 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors text-center\"\n >\n View Details\n </button>\n {isInCart && onUpdateQuantity ? (\n // Quantity controls when in cart\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onRemoveFromCart}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-button transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity - 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium text-text-primary\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity + 1)}\n className=\"p-2 text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n ) : (\n // Add to cart button\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={justAdded}\n className={`flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-center flex items-center justify-center gap-1.5 ${\n justAdded\n ? 'bg-status-success-bg text-action-primary-text hover:bg-status-success-bg/90'\n : 'text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover'\n }`}\n >\n {justAdded ? (\n <>\n <Check className=\"size-4\" />\n Added\n </>\n ) : (\n <>\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </>\n )}\n </button>\n )}\n </footer>\n </article>\n );\n};\n\nexport const RecommendedAddonsSection: FC<RecommendedAddonsSectionProps> = ({\n addons,\n hasSubscription,\n onViewAddon,\n onPurchaseAddon,\n onViewAllAddons,\n onAddToCart,\n isInCart,\n getCartQuantity,\n onUpdateQuantity,\n onRemoveFromCart,\n maxAddons = 3,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n // Only show if user has a subscription\n if (!hasSubscription) {\n return null;\n }\n\n // Limit addons to maxAddons\n const displayAddons = addons.slice(0, maxAddons);\n\n if (isLoading) {\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n <header>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.overview.recommendedAddons', 'Recommended Add-ons')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </header>\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-6 animate-spin text-text-secondary\" />\n </div>\n </section>\n );\n }\n\n if (displayAddons.length === 0) {\n return null;\n }\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Sparkles className=\"size-5 text-status-warning-text\" />\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Recommended Add-ons</h3>\n <p className=\"text-sm text-text-secondary\">\n Enhance your subscription with these popular add-ons\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-text-link hover:text-text-link transition-colors\"\n >\n View All\n <ChevronRight className=\"size-4\" />\n </button>\n </header>\n\n {/* Addons Grid - Limited to maxAddons */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {displayAddons.map((addon) => (\n <AddonCard\n key={addon.id}\n addon={addon}\n onView={() => onViewAddon(addon.id)}\n onPurchase={() => onPurchaseAddon(addon.id)}\n onAddToCart={onAddToCart ? () => onAddToCart(addon) : undefined}\n isInCart={isInCart ? isInCart(addon.id) : false}\n cartQuantity={getCartQuantity ? getCartQuantity(addon.id) : 0}\n onUpdateQuantity={\n onUpdateQuantity ? (qty) => onUpdateQuantity(addon.id, qty) : undefined\n }\n onRemoveFromCart={onRemoveFromCart ? () => onRemoveFromCart(addon.id) : undefined}\n />\n ))}\n </div>\n\n {/* See More Link - show if there are more addons */}\n {addons.length > maxAddons && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-2 text-sm font-medium text-text-secondary hover:text-text-primary transition-colors\"\n >\n View all {addons.length} add-ons\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
|