@burdenoff/microfe-billing 2026.614.1 → 2026.623.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 +7 -6
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/BillingRoot.js +1 -1
- package/dist/billing/BillingRoot.js.map +1 -1
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js +22 -22
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +31 -31
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +39 -39
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +29 -29
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +91 -91
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +40 -40
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +115 -115
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +75 -72
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +126 -126
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +45 -45
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +25 -25
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +31 -35
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js +9 -9
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js +31 -31
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/PromoCodeInput.js +8 -8
- package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +9 -9
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
- package/dist/billing/modules/checkout/components/StripePaymentForm.js +21 -21
- package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +11 -11
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +31 -31
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +23 -23
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +91 -91
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +62 -62
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +72 -60
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +66 -66
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +46 -46
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +19 -19
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +37 -37
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +90 -90
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +41 -41
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +31 -31
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +35 -35
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +24 -24
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +99 -99
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +23 -23
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +22 -22
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +58 -58
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +126 -126
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +83 -79
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +16 -16
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +40 -40
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +35 -35
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +57 -57
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +35 -35
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +57 -57
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +52 -52
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +40 -39
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +22 -19
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +34 -34
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +47 -47
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +39 -39
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +52 -52
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +23 -23
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -246
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +88 -85
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +51 -51
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +206 -196
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/components/ServerError.js +4 -4
- package/dist/billing/shared/components/ServerError.js.map +1 -1
- package/dist/billing/shared/components/StatCard.js +11 -33
- package/dist/billing/shared/components/StatCard.js.map +1 -1
- package/dist/billing/shared/ui/Card.js +15 -0
- package/dist/billing/shared/ui/Card.js.map +1 -0
- package/dist/billing/shared/ui/MetricCard.js +44 -0
- package/dist/billing/shared/ui/MetricCard.js.map +1 -0
- package/dist/billing/shared/ui/ProgressBar.js +30 -0
- package/dist/billing/shared/ui/ProgressBar.js.map +1 -0
- package/dist/billing/shared/ui/SectionHeader.js +2 -0
- package/dist/billing/shared/ui/StatusPill.js +28 -0
- package/dist/billing/shared/ui/StatusPill.js.map +1 -0
- package/dist/billing/shared/ui/index.js +5 -0
- package/dist/billing/shared/utils/tokens.js +13 -30
- package/dist/billing/shared/utils/tokens.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BillingAccountSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/BillingAccountSelector.tsx"],"sourcesContent":["/**\n * Billing Account Selector Component\n * Dropdown selector for choosing billing account in checkout\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { Building2, CreditCard, ChevronDown, Check, Lock } from 'lucide-react';\nimport type { CheckoutBillingAccount } from '../types';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountSelectorProps {\n accounts: CheckoutBillingAccount[];\n selectedAccountId: string | null;\n onSelect: (account: CheckoutBillingAccount) => void;\n isLoading?: boolean;\n disabled?: boolean;\n disabledMessage?: string;\n}\n\nexport const BillingAccountSelector: FC<BillingAccountSelectorProps> = ({\n accounts,\n selectedAccountId,\n onSelect,\n isLoading = false,\n disabled = false,\n disabledMessage,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Close on escape key\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }\n }, [isOpen]);\n\n const selectedAccount = accounts.find((a) => a.id === selectedAccountId);\n\n if (isLoading) {\n return (\n <div className=\"space-y-2\">\n <div className=\"h-4 w-28 bg-muted animate-pulse rounded\" />\n <div className=\"h-12 bg-muted animate-pulse rounded-lg\" />\n </div>\n );\n }\n\n if (accounts.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed border-border p-4 text-center\">\n <Building2 className=\"mx-auto size-6 text-muted-foreground\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">No billing accounts found</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2 relative\" ref={dropdownRef}>\n <div className=\"flex items-center justify-between\">\n <label className=\"text-sm font-medium text-foreground\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </label>\n {disabled && (\n <span className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Lock className=\"size-3\" />\n Locked\n </span>\n )}\n </div>\n\n {/* Dropdown trigger */}\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n className={`\n w-full flex items-center justify-between gap-3 p-3 rounded-lg border\n transition-colors text-left\n ${\n disabled\n ? 'border-border bg-muted/30 cursor-not-allowed opacity-80'\n : isOpen\n ? 'border-primary ring-1 ring-primary'\n : 'border-border hover:border-primary/50'\n }\n `}\n >\n {selectedAccount ? (\n <div className=\"flex items-center gap-3 min-w-0 flex-1\">\n <div\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ${\n disabled ? 'bg-muted text-muted-foreground' : 'bg-primary/10 text-primary'\n }`}\n >\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-foreground truncate\">{selectedAccount.name}</span>\n {!selectedAccount.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-destructive/10 text-destructive shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">{selectedAccount.email}</p>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-foreground\">\n <CreditCard className=\"size-3.5 text-muted-foreground\" />\n {formatCurrency(selectedAccount.creditAmount, 'CREDITS')}\n </div>\n </div>\n </div>\n ) : (\n <span className=\"text-muted-foreground\">Select billing account</span>\n )}\n {disabled ? (\n <Lock className=\"size-4 shrink-0 text-muted-foreground\" />\n ) : (\n <ChevronDown\n className={`size-4 shrink-0 text-muted-foreground transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n )}\n </button>\n\n {/* Disabled message */}\n {disabled && disabledMessage && (\n <p className=\"text-xs text-muted-foreground\">{disabledMessage}</p>\n )}\n\n {/* Dropdown menu */}\n {isOpen && !disabled && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-64 overflow-auto rounded-lg border border-border bg-popover shadow-lg\">\n {accounts.map((account) => {\n const isSelected = account.id === selectedAccountId;\n return (\n <button\n key={account.id}\n type=\"button\"\n onClick={() => {\n onSelect(account);\n setIsOpen(false);\n }}\n className={`\n w-full flex items-center gap-3 p-3 text-left\n transition-colors\n ${isSelected ? 'bg-primary/5' : 'hover:bg-muted/50'}\n `}\n >\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary\">\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-foreground truncate\">{account.name}</span>\n {!account.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-destructive/10 text-destructive shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">{account.email}</p>\n </div>\n <div className=\"shrink-0 flex items-center gap-2\">\n <div className=\"text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-foreground\">\n <CreditCard className=\"size-3.5 text-muted-foreground\" />\n {formatCurrency(account.creditAmount, 'CREDITS')}\n </div>\n </div>\n {isSelected && <Check className=\"size-4 text-primary\" />}\n </div>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BillingAccountSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/BillingAccountSelector.tsx"],"sourcesContent":["/**\n * Billing Account Selector Component\n * Dropdown selector for choosing billing account in checkout\n */\n\nimport { type FC, useState, useRef, useEffect } from 'react';\nimport { Building2, CreditCard, ChevronDown, Check, Lock } from 'lucide-react';\nimport type { CheckoutBillingAccount } from '../types';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountSelectorProps {\n accounts: CheckoutBillingAccount[];\n selectedAccountId: string | null;\n onSelect: (account: CheckoutBillingAccount) => void;\n isLoading?: boolean;\n disabled?: boolean;\n disabledMessage?: string;\n}\n\nexport const BillingAccountSelector: FC<BillingAccountSelectorProps> = ({\n accounts,\n selectedAccountId,\n onSelect,\n isLoading = false,\n disabled = false,\n disabledMessage,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n // Close on escape key\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n setIsOpen(false);\n }\n };\n\n if (isOpen) {\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }\n }, [isOpen]);\n\n const selectedAccount = accounts.find((a) => a.id === selectedAccountId);\n\n if (isLoading) {\n return (\n <div className=\"space-y-2\">\n <div className=\"h-4 w-28 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-12 bg-bg-sunken animate-pulse rounded-lg\" />\n </div>\n );\n }\n\n if (accounts.length === 0) {\n return (\n <div className=\"rounded-lg border border-dashed border-border-subtle p-4 text-center\">\n <Building2 className=\"mx-auto size-6 text-text-secondary\" />\n <p className=\"mt-2 text-sm text-text-secondary\">No billing accounts found</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2 relative\" ref={dropdownRef}>\n <div className=\"flex items-center justify-between\">\n <label className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </label>\n {disabled && (\n <span className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Lock className=\"size-3\" />\n Locked\n </span>\n )}\n </div>\n\n {/* Dropdown trigger */}\n <button\n type=\"button\"\n onClick={() => !disabled && setIsOpen(!isOpen)}\n disabled={disabled}\n className={`\n w-full flex items-center justify-between gap-3 p-3 rounded-lg border\n transition-colors text-left\n ${\n disabled\n ? 'border-border-subtle bg-bg-sunken cursor-not-allowed opacity-80'\n : isOpen\n ? 'border-border-strong ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong'\n }\n `}\n >\n {selectedAccount ? (\n <div className=\"flex items-center gap-3 min-w-0 flex-1\">\n <div\n className={`flex size-8 shrink-0 items-center justify-center rounded-full ${\n disabled\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-text-link'\n }`}\n >\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary truncate\">\n {selectedAccount.name}\n </span>\n {!selectedAccount.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-status-error-bg-subtle text-status-error-text shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary truncate\">{selectedAccount.email}</p>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-text-primary\">\n <CreditCard className=\"size-3.5 text-text-secondary\" />\n {formatCurrency(selectedAccount.creditAmount, 'CREDITS')}\n </div>\n </div>\n </div>\n ) : (\n <span className=\"text-text-secondary\">Select billing account</span>\n )}\n {disabled ? (\n <Lock className=\"size-4 shrink-0 text-text-secondary\" />\n ) : (\n <ChevronDown\n className={`size-4 shrink-0 text-text-secondary transition-transform ${\n isOpen ? 'rotate-180' : ''\n }`}\n />\n )}\n </button>\n\n {/* Disabled message */}\n {disabled && disabledMessage && (\n <p className=\"text-xs text-text-secondary\">{disabledMessage}</p>\n )}\n\n {/* Dropdown menu */}\n {isOpen && !disabled && (\n <div className=\"absolute left-0 right-0 z-50 mt-1 max-h-64 overflow-auto rounded-card border border-border-subtle bg-bg-elevated shadow-elevation-3\">\n {accounts.map((account) => {\n const isSelected = account.id === selectedAccountId;\n return (\n <button\n key={account.id}\n type=\"button\"\n onClick={() => {\n onSelect(account);\n setIsOpen(false);\n }}\n className={`\n w-full flex items-center gap-3 p-3 text-left\n transition-colors\n ${isSelected ? 'bg-[var(--color-accent-soft)]' : 'hover:bg-bg-sunken'}\n `}\n >\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-link\">\n <Building2 className=\"size-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-text-primary truncate\">{account.name}</span>\n {!account.isActive && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-status-error-bg-subtle text-status-error-text shrink-0\">\n Inactive\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary truncate\">{account.email}</p>\n </div>\n <div className=\"shrink-0 flex items-center gap-2\">\n <div className=\"text-right\">\n <div className=\"flex items-center gap-1 text-sm font-medium text-text-primary\">\n <CreditCard className=\"size-3.5 text-text-secondary\" />\n {formatCurrency(account.creditAmount, 'CREDITS')}\n </div>\n </div>\n {isSelected && <Check className=\"size-4 text-text-link\" />}\n </div>\n </button>\n );\n })}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -62,22 +62,22 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
62
62
|
return !d && !h.contactNumber.trim() && (e.contactNumber = "Phone number is required for payment processing"), f || (h.line1.trim() || (e.line1 = "Street address is required"), h.city.trim() || (e.city = "City is required"), h.postalCode.trim() || (e.postalCode = "Postal code is required"), h.country.trim() || (e.country = "Country is required")), v(e), Object.keys(e).length === 0;
|
|
63
63
|
};
|
|
64
64
|
return /* @__PURE__ */ s("div", {
|
|
65
|
-
className: "fixed inset-0 z-50 flex items-center justify-center bg-
|
|
65
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4",
|
|
66
66
|
children: /* @__PURE__ */ c("div", {
|
|
67
|
-
className: "bg-
|
|
67
|
+
className: "bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-lg max-h-[90vh] overflow-y-auto",
|
|
68
68
|
children: [
|
|
69
69
|
/* @__PURE__ */ c("div", {
|
|
70
|
-
className: "flex items-start justify-between p-6 border-b border-border",
|
|
70
|
+
className: "flex items-start justify-between p-6 border-b border-border-subtle",
|
|
71
71
|
children: [/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("h2", {
|
|
72
|
-
className: "text-lg font-semibold text-
|
|
72
|
+
className: "text-lg font-semibold text-text-primary",
|
|
73
73
|
children: "Complete billing details"
|
|
74
74
|
}), /* @__PURE__ */ s("p", {
|
|
75
|
-
className: "text-sm text-
|
|
75
|
+
className: "text-sm text-text-secondary mt-1",
|
|
76
76
|
children: "We need a few more details before processing your payment."
|
|
77
77
|
})] }), /* @__PURE__ */ s("button", {
|
|
78
78
|
type: "button",
|
|
79
79
|
onClick: m,
|
|
80
|
-
className: "p-2 text-
|
|
80
|
+
className: "p-2 text-text-secondary hover:text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
|
|
81
81
|
children: /* @__PURE__ */ s(o, { className: "size-4" })
|
|
82
82
|
})]
|
|
83
83
|
}),
|
|
@@ -91,14 +91,14 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
91
91
|
!d && /* @__PURE__ */ c("section", {
|
|
92
92
|
className: "space-y-3",
|
|
93
93
|
children: [/* @__PURE__ */ c("div", {
|
|
94
|
-
className: "flex items-center gap-2 text-sm font-medium text-
|
|
95
|
-
children: [/* @__PURE__ */ s(a, { className: "size-4 text-
|
|
94
|
+
className: "flex items-center gap-2 text-sm font-medium text-text-primary",
|
|
95
|
+
children: [/* @__PURE__ */ s(a, { className: "size-4 text-text-secondary" }), "Contact number"]
|
|
96
96
|
}), /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("input", {
|
|
97
97
|
type: "tel",
|
|
98
98
|
value: h.contactNumber,
|
|
99
99
|
onChange: (e) => T("contactNumber", e.target.value),
|
|
100
100
|
placeholder: "+91 98765 43210",
|
|
101
|
-
className: `w-full px-3 py-2 text-sm border rounded-
|
|
101
|
+
className: `w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${_.contactNumber ? "border-status-error-border" : "border-border-subtle"}`
|
|
102
102
|
}), _.contactNumber && /* @__PURE__ */ s("p", {
|
|
103
103
|
className: "mt-1 text-xs text-status-error-text",
|
|
104
104
|
children: _.contactNumber
|
|
@@ -107,18 +107,18 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
107
107
|
!f && /* @__PURE__ */ c("section", {
|
|
108
108
|
className: "space-y-3",
|
|
109
109
|
children: [/* @__PURE__ */ c("div", {
|
|
110
|
-
className: "flex items-center gap-2 text-sm font-medium text-
|
|
111
|
-
children: [/* @__PURE__ */ s(i, { className: "size-4 text-
|
|
110
|
+
className: "flex items-center gap-2 text-sm font-medium text-text-primary",
|
|
111
|
+
children: [/* @__PURE__ */ s(i, { className: "size-4 text-text-secondary" }), "Billing address"]
|
|
112
112
|
}), /* @__PURE__ */ c("div", {
|
|
113
113
|
className: "space-y-3",
|
|
114
114
|
children: [
|
|
115
115
|
/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("label", {
|
|
116
|
-
className: "block text-xs text-
|
|
116
|
+
className: "block text-xs text-text-secondary mb-1",
|
|
117
117
|
children: "Country"
|
|
118
118
|
}), /* @__PURE__ */ s("select", {
|
|
119
119
|
value: h.country,
|
|
120
120
|
onChange: (e) => T("country", e.target.value),
|
|
121
|
-
className: `w-full px-3 py-2 text-sm border rounded-
|
|
121
|
+
className: `w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${_.country ? "border-status-error-border" : "border-border-subtle"}`,
|
|
122
122
|
children: l.map((e) => /* @__PURE__ */ s("option", {
|
|
123
123
|
value: e.value,
|
|
124
124
|
children: e.label
|
|
@@ -126,7 +126,7 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
126
126
|
})] }),
|
|
127
127
|
/* @__PURE__ */ c("div", { children: [
|
|
128
128
|
/* @__PURE__ */ s("label", {
|
|
129
|
-
className: "block text-xs text-
|
|
129
|
+
className: "block text-xs text-text-secondary mb-1",
|
|
130
130
|
children: "Street address"
|
|
131
131
|
}),
|
|
132
132
|
/* @__PURE__ */ s("input", {
|
|
@@ -134,7 +134,7 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
134
134
|
value: h.line1,
|
|
135
135
|
onChange: (e) => T("line1", e.target.value),
|
|
136
136
|
placeholder: "123 Main St",
|
|
137
|
-
className: `w-full px-3 py-2 text-sm border rounded-
|
|
137
|
+
className: `w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${_.line1 ? "border-status-error-border" : "border-border-subtle"}`
|
|
138
138
|
}),
|
|
139
139
|
_.line1 && /* @__PURE__ */ s("p", {
|
|
140
140
|
className: "mt-1 text-xs text-status-error-text",
|
|
@@ -142,27 +142,23 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
142
142
|
})
|
|
143
143
|
] }),
|
|
144
144
|
/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ c("label", {
|
|
145
|
-
className: "block text-xs text-
|
|
146
|
-
children: [
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
className: "text-muted-foreground/60",
|
|
151
|
-
children: "(optional)"
|
|
152
|
-
})
|
|
153
|
-
]
|
|
145
|
+
className: "block text-xs text-text-secondary mb-1",
|
|
146
|
+
children: ["Apartment, suite, etc. ", /* @__PURE__ */ s("span", {
|
|
147
|
+
className: "text-text-muted",
|
|
148
|
+
children: "(optional)"
|
|
149
|
+
})]
|
|
154
150
|
}), /* @__PURE__ */ s("input", {
|
|
155
151
|
type: "text",
|
|
156
152
|
value: h.line2,
|
|
157
153
|
onChange: (e) => T("line2", e.target.value),
|
|
158
154
|
placeholder: "Apt 4B",
|
|
159
|
-
className: "w-full px-3 py-2 text-sm border border-border rounded-
|
|
155
|
+
className: "w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
160
156
|
})] }),
|
|
161
157
|
/* @__PURE__ */ c("div", {
|
|
162
158
|
className: "grid grid-cols-2 gap-3",
|
|
163
159
|
children: [/* @__PURE__ */ c("div", { children: [
|
|
164
160
|
/* @__PURE__ */ s("label", {
|
|
165
|
-
className: "block text-xs text-
|
|
161
|
+
className: "block text-xs text-text-secondary mb-1",
|
|
166
162
|
children: "City"
|
|
167
163
|
}),
|
|
168
164
|
/* @__PURE__ */ s("input", {
|
|
@@ -170,16 +166,16 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
170
166
|
value: h.city,
|
|
171
167
|
onChange: (e) => T("city", e.target.value),
|
|
172
168
|
placeholder: "Mumbai",
|
|
173
|
-
className: `w-full px-3 py-2 text-sm border rounded-
|
|
169
|
+
className: `w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${_.city ? "border-status-error-border" : "border-border-subtle"}`
|
|
174
170
|
}),
|
|
175
171
|
_.city && /* @__PURE__ */ s("p", {
|
|
176
172
|
className: "mt-1 text-xs text-status-error-text",
|
|
177
173
|
children: _.city
|
|
178
174
|
})
|
|
179
175
|
] }), /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ c("label", {
|
|
180
|
-
className: "block text-xs text-
|
|
176
|
+
className: "block text-xs text-text-secondary mb-1",
|
|
181
177
|
children: ["State ", /* @__PURE__ */ s("span", {
|
|
182
|
-
className: "text-muted
|
|
178
|
+
className: "text-text-muted",
|
|
183
179
|
children: "(optional)"
|
|
184
180
|
})]
|
|
185
181
|
}), /* @__PURE__ */ s("input", {
|
|
@@ -187,12 +183,12 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
187
183
|
value: h.state,
|
|
188
184
|
onChange: (e) => T("state", e.target.value),
|
|
189
185
|
placeholder: "Maharashtra",
|
|
190
|
-
className: "w-full px-3 py-2 text-sm border border-border rounded-
|
|
186
|
+
className: "w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
191
187
|
})] })]
|
|
192
188
|
}),
|
|
193
189
|
/* @__PURE__ */ c("div", { children: [
|
|
194
190
|
/* @__PURE__ */ s("label", {
|
|
195
|
-
className: "block text-xs text-
|
|
191
|
+
className: "block text-xs text-text-secondary mb-1",
|
|
196
192
|
children: "Postal code"
|
|
197
193
|
}),
|
|
198
194
|
/* @__PURE__ */ s("input", {
|
|
@@ -200,7 +196,7 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
200
196
|
value: h.postalCode,
|
|
201
197
|
onChange: (e) => T("postalCode", e.target.value),
|
|
202
198
|
placeholder: "400001",
|
|
203
|
-
className: `w-full px-3 py-2 text-sm border rounded-
|
|
199
|
+
className: `w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${_.postalCode ? "border-status-error-border" : "border-border-subtle"}`
|
|
204
200
|
}),
|
|
205
201
|
_.postalCode && /* @__PURE__ */ s("p", {
|
|
206
202
|
className: "mt-1 text-xs text-status-error-text",
|
|
@@ -213,12 +209,12 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
213
209
|
]
|
|
214
210
|
}),
|
|
215
211
|
/* @__PURE__ */ c("div", {
|
|
216
|
-
className: "flex items-center justify-end gap-3 p-6 border-t border-border",
|
|
212
|
+
className: "flex items-center justify-end gap-3 p-6 border-t border-border-subtle",
|
|
217
213
|
children: [/* @__PURE__ */ s("button", {
|
|
218
214
|
type: "button",
|
|
219
215
|
onClick: m,
|
|
220
216
|
disabled: y,
|
|
221
|
-
className: "px-4 py-2 text-sm font-medium text-
|
|
217
|
+
className: "px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors",
|
|
222
218
|
children: "Cancel"
|
|
223
219
|
}), /* @__PURE__ */ c("button", {
|
|
224
220
|
type: "button",
|
|
@@ -247,7 +243,7 @@ var d = ({ billingAccountId: u, hasPhone: d, hasAddress: f, onCompleted: p, onCa
|
|
|
247
243
|
}
|
|
248
244
|
},
|
|
249
245
|
disabled: y,
|
|
250
|
-
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-
|
|
246
|
+
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover rounded-lg transition-colors disabled:opacity-50",
|
|
251
247
|
children: [y && /* @__PURE__ */ s(r, { className: "size-4 animate-spin" }), "Save and continue"]
|
|
252
248
|
})]
|
|
253
249
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompleteBillingAccountModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/CompleteBillingAccountModal.tsx"],"sourcesContent":["/**\n * Modal shown at checkout when the selected billing account is missing\n * a billing address or contact number — fields that can't be auto-filled\n * during account creation but are required by payment providers.\n */\n\nimport { type FC, useState } from 'react';\nimport { MapPin, Phone, Loader2, X } from 'lucide-react';\nimport {\n useSetBillingAddressMutation,\n useUpdateBillingAccountMutation,\n} from '../../../../generated/global-operations';\n\nconst COUNTRY_OPTIONS = [\n { value: 'IN', label: 'India' },\n { value: 'US', label: 'United States' },\n { value: 'GB', label: 'United Kingdom' },\n { value: 'AU', label: 'Australia' },\n { value: 'CA', label: 'Canada' },\n { value: 'SG', label: 'Singapore' },\n { value: 'DE', label: 'Germany' },\n { value: 'FR', label: 'France' },\n { value: 'AE', label: 'UAE' },\n];\n\ninterface FormState {\n contactNumber: string;\n line1: string;\n line2: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n}\n\ninterface FormErrors {\n contactNumber?: string;\n line1?: string;\n city?: string;\n postalCode?: string;\n country?: string;\n}\n\ninterface Props {\n billingAccountId: string;\n /** Whether contactNumber is already filled on the account */\n hasPhone: boolean;\n /** Whether a billing address already exists (with line1 + postalCode) */\n hasAddress: boolean;\n onCompleted: () => void;\n onCancel: () => void;\n}\n\n/** Returns true when a billing account has everything required for checkout. */\nexport function isBillingAccountComplete(account: {\n contactNumber?: string | null;\n billingAddresses?: Array<{\n line1?: string | null;\n city?: string | null;\n postalCode?: string | null;\n country?: string | null;\n }> | null;\n}): boolean {\n const hasPhone = Boolean(account.contactNumber?.trim());\n const primaryAddress = account.billingAddresses?.[0];\n const hasAddress = Boolean(\n primaryAddress?.line1?.trim() &&\n primaryAddress?.city?.trim() &&\n primaryAddress?.postalCode?.trim() &&\n primaryAddress?.country?.trim()\n );\n return hasPhone && hasAddress;\n}\n\nexport const CompleteBillingAccountModal: FC<Props> = ({\n billingAccountId,\n hasPhone,\n hasAddress,\n onCompleted,\n onCancel,\n}) => {\n const [form, setForm] = useState<FormState>({\n contactNumber: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n country: 'IN',\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [submitting, setSubmitting] = useState(false);\n const [serverError, setServerError] = useState<string | null>(null);\n\n const [setBillingAddress] = useSetBillingAddressMutation();\n const [updateBillingAccount] = useUpdateBillingAccountMutation();\n\n const set = (field: keyof FormState, value: string) =>\n setForm((prev) => ({ ...prev, [field]: value }));\n\n const validate = (): boolean => {\n const e: FormErrors = {};\n if (!hasPhone && !form.contactNumber.trim()) {\n e.contactNumber = 'Phone number is required for payment processing';\n }\n if (!hasAddress) {\n if (!form.line1.trim()) e.line1 = 'Street address is required';\n if (!form.city.trim()) e.city = 'City is required';\n if (!form.postalCode.trim()) e.postalCode = 'Postal code is required';\n if (!form.country.trim()) e.country = 'Country is required';\n }\n setErrors(e);\n return Object.keys(e).length === 0;\n };\n\n const handleSubmit = async () => {\n if (!validate()) return;\n setSubmitting(true);\n setServerError(null);\n\n try {\n const ops: Promise<unknown>[] = [];\n\n if (!hasPhone && form.contactNumber.trim()) {\n ops.push(\n updateBillingAccount({\n variables: {\n input: {\n id: billingAccountId,\n contactNumber: form.contactNumber.trim(),\n },\n },\n })\n );\n }\n\n if (!hasAddress) {\n ops.push(\n setBillingAddress({\n variables: {\n input: {\n billingAccountId,\n line1: form.line1.trim(),\n ...(form.line2.trim() ? { line2: form.line2.trim() } : {}),\n city: form.city.trim(),\n ...(form.state.trim() ? { state: form.state.trim() } : {}),\n postalCode: form.postalCode.trim(),\n country: form.country,\n },\n },\n })\n );\n }\n\n await Promise.all(ops);\n onCompleted();\n } catch {\n setServerError('Failed to save billing details. Please try again.');\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"bg-card border border-border rounded-xl shadow-xl w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n {/* Header */}\n <div className=\"flex items-start justify-between p-6 border-b border-border\">\n <div>\n <h2 className=\"text-lg font-semibold text-foreground\">Complete billing details</h2>\n <p className=\"text-sm text-muted-foreground mt-1\">\n We need a few more details before processing your payment.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-2 text-muted-foreground hover:text-foreground rounded-lg hover:bg-muted transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"p-6 space-y-6\">\n {serverError && (\n <div className=\"p-3 rounded-lg bg-status-error-bg-subtle border border-status-error-border text-sm text-status-error-text\">\n {serverError}\n </div>\n )}\n\n {/* Phone */}\n {!hasPhone && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-foreground\">\n <Phone className=\"size-4 text-muted-foreground\" />\n Contact number\n </div>\n <div>\n <input\n type=\"tel\"\n value={form.contactNumber}\n onChange={(e) => set('contactNumber', e.target.value)}\n placeholder=\"+91 98765 43210\"\n className={`w-full px-3 py-2 text-sm border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 ${\n errors.contactNumber ? 'border-status-error-border' : 'border-border'\n }`}\n />\n {errors.contactNumber && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.contactNumber}</p>\n )}\n </div>\n </section>\n )}\n\n {/* Address */}\n {!hasAddress && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-foreground\">\n <MapPin className=\"size-4 text-muted-foreground\" />\n Billing address\n </div>\n\n <div className=\"space-y-3\">\n {/* Country */}\n <div>\n <label className=\"block text-xs text-muted-foreground mb-1\">Country</label>\n <select\n value={form.country}\n onChange={(e) => set('country', e.target.value)}\n className={`w-full px-3 py-2 text-sm border rounded-lg bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 ${\n errors.country ? 'border-status-error-border' : 'border-border'\n }`}\n >\n {COUNTRY_OPTIONS.map((c) => (\n <option key={c.value} value={c.value}>\n {c.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Line 1 */}\n <div>\n <label className=\"block text-xs text-muted-foreground mb-1\">Street address</label>\n <input\n type=\"text\"\n value={form.line1}\n onChange={(e) => set('line1', e.target.value)}\n placeholder=\"123 Main St\"\n className={`w-full px-3 py-2 text-sm border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 ${\n errors.line1 ? 'border-status-error-border' : 'border-border'\n }`}\n />\n {errors.line1 && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.line1}</p>\n )}\n </div>\n\n {/* Line 2 */}\n <div>\n <label className=\"block text-xs text-muted-foreground mb-1\">\n Apartment, suite, etc.{' '}\n <span className=\"text-muted-foreground/60\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.line2}\n onChange={(e) => set('line2', e.target.value)}\n placeholder=\"Apt 4B\"\n className=\"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50\"\n />\n </div>\n\n {/* City + State */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs text-muted-foreground mb-1\">City</label>\n <input\n type=\"text\"\n value={form.city}\n onChange={(e) => set('city', e.target.value)}\n placeholder=\"Mumbai\"\n className={`w-full px-3 py-2 text-sm border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 ${\n errors.city ? 'border-status-error-border' : 'border-border'\n }`}\n />\n {errors.city && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.city}</p>\n )}\n </div>\n <div>\n <label className=\"block text-xs text-muted-foreground mb-1\">\n State <span className=\"text-muted-foreground/60\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.state}\n onChange={(e) => set('state', e.target.value)}\n placeholder=\"Maharashtra\"\n className=\"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50\"\n />\n </div>\n </div>\n\n {/* Postal code */}\n <div>\n <label className=\"block text-xs text-muted-foreground mb-1\">Postal code</label>\n <input\n type=\"text\"\n value={form.postalCode}\n onChange={(e) => set('postalCode', e.target.value)}\n placeholder=\"400001\"\n className={`w-full px-3 py-2 text-sm border rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 ${\n errors.postalCode ? 'border-status-error-border' : 'border-border'\n }`}\n />\n {errors.postalCode && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.postalCode}</p>\n )}\n </div>\n </div>\n </section>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 p-6 border-t border-border\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={submitting}\n className=\"px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={submitting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary hover:bg-primary/90 rounded-lg transition-colors disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"size-4 animate-spin\" />}\n Save and continue\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompleteBillingAccountModal.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/CompleteBillingAccountModal.tsx"],"sourcesContent":["/**\n * Modal shown at checkout when the selected billing account is missing\n * a billing address or contact number — fields that can't be auto-filled\n * during account creation but are required by payment providers.\n */\n\nimport { type FC, useState } from 'react';\nimport { MapPin, Phone, Loader2, X } from 'lucide-react';\nimport {\n useSetBillingAddressMutation,\n useUpdateBillingAccountMutation,\n} from '../../../../generated/global-operations';\n\nconst COUNTRY_OPTIONS = [\n { value: 'IN', label: 'India' },\n { value: 'US', label: 'United States' },\n { value: 'GB', label: 'United Kingdom' },\n { value: 'AU', label: 'Australia' },\n { value: 'CA', label: 'Canada' },\n { value: 'SG', label: 'Singapore' },\n { value: 'DE', label: 'Germany' },\n { value: 'FR', label: 'France' },\n { value: 'AE', label: 'UAE' },\n];\n\ninterface FormState {\n contactNumber: string;\n line1: string;\n line2: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n}\n\ninterface FormErrors {\n contactNumber?: string;\n line1?: string;\n city?: string;\n postalCode?: string;\n country?: string;\n}\n\ninterface Props {\n billingAccountId: string;\n /** Whether contactNumber is already filled on the account */\n hasPhone: boolean;\n /** Whether a billing address already exists (with line1 + postalCode) */\n hasAddress: boolean;\n onCompleted: () => void;\n onCancel: () => void;\n}\n\n/** Returns true when a billing account has everything required for checkout. */\nexport function isBillingAccountComplete(account: {\n contactNumber?: string | null;\n billingAddresses?: Array<{\n line1?: string | null;\n city?: string | null;\n postalCode?: string | null;\n country?: string | null;\n }> | null;\n}): boolean {\n const hasPhone = Boolean(account.contactNumber?.trim());\n const primaryAddress = account.billingAddresses?.[0];\n const hasAddress = Boolean(\n primaryAddress?.line1?.trim() &&\n primaryAddress?.city?.trim() &&\n primaryAddress?.postalCode?.trim() &&\n primaryAddress?.country?.trim()\n );\n return hasPhone && hasAddress;\n}\n\nexport const CompleteBillingAccountModal: FC<Props> = ({\n billingAccountId,\n hasPhone,\n hasAddress,\n onCompleted,\n onCancel,\n}) => {\n const [form, setForm] = useState<FormState>({\n contactNumber: '',\n line1: '',\n line2: '',\n city: '',\n state: '',\n postalCode: '',\n country: 'IN',\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [submitting, setSubmitting] = useState(false);\n const [serverError, setServerError] = useState<string | null>(null);\n\n const [setBillingAddress] = useSetBillingAddressMutation();\n const [updateBillingAccount] = useUpdateBillingAccountMutation();\n\n const set = (field: keyof FormState, value: string) =>\n setForm((prev) => ({ ...prev, [field]: value }));\n\n const validate = (): boolean => {\n const e: FormErrors = {};\n if (!hasPhone && !form.contactNumber.trim()) {\n e.contactNumber = 'Phone number is required for payment processing';\n }\n if (!hasAddress) {\n if (!form.line1.trim()) e.line1 = 'Street address is required';\n if (!form.city.trim()) e.city = 'City is required';\n if (!form.postalCode.trim()) e.postalCode = 'Postal code is required';\n if (!form.country.trim()) e.country = 'Country is required';\n }\n setErrors(e);\n return Object.keys(e).length === 0;\n };\n\n const handleSubmit = async () => {\n if (!validate()) return;\n setSubmitting(true);\n setServerError(null);\n\n try {\n const ops: Promise<unknown>[] = [];\n\n if (!hasPhone && form.contactNumber.trim()) {\n ops.push(\n updateBillingAccount({\n variables: {\n input: {\n id: billingAccountId,\n contactNumber: form.contactNumber.trim(),\n },\n },\n })\n );\n }\n\n if (!hasAddress) {\n ops.push(\n setBillingAddress({\n variables: {\n input: {\n billingAccountId,\n line1: form.line1.trim(),\n ...(form.line2.trim() ? { line2: form.line2.trim() } : {}),\n city: form.city.trim(),\n ...(form.state.trim() ? { state: form.state.trim() } : {}),\n postalCode: form.postalCode.trim(),\n country: form.country,\n },\n },\n })\n );\n }\n\n await Promise.all(ops);\n onCompleted();\n } catch {\n setServerError('Failed to save billing details. Please try again.');\n } finally {\n setSubmitting(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n {/* Header */}\n <div className=\"flex items-start justify-between p-6 border-b border-border-subtle\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Complete billing details</h2>\n <p className=\"text-sm text-text-secondary mt-1\">\n We need a few more details before processing your payment.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"p-2 text-text-secondary hover:text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"p-6 space-y-6\">\n {serverError && (\n <div className=\"p-3 rounded-lg bg-status-error-bg-subtle border border-status-error-border text-sm text-status-error-text\">\n {serverError}\n </div>\n )}\n\n {/* Phone */}\n {!hasPhone && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Phone className=\"size-4 text-text-secondary\" />\n Contact number\n </div>\n <div>\n <input\n type=\"tel\"\n value={form.contactNumber}\n onChange={(e) => set('contactNumber', e.target.value)}\n placeholder=\"+91 98765 43210\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.contactNumber ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.contactNumber && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.contactNumber}</p>\n )}\n </div>\n </section>\n )}\n\n {/* Address */}\n {!hasAddress && (\n <section className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <MapPin className=\"size-4 text-text-secondary\" />\n Billing address\n </div>\n\n <div className=\"space-y-3\">\n {/* Country */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Country</label>\n <select\n value={form.country}\n onChange={(e) => set('country', e.target.value)}\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.country ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n >\n {COUNTRY_OPTIONS.map((c) => (\n <option key={c.value} value={c.value}>\n {c.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Line 1 */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Street address</label>\n <input\n type=\"text\"\n value={form.line1}\n onChange={(e) => set('line1', e.target.value)}\n placeholder=\"123 Main St\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.line1 ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.line1 && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.line1}</p>\n )}\n </div>\n\n {/* Line 2 */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Apartment, suite, etc. <span className=\"text-text-muted\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.line2}\n onChange={(e) => set('line2', e.target.value)}\n placeholder=\"Apt 4B\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* City + State */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">City</label>\n <input\n type=\"text\"\n value={form.city}\n onChange={(e) => set('city', e.target.value)}\n placeholder=\"Mumbai\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.city ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.city && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.city}</p>\n )}\n </div>\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n State <span className=\"text-text-muted\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={form.state}\n onChange={(e) => set('state', e.target.value)}\n placeholder=\"Maharashtra\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n {/* Postal code */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Postal code</label>\n <input\n type=\"text\"\n value={form.postalCode}\n onChange={(e) => set('postalCode', e.target.value)}\n placeholder=\"400001\"\n className={`w-full px-3 py-2 text-sm border rounded-input bg-bg-surface text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n errors.postalCode ? 'border-status-error-border' : 'border-border-subtle'\n }`}\n />\n {errors.postalCode && (\n <p className=\"mt-1 text-xs text-status-error-text\">{errors.postalCode}</p>\n )}\n </div>\n </div>\n </section>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 p-6 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={submitting}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={submitting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg hover:bg-action-primary-bgHover rounded-lg transition-colors disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"size-4 animate-spin\" />}\n Save and continue\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -55,11 +55,11 @@ var l = [
|
|
|
55
55
|
children: [/* @__PURE__ */ s("div", {
|
|
56
56
|
className: "flex items-center gap-2",
|
|
57
57
|
children: [/* @__PURE__ */ o(r, { className: "size-4 text-status-warning-text" }), /* @__PURE__ */ o("span", {
|
|
58
|
-
className: "font-medium text-
|
|
58
|
+
className: "font-medium text-text-primary",
|
|
59
59
|
children: v("billing.checkout.creditAmount", "Credit Amount")
|
|
60
60
|
})]
|
|
61
61
|
}), /* @__PURE__ */ s("span", {
|
|
62
|
-
className: "text-sm text-
|
|
62
|
+
className: "text-sm text-text-secondary",
|
|
63
63
|
children: [e(m, h), "/credit"]
|
|
64
64
|
})]
|
|
65
65
|
}),
|
|
@@ -70,7 +70,7 @@ var l = [
|
|
|
70
70
|
type: "button",
|
|
71
71
|
onClick: () => w(100),
|
|
72
72
|
disabled: g || u <= f,
|
|
73
|
-
className: "p-2 rounded-
|
|
73
|
+
className: "p-2 rounded-button border border-border-subtle hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed",
|
|
74
74
|
"aria-label": "Decrease by 100",
|
|
75
75
|
children: /* @__PURE__ */ o(i, { className: "size-4" })
|
|
76
76
|
}),
|
|
@@ -84,14 +84,14 @@ var l = [
|
|
|
84
84
|
onChange: x,
|
|
85
85
|
onBlur: S,
|
|
86
86
|
disabled: g,
|
|
87
|
-
className: "w-full px-4 py-2 text-center text-lg font-semibold\n rounded-
|
|
87
|
+
className: "w-full px-4 py-2 text-center text-lg font-semibold\n rounded-button border border-border-subtle bg-bg-surface text-text-primary\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n disabled:opacity-50 disabled:cursor-not-allowed"
|
|
88
88
|
})
|
|
89
89
|
}),
|
|
90
90
|
/* @__PURE__ */ o("button", {
|
|
91
91
|
type: "button",
|
|
92
92
|
onClick: () => C(100),
|
|
93
93
|
disabled: g || u >= p,
|
|
94
|
-
className: "p-2 rounded-
|
|
94
|
+
className: "p-2 rounded-button border border-border-subtle hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed",
|
|
95
95
|
"aria-label": "Increase by 100",
|
|
96
96
|
children: /* @__PURE__ */ o(a, { className: "size-4" })
|
|
97
97
|
})
|
|
@@ -104,20 +104,20 @@ var l = [
|
|
|
104
104
|
onClick: () => T(e),
|
|
105
105
|
disabled: g,
|
|
106
106
|
className: `
|
|
107
|
-
flex-1 px-2 py-1.5 rounded-
|
|
108
|
-
${u === e ? "border-
|
|
107
|
+
flex-1 px-2 py-1.5 rounded-button border text-sm font-medium transition-colors
|
|
108
|
+
${u === e ? "border-border-strong bg-[var(--color-accent-soft)] text-text-link" : "border-border-subtle hover:border-border-strong hover:bg-bg-sunken text-text-primary"}
|
|
109
109
|
disabled:opacity-50 disabled:cursor-not-allowed
|
|
110
110
|
`,
|
|
111
111
|
children: e.toLocaleString()
|
|
112
112
|
}, e))
|
|
113
113
|
}),
|
|
114
114
|
/* @__PURE__ */ s("div", {
|
|
115
|
-
className: "flex items-center justify-between p-3 rounded-
|
|
115
|
+
className: "flex items-center justify-between p-3 rounded-button bg-status-warning-bg-subtle border border-border-subtle",
|
|
116
116
|
children: [/* @__PURE__ */ s("span", {
|
|
117
117
|
className: "text-sm text-status-warning-text",
|
|
118
118
|
children: [u.toLocaleString(), " credits"]
|
|
119
119
|
}), /* @__PURE__ */ o("span", {
|
|
120
|
-
className: "font-semibold text-
|
|
120
|
+
className: "font-semibold text-text-primary",
|
|
121
121
|
children: e(E, h)
|
|
122
122
|
})]
|
|
123
123
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreditsAmountInput.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/CreditsAmountInput.tsx"],"sourcesContent":["/**\n * Credits Amount Input Component\n * Compact input for specifying how many credits to purchase\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { Coins, Minus, Plus } from 'lucide-react';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CreditsAmountInputProps {\n value: number;\n onChange: (value: number) => void;\n minAmount?: number;\n maxAmount?: number;\n costPerCredit?: number;\n currency?: string;\n disabled?: boolean;\n}\n\nconst QUICK_AMOUNTS = [100, 500, 1000, 5000];\n\nexport const CreditsAmountInput: FC<CreditsAmountInputProps> = ({\n value,\n onChange,\n minAmount = 1,\n maxAmount = 100000,\n costPerCredit = 0.01,\n currency = 'USD',\n disabled = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [inputValue, setInputValue] = useState(value.toString());\n\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.target.value.replace(/[^0-9]/g, '');\n setInputValue(raw);\n\n const parsed = parseInt(raw, 10);\n if (!isNaN(parsed) && parsed >= minAmount && parsed <= maxAmount) {\n onChange(parsed);\n }\n },\n [onChange, minAmount, maxAmount]\n );\n\n const handleBlur = useCallback(() => {\n let parsed = parseInt(inputValue, 10);\n if (isNaN(parsed) || parsed < minAmount) {\n parsed = minAmount;\n } else if (parsed > maxAmount) {\n parsed = maxAmount;\n }\n setInputValue(parsed.toString());\n onChange(parsed);\n }, [inputValue, onChange, minAmount, maxAmount]);\n\n const handleIncrement = useCallback(\n (amount: number) => {\n const newValue = Math.min(value + amount, maxAmount);\n setInputValue(newValue.toString());\n onChange(newValue);\n },\n [value, onChange, maxAmount]\n );\n\n const handleDecrement = useCallback(\n (amount: number) => {\n const newValue = Math.max(value - amount, minAmount);\n setInputValue(newValue.toString());\n onChange(newValue);\n },\n [value, onChange, minAmount]\n );\n\n const handleQuickAmount = useCallback(\n (amount: number) => {\n setInputValue(amount.toString());\n onChange(amount);\n },\n [onChange]\n );\n\n const totalCost = value * costPerCredit;\n\n return (\n <div className=\"space-y-3\">\n {/* Header - compact */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Coins className=\"size-4 text-status-warning-text\" />\n <span className=\"font-medium text-
|
|
1
|
+
{"version":3,"file":"CreditsAmountInput.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/CreditsAmountInput.tsx"],"sourcesContent":["/**\n * Credits Amount Input Component\n * Compact input for specifying how many credits to purchase\n */\n\nimport { type FC, useState, useCallback } from 'react';\nimport { Coins, Minus, Plus } from 'lucide-react';\nimport { formatCurrency } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CreditsAmountInputProps {\n value: number;\n onChange: (value: number) => void;\n minAmount?: number;\n maxAmount?: number;\n costPerCredit?: number;\n currency?: string;\n disabled?: boolean;\n}\n\nconst QUICK_AMOUNTS = [100, 500, 1000, 5000];\n\nexport const CreditsAmountInput: FC<CreditsAmountInputProps> = ({\n value,\n onChange,\n minAmount = 1,\n maxAmount = 100000,\n costPerCredit = 0.01,\n currency = 'USD',\n disabled = false,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const [inputValue, setInputValue] = useState(value.toString());\n\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.target.value.replace(/[^0-9]/g, '');\n setInputValue(raw);\n\n const parsed = parseInt(raw, 10);\n if (!isNaN(parsed) && parsed >= minAmount && parsed <= maxAmount) {\n onChange(parsed);\n }\n },\n [onChange, minAmount, maxAmount]\n );\n\n const handleBlur = useCallback(() => {\n let parsed = parseInt(inputValue, 10);\n if (isNaN(parsed) || parsed < minAmount) {\n parsed = minAmount;\n } else if (parsed > maxAmount) {\n parsed = maxAmount;\n }\n setInputValue(parsed.toString());\n onChange(parsed);\n }, [inputValue, onChange, minAmount, maxAmount]);\n\n const handleIncrement = useCallback(\n (amount: number) => {\n const newValue = Math.min(value + amount, maxAmount);\n setInputValue(newValue.toString());\n onChange(newValue);\n },\n [value, onChange, maxAmount]\n );\n\n const handleDecrement = useCallback(\n (amount: number) => {\n const newValue = Math.max(value - amount, minAmount);\n setInputValue(newValue.toString());\n onChange(newValue);\n },\n [value, onChange, minAmount]\n );\n\n const handleQuickAmount = useCallback(\n (amount: number) => {\n setInputValue(amount.toString());\n onChange(amount);\n },\n [onChange]\n );\n\n const totalCost = value * costPerCredit;\n\n return (\n <div className=\"space-y-3\">\n {/* Header - compact */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Coins className=\"size-4 text-status-warning-text\" />\n <span className=\"font-medium text-text-primary\">\n {tr('billing.checkout.creditAmount', 'Credit Amount')}\n </span>\n </div>\n <span className=\"text-sm text-text-secondary\">\n {formatCurrency(costPerCredit, currency)}/credit\n </span>\n </div>\n\n {/* Input row - compact */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleDecrement(100)}\n disabled={disabled || value <= minAmount}\n className=\"p-2 rounded-button border border-border-subtle hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed\"\n aria-label=\"Decrease by 100\"\n >\n <Minus className=\"size-4\" />\n </button>\n\n <div className=\"relative flex-1\">\n <input\n type=\"text\"\n inputMode=\"numeric\"\n aria-label={tr('billing.checkout.creditAmountInput', 'Credit amount')}\n value={inputValue}\n onChange={handleInputChange}\n onBlur={handleBlur}\n disabled={disabled}\n className=\"w-full px-4 py-2 text-center text-lg font-semibold\n rounded-button border border-border-subtle bg-bg-surface text-text-primary\n focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-border-strong\n disabled:opacity-50 disabled:cursor-not-allowed\"\n />\n </div>\n\n <button\n type=\"button\"\n onClick={() => handleIncrement(100)}\n disabled={disabled || value >= maxAmount}\n className=\"p-2 rounded-button border border-border-subtle hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed\"\n aria-label=\"Increase by 100\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n\n {/* Quick amount buttons - compact row */}\n <div className=\"flex gap-2\">\n {QUICK_AMOUNTS.map((amount) => (\n <button\n key={amount}\n type=\"button\"\n onClick={() => handleQuickAmount(amount)}\n disabled={disabled}\n className={`\n flex-1 px-2 py-1.5 rounded-button border text-sm font-medium transition-colors\n ${\n value === amount\n ? 'border-border-strong bg-[var(--color-accent-soft)] text-text-link'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken text-text-primary'\n }\n disabled:opacity-50 disabled:cursor-not-allowed\n `}\n >\n {amount.toLocaleString()}\n </button>\n ))}\n </div>\n\n {/* Cost summary - compact inline */}\n <div className=\"flex items-center justify-between p-3 rounded-button bg-status-warning-bg-subtle border border-border-subtle\">\n <span className=\"text-sm text-status-warning-text\">{value.toLocaleString()} credits</span>\n <span className=\"font-semibold text-text-primary\">\n {formatCurrency(totalCost, currency)}\n </span>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|