@burdenoff/microfe-billing 2026.915.1 → 2026.915.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"CreditWithdrawalPage.js","names":[],"sources":["../../../../../src/billing/modules/credits/pages/CreditWithdrawalPage.tsx"],"sourcesContent":["/**\n * Credits Module - Credit Withdrawal Page\n * Allows users to withdraw withdrawable credits (purchased + earned) to a payout account.\n */\n\nimport { useState, useEffect, type FC } from 'react';\nimport {\n CreditWithdrawalHistoryDocument,\n GetCreditBalanceByTypeDocument,\n RequestCreditWithdrawalDocument,\n useGetCostOfCreditsCheckoutQuery,\n} from '../../../../generated/global-operations';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport {\n useBillingAccount,\n useBillingAccounts,\n useDefaultBillingAccount,\n} from '../../subscriptions/hooks';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport {\n fetchPayoutAccounts,\n addPayoutAccount,\n type PayoutAccount,\n type PayoutRequest,\n} from '../../earnings/api';\nimport { formatDate, formatRelativeTime } from '../../../shared/utils/format';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport type { CreditBalanceByType } from '../../../shared/types';\nimport { PagePurpose, EmphasisPanel, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\n\n// ============================================================================\n// API helpers (credit-specific)\n// ============================================================================\n\ninterface GatewayContext {\n apiGatewayUrl?: string;\n authToken?: string;\n orgId?: string;\n}\n\nasync function fetchCreditBalanceByType(\n billingAccountId: string,\n ctx: GatewayContext\n): Promise<CreditBalanceByType> {\n const data = await directBillingGraphqlRequest<\n { getCreditBalanceByType: CreditBalanceByType },\n { billingAccountId: string }\n >({\n ...ctx,\n query: GetCreditBalanceByTypeDocument,\n variables: { billingAccountId },\n });\n return data.getCreditBalanceByType;\n}\n\nasync function requestCreditWithdrawal(\n input: {\n billingAccountId: string;\n creditAmount: number;\n payoutAccountId: string;\n currency?: string;\n notes?: string;\n },\n ctx: GatewayContext\n): Promise<{ id: string; status: string; requestedAmount: number; currency: string }> {\n const data = await directBillingGraphqlRequest<\n {\n requestCreditWithdrawal: {\n id: string;\n status: string;\n requestedAmount: number;\n currency: string;\n };\n },\n { input: typeof input }\n >({\n ...ctx,\n query: RequestCreditWithdrawalDocument,\n variables: { input },\n });\n return data.requestCreditWithdrawal;\n}\n\nasync function fetchCreditWithdrawalHistory(\n billingAccountId: string,\n ctx: GatewayContext\n): Promise<PayoutRequest[]> {\n const data = await directBillingGraphqlRequest<\n { myPayoutRequests: { items: PayoutRequest[] } },\n { billingAccountId: string }\n >({\n ...ctx,\n query: CreditWithdrawalHistoryDocument,\n variables: { billingAccountId },\n });\n return data.myPayoutRequests.items;\n}\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\ninterface BalanceCardProps {\n label: string;\n value: number;\n description?: string;\n highlight?: boolean;\n}\n\nconst BalanceCard: FC<BalanceCardProps> = ({ label, value, description, highlight }) => {\n const body = (\n <>\n <p className=\"text-sm text-text-secondary\">{label}</p>\n <p\n className={`text-2xl font-bold mt-1 ${highlight ? 'text-text-link' : 'text-text-primary'}`}\n >\n {value.toLocaleString()} <span className=\"text-base font-normal\">credits</span>\n </p>\n {description && <p className=\"text-xs text-text-secondary mt-1\">{description}</p>}\n </>\n );\n // The withdrawable balance is the single most important figure on this page —\n // render it as the page's one emphasis zone.\n if (highlight) {\n return <EmphasisPanel className=\"p-5\">{body}</EmphasisPanel>;\n }\n return <div className=\"border border-border-subtle rounded-lg p-5 bg-bg-surface\">{body}</div>;\n};\n\nfunction getWithdrawalStatusColor(status: string): string {\n switch (status.toUpperCase()) {\n case 'PAID':\n return statusTokens.success.text;\n case 'APPROVED':\n case 'PROCESSING':\n return statusTokens.info.text;\n case 'REQUESTED':\n return statusTokens.warning.text;\n case 'REJECTED':\n case 'FAILED':\n case 'CANCELED':\n return statusTokens.error.text;\n default:\n return 'text-text-secondary';\n }\n}\n\nfunction getWithdrawalStatusBg(status: string): string {\n switch (status.toUpperCase()) {\n case 'PAID':\n return `${statusTokens.success.bg} ${statusTokens.success.text}`;\n case 'APPROVED':\n case 'PROCESSING':\n return `${statusTokens.info.bg} ${statusTokens.info.text}`;\n case 'REQUESTED':\n return `${statusTokens.warning.bg} ${statusTokens.warning.text}`;\n case 'REJECTED':\n case 'FAILED':\n case 'CANCELED':\n return `${statusTokens.error.bg} ${statusTokens.error.text}`;\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\n// ============================================================================\n// Main Page\n// ============================================================================\n\nexport const CreditWithdrawalPage: FC = () => {\n const permissions = useBillingPermissions();\n const { billingAccounts } = useBillingAccounts();\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string | undefined>(\n undefined\n );\n const { billingAccount: defaultBillingAccount, isLoading: isLoadingDefaultAccount } =\n useDefaultBillingAccount();\n const { billingAccount: explicitBillingAccount, isLoading: isLoadingExplicitAccount } =\n useBillingAccount(selectedBillingAccountId);\n const billingAccount = selectedBillingAccountId ? explicitBillingAccount : defaultBillingAccount;\n const isLoadingAccount = selectedBillingAccountId\n ? isLoadingExplicitAccount\n : isLoadingDefaultAccount;\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const { emit } = useBillingEventEmitter();\n\n // Balance state\n const [balance, setBalance] = useState<CreditBalanceByType | null>(null);\n const [isLoadingBalance, setIsLoadingBalance] = useState(false);\n const [balanceError, setBalanceError] = useState<string | null>(null);\n\n // Payout accounts state\n const [payoutAccounts, setPayoutAccounts] = useState<PayoutAccount[]>([]);\n const [isLoadingAccounts, setIsLoadingAccounts] = useState(false);\n\n // Withdrawal history state\n const [history, setHistory] = useState<PayoutRequest[]>([]);\n const [isLoadingHistory, setIsLoadingHistory] = useState(false);\n\n // Withdrawal form state\n const [selectedAccountId, setSelectedAccountId] = useState<string>('');\n const [creditAmount, setCreditAmount] = useState<string>('');\n const [notes, setNotes] = useState<string>('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n // New payout account form state\n const [showAddAccount, setShowAddAccount] = useState(false);\n const [accountForm, setAccountForm] = useState({\n label: '',\n type: 'BANK_ACCOUNT' as 'BANK_ACCOUNT' | 'UPI',\n accountHolderName: '',\n accountNumber: '',\n ifscCode: '',\n routingNumber: '',\n upiId: '',\n country: '',\n currency: 'USD',\n });\n const [isAddingAccount, setIsAddingAccount] = useState(false);\n const [addAccountError, setAddAccountError] = useState<string | null>(null);\n\n const billingAccountId = billingAccount?.id;\n\n const getCtx = (): GatewayContext => ({\n apiGatewayUrl,\n authToken,\n orgId,\n });\n\n const loadBalance = async () => {\n if (!billingAccountId) return;\n setIsLoadingBalance(true);\n setBalanceError(null);\n try {\n const b = await fetchCreditBalanceByType(billingAccountId, getCtx());\n setBalance(b);\n } catch (e) {\n setBalanceError(e instanceof Error ? e.message : 'Failed to load balance');\n } finally {\n setIsLoadingBalance(false);\n }\n };\n\n const loadAccounts = async () => {\n if (!billingAccountId) return;\n setIsLoadingAccounts(true);\n try {\n const accounts = await fetchPayoutAccounts(billingAccountId, getCtx());\n setPayoutAccounts(accounts);\n const defaultAcc = accounts.find((a) => a.isDefault) ?? accounts[0];\n if (defaultAcc) setSelectedAccountId(defaultAcc.id);\n } catch {\n // silently fail - user will see empty state\n } finally {\n setIsLoadingAccounts(false);\n }\n };\n\n const loadHistory = async () => {\n if (!billingAccountId) return;\n setIsLoadingHistory(true);\n try {\n const items = await fetchCreditWithdrawalHistory(billingAccountId, getCtx());\n setHistory(items);\n } catch {\n // silently fail\n } finally {\n setIsLoadingHistory(false);\n }\n };\n\n useEffect(() => {\n if (billingAccountId) {\n void loadBalance();\n void loadAccounts();\n void loadHistory();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [billingAccountId]);\n\n // Sync payout account currency to billing account currency when it loads\n useEffect(() => {\n if (billingAccount?.currency) {\n setAccountForm((f) => ({ ...f, currency: billingAccount.currency ?? 'USD' }));\n }\n }, [billingAccount?.currency]);\n\n const handleAddAccount = async () => {\n if (!billingAccountId) return;\n setAddAccountError(null);\n setIsAddingAccount(true);\n try {\n const isIndia =\n accountForm.country.trim().toUpperCase() === 'IN' ||\n accountForm.country.trim().toLowerCase() === 'india';\n\n await addPayoutAccount(\n {\n billingAccountId,\n label: accountForm.label,\n type: accountForm.type,\n accountHolderName: accountForm.accountHolderName,\n country: accountForm.country || undefined,\n currency: accountForm.currency,\n ...(accountForm.type === 'BANK_ACCOUNT' && {\n accountNumber: accountForm.accountNumber,\n ...(isIndia\n ? { ifsc: accountForm.ifscCode }\n : { routingNumber: accountForm.routingNumber }),\n }),\n ...(accountForm.type === 'UPI' && { upiId: accountForm.upiId }),\n },\n getCtx()\n );\n emit('billing.payout_account.added', {\n route: '/billing/credits/withdraw',\n entityId: billingAccountId,\n source: 'credit-withdrawal',\n });\n await loadAccounts();\n setShowAddAccount(false);\n setAccountForm({\n label: '',\n type: 'BANK_ACCOUNT' as 'BANK_ACCOUNT' | 'UPI',\n accountHolderName: '',\n accountNumber: '',\n ifscCode: '',\n routingNumber: '',\n upiId: '',\n country: '',\n currency: 'USD',\n });\n } catch (e) {\n setAddAccountError(e instanceof Error ? e.message : 'Failed to add account');\n } finally {\n setIsAddingAccount(false);\n }\n };\n\n const handleWithdraw = async () => {\n if (!billingAccountId || !selectedAccountId) return;\n const amount = parseFloat(creditAmount);\n if (!amount || amount <= 0) return;\n\n setSubmitError(null);\n setIsSubmitting(true);\n try {\n const withdrawal = await requestCreditWithdrawal(\n {\n billingAccountId,\n creditAmount: amount,\n payoutAccountId: selectedAccountId,\n notes: notes || undefined,\n },\n getCtx()\n );\n emit('billing.credits.withdrawal_requested', {\n route: '/billing/credits/withdraw',\n entityId: withdrawal.id,\n source: 'credit-withdrawal',\n amount,\n });\n setSubmitSuccess(true);\n setCreditAmount('');\n setNotes('');\n await loadBalance();\n await loadHistory();\n } catch (e) {\n setSubmitError(e instanceof Error ? e.message : 'Withdrawal request failed');\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const accountCurrency = billingAccount?.currency ?? 'USD';\n const withdrawAmount = parseFloat(creditAmount) || 0;\n\n const { data: costData, loading: isFetchingCost } = useGetCostOfCreditsCheckoutQuery({\n variables: { creditAmount: withdrawAmount, currency: accountCurrency },\n skip: withdrawAmount <= 0 || !billingAccount,\n });\n\n const estimatedPayout = costData?.getCostOfCredits?.amount;\n\n // Permission check\n if (!permissions.canWithdrawCredits) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don&apos;t have permission to withdraw credits.\n </p>\n </div>\n </div>\n );\n }\n\n if (!isLoadingAccount && !billingAccount) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">No Billing Account</h2>\n <p className=\"text-sm text-text-secondary\">\n You need a billing account to withdraw credits.\n </p>\n </div>\n </div>\n );\n }\n\n if (isLoadingAccount && !billingAccount) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"h-8 w-64 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-5\">\n <div className=\"h-4 w-24 bg-bg-sunken animate-pulse rounded mb-2\" />\n <div className=\"h-8 w-32 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n const withdrawable = balance?.withdrawable ?? 0;\n // Small epsilon so a Decimal→number conversion on the server (e.g.\n // 34.999999999999997) can never make \"the exact max\" silently fail this\n // check with no visible reason (BOFF-7192).\n const AMOUNT_EPSILON = 0.005;\n const amountValid = withdrawAmount > 0 && withdrawAmount <= withdrawable + AMOUNT_EPSILON;\n const activeAccounts = payoutAccounts.filter((a) => a.status !== 'ARCHIVED');\n\n // Exactly which condition is currently blocking the request — shown next to\n // the button instead of just disabling it silently (BOFF-7192).\n const withdrawBlockedReason = isSubmitting\n ? null\n : activeAccounts.length === 0\n ? 'Add a payout method above before requesting a withdrawal.'\n : !selectedAccountId\n ? 'Select a payout method above.'\n : !(withdrawAmount > 0)\n ? 'Enter an amount to withdraw.'\n : withdrawAmount > withdrawable + AMOUNT_EPSILON\n ? `Amount exceeds your withdrawable balance of ${withdrawable.toLocaleString()} credits.`\n : null;\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n Withdraw Credits\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Convert your withdrawable credits (purchased &amp; earned) to a payout\n </p>\n <PagePurpose className=\"mt-3\">\n Turn the credits you bought or earned (from sales and referrals) back into real money. Add\n a bank account or UPI as a payout destination, request a withdrawal for any amount up to\n your withdrawable balance, and track each request here until it is paid. Granted credits\n (coupons, admin grants) are not withdrawable.\n </PagePurpose>\n </div>\n\n {/* Billing Account Switcher — only shown when the org has more than one account */}\n {billingAccounts.length > 1 && (\n <select\n value={billingAccount?.id ?? ''}\n onChange={(e) => setSelectedBillingAccountId(e.target.value)}\n aria-label=\"Switch billing account\"\n className=\"px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] w-full sm:min-w-[200px] sm:w-auto\"\n >\n {billingAccounts.map((account) => (\n <option key={account.id} value={account.id}>\n {account.name}\n </option>\n ))}\n </select>\n )}\n </div>\n\n {/* Balance breakdown */}\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {isLoadingBalance ? (\n [1, 2, 3, 4].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-5\">\n <div className=\"h-4 w-20 bg-bg-sunken animate-pulse rounded mb-2\" />\n <div className=\"h-8 w-28 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))\n ) : balanceError ? (\n <div className=\"col-span-4 text-sm text-status-error-text\">{balanceError}</div>\n ) : (\n <>\n <BalanceCard\n label=\"Purchased\"\n value={balance?.purchased ?? 0}\n description=\"From credit purchases\"\n />\n <BalanceCard\n label=\"Earned\"\n value={balance?.earned ?? 0}\n description=\"From sales & referrals\"\n />\n <BalanceCard\n label=\"Granted\"\n value={balance?.granted ?? 0}\n description=\"Coupons & admin grants — non-withdrawable\"\n />\n <BalanceCard\n label=\"Withdrawable\"\n value={withdrawable}\n description=\"Purchased + earned\"\n highlight\n />\n </>\n )}\n </div>\n\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"flex items-center gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border text-status-success-text\">\n <svg className=\"size-5 shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n <div>\n <p className=\"font-medium\">Withdrawal request submitted</p>\n <p className=\"text-sm opacity-80\">Our team will review and process your request.</p>\n </div>\n <button\n type=\"button\"\n onClick={() => setSubmitSuccess(false)}\n className=\"ml-auto text-current opacity-60 hover:opacity-100\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n )}\n\n {/* Main two-column layout */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Left: Payout account management */}\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Payout Account</h2>\n <button\n type=\"button\"\n onClick={() => setShowAddAccount((v) => !v)}\n className=\"text-sm text-text-link hover:underline\"\n >\n {showAddAccount ? 'Cancel' : '+ Add new'}\n </button>\n </div>\n\n {/* Add account form */}\n {showAddAccount && (\n <div className=\"border border-border-subtle rounded-lg p-4 space-y-3 bg-bg-sunken\">\n <h3 className=\"text-sm font-medium text-text-primary\">New Payout Account</h3>\n\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Label</label>\n <input\n type=\"text\"\n value={accountForm.label}\n onChange={(e) => setAccountForm((f) => ({ ...f, label: e.target.value }))}\n placeholder=\"e.g. My Bank Account\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Account Holder Name\n </label>\n <input\n type=\"text\"\n value={accountForm.accountHolderName}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, accountHolderName: e.target.value }))\n }\n placeholder=\"Full name as on bank account\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Type</label>\n <select\n value={accountForm.type}\n onChange={(e) =>\n setAccountForm((f) => ({\n ...f,\n type: e.target.value as typeof accountForm.type,\n }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"BANK_ACCOUNT\">Bank Account</option>\n <option value=\"UPI\">UPI</option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Country</label>\n <input\n type=\"text\"\n value={accountForm.country}\n onChange={(e) => setAccountForm((f) => ({ ...f, country: e.target.value }))}\n placeholder=\"e.g. India, US, UK\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n {accountForm.type === 'BANK_ACCOUNT' && (\n <>\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Account Number</label>\n <input\n type=\"text\"\n value={accountForm.accountNumber}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, accountNumber: e.target.value }))\n }\n placeholder=\"Account number\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n {accountForm.country.trim().toUpperCase() === 'IN' ||\n accountForm.country.trim().toLowerCase() === 'india' ? (\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">IFSC Code</label>\n <input\n type=\"text\"\n value={accountForm.ifscCode}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, ifscCode: e.target.value.toUpperCase() }))\n }\n placeholder=\"e.g. SBIN0001234\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n ) : (\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Routing Number\n </label>\n <input\n type=\"text\"\n value={accountForm.routingNumber}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, routingNumber: e.target.value }))\n }\n placeholder=\"Routing / sort code\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n )}\n </>\n )}\n\n {accountForm.type === 'UPI' && (\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">UPI ID</label>\n <input\n type=\"text\"\n value={accountForm.upiId}\n onChange={(e) => setAccountForm((f) => ({ ...f, upiId: e.target.value }))}\n placeholder=\"yourname@upi\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n )}\n\n {addAccountError && (\n <p className=\"text-xs text-status-error-text\">{addAccountError}</p>\n )}\n\n <button\n type=\"button\"\n onClick={() => void handleAddAccount()}\n disabled={isAddingAccount || !accountForm.label || !accountForm.accountHolderName}\n className=\"w-full py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isAddingAccount ? 'Saving…' : 'Save Account'}\n </button>\n </div>\n )}\n\n {/* Existing accounts */}\n {isLoadingAccounts ? (\n <div className=\"space-y-2\">\n {[1, 2].map((i) => (\n <div\n key={i}\n className=\"h-16 border border-border-subtle rounded-lg bg-bg-sunken animate-pulse\"\n />\n ))}\n </div>\n ) : activeAccounts.length === 0 && !showAddAccount ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No payout accounts yet\"\n description=\"Add a bank account or UPI to receive your withdrawn credits.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddAccount(true)}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Add a bank account or UPI\n </button>\n }\n />\n ) : (\n <div className=\"space-y-2\">\n {activeAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => setSelectedAccountId(account.id)}\n className={`w-full text-left p-4 border rounded-lg transition-colors ${\n selectedAccountId === account.id\n ? 'border-border-strong bg-[var(--color-accent-soft)]'\n : 'border-border-subtle bg-bg-surface hover:border-border-strong'\n }`}\n >\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{account.label}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {account.type.replaceAll('_', ' ')}\n {account.displayName ? ` · ${account.displayName}` : ''}\n {account.accountLast4 ? ` ···${account.accountLast4}` : ''}\n {account.country ? ` · ${account.country}` : ''}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {account.isDefault && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-[var(--color-accent-soft)] text-text-link font-medium\">\n Default\n </span>\n )}\n <span\n className={`text-xs px-2 py-0.5 rounded font-medium ${\n account.status === 'ACTIVE'\n ? `${statusTokens.success.bg} ${statusTokens.success.text}`\n : account.status === 'PENDING_VERIFICATION'\n ? `${statusTokens.warning.bg} ${statusTokens.warning.text}`\n : `${statusTokens.error.bg} ${statusTokens.error.text}`\n }`}\n >\n {account.status.replaceAll('_', ' ').toLowerCase()}\n </span>\n {selectedAccountId === account.id && (\n <svg\n className=\"size-4 text-text-link shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n )}\n </div>\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Right: Withdrawal request form */}\n <div className=\"space-y-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Request Withdrawal</h2>\n\n <div className=\"border border-border-subtle rounded-lg p-5 bg-bg-surface space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Credits to Withdraw\n </label>\n <input\n type=\"number\"\n min={1}\n max={withdrawable}\n step={1}\n value={creditAmount}\n onChange={(e) => setCreditAmount(e.target.value)}\n placeholder=\"Enter credit amount\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-secondary mt-1\">\n Max withdrawable:{' '}\n <span className=\"font-medium\">{withdrawable.toLocaleString()} credits</span>\n </p>\n {withdrawAmount > 0 && withdrawAmount <= withdrawable + AMOUNT_EPSILON && (\n <p className=\"text-xs text-text-secondary mt-1\">\n {isFetchingCost ? (\n 'Calculating…'\n ) : estimatedPayout != null ? (\n <>\n Estimated payout:{' '}\n <span className=\"font-semibold text-text-primary\">\n {estimatedPayout.toLocaleString(undefined, {\n style: 'currency',\n currency: accountCurrency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}\n </span>{' '}\n at the current credit rate\n </>\n ) : null}\n </p>\n )}\n {withdrawAmount > 0 && withdrawAmount > withdrawable + AMOUNT_EPSILON && (\n <p className=\"text-xs text-status-error-text mt-1\">\n Amount exceeds your withdrawable balance\n </p>\n )}\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Notes <span className=\"text-text-secondary font-normal\">(optional)</span>\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Any notes for this withdrawal request\"\n rows={2}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n {submitError && (\n <div className=\"p-3 rounded-button bg-status-error-bg-subtle border border-status-error-border\">\n <p className=\"text-sm text-status-error-text\">{submitError}</p>\n </div>\n )}\n\n <div className=\"pt-1 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary mb-3\">\n Credits will be converted to {accountCurrency} at the current credit rate.\n Withdrawals are reviewed before processing and may take 3–5 business days.\n </p>\n {/* Always say exactly why the button is disabled — never just gray it\n out with no explanation (BOFF-7192). */}\n {withdrawBlockedReason && (\n <p className=\"text-xs text-status-warning-text mb-2\">{withdrawBlockedReason}</p>\n )}\n <button\n type=\"button\"\n onClick={() => void handleWithdraw()}\n disabled={\n isSubmitting || !amountValid || !selectedAccountId || activeAccounts.length === 0\n }\n className=\"w-full py-2.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isSubmitting ? 'Submitting…' : 'Request Withdrawal'}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Withdrawal history */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Withdrawal History</h2>\n <button\n type=\"button\"\n onClick={() => void loadHistory()}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n aria-label=\"Refresh withdrawal history\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n </div>\n\n {isLoadingHistory ? (\n <div className=\"space-y-2\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-16 border border-border-subtle rounded-lg bg-bg-sunken animate-pulse\"\n />\n ))}\n </div>\n ) : history.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No withdrawal requests yet\"\n description=\"Once you request a withdrawal, its status will appear here.\"\n />\n ) : (\n <div className=\"border border-border-subtle rounded-lg overflow-hidden\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken border-b border-border-subtle\">\n <tr>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Amount\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Status\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide hidden sm:table-cell\">\n Payout Account\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide hidden md:table-cell\">\n Requested\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide hidden md:table-cell\">\n Completed\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-subtle\">\n {history.map((req) => (\n <tr key={req.id} className=\"hover:bg-bg-sunken transition-colors\">\n <td className=\"px-4 py-3 font-medium text-text-primary\">\n {req.requestedAmount.toLocaleString()} {req.currency}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${getWithdrawalStatusBg(req.status)}`}\n >\n {req.status.charAt(0) + req.status.slice(1).toLowerCase()}\n </span>\n {req.failureReason && (\n <p\n className=\"text-xs text-status-error-text mt-0.5 max-w-[180px] truncate\"\n title={req.failureReason}\n >\n {req.failureReason}\n </p>\n )}\n </td>\n <td className=\"px-4 py-3 text-text-secondary hidden sm:table-cell\">\n {req.payoutAccount ? (\n <span>\n {req.payoutAccount.label}\n {req.payoutAccount.accountLast4\n ? ` ···${req.payoutAccount.accountLast4}`\n : ''}\n </span>\n ) : (\n '—'\n )}\n </td>\n <td className=\"px-4 py-3 text-text-secondary hidden md:table-cell\">\n <span title={formatDate(req.createdAt, 'long')}>\n {formatRelativeTime(req.createdAt)}\n </span>\n </td>\n <td className=\"px-4 py-3 text-text-secondary hidden md:table-cell\">\n {req.paidAt ? (\n <span className={getWithdrawalStatusColor('PAID')}>\n {formatDate(req.paidAt, 'short')}\n </span>\n ) : (\n '—'\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreditWithdrawalPage.js","names":[],"sources":["../../../../../src/billing/modules/credits/pages/CreditWithdrawalPage.tsx"],"sourcesContent":["/**\n * Credits Module - Credit Withdrawal Page\n * Allows users to withdraw withdrawable credits (purchased + earned) to a payout account.\n */\n\nimport { useState, useEffect, type FC } from 'react';\nimport {\n CreditWithdrawalHistoryDocument,\n GetCreditBalanceByTypeDocument,\n RequestCreditWithdrawalDocument,\n useGetCostOfCreditsCheckoutQuery,\n} from '../../../../generated/global-operations';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport {\n useBillingAccount,\n useBillingAccounts,\n useDefaultBillingAccount,\n} from '../../subscriptions/hooks';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport {\n fetchPayoutAccounts,\n addPayoutAccount,\n type PayoutAccount,\n type PayoutRequest,\n} from '../../earnings/api';\nimport { formatDate, formatRelativeTime } from '../../../shared/utils/format';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport type { CreditBalanceByType } from '../../../shared/types';\nimport { PagePurpose, EmphasisPanel, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\n\n// ============================================================================\n// API helpers (credit-specific)\n// ============================================================================\n\ninterface GatewayContext {\n apiGatewayUrl?: string;\n authToken?: string;\n orgId?: string;\n}\n\nasync function fetchCreditBalanceByType(\n billingAccountId: string,\n ctx: GatewayContext\n): Promise<CreditBalanceByType> {\n const data = await directBillingGraphqlRequest<\n { getCreditBalanceByType: CreditBalanceByType },\n { billingAccountId: string }\n >({\n ...ctx,\n query: GetCreditBalanceByTypeDocument,\n variables: { billingAccountId },\n });\n return data.getCreditBalanceByType;\n}\n\nasync function requestCreditWithdrawal(\n input: {\n billingAccountId: string;\n creditAmount: number;\n payoutAccountId: string;\n currency?: string;\n notes?: string;\n },\n ctx: GatewayContext\n): Promise<{ id: string; status: string; requestedAmount: number; currency: string }> {\n const data = await directBillingGraphqlRequest<\n {\n requestCreditWithdrawal: {\n id: string;\n status: string;\n requestedAmount: number;\n currency: string;\n };\n },\n { input: typeof input }\n >({\n ...ctx,\n query: RequestCreditWithdrawalDocument,\n variables: { input },\n });\n return data.requestCreditWithdrawal;\n}\n\nasync function fetchCreditWithdrawalHistory(\n billingAccountId: string,\n ctx: GatewayContext\n): Promise<PayoutRequest[]> {\n const data = await directBillingGraphqlRequest<\n { myPayoutRequests: { items: PayoutRequest[] } },\n { billingAccountId: string }\n >({\n ...ctx,\n query: CreditWithdrawalHistoryDocument,\n variables: { billingAccountId },\n });\n return data.myPayoutRequests.items;\n}\n\n// ============================================================================\n// Sub-components\n// ============================================================================\n\ninterface BalanceCardProps {\n label: string;\n value: number;\n description?: string;\n highlight?: boolean;\n}\n\nconst BalanceCard: FC<BalanceCardProps> = ({ label, value, description, highlight }) => {\n const body = (\n <>\n <p className=\"text-sm text-text-secondary\">{label}</p>\n <p\n className={`text-2xl font-bold mt-1 ${highlight ? 'text-text-link' : 'text-text-primary'}`}\n >\n {value.toLocaleString()} <span className=\"text-base font-normal\">credits</span>\n </p>\n {description && <p className=\"text-xs text-text-secondary mt-1\">{description}</p>}\n </>\n );\n // The withdrawable balance is the single most important figure on this page —\n // render it as the page's one emphasis zone.\n if (highlight) {\n return <EmphasisPanel className=\"p-5\">{body}</EmphasisPanel>;\n }\n return <div className=\"border border-border-subtle rounded-lg p-5 bg-bg-surface\">{body}</div>;\n};\n\nfunction getWithdrawalStatusColor(status: string): string {\n switch (status.toUpperCase()) {\n case 'PAID':\n return statusTokens.success.text;\n case 'APPROVED':\n case 'PROCESSING':\n return statusTokens.info.text;\n case 'REQUESTED':\n return statusTokens.warning.text;\n case 'REJECTED':\n case 'FAILED':\n case 'CANCELED':\n return statusTokens.error.text;\n default:\n return 'text-text-secondary';\n }\n}\n\nfunction getWithdrawalStatusBg(status: string): string {\n switch (status.toUpperCase()) {\n case 'PAID':\n return `${statusTokens.success.bg} ${statusTokens.success.text}`;\n case 'APPROVED':\n case 'PROCESSING':\n return `${statusTokens.info.bg} ${statusTokens.info.text}`;\n case 'REQUESTED':\n return `${statusTokens.warning.bg} ${statusTokens.warning.text}`;\n case 'REJECTED':\n case 'FAILED':\n case 'CANCELED':\n return `${statusTokens.error.bg} ${statusTokens.error.text}`;\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\n// ============================================================================\n// Main Page\n// ============================================================================\n\nexport const CreditWithdrawalPage: FC = () => {\n const permissions = useBillingPermissions();\n const { billingAccounts } = useBillingAccounts();\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string | undefined>(\n undefined\n );\n const { billingAccount: defaultBillingAccount, isLoading: isLoadingDefaultAccount } =\n useDefaultBillingAccount();\n const { billingAccount: explicitBillingAccount, isLoading: isLoadingExplicitAccount } =\n useBillingAccount(selectedBillingAccountId);\n const billingAccount = selectedBillingAccountId ? explicitBillingAccount : defaultBillingAccount;\n const isLoadingAccount = selectedBillingAccountId\n ? isLoadingExplicitAccount\n : isLoadingDefaultAccount;\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const { emit } = useBillingEventEmitter();\n\n // Balance state\n const [balance, setBalance] = useState<CreditBalanceByType | null>(null);\n const [isLoadingBalance, setIsLoadingBalance] = useState(false);\n const [balanceError, setBalanceError] = useState<string | null>(null);\n\n // Payout accounts state\n const [payoutAccounts, setPayoutAccounts] = useState<PayoutAccount[]>([]);\n const [isLoadingAccounts, setIsLoadingAccounts] = useState(false);\n\n // Withdrawal history state\n const [history, setHistory] = useState<PayoutRequest[]>([]);\n const [isLoadingHistory, setIsLoadingHistory] = useState(false);\n\n // Withdrawal form state\n const [selectedAccountId, setSelectedAccountId] = useState<string>('');\n const [creditAmount, setCreditAmount] = useState<string>('');\n const [notes, setNotes] = useState<string>('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n // New payout account form state\n const [showAddAccount, setShowAddAccount] = useState(false);\n const [accountForm, setAccountForm] = useState({\n label: '',\n type: 'BANK_ACCOUNT' as 'BANK_ACCOUNT' | 'UPI',\n accountHolderName: '',\n accountNumber: '',\n ifscCode: '',\n routingNumber: '',\n upiId: '',\n country: '',\n currency: 'USD',\n });\n const [isAddingAccount, setIsAddingAccount] = useState(false);\n const [addAccountError, setAddAccountError] = useState<string | null>(null);\n\n const billingAccountId = billingAccount?.id;\n\n const getCtx = (): GatewayContext => ({\n apiGatewayUrl,\n authToken,\n orgId,\n });\n\n const loadBalance = async () => {\n if (!billingAccountId) return;\n setIsLoadingBalance(true);\n setBalanceError(null);\n try {\n const b = await fetchCreditBalanceByType(billingAccountId, getCtx());\n setBalance(b);\n } catch (e) {\n setBalanceError(e instanceof Error ? e.message : 'Failed to load balance');\n } finally {\n setIsLoadingBalance(false);\n }\n };\n\n const loadAccounts = async () => {\n if (!billingAccountId) return;\n setIsLoadingAccounts(true);\n try {\n const accounts = await fetchPayoutAccounts(billingAccountId, getCtx());\n setPayoutAccounts(accounts);\n const defaultAcc = accounts.find((a) => a.isDefault) ?? accounts[0];\n if (defaultAcc) setSelectedAccountId(defaultAcc.id);\n } catch {\n // silently fail - user will see empty state\n } finally {\n setIsLoadingAccounts(false);\n }\n };\n\n const loadHistory = async () => {\n if (!billingAccountId) return;\n setIsLoadingHistory(true);\n try {\n const items = await fetchCreditWithdrawalHistory(billingAccountId, getCtx());\n setHistory(items);\n } catch {\n // silently fail\n } finally {\n setIsLoadingHistory(false);\n }\n };\n\n useEffect(() => {\n if (billingAccountId) {\n void loadBalance();\n void loadAccounts();\n void loadHistory();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [billingAccountId]);\n\n // Sync payout account currency to billing account currency when it loads\n useEffect(() => {\n if (billingAccount?.currency) {\n setAccountForm((f) => ({ ...f, currency: billingAccount.currency ?? 'USD' }));\n }\n }, [billingAccount?.currency]);\n\n const handleAddAccount = async () => {\n if (!billingAccountId) return;\n setAddAccountError(null);\n setIsAddingAccount(true);\n try {\n const isIndia =\n accountForm.country.trim().toUpperCase() === 'IN' ||\n accountForm.country.trim().toLowerCase() === 'india';\n\n await addPayoutAccount(\n {\n billingAccountId,\n label: accountForm.label,\n type: accountForm.type,\n accountHolderName: accountForm.accountHolderName,\n country: accountForm.country || undefined,\n currency: accountForm.currency,\n ...(accountForm.type === 'BANK_ACCOUNT' && {\n accountNumber: accountForm.accountNumber,\n ...(isIndia\n ? { ifsc: accountForm.ifscCode }\n : { routingNumber: accountForm.routingNumber }),\n }),\n ...(accountForm.type === 'UPI' && { upiId: accountForm.upiId }),\n },\n getCtx()\n );\n emit('billing.payout_account.added', {\n route: '/billing/credits/withdraw',\n entityId: billingAccountId,\n source: 'credit-withdrawal',\n });\n await loadAccounts();\n setShowAddAccount(false);\n setAccountForm({\n label: '',\n type: 'BANK_ACCOUNT' as 'BANK_ACCOUNT' | 'UPI',\n accountHolderName: '',\n accountNumber: '',\n ifscCode: '',\n routingNumber: '',\n upiId: '',\n country: '',\n currency: 'USD',\n });\n } catch (e) {\n setAddAccountError(e instanceof Error ? e.message : 'Failed to add account');\n } finally {\n setIsAddingAccount(false);\n }\n };\n\n const handleWithdraw = async () => {\n if (!billingAccountId || !selectedAccountId) return;\n const amount = parseFloat(creditAmount);\n if (!amount || amount <= 0) return;\n\n setSubmitError(null);\n setIsSubmitting(true);\n try {\n const withdrawal = await requestCreditWithdrawal(\n {\n billingAccountId,\n creditAmount: amount,\n payoutAccountId: selectedAccountId,\n notes: notes || undefined,\n },\n getCtx()\n );\n emit('billing.credits.withdrawal_requested', {\n route: '/billing/credits/withdraw',\n entityId: withdrawal.id,\n source: 'credit-withdrawal',\n amount,\n });\n setSubmitSuccess(true);\n setCreditAmount('');\n setNotes('');\n await loadBalance();\n await loadHistory();\n } catch (e) {\n setSubmitError(e instanceof Error ? e.message : 'Withdrawal request failed');\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const accountCurrency = billingAccount?.currency ?? 'USD';\n const withdrawAmount = parseFloat(creditAmount) || 0;\n\n const { data: costData, loading: isFetchingCost } = useGetCostOfCreditsCheckoutQuery({\n variables: { creditAmount: withdrawAmount, currency: accountCurrency },\n skip: withdrawAmount <= 0 || !billingAccount,\n });\n\n const estimatedPayout = costData?.getCostOfCredits?.amount;\n\n // Permission check\n if (!permissions.canWithdrawCredits) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don&apos;t have permission to withdraw credits.\n </p>\n </div>\n </div>\n );\n }\n\n if (!isLoadingAccount && !billingAccount) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">No Billing Account</h2>\n <p className=\"text-sm text-text-secondary\">\n You need a billing account to withdraw credits.\n </p>\n </div>\n </div>\n );\n }\n\n if (isLoadingAccount && !billingAccount) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"h-8 w-64 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-5\">\n <div className=\"h-4 w-24 bg-bg-sunken animate-pulse rounded mb-2\" />\n <div className=\"h-8 w-32 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n const withdrawable = balance?.withdrawable ?? 0;\n // Small epsilon so a Decimal→number conversion on the server (e.g.\n // 34.999999999999997) can never make \"the exact max\" silently fail this\n // check with no visible reason (BOFF-7192).\n const AMOUNT_EPSILON = 0.005;\n const amountValid = withdrawAmount > 0 && withdrawAmount <= withdrawable + AMOUNT_EPSILON;\n const activeAccounts = payoutAccounts.filter((a) => a.status !== 'ARCHIVED');\n\n // Exactly which condition is currently blocking the request — shown next to\n // the button instead of just disabling it silently (BOFF-7192).\n const withdrawBlockedReason = isSubmitting\n ? null\n : activeAccounts.length === 0\n ? 'Add a payout method above before requesting a withdrawal.'\n : !selectedAccountId\n ? 'Select a payout method above.'\n : !(withdrawAmount > 0)\n ? 'Enter an amount to withdraw.'\n : withdrawAmount > withdrawable + AMOUNT_EPSILON\n ? `Amount exceeds your withdrawable balance of ${withdrawable.toLocaleString()} credits.`\n : null;\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n Withdraw Credits\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Convert your withdrawable credits (purchased &amp; earned) to a payout\n </p>\n <PagePurpose className=\"mt-3\">\n Turn the credits you bought or earned (from sales and referrals) back into real money.\n Add a bank account or UPI as a payout destination, request a withdrawal for any amount\n up to your withdrawable balance, and track each request here until it is paid. Granted\n credits (coupons, admin grants) are not withdrawable.\n </PagePurpose>\n </div>\n\n {/* Billing Account Switcher — only shown when the org has more than one account */}\n {billingAccounts.length > 1 && (\n <select\n value={billingAccount?.id ?? ''}\n onChange={(e) => setSelectedBillingAccountId(e.target.value)}\n aria-label=\"Switch billing account\"\n className=\"px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] w-full sm:min-w-[200px] sm:w-auto\"\n >\n {billingAccounts.map((account) => (\n <option key={account.id} value={account.id}>\n {account.name}\n </option>\n ))}\n </select>\n )}\n </div>\n\n {/* Balance breakdown */}\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {isLoadingBalance ? (\n [1, 2, 3, 4].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-5\">\n <div className=\"h-4 w-20 bg-bg-sunken animate-pulse rounded mb-2\" />\n <div className=\"h-8 w-28 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))\n ) : balanceError ? (\n <div className=\"col-span-4 text-sm text-status-error-text\">{balanceError}</div>\n ) : (\n <>\n <BalanceCard\n label=\"Purchased\"\n value={balance?.purchased ?? 0}\n description=\"From credit purchases\"\n />\n <BalanceCard\n label=\"Earned\"\n value={balance?.earned ?? 0}\n description=\"From sales & referrals\"\n />\n <BalanceCard\n label=\"Granted\"\n value={balance?.granted ?? 0}\n description=\"Coupons & admin grants — non-withdrawable\"\n />\n <BalanceCard\n label=\"Withdrawable\"\n value={withdrawable}\n description=\"Purchased + earned\"\n highlight\n />\n </>\n )}\n </div>\n\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"flex items-center gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border text-status-success-text\">\n <svg className=\"size-5 shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n <div>\n <p className=\"font-medium\">Withdrawal request submitted</p>\n <p className=\"text-sm opacity-80\">Our team will review and process your request.</p>\n </div>\n <button\n type=\"button\"\n onClick={() => setSubmitSuccess(false)}\n className=\"ml-auto text-current opacity-60 hover:opacity-100\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n )}\n\n {/* Main two-column layout */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Left: Payout account management */}\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Payout Account</h2>\n <button\n type=\"button\"\n onClick={() => setShowAddAccount((v) => !v)}\n className=\"text-sm text-text-link hover:underline\"\n >\n {showAddAccount ? 'Cancel' : '+ Add new'}\n </button>\n </div>\n\n {/* Add account form */}\n {showAddAccount && (\n <div className=\"border border-border-subtle rounded-lg p-4 space-y-3 bg-bg-sunken\">\n <h3 className=\"text-sm font-medium text-text-primary\">New Payout Account</h3>\n\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Label</label>\n <input\n type=\"text\"\n value={accountForm.label}\n onChange={(e) => setAccountForm((f) => ({ ...f, label: e.target.value }))}\n placeholder=\"e.g. My Bank Account\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Account Holder Name\n </label>\n <input\n type=\"text\"\n value={accountForm.accountHolderName}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, accountHolderName: e.target.value }))\n }\n placeholder=\"Full name as on bank account\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Type</label>\n <select\n value={accountForm.type}\n onChange={(e) =>\n setAccountForm((f) => ({\n ...f,\n type: e.target.value as typeof accountForm.type,\n }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"BANK_ACCOUNT\">Bank Account</option>\n <option value=\"UPI\">UPI</option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Country</label>\n <input\n type=\"text\"\n value={accountForm.country}\n onChange={(e) => setAccountForm((f) => ({ ...f, country: e.target.value }))}\n placeholder=\"e.g. India, US, UK\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n {accountForm.type === 'BANK_ACCOUNT' && (\n <>\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">Account Number</label>\n <input\n type=\"text\"\n value={accountForm.accountNumber}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, accountNumber: e.target.value }))\n }\n placeholder=\"Account number\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n {accountForm.country.trim().toUpperCase() === 'IN' ||\n accountForm.country.trim().toLowerCase() === 'india' ? (\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">IFSC Code</label>\n <input\n type=\"text\"\n value={accountForm.ifscCode}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, ifscCode: e.target.value.toUpperCase() }))\n }\n placeholder=\"e.g. SBIN0001234\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n ) : (\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n Routing Number\n </label>\n <input\n type=\"text\"\n value={accountForm.routingNumber}\n onChange={(e) =>\n setAccountForm((f) => ({ ...f, routingNumber: e.target.value }))\n }\n placeholder=\"Routing / sort code\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n )}\n </>\n )}\n\n {accountForm.type === 'UPI' && (\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">UPI ID</label>\n <input\n type=\"text\"\n value={accountForm.upiId}\n onChange={(e) => setAccountForm((f) => ({ ...f, upiId: e.target.value }))}\n placeholder=\"yourname@upi\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n )}\n\n {addAccountError && (\n <p className=\"text-xs text-status-error-text\">{addAccountError}</p>\n )}\n\n <button\n type=\"button\"\n onClick={() => void handleAddAccount()}\n disabled={isAddingAccount || !accountForm.label || !accountForm.accountHolderName}\n className=\"w-full py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isAddingAccount ? 'Saving…' : 'Save Account'}\n </button>\n </div>\n )}\n\n {/* Existing accounts */}\n {isLoadingAccounts ? (\n <div className=\"space-y-2\">\n {[1, 2].map((i) => (\n <div\n key={i}\n className=\"h-16 border border-border-subtle rounded-lg bg-bg-sunken animate-pulse\"\n />\n ))}\n </div>\n ) : activeAccounts.length === 0 && !showAddAccount ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No payout accounts yet\"\n description=\"Add a bank account or UPI to receive your withdrawn credits.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddAccount(true)}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Add a bank account or UPI\n </button>\n }\n />\n ) : (\n <div className=\"space-y-2\">\n {activeAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => setSelectedAccountId(account.id)}\n className={`w-full text-left p-4 border rounded-lg transition-colors ${\n selectedAccountId === account.id\n ? 'border-border-strong bg-[var(--color-accent-soft)]'\n : 'border-border-subtle bg-bg-surface hover:border-border-strong'\n }`}\n >\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{account.label}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {account.type.replaceAll('_', ' ')}\n {account.displayName ? ` · ${account.displayName}` : ''}\n {account.accountLast4 ? ` ···${account.accountLast4}` : ''}\n {account.country ? ` · ${account.country}` : ''}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {account.isDefault && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-[var(--color-accent-soft)] text-text-link font-medium\">\n Default\n </span>\n )}\n <span\n className={`text-xs px-2 py-0.5 rounded font-medium ${\n account.status === 'ACTIVE'\n ? `${statusTokens.success.bg} ${statusTokens.success.text}`\n : account.status === 'PENDING_VERIFICATION'\n ? `${statusTokens.warning.bg} ${statusTokens.warning.text}`\n : `${statusTokens.error.bg} ${statusTokens.error.text}`\n }`}\n >\n {account.status.replaceAll('_', ' ').toLowerCase()}\n </span>\n {selectedAccountId === account.id && (\n <svg\n className=\"size-4 text-text-link shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n )}\n </div>\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Right: Withdrawal request form */}\n <div className=\"space-y-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Request Withdrawal</h2>\n\n <div className=\"border border-border-subtle rounded-lg p-5 bg-bg-surface space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Credits to Withdraw\n </label>\n <input\n type=\"number\"\n min={1}\n max={withdrawable}\n step={1}\n value={creditAmount}\n onChange={(e) => setCreditAmount(e.target.value)}\n placeholder=\"Enter credit amount\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-secondary mt-1\">\n Max withdrawable:{' '}\n <span className=\"font-medium\">{withdrawable.toLocaleString()} credits</span>\n </p>\n {withdrawAmount > 0 && withdrawAmount <= withdrawable + AMOUNT_EPSILON && (\n <p className=\"text-xs text-text-secondary mt-1\">\n {isFetchingCost ? (\n 'Calculating…'\n ) : estimatedPayout != null ? (\n <>\n Estimated payout:{' '}\n <span className=\"font-semibold text-text-primary\">\n {estimatedPayout.toLocaleString(undefined, {\n style: 'currency',\n currency: accountCurrency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}\n </span>{' '}\n at the current credit rate\n </>\n ) : null}\n </p>\n )}\n {withdrawAmount > 0 && withdrawAmount > withdrawable + AMOUNT_EPSILON && (\n <p className=\"text-xs text-status-error-text mt-1\">\n Amount exceeds your withdrawable balance\n </p>\n )}\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Notes <span className=\"text-text-secondary font-normal\">(optional)</span>\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n placeholder=\"Any notes for this withdrawal request\"\n rows={2}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-button bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n {submitError && (\n <div className=\"p-3 rounded-button bg-status-error-bg-subtle border border-status-error-border\">\n <p className=\"text-sm text-status-error-text\">{submitError}</p>\n </div>\n )}\n\n <div className=\"pt-1 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary mb-3\">\n Credits will be converted to {accountCurrency} at the current credit rate.\n Withdrawals are reviewed before processing and may take 3–5 business days.\n </p>\n {/* Always say exactly why the button is disabled — never just gray it\n out with no explanation (BOFF-7192). */}\n {withdrawBlockedReason && (\n <p className=\"text-xs text-status-warning-text mb-2\">{withdrawBlockedReason}</p>\n )}\n <button\n type=\"button\"\n onClick={() => void handleWithdraw()}\n disabled={\n isSubmitting || !amountValid || !selectedAccountId || activeAccounts.length === 0\n }\n className=\"w-full py-2.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isSubmitting ? 'Submitting…' : 'Request Withdrawal'}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Withdrawal history */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Withdrawal History</h2>\n <button\n type=\"button\"\n onClick={() => void loadHistory()}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n aria-label=\"Refresh withdrawal history\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n </button>\n </div>\n\n {isLoadingHistory ? (\n <div className=\"space-y-2\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-16 border border-border-subtle rounded-lg bg-bg-sunken animate-pulse\"\n />\n ))}\n </div>\n ) : history.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No withdrawal requests yet\"\n description=\"Once you request a withdrawal, its status will appear here.\"\n />\n ) : (\n <div className=\"border border-border-subtle rounded-lg overflow-hidden\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken border-b border-border-subtle\">\n <tr>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Amount\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide\">\n Status\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide hidden sm:table-cell\">\n Payout Account\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide hidden md:table-cell\">\n Requested\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium text-text-secondary uppercase tracking-wide hidden md:table-cell\">\n Completed\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-subtle\">\n {history.map((req) => (\n <tr key={req.id} className=\"hover:bg-bg-sunken transition-colors\">\n <td className=\"px-4 py-3 font-medium text-text-primary\">\n {req.requestedAmount.toLocaleString()} {req.currency}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${getWithdrawalStatusBg(req.status)}`}\n >\n {req.status.charAt(0) + req.status.slice(1).toLowerCase()}\n </span>\n {req.failureReason && (\n <p\n className=\"text-xs text-status-error-text mt-0.5 max-w-[180px] truncate\"\n title={req.failureReason}\n >\n {req.failureReason}\n </p>\n )}\n </td>\n <td className=\"px-4 py-3 text-text-secondary hidden sm:table-cell\">\n {req.payoutAccount ? (\n <span>\n {req.payoutAccount.label}\n {req.payoutAccount.accountLast4\n ? ` ···${req.payoutAccount.accountLast4}`\n : ''}\n </span>\n ) : (\n '—'\n )}\n </td>\n <td className=\"px-4 py-3 text-text-secondary hidden md:table-cell\">\n <span title={formatDate(req.createdAt, 'long')}>\n {formatRelativeTime(req.createdAt)}\n </span>\n </td>\n <td className=\"px-4 py-3 text-text-secondary hidden md:table-cell\">\n {req.paidAt ? (\n <span className={getWithdrawalStatusColor('PAID')}>\n {formatDate(req.paidAt, 'short')}\n </span>\n ) : (\n '—'\n )}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":[],"sources":["../../../../src/billing/modules/earnings/api.ts"],"sourcesContent":["import { directBillingGraphqlRequest } from '../../shared/utils';\nimport { gatewayGraphqlRequest } from '../../shared/utils/gatewayGraphql';\nimport {\n AddPayoutAccountDocument,\n AdminDeveloperRevenueOrgDetailDocument,\n AdminDeveloperRevenueOrgDocument,\n AdminDeveloperRevenueOrgMonthlyDocument,\n AdminDeveloperRevenueOrgsDocument,\n AdminPayoutRequestsDocument,\n AdminPayoutSummaryDocument,\n ApprovePayoutRequestDocument,\n ArchivePayoutAccountDocument,\n BillingDeveloperPortalRevenueDocument,\n GetCostOfCreditsDocument,\n GetCreditBalanceByTypeDocument,\n MarkPayoutPaidDocument,\n MyEarningsEntriesDocument,\n MyEarningsSummaryDocument,\n MyPayoutAccountsDocument,\n MyPayoutRequestsDocument,\n ProcessPayoutRequestDocument,\n RejectPayoutRequestDocument,\n RetrySaleCreditSyncDocument,\n SetDefaultPayoutAccountDocument,\n UpdatePayoutAccountDocument,\n} from '../../../generated/global-operations';\n\nexport interface EarningsSummary {\n totalLifetimeAmount: number;\n availableAmount: number;\n reservedAmount: number;\n paidAmount: number;\n pendingAmount: number;\n moneyAmount: number;\n creditsAmount: number;\n categoryBreakdown?: Array<{\n category: 'AFFILIATE' | 'STORE' | 'OTHER';\n amount: number;\n count: number;\n }>;\n}\n\nexport interface EarningsEntry {\n id: string;\n sourceCategory: 'AFFILIATE' | 'STORE' | 'OTHER';\n sourceSubcategory?: string | null;\n description?: string | null;\n unitType: 'MONEY' | 'CREDITS';\n amount: number;\n currency: string;\n status: 'AVAILABLE' | 'RESERVED' | 'PAID' | 'REVERSED';\n createdAt: string;\n}\n\nexport interface PayoutAccount {\n id: string;\n label: string;\n type: 'BANK_ACCOUNT' | 'UPI' | 'WALLET';\n providerPreference: 'AUTO' | 'STRIPE' | 'RAZORPAY' | 'MANUAL';\n status: 'PENDING_VERIFICATION' | 'ACTIVE' | 'FAILED_VERIFICATION' | 'ARCHIVED';\n displayName?: string | null;\n accountLast4?: string | null;\n country?: string | null;\n currency: string;\n isDefault: boolean;\n accountDetails?: Record<string, unknown> | null;\n}\n\nexport interface PayoutRequest {\n id: string;\n billingAccountId?: string | null;\n requesterUserId?: string | null;\n requestedAmount: number;\n approvedAmount?: number | null;\n currency: string;\n provider: 'AUTO' | 'STRIPE' | 'RAZORPAY' | 'MANUAL';\n status: 'REQUESTED' | 'APPROVED' | 'REJECTED' | 'PROCESSING' | 'PAID' | 'FAILED' | 'CANCELED';\n /** See PayoutRequestKind on the backend — what approving this request actually does. */\n requestKind: 'CREDIT_CONVERSION' | 'CASH_PAYOUT';\n externalReference?: string | null;\n failureReason?: string | null;\n internalNotes?: string | null;\n userNotes?: string | null;\n createdAt: string;\n processedAt?: string | null;\n paidAt?: string | null;\n payoutAccount?: PayoutAccount | null;\n}\n\nexport interface PayoutAdminSummary {\n totalRequestedAmount: number;\n totalProcessingAmount: number;\n totalPaidAmount: number;\n pendingCount: number;\n failedCount: number;\n statusBreakdown?: Array<{ status: string; count: number; totalAmount: number }>;\n providerBreakdown?: Array<{ provider: string; count: number; totalAmount: number }>;\n}\n\nexport interface AdminPayoutRequestsPage {\n items: PayoutRequest[];\n totalCount: number;\n hasMore: boolean;\n}\n\nexport interface PayoutRequestFilter {\n billingAccountId?: string;\n provider?: PayoutRequest['provider'];\n status?: PayoutRequest['status'];\n startDate?: string;\n endDate?: string;\n}\n\nexport interface CreditBalanceByType {\n purchased: number;\n earned: number;\n granted: number;\n withdrawable: number;\n total: number;\n}\n\nexport interface DeveloperOrgMonthlyRevenuePoint {\n month: string;\n grossRevenue: number;\n platformFees: number;\n netRevenue: number;\n refunds: number;\n salesCount: number;\n}\n\nexport interface DeveloperPortalRevenueSummary {\n totalGrossRevenue: number;\n totalNetRevenue: number;\n totalPlatformFees: number;\n totalRefunds: number;\n totalRefundsCount: number;\n totalSales: number;\n pendingBalance: number;\n nextPayoutDate?: string | null;\n nextPayoutAmount?: number | null;\n}\n\nexport interface DeveloperPortalRevenueSale {\n id: string;\n grossAmount: number;\n netAmount: number;\n currency: string;\n pricingModel: string;\n status: string;\n billingCreditSyncStatus?: string | null;\n billingCreditSyncError?: string | null;\n billingCreditSyncedAt?: string | null;\n purchasedAt: string;\n application?: {\n id: string;\n name: string;\n slug: string;\n } | null;\n}\n\nexport interface DeveloperPortalRevenuePayout {\n id: string;\n periodLabel?: string | null;\n netAmount: number;\n currency: string;\n status: string;\n scheduledAt?: string | null;\n completedAt?: string | null;\n}\n\nexport interface DeveloperPortalRevenueDashboard {\n summary: DeveloperPortalRevenueSummary | null;\n sales: DeveloperPortalRevenueSale[];\n payouts: DeveloperPortalRevenuePayout[];\n}\n\nexport interface AdminDeveloperRevenueOrg {\n id: string;\n name: string;\n slug: string;\n billingAccountId?: string | null;\n isVerified: boolean;\n totalApps: number;\n publishedApps: number;\n totalInstalls: number;\n activeInstalls: number;\n averageRating?: number | null;\n totalRevenue: number;\n totalPlatformFees: number;\n totalPayouts: number;\n pendingBalance: number;\n lifetimeRevenue: number;\n platformFeeRate: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface AdminDeveloperRevenueOrgDetail {\n summary: DeveloperPortalRevenueSummary | null;\n unpaidSales: {\n totalCount: number;\n items: DeveloperPortalRevenueSale[];\n };\n}\n\ninterface GatewayContext {\n apiGatewayUrl?: string;\n authToken?: string;\n orgId?: string;\n}\n\nexport async function fetchDeveloperPortalRevenueDashboard(\n organizationId: string,\n context: GatewayContext\n): Promise<DeveloperPortalRevenueDashboard> {\n type RevenueQueryResult = {\n developerOrgRevenueSummary: DeveloperPortalRevenueSummary | null;\n developerOrgSales: { edges: Array<{ node: DeveloperPortalRevenueSale }> };\n developerOrgPayouts: { edges: Array<{ node: DeveloperPortalRevenuePayout }> };\n };\n\n const data = await gatewayGraphqlRequest<RevenueQueryResult, { organizationId: string }>({\n ...context,\n query: BillingDeveloperPortalRevenueDocument,\n variables: { organizationId },\n });\n\n return {\n summary: data.developerOrgRevenueSummary,\n sales: data.developerOrgSales.edges.map((edge) => edge.node),\n payouts: data.developerOrgPayouts.edges.map((edge) => edge.node),\n };\n}\n\nexport async function fetchEarningsSummary(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myEarningsSummary: EarningsSummary;\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyEarningsSummaryDocument,\n variables: { billingAccountId },\n });\n\n return data.myEarningsSummary;\n}\n\nexport async function fetchEarningsEntries(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myEarningsEntries: { items: EarningsEntry[] };\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyEarningsEntriesDocument,\n variables: { billingAccountId },\n });\n\n return data.myEarningsEntries.items;\n}\n\nexport async function fetchPayoutAccounts(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myPayoutAccounts: PayoutAccount[];\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyPayoutAccountsDocument,\n variables: { billingAccountId },\n });\n\n return data.myPayoutAccounts;\n}\n\nexport async function fetchPayoutRequests(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myPayoutRequests: { items: PayoutRequest[] };\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyPayoutRequestsDocument,\n variables: { billingAccountId },\n });\n\n return data.myPayoutRequests.items;\n}\n\nexport async function addPayoutAccount(input: Record<string, unknown>, context: GatewayContext) {\n await directBillingGraphqlRequest<\n {\n addPayoutAccount: { id: string };\n },\n { input: Record<string, unknown> }\n >({\n ...context,\n query: AddPayoutAccountDocument,\n variables: { input },\n });\n}\n\nexport async function updatePayoutAccount(\n id: string,\n input: Record<string, unknown>,\n context: GatewayContext\n) {\n await directBillingGraphqlRequest<\n { updatePayoutAccount: { id: string } },\n { id: string; input: Record<string, unknown> }\n >({\n ...context,\n query: UpdatePayoutAccountDocument,\n variables: { id, input },\n });\n}\n\nexport async function setDefaultPayoutAccount(id: string, context: GatewayContext) {\n await directBillingGraphqlRequest<{ setDefaultPayoutAccount: { id: string } }, { id: string }>({\n ...context,\n query: SetDefaultPayoutAccountDocument,\n variables: { id },\n });\n}\n\nexport async function archivePayoutAccount(id: string, context: GatewayContext) {\n await directBillingGraphqlRequest<{ archivePayoutAccount: boolean }, { id: string }>({\n ...context,\n query: ArchivePayoutAccountDocument,\n variables: { id },\n });\n}\n\nexport async function requestPayout(_input: Record<string, unknown>, _context: GatewayContext) {\n throw new Error('Payout request not yet implemented');\n}\n\nexport async function fetchAdminPayoutSummary(context: GatewayContext) {\n const data = await directBillingGraphqlRequest<{\n adminPayoutSummary: PayoutAdminSummary;\n }>({\n ...context,\n query: AdminPayoutSummaryDocument,\n });\n\n return data.adminPayoutSummary;\n}\n\nexport async function fetchAdminPayoutRequests(\n context: GatewayContext,\n pagination: { limit?: number; offset?: number; filter?: PayoutRequestFilter } = {}\n) {\n const data = await directBillingGraphqlRequest<\n {\n adminPayoutRequests: AdminPayoutRequestsPage;\n },\n {\n limit: number;\n offset: number;\n filter?: PayoutRequestFilter;\n }\n >({\n ...context,\n query: AdminPayoutRequestsDocument,\n variables: {\n limit: pagination.limit ?? 50,\n offset: pagination.offset ?? 0,\n filter: pagination.filter,\n },\n });\n\n return data.adminPayoutRequests;\n}\n\nexport async function fetchAdminDeveloperRevenueOrgs(context: GatewayContext) {\n const data = await gatewayGraphqlRequest<{\n developerOrganizations: {\n edges: Array<{ node: AdminDeveloperRevenueOrg }>;\n };\n }>({\n ...context,\n query: AdminDeveloperRevenueOrgsDocument,\n });\n\n return data.developerOrganizations.edges.map((edge) => edge.node);\n}\n\nexport async function fetchAdminDeveloperRevenueOrg(\n organizationId: string,\n context: GatewayContext\n) {\n const data = await gatewayGraphqlRequest<\n { developerOrganization: AdminDeveloperRevenueOrg | null },\n { organizationId: string }\n >({\n ...context,\n query: AdminDeveloperRevenueOrgDocument,\n variables: { organizationId },\n });\n\n return data.developerOrganization;\n}\n\nexport async function fetchAdminDeveloperRevenueOrgDetail(\n organizationId: string,\n context: GatewayContext,\n range: { startDate?: string; endDate?: string } = {}\n) {\n const data = await gatewayGraphqlRequest<\n {\n developerOrgRevenueSummary: DeveloperPortalRevenueSummary | null;\n developerOrgSales: { totalCount: number; edges: Array<{ node: DeveloperPortalRevenueSale }> };\n },\n {\n organizationId: string;\n period: string;\n salesLimit: number;\n salesOffset: number;\n startDate?: string;\n endDate?: string;\n }\n >({\n ...context,\n query: AdminDeveloperRevenueOrgDetailDocument,\n variables: {\n organizationId,\n period: 'monthly',\n salesLimit: 50,\n salesOffset: 0,\n startDate: range.startDate,\n endDate: range.endDate,\n },\n });\n\n return {\n summary: data.developerOrgRevenueSummary,\n unpaidSales: {\n totalCount: data.developerOrgSales.totalCount,\n items: data.developerOrgSales.edges.map((edge) => edge.node),\n },\n } satisfies AdminDeveloperRevenueOrgDetail;\n}\n\n/**\n * Estimated fiat cost of a raw credit amount at the current global credit\n * rate (1 credit = $0.01 by default). Credit-balance figures from\n * getCreditBalanceByType are raw credit units, NOT currency — never format\n * them with formatCurrency directly.\n */\nexport async function fetchCreditCost(\n creditAmount: number,\n currency: string,\n context: GatewayContext\n) {\n const data = await directBillingGraphqlRequest<\n { getCostOfCredits: { amount: number } },\n { creditAmount: number; currency: string }\n >({\n ...context,\n query: GetCostOfCreditsDocument,\n variables: { creditAmount, currency },\n });\n\n return data.getCostOfCredits.amount;\n}\n\nexport async function fetchAdminDeveloperRevenueOrgMonthly(\n organizationId: string,\n context: GatewayContext,\n months = 12\n) {\n const data = await gatewayGraphqlRequest<\n { developerOrgMonthlyRevenue: DeveloperOrgMonthlyRevenuePoint[] },\n { organizationId: string; months: number }\n >({\n ...context,\n query: AdminDeveloperRevenueOrgMonthlyDocument,\n variables: { organizationId, months },\n });\n\n return data.developerOrgMonthlyRevenue;\n}\n\n/**\n * Current credit balance for a devorg's linked billing account. Callable for\n * any billingAccountId as a billing admin — getCreditBalanceByType's\n * `scopeType: \"billing\"` RBAC bypasses org ownership for super_admin actors.\n */\nexport async function fetchBillingAccountCreditBalance(\n billingAccountId: string,\n context: GatewayContext\n) {\n const data = await directBillingGraphqlRequest<\n { getCreditBalanceByType: CreditBalanceByType },\n { billingAccountId: string }\n >({\n ...context,\n query: GetCreditBalanceByTypeDocument,\n variables: { billingAccountId },\n });\n\n return data.getCreditBalanceByType;\n}\n\nexport async function retrySaleCreditSync(saleId: string, context: GatewayContext) {\n const data = await gatewayGraphqlRequest<\n {\n retrySaleCreditSync: Pick<\n DeveloperPortalRevenueSale,\n 'id' | 'billingCreditSyncStatus' | 'billingCreditSyncError' | 'billingCreditSyncedAt'\n >;\n },\n { saleId: string }\n >({\n ...context,\n query: RetrySaleCreditSyncDocument,\n variables: { saleId },\n });\n\n return data.retrySaleCreditSync;\n}\n\nexport async function approvePayoutRequest(\n id: string,\n notes: string | null,\n context: GatewayContext\n) {\n await directBillingGraphqlRequest<\n {\n approvePayoutRequest: { id: string };\n },\n { id: string; notes: string | null }\n >({\n ...context,\n query: ApprovePayoutRequestDocument,\n variables: { id, notes },\n });\n}\n\nexport async function rejectPayoutRequest(id: string, reason: string, context: GatewayContext) {\n await directBillingGraphqlRequest<\n {\n rejectPayoutRequest: { id: string };\n },\n { id: string; reason: string }\n >({\n ...context,\n query: RejectPayoutRequestDocument,\n variables: { id, reason },\n });\n}\n\nexport async function processPayoutRequest(id: string, context: GatewayContext) {\n await directBillingGraphqlRequest<\n {\n processPayoutRequest: { id: string };\n },\n { id: string }\n >({\n ...context,\n query: ProcessPayoutRequestDocument,\n variables: { id },\n });\n}\n\nexport async function markPayoutPaid(\n id: string,\n externalReference: string | null,\n notes: string | null,\n context: GatewayContext\n) {\n await directBillingGraphqlRequest<\n { markPayoutPaid: { id: string } },\n { id: string; externalReference: string | null; notes: string | null }\n >({\n ...context,\n query: MarkPayoutPaidDocument,\n variables: { id, externalReference, notes },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../../../src/billing/modules/earnings/api.ts"],"sourcesContent":["import { directBillingGraphqlRequest } from '../../shared/utils';\nimport { gatewayGraphqlRequest } from '../../shared/utils/gatewayGraphql';\nimport {\n AddPayoutAccountDocument,\n AdminDeveloperRevenueOrgDetailDocument,\n AdminDeveloperRevenueOrgDocument,\n AdminDeveloperRevenueOrgMonthlyDocument,\n AdminDeveloperRevenueOrgsDocument,\n AdminPayoutRequestsDocument,\n AdminPayoutSummaryDocument,\n ApprovePayoutRequestDocument,\n ArchivePayoutAccountDocument,\n BillingDeveloperPortalRevenueDocument,\n GetCostOfCreditsDocument,\n GetCreditBalanceByTypeDocument,\n MarkPayoutPaidDocument,\n MyEarningsEntriesDocument,\n MyEarningsSummaryDocument,\n MyPayoutAccountsDocument,\n MyPayoutRequestsDocument,\n ProcessPayoutRequestDocument,\n RejectPayoutRequestDocument,\n RetrySaleCreditSyncDocument,\n SetDefaultPayoutAccountDocument,\n UpdatePayoutAccountDocument,\n} from '../../../generated/global-operations';\n\nexport interface EarningsSummary {\n totalLifetimeAmount: number;\n availableAmount: number;\n reservedAmount: number;\n paidAmount: number;\n pendingAmount: number;\n moneyAmount: number;\n creditsAmount: number;\n categoryBreakdown?: Array<{\n category: 'AFFILIATE' | 'STORE' | 'OTHER';\n amount: number;\n count: number;\n }>;\n}\n\nexport interface EarningsEntry {\n id: string;\n sourceCategory: 'AFFILIATE' | 'STORE' | 'OTHER';\n sourceSubcategory?: string | null;\n description?: string | null;\n unitType: 'MONEY' | 'CREDITS';\n amount: number;\n currency: string;\n status: 'AVAILABLE' | 'RESERVED' | 'PAID' | 'REVERSED';\n createdAt: string;\n}\n\nexport interface PayoutAccount {\n id: string;\n label: string;\n type: 'BANK_ACCOUNT' | 'UPI' | 'WALLET';\n providerPreference: 'AUTO' | 'STRIPE' | 'RAZORPAY' | 'MANUAL';\n status: 'PENDING_VERIFICATION' | 'ACTIVE' | 'FAILED_VERIFICATION' | 'ARCHIVED';\n displayName?: string | null;\n accountLast4?: string | null;\n country?: string | null;\n currency: string;\n isDefault: boolean;\n accountDetails?: Record<string, unknown> | null;\n}\n\nexport interface PayoutRequest {\n id: string;\n billingAccountId?: string | null;\n requesterUserId?: string | null;\n requestedAmount: number;\n approvedAmount?: number | null;\n currency: string;\n provider: 'AUTO' | 'STRIPE' | 'RAZORPAY' | 'MANUAL';\n status: 'REQUESTED' | 'APPROVED' | 'REJECTED' | 'PROCESSING' | 'PAID' | 'FAILED' | 'CANCELED';\n /** See PayoutRequestKind on the backend — what approving this request actually does. */\n requestKind: 'CREDIT_CONVERSION' | 'CASH_PAYOUT';\n externalReference?: string | null;\n failureReason?: string | null;\n internalNotes?: string | null;\n userNotes?: string | null;\n createdAt: string;\n processedAt?: string | null;\n paidAt?: string | null;\n payoutAccount?: PayoutAccount | null;\n}\n\nexport interface PayoutAdminSummary {\n totalRequestedAmount: number;\n totalProcessingAmount: number;\n totalPaidAmount: number;\n pendingCount: number;\n failedCount: number;\n statusBreakdown?: Array<{ status: string; count: number; totalAmount: number }>;\n providerBreakdown?: Array<{ provider: string; count: number; totalAmount: number }>;\n}\n\nexport interface AdminPayoutRequestsPage {\n items: PayoutRequest[];\n totalCount: number;\n hasMore: boolean;\n}\n\nexport interface PayoutRequestFilter {\n billingAccountId?: string;\n provider?: PayoutRequest['provider'];\n status?: PayoutRequest['status'];\n startDate?: string;\n endDate?: string;\n}\n\nexport interface CreditBalanceByType {\n purchased: number;\n earned: number;\n granted: number;\n withdrawable: number;\n total: number;\n}\n\nexport interface DeveloperOrgMonthlyRevenuePoint {\n month: string;\n grossRevenue: number;\n platformFees: number;\n netRevenue: number;\n refunds: number;\n salesCount: number;\n}\n\nexport interface DeveloperPortalRevenueSummary {\n currency: string;\n totalGrossRevenue: number;\n totalNetRevenue: number;\n totalPlatformFees: number;\n totalRefunds: number;\n totalRefundsCount: number;\n totalSales: number;\n pendingBalance: number;\n nextPayoutDate?: string | null;\n nextPayoutAmount?: number | null;\n}\n\nexport interface DeveloperPortalRevenueSale {\n id: string;\n grossAmount: number;\n netAmount: number;\n currency: string;\n pricingModel: string;\n status: string;\n billingCreditSyncStatus?: string | null;\n billingCreditSyncError?: string | null;\n billingCreditSyncedAt?: string | null;\n purchasedAt: string;\n application?: {\n id: string;\n name: string;\n slug: string;\n } | null;\n}\n\nexport interface DeveloperPortalRevenuePayout {\n id: string;\n periodLabel?: string | null;\n netAmount: number;\n currency: string;\n status: string;\n scheduledAt?: string | null;\n completedAt?: string | null;\n}\n\nexport interface DeveloperPortalRevenueDashboard {\n summary: DeveloperPortalRevenueSummary | null;\n sales: DeveloperPortalRevenueSale[];\n payouts: DeveloperPortalRevenuePayout[];\n}\n\nexport interface AdminDeveloperRevenueOrg {\n id: string;\n name: string;\n slug: string;\n billingAccountId?: string | null;\n isVerified: boolean;\n totalApps: number;\n publishedApps: number;\n totalInstalls: number;\n activeInstalls: number;\n averageRating?: number | null;\n totalRevenue: number;\n totalPlatformFees: number;\n totalPayouts: number;\n pendingBalance: number;\n lifetimeRevenue: number;\n platformFeeRate: number;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface AdminDeveloperRevenueOrgDetail {\n summary: DeveloperPortalRevenueSummary | null;\n unpaidSales: {\n totalCount: number;\n items: DeveloperPortalRevenueSale[];\n };\n}\n\ninterface GatewayContext {\n apiGatewayUrl?: string;\n authToken?: string;\n orgId?: string;\n}\n\nexport async function fetchDeveloperPortalRevenueDashboard(\n organizationId: string,\n context: GatewayContext\n): Promise<DeveloperPortalRevenueDashboard> {\n type RevenueQueryResult = {\n developerOrgRevenueSummary: DeveloperPortalRevenueSummary | null;\n developerOrgSales: { edges: Array<{ node: DeveloperPortalRevenueSale }> };\n developerOrgPayouts: { edges: Array<{ node: DeveloperPortalRevenuePayout }> };\n };\n\n const data = await gatewayGraphqlRequest<RevenueQueryResult, { organizationId: string }>({\n ...context,\n query: BillingDeveloperPortalRevenueDocument,\n variables: { organizationId },\n });\n\n return {\n summary: data.developerOrgRevenueSummary,\n sales: data.developerOrgSales.edges.map((edge) => edge.node),\n payouts: data.developerOrgPayouts.edges.map((edge) => edge.node),\n };\n}\n\nexport async function fetchEarningsSummary(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myEarningsSummary: EarningsSummary;\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyEarningsSummaryDocument,\n variables: { billingAccountId },\n });\n\n return data.myEarningsSummary;\n}\n\nexport async function fetchEarningsEntries(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myEarningsEntries: { items: EarningsEntry[] };\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyEarningsEntriesDocument,\n variables: { billingAccountId },\n });\n\n return data.myEarningsEntries.items;\n}\n\nexport async function fetchPayoutAccounts(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myPayoutAccounts: PayoutAccount[];\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyPayoutAccountsDocument,\n variables: { billingAccountId },\n });\n\n return data.myPayoutAccounts;\n}\n\nexport async function fetchPayoutRequests(billingAccountId: string, context: GatewayContext) {\n const data = await directBillingGraphqlRequest<\n {\n myPayoutRequests: { items: PayoutRequest[] };\n },\n { billingAccountId: string }\n >({\n ...context,\n query: MyPayoutRequestsDocument,\n variables: { billingAccountId },\n });\n\n return data.myPayoutRequests.items;\n}\n\nexport async function addPayoutAccount(input: Record<string, unknown>, context: GatewayContext) {\n await directBillingGraphqlRequest<\n {\n addPayoutAccount: { id: string };\n },\n { input: Record<string, unknown> }\n >({\n ...context,\n query: AddPayoutAccountDocument,\n variables: { input },\n });\n}\n\nexport async function updatePayoutAccount(\n id: string,\n input: Record<string, unknown>,\n context: GatewayContext\n) {\n await directBillingGraphqlRequest<\n { updatePayoutAccount: { id: string } },\n { id: string; input: Record<string, unknown> }\n >({\n ...context,\n query: UpdatePayoutAccountDocument,\n variables: { id, input },\n });\n}\n\nexport async function setDefaultPayoutAccount(id: string, context: GatewayContext) {\n await directBillingGraphqlRequest<{ setDefaultPayoutAccount: { id: string } }, { id: string }>({\n ...context,\n query: SetDefaultPayoutAccountDocument,\n variables: { id },\n });\n}\n\nexport async function archivePayoutAccount(id: string, context: GatewayContext) {\n await directBillingGraphqlRequest<{ archivePayoutAccount: boolean }, { id: string }>({\n ...context,\n query: ArchivePayoutAccountDocument,\n variables: { id },\n });\n}\n\nexport async function requestPayout(_input: Record<string, unknown>, _context: GatewayContext) {\n throw new Error('Payout request not yet implemented');\n}\n\nexport async function fetchAdminPayoutSummary(context: GatewayContext) {\n const data = await directBillingGraphqlRequest<{\n adminPayoutSummary: PayoutAdminSummary;\n }>({\n ...context,\n query: AdminPayoutSummaryDocument,\n });\n\n return data.adminPayoutSummary;\n}\n\nexport async function fetchAdminPayoutRequests(\n context: GatewayContext,\n pagination: { limit?: number; offset?: number; filter?: PayoutRequestFilter } = {}\n) {\n const data = await directBillingGraphqlRequest<\n {\n adminPayoutRequests: AdminPayoutRequestsPage;\n },\n {\n limit: number;\n offset: number;\n filter?: PayoutRequestFilter;\n }\n >({\n ...context,\n query: AdminPayoutRequestsDocument,\n variables: {\n limit: pagination.limit ?? 50,\n offset: pagination.offset ?? 0,\n filter: pagination.filter,\n },\n });\n\n return data.adminPayoutRequests;\n}\n\nexport async function fetchAdminDeveloperRevenueOrgs(context: GatewayContext) {\n const data = await gatewayGraphqlRequest<{\n developerOrganizations: {\n edges: Array<{ node: AdminDeveloperRevenueOrg }>;\n };\n }>({\n ...context,\n query: AdminDeveloperRevenueOrgsDocument,\n });\n\n return data.developerOrganizations.edges.map((edge) => edge.node);\n}\n\nexport async function fetchAdminDeveloperRevenueOrg(\n organizationId: string,\n context: GatewayContext\n) {\n const data = await gatewayGraphqlRequest<\n { developerOrganization: AdminDeveloperRevenueOrg | null },\n { organizationId: string }\n >({\n ...context,\n query: AdminDeveloperRevenueOrgDocument,\n variables: { organizationId },\n });\n\n return data.developerOrganization;\n}\n\nexport async function fetchAdminDeveloperRevenueOrgDetail(\n organizationId: string,\n context: GatewayContext,\n range: { startDate?: string; endDate?: string } = {}\n) {\n const data = await gatewayGraphqlRequest<\n {\n developerOrgRevenueSummary: DeveloperPortalRevenueSummary | null;\n developerOrgSales: { totalCount: number; edges: Array<{ node: DeveloperPortalRevenueSale }> };\n },\n {\n organizationId: string;\n period: string;\n salesLimit: number;\n salesOffset: number;\n startDate?: string;\n endDate?: string;\n }\n >({\n ...context,\n query: AdminDeveloperRevenueOrgDetailDocument,\n variables: {\n organizationId,\n period: 'monthly',\n salesLimit: 50,\n salesOffset: 0,\n startDate: range.startDate,\n endDate: range.endDate,\n },\n });\n\n return {\n summary: data.developerOrgRevenueSummary,\n unpaidSales: {\n totalCount: data.developerOrgSales.totalCount,\n items: data.developerOrgSales.edges.map((edge) => edge.node),\n },\n } satisfies AdminDeveloperRevenueOrgDetail;\n}\n\n/**\n * Estimated fiat cost of a raw credit amount at the current global credit\n * rate (1 credit = $0.01 by default). Credit-balance figures from\n * getCreditBalanceByType are raw credit units, NOT currency — never format\n * them with formatCurrency directly.\n */\nexport async function fetchCreditCost(\n creditAmount: number,\n currency: string,\n context: GatewayContext\n) {\n const data = await directBillingGraphqlRequest<\n { getCostOfCredits: { amount: number } },\n { creditAmount: number; currency: string }\n >({\n ...context,\n query: GetCostOfCreditsDocument,\n variables: { creditAmount, currency },\n });\n\n return data.getCostOfCredits.amount;\n}\n\nexport async function fetchAdminDeveloperRevenueOrgMonthly(\n organizationId: string,\n context: GatewayContext,\n months = 12\n) {\n const data = await gatewayGraphqlRequest<\n { developerOrgMonthlyRevenue: DeveloperOrgMonthlyRevenuePoint[] },\n { organizationId: string; months: number }\n >({\n ...context,\n query: AdminDeveloperRevenueOrgMonthlyDocument,\n variables: { organizationId, months },\n });\n\n return data.developerOrgMonthlyRevenue;\n}\n\n/**\n * Current credit balance for a devorg's linked billing account. Callable for\n * any billingAccountId as a billing admin — getCreditBalanceByType's\n * `scopeType: \"billing\"` RBAC bypasses org ownership for super_admin actors.\n */\nexport async function fetchBillingAccountCreditBalance(\n billingAccountId: string,\n context: GatewayContext\n) {\n const data = await directBillingGraphqlRequest<\n { getCreditBalanceByType: CreditBalanceByType },\n { billingAccountId: string }\n >({\n ...context,\n query: GetCreditBalanceByTypeDocument,\n variables: { billingAccountId },\n });\n\n return data.getCreditBalanceByType;\n}\n\nexport async function retrySaleCreditSync(saleId: string, context: GatewayContext) {\n const data = await gatewayGraphqlRequest<\n {\n retrySaleCreditSync: Pick<\n DeveloperPortalRevenueSale,\n 'id' | 'billingCreditSyncStatus' | 'billingCreditSyncError' | 'billingCreditSyncedAt'\n >;\n },\n { saleId: string }\n >({\n ...context,\n query: RetrySaleCreditSyncDocument,\n variables: { saleId },\n });\n\n return data.retrySaleCreditSync;\n}\n\nexport async function approvePayoutRequest(\n id: string,\n notes: string | null,\n context: GatewayContext\n) {\n await directBillingGraphqlRequest<\n {\n approvePayoutRequest: { id: string };\n },\n { id: string; notes: string | null }\n >({\n ...context,\n query: ApprovePayoutRequestDocument,\n variables: { id, notes },\n });\n}\n\nexport async function rejectPayoutRequest(id: string, reason: string, context: GatewayContext) {\n await directBillingGraphqlRequest<\n {\n rejectPayoutRequest: { id: string };\n },\n { id: string; reason: string }\n >({\n ...context,\n query: RejectPayoutRequestDocument,\n variables: { id, reason },\n });\n}\n\nexport async function processPayoutRequest(id: string, context: GatewayContext) {\n await directBillingGraphqlRequest<\n {\n processPayoutRequest: { id: string };\n },\n { id: string }\n >({\n ...context,\n query: ProcessPayoutRequestDocument,\n variables: { id },\n });\n}\n\nexport async function markPayoutPaid(\n id: string,\n externalReference: string | null,\n notes: string | null,\n context: GatewayContext\n) {\n await directBillingGraphqlRequest<\n { markPayoutPaid: { id: string } },\n { id: string; externalReference: string | null; notes: string | null }\n >({\n ...context,\n query: MarkPayoutPaidDocument,\n variables: { id, externalReference, notes },\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DeveloperRevenueOrgDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/DeveloperRevenueOrgDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n BadgeCheck,\n CreditCard,\n Loader2,\n TrendingDown,\n TrendingUp,\n Wallet,\n} from 'lucide-react';\n\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError, StatCard } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { formatCredits, formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport {\n fetchAdminDeveloperRevenueOrg,\n fetchAdminDeveloperRevenueOrgDetail,\n fetchAdminDeveloperRevenueOrgMonthly,\n fetchAdminPayoutRequests,\n fetchBillingAccountCreditBalance,\n fetchCreditCost,\n retrySaleCreditSync,\n type AdminDeveloperRevenueOrg,\n type AdminDeveloperRevenueOrgDetail,\n type CreditBalanceByType,\n type DeveloperOrgMonthlyRevenuePoint,\n type PayoutRequest,\n} from '../api';\n\nconst ALL_TIME = 'all';\n\nconst PAYOUT_STATUS_LABEL: Record<string, string> = {\n REQUESTED: 'Requested',\n APPROVED: 'Approved',\n REJECTED: 'Rejected',\n PROCESSING: 'Processing',\n PAID: 'Paid',\n FAILED: 'Failed',\n CANCELED: 'Canceled',\n};\n\nconst PAYOUT_STATUS_COLORS: Record<string, string> = {\n REQUESTED: 'bg-status-warning-bg-subtle text-status-warning-text',\n APPROVED: 'bg-status-info-bg-subtle text-status-info-text',\n REJECTED: 'bg-status-error-bg-subtle text-status-error-text',\n PROCESSING: 'bg-status-info-bg-subtle text-status-info-text',\n PAID: 'bg-status-success-bg-subtle text-status-success-text',\n FAILED: 'bg-status-error-bg-subtle text-status-error-text',\n CANCELED: 'bg-bg-sunken text-text-muted',\n};\n\nfunction PayoutStatusBadge({ status }: { status: string }) {\n return (\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${PAYOUT_STATUS_COLORS[status] ?? 'bg-bg-sunken text-text-muted'}`}\n >\n {PAYOUT_STATUS_LABEL[status] ?? status}\n </span>\n );\n}\n\nfunction formatMonthLabel(iso: string): string {\n return new Date(iso).toLocaleDateString('en-US', { month: 'short', year: 'numeric' });\n}\n\n/**\n * Turns a month-start ISO string (as returned by developerOrgMonthlyRevenue,\n * always the first instant of a calendar month) into a [startDate, endDate]\n * range covering that whole month, for scoping the sales/withdrawal queries.\n * ALL_TIME returns an empty range (no filtering).\n */\nfunction monthRangeFor(month: string): { startDate?: string; endDate?: string } {\n if (month === ALL_TIME) return {};\n const start = new Date(month);\n if (Number.isNaN(start.getTime())) return {};\n const startDate = new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), 1)).toISOString();\n const endDate = new Date(\n Date.UTC(start.getUTCFullYear(), start.getUTCMonth() + 1, 1) - 1\n ).toISOString();\n return { startDate, endDate };\n}\n\nfunction MonthlyRevenueTrend({ points }: { points: DeveloperOrgMonthlyRevenuePoint[] }) {\n if (points.length === 0) {\n return (\n <div className=\"px-5 py-10 text-center text-sm text-text-muted\">\n No sales recorded yet — the revenue trend will appear once the first sale comes in.\n </div>\n );\n }\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead className=\"bg-bg-sunken/60 text-left text-xs uppercase tracking-wide text-text-muted\">\n <tr>\n <th className=\"px-4 py-3\">Month</th>\n <th className=\"px-4 py-3\">Gross revenue</th>\n <th className=\"px-4 py-3\">Platform fee</th>\n <th className=\"px-4 py-3\">Net revenue</th>\n <th className=\"px-4 py-3\">Sales</th>\n <th className=\"px-4 py-3\">Growth (MoM)</th>\n </tr>\n </thead>\n <tbody>\n {points.map((point, index) => {\n const prior = index > 0 ? points[index - 1] : null;\n const growthPct =\n prior && prior.grossRevenue > 0\n ? ((point.grossRevenue - prior.grossRevenue) / prior.grossRevenue) * 100\n : null;\n\n return (\n <tr key={point.month} className=\"border-t border-border-seam\">\n <td className=\"px-4 py-3 font-medium text-text-primary\">\n {formatMonthLabel(point.month)}\n </td>\n <td className=\"px-4 py-3 text-text-primary\">\n {formatCurrency(point.grossRevenue, 'USD')}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {formatCurrency(point.platformFees, 'USD')}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {formatCurrency(point.netRevenue, 'USD')}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">{point.salesCount}</td>\n <td className=\"px-4 py-3\">\n {growthPct === null ? (\n <span className=\"text-text-muted\">—</span>\n ) : (\n <span\n className={`inline-flex items-center gap-1 font-medium ${\n growthPct >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n }`}\n >\n {growthPct >= 0 ? (\n <TrendingUp className=\"size-3.5\" />\n ) : (\n <TrendingDown className=\"size-3.5\" />\n )}\n {growthPct >= 0 ? '+' : ''}\n {growthPct.toFixed(1)}%\n </span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction WithdrawalHistory({ requests }: { requests: PayoutRequest[] }) {\n if (requests.length === 0) {\n return (\n <div className=\"px-5 py-10 text-center text-sm text-text-muted\">\n No withdrawal requests in this period.\n </div>\n );\n }\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead className=\"bg-bg-sunken/60 text-left text-xs uppercase tracking-wide text-text-muted\">\n <tr>\n <th className=\"px-4 py-3\">Requested</th>\n <th className=\"px-4 py-3\">Kind</th>\n <th className=\"px-4 py-3\">Amount</th>\n <th className=\"px-4 py-3\">Status</th>\n <th className=\"px-4 py-3\">Paid</th>\n <th className=\"px-4 py-3\">Notes</th>\n </tr>\n </thead>\n <tbody>\n {requests.map((request) => (\n <tr key={request.id} className=\"border-t border-border-seam\">\n <td className=\"px-4 py-3 text-text-secondary\">{formatDateTime(request.createdAt)}</td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {request.requestKind === 'CREDIT_CONVERSION' ? 'Revenue-share credits' : 'Cash payout'}\n </td>\n <td className=\"px-4 py-3 font-medium text-text-primary\">\n {formatCurrency(request.requestedAmount, request.currency)}\n </td>\n <td className=\"px-4 py-3\">\n <PayoutStatusBadge status={request.status} />\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {request.paidAt ? formatDateTime(request.paidAt) : '—'}\n </td>\n <td className=\"px-4 py-3 text-text-muted\">\n {request.failureReason || request.userNotes || '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nexport const DeveloperRevenueOrgDetailPage: FC = () => {\n const { organizationId } = useParams<{ organizationId: string }>();\n const permissions = useBillingPermissions();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const navigateTo = useBillingNavigate();\n\n const [org, setOrg] = useState<AdminDeveloperRevenueOrg | null>(null);\n const [monthly, setMonthly] = useState<DeveloperOrgMonthlyRevenuePoint[]>([]);\n const [creditBalance, setCreditBalance] = useState<CreditBalanceByType | null>(null);\n const [creditBalanceUsd, setCreditBalanceUsd] = useState<number | null>(null);\n // Lifetime (unscoped) withdrawal history — drives the \"Already withdrawn\" /\n // in-flight totals up top, which must stay accurate regardless of which\n // month the tables below are currently scoped to.\n const [lifetimePayouts, setLifetimePayouts] = useState<PayoutRequest[]>([]);\n // Period-scoped data — re-fetched whenever selectedMonth changes.\n const [detail, setDetail] = useState<AdminDeveloperRevenueOrgDetail | null>(null);\n const [payoutHistory, setPayoutHistory] = useState<PayoutRequest[]>([]);\n const [selectedMonth, setSelectedMonth] = useState<string>(ALL_TIME);\n\n const [loading, setLoading] = useState(true);\n const [periodLoading, setPeriodLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [retryingSaleId, setRetryingSaleId] = useState<string | null>(null);\n\n const requestContext = useMemo(\n () => ({ apiGatewayUrl, authToken, orgId }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadPeriod = useCallback(\n async (organizationIdValue: string, billingAccountId: string | null | undefined, month: string) => {\n const range = monthRangeFor(month);\n const [detailResult, payoutsResult] = await Promise.all([\n fetchAdminDeveloperRevenueOrgDetail(organizationIdValue, requestContext, range),\n billingAccountId\n ? fetchAdminPayoutRequests(requestContext, {\n limit: 100,\n filter: { billingAccountId, ...range },\n })\n : Promise.resolve({ items: [], totalCount: 0, hasMore: false }),\n ]);\n setDetail(detailResult);\n setPayoutHistory(payoutsResult.items);\n },\n [requestContext]\n );\n\n const load = useCallback(async () => {\n if (!organizationId) return;\n setLoading(true);\n setError(null);\n try {\n const [orgResult, monthlyResult] = await Promise.all([\n fetchAdminDeveloperRevenueOrg(organizationId, requestContext),\n fetchAdminDeveloperRevenueOrgMonthly(organizationId, requestContext, 12),\n ]);\n setOrg(orgResult);\n setMonthly(monthlyResult);\n\n const billingAccountId = orgResult?.billingAccountId ?? null;\n const defaultMonth =\n monthlyResult.length > 0 ? monthlyResult[monthlyResult.length - 1].month : ALL_TIME;\n setSelectedMonth(defaultMonth);\n\n const [balanceResult, lifetimeResult] = await Promise.all([\n billingAccountId\n ? fetchBillingAccountCreditBalance(billingAccountId, requestContext)\n : Promise.resolve(null),\n billingAccountId\n ? fetchAdminPayoutRequests(requestContext, { limit: 100, filter: { billingAccountId } })\n : Promise.resolve({ items: [], totalCount: 0, hasMore: false }),\n loadPeriod(organizationId, billingAccountId, defaultMonth),\n ]);\n setCreditBalance(balanceResult);\n setLifetimePayouts(lifetimeResult.items);\n\n if (balanceResult && balanceResult.total > 0) {\n fetchCreditCost(balanceResult.total, 'USD', requestContext)\n .then(setCreditBalanceUsd)\n .catch(() => setCreditBalanceUsd(null));\n } else {\n setCreditBalanceUsd(0);\n }\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load revenue detail'));\n } finally {\n setLoading(false);\n }\n }, [organizationId, requestContext, loadPeriod]);\n\n useEffect(() => {\n if (!permissions.isBillingAdmin) return;\n void load();\n }, [load, permissions.isBillingAdmin]);\n\n const handleMonthChange = (month: string) => {\n if (!organizationId || !org) return;\n setSelectedMonth(month);\n setPeriodLoading(true);\n void loadPeriod(organizationId, org.billingAccountId, month)\n .catch((loadError) => {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load period data'));\n })\n .finally(() => setPeriodLoading(false));\n };\n\n if (!permissions.isBillingAdmin) {\n return <AccessDenied message=\"You don't have permission to view developer revenue.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Developer revenue unavailable\"\n message={error.message}\n showRetry\n onRetry={() => void load()}\n />\n );\n }\n\n if (loading || !org) {\n return (\n <div className=\"flex items-center justify-center py-20 text-text-secondary\">\n <Loader2 className=\"size-8 animate-spin\" />\n </div>\n );\n }\n\n const withdrawn = lifetimePayouts\n .filter((request) => request.status === 'PAID')\n .reduce((sum, request) => sum + (request.approvedAmount ?? request.requestedAmount), 0);\n const inFlight = lifetimePayouts\n .filter((request) => ['REQUESTED', 'APPROVED', 'PROCESSING'].includes(request.status))\n .reduce((sum, request) => sum + request.requestedAmount, 0);\n\n // Most-recent-first for the dropdown; `monthly` itself stays ascending\n // (oldest first) since that's what the trend table's MoM math needs.\n const monthOptions = [...monthly].reverse();\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={org.name}\n description={`/${org.slug}${org.isVerified ? ' · Verified developer organization' : ''}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/developer-revenue')}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to developer revenue\n </button>\n }\n />\n\n <PagePurpose>\n Store revenue for this org is credited into its linked billing account as earned credits.\n The developer withdraws those credits later through their own billing withdrawal flow — this\n page is a read-and-verify view of that whole loop, from sale to payout.\n </PagePurpose>\n\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n label=\"Gross revenue (all time)\"\n value={formatCurrency(org.totalRevenue, 'USD')}\n icon={<TrendingUp className=\"size-4\" />}\n />\n <StatCard\n label=\"Platform fees (all time)\"\n value={formatCurrency(org.totalPlatformFees, 'USD')}\n icon={<CreditCard className=\"size-4\" />}\n />\n <StatCard\n label=\"Pending earned credits\"\n value={formatCurrency(detail?.summary?.pendingBalance ?? org.pendingBalance, 'USD')}\n icon={<Wallet className=\"size-4\" />}\n />\n <StatCard\n label=\"Already withdrawn (all time)\"\n value={formatCurrency(withdrawn, 'USD')}\n icon={<BadgeCheck className=\"size-4\" />}\n />\n </div>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Billing account balance</h2>\n <p className=\"mt-1 text-sm text-text-muted\">\n The current credit ledger on the org&apos;s linked billing account, right now. These are\n credit units, not currency — 1 credit is worth a small fraction of $1 at the platform&apos;s\n current credit rate.\n </p>\n </div>\n {!org.billingAccountId ? (\n <div className=\"px-5 py-8 text-center text-sm text-text-muted\">\n No billing account is linked yet — revenue cannot be credited until this org links one.\n </div>\n ) : !creditBalance ? (\n <div className=\"px-5 py-8 text-center text-sm text-text-muted\">\n Credit balance could not be loaded.\n </div>\n ) : (\n <>\n <div className=\"grid gap-3 p-5 md:grid-cols-3 xl:grid-cols-5\">\n <StatCard label=\"Earned\" value={formatCredits(creditBalance.earned)} />\n <StatCard label=\"Purchased\" value={formatCredits(creditBalance.purchased)} />\n <StatCard label=\"Granted\" value={formatCredits(creditBalance.granted)} />\n <StatCard label=\"Withdrawable\" value={formatCredits(creditBalance.withdrawable)} />\n <StatCard label=\"Total balance\" value={formatCredits(creditBalance.total)} />\n </div>\n <div className=\"px-5 pb-4 text-xs text-text-muted\">\n {creditBalanceUsd === null\n ? 'Estimated cash value could not be loaded.'\n : `≈ ${formatCurrency(creditBalanceUsd, 'USD')} total at the current credit rate.`}\n </div>\n </>\n )}\n {inFlight > 0 && (\n <div className=\"border-t border-border-seam bg-status-warning-bg-subtle px-5 py-3 text-xs text-status-warning-text\">\n {formatCurrency(inFlight, 'USD')} is currently tied up in a pending or approved\n withdrawal request — it has already left the withdrawable balance above.\n </div>\n )}\n </section>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Monthly revenue trend</h2>\n <p className=\"mt-1 text-sm text-text-muted\">\n Last 12 months of gross/net revenue for this org, computed from completed sales.\n </p>\n </div>\n <MonthlyRevenueTrend points={monthly} />\n </section>\n\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-card border border-border-seam bg-bg-surface px-5 py-4 shadow-[var(--shadow-elevation-1)]\">\n <div>\n <h2 className=\"text-sm font-semibold text-text-primary\">Viewing activity for</h2>\n <p className=\"mt-1 text-xs text-text-muted\">\n Scopes both the withdrawal history and sales tables below — pick a single month to keep\n long-running orgs easy to review, or switch to all time.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {periodLoading && <Loader2 className=\"size-4 animate-spin text-text-muted\" />}\n <select\n value={selectedMonth}\n onChange={(event) => handleMonthChange(event.target.value)}\n disabled={periodLoading}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary disabled:opacity-50\"\n >\n <option value={ALL_TIME}>All time</option>\n {monthOptions.map((point) => (\n <option key={point.month} value={point.month}>\n {formatMonthLabel(point.month)}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Withdrawal history</h2>\n <p className=\"mt-1 text-sm text-text-muted\">\n Payout requests this org made in{' '}\n {selectedMonth === ALL_TIME ? 'all time' : formatMonthLabel(selectedMonth)} — both\n revenue-share credit conversions and cash payouts — with current status.\n </p>\n </div>\n <div className={periodLoading ? 'pointer-events-none opacity-50' : undefined}>\n <WithdrawalHistory requests={payoutHistory} />\n </div>\n </section>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Sales currently contributing to earned credits\n </h2>\n <p className=\"mt-1 text-xs text-text-muted\">\n Unpaid sales purchased in{' '}\n {selectedMonth === ALL_TIME ? 'all time' : formatMonthLabel(selectedMonth)} that should\n exist as earned credits in the linked billing account until the developer withdraws them.\n </p>\n </div>\n <div\n className={`overflow-x-auto ${periodLoading ? 'pointer-events-none opacity-50' : ''}`}\n >\n <table className=\"min-w-full text-sm\">\n <thead className=\"bg-bg-sunken/60 text-left text-xs uppercase tracking-wide text-text-muted\">\n <tr>\n <th className=\"px-4 py-3\">Application</th>\n <th className=\"px-4 py-3\">Purchased</th>\n <th className=\"px-4 py-3\">Net revenue</th>\n <th className=\"px-4 py-3\">Credit sync</th>\n <th className=\"px-4 py-3\">Status</th>\n </tr>\n </thead>\n <tbody>\n {!detail || detail.unpaidSales.items.length === 0 ? (\n <tr>\n <td colSpan={5} className=\"px-4 py-8 text-center text-sm text-text-muted\">\n No outstanding unpaid sales in this period.\n </td>\n </tr>\n ) : (\n detail.unpaidSales.items.map((sale) => (\n <tr key={sale.id} className=\"border-t border-border-seam\">\n <td className=\"px-4 py-3\">\n <div className=\"font-medium text-text-primary\">\n {sale.application?.name ?? 'Unknown app'}\n </div>\n <div className=\"text-xs text-text-muted\">/{sale.application?.slug ?? 'unknown'}</div>\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {formatDateTime(sale.purchasedAt)}\n </td>\n <td className=\"px-4 py-3 text-text-primary\">\n {formatCurrency(sale.netAmount, sale.currency)}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n <div>{sale.billingCreditSyncStatus ?? 'UNKNOWN'}</div>\n {sale.billingCreditSyncError && (\n <div className=\"mt-1 text-xs text-status-error-text\">\n {sale.billingCreditSyncError}\n </div>\n )}\n {sale.billingCreditSyncStatus !== 'SYNCED' && org.billingAccountId && (\n <button\n type=\"button\"\n onClick={() => {\n if (!organizationId) return;\n void (async () => {\n setRetryingSaleId(sale.id);\n try {\n await retrySaleCreditSync(sale.id, requestContext);\n await loadPeriod(organizationId, org.billingAccountId, selectedMonth);\n } finally {\n setRetryingSaleId(null);\n }\n })();\n }}\n disabled={retryingSaleId === sale.id}\n className=\"mt-2 rounded-md border border-border-subtle px-2 py-1 text-xs font-medium text-text-primary disabled:opacity-50\"\n >\n {retryingSaleId === sale.id ? 'Retrying…' : 'Retry sync'}\n </button>\n )}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">{sale.status}</td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n </section>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiCA,IAAM,IAAW,OAEX,IAA8C;CAClD,WAAW;CACX,UAAU;CACV,UAAU;CACV,YAAY;CACZ,MAAM;CACN,QAAQ;CACR,UAAU;AACZ,GAEM,IAA+C;CACnD,WAAW;CACX,UAAU;CACV,UAAU;CACV,YAAY;CACZ,MAAM;CACN,QAAQ;CACR,UAAU;AACZ;AAEA,SAAS,EAAkB,EAAE,aAA8B;CACzD,OACE,kBAAC,QAAD;EACE,WAAW,2EAA2E,EAAqB,MAAW;YAErH,EAAoB,MAAW;CAC5B,CAAA;AAEV;AAEA,SAAS,EAAiB,GAAqB;CAC7C,OAAO,IAAI,KAAK,CAAG,EAAE,mBAAmB,SAAS;EAAE,OAAO;EAAS,MAAM;CAAU,CAAC;AACtF;AAQA,SAAS,GAAc,GAAyD;CAC9E,IAAI,MAAU,GAAU,OAAO,CAAC;CAChC,IAAM,IAAQ,IAAI,KAAK,CAAK;CAM5B,OALI,OAAO,MAAM,EAAM,QAAQ,CAAC,IAAU,CAAC,IAKpC;EAAE,WAJS,IAAI,KAAK,KAAK,IAAI,EAAM,eAAe,GAAG,EAAM,YAAY,GAAG,CAAC,CAAC,EAAE,YAI5E;EAAW,0BAHJ,IAAI,KAClB,KAAK,IAAI,EAAM,eAAe,GAAG,EAAM,YAAY,IAAI,GAAG,CAAC,IAAI,CACjE,GAAE,YACkB;CAAQ;AAC9B;AAEA,SAAS,GAAoB,EAAE,aAAyD;CAStF,OARI,EAAO,WAAW,IAElB,kBAAC,OAAD;EAAK,WAAU;YAAiD;CAE3D,CAAA,IAKP,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,SAAD;GAAO,WAAU;aAAjB,CACE,kBAAC,SAAD;IAAO,WAAU;cACf,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAS,CAAA;KACnC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAiB,CAAA;KAC3C,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAgB,CAAA;KAC1C,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAe,CAAA;KACzC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAS,CAAA;KACnC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAgB,CAAA;IACxC,EAAA,CAAA;GACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAO,KAAK,GAAO,MAAU;IAC5B,IAAM,IAAQ,IAAQ,IAAI,EAAO,IAAQ,KAAK,MACxC,IACJ,KAAS,EAAM,eAAe,KACxB,EAAM,eAAe,EAAM,gBAAgB,EAAM,eAAgB,MACnE;IAEN,OACE,kBAAC,MAAD;KAAsB,WAAU;eAAhC;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAiB,EAAM,KAAK;MAC3B,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAe,EAAM,cAAc,KAAK;MACvC,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAe,EAAM,cAAc,KAAK;MACvC,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAe,EAAM,YAAY,KAAK;MACrC,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBAAiC,EAAM;MAAe,CAAA;MACpE,kBAAC,MAAD;OAAI,WAAU;iBACX,MAAc,OACb,kBAAC,QAAD;QAAM,WAAU;kBAAkB;OAAO,CAAA,IAEzC,kBAAC,QAAD;QACE,WAAW,8CACT,KAAa,IAAI,6BAA6B;kBAFlD;SAMI,EADD,KAAa,IACX,IAEA,GAFD,EAAY,WAAU,WAAY,CAEE;SAErC,KAAa,IAAI,MAAM;SACvB,EAAU,QAAQ,CAAC;SAAE;QAClB;;MAEN,CAAA;KACF;OAjCK,EAAM,KAiCX;GAER,CAAC,EACI,CAAA,CACF;;CACJ,CAAA;AAET;AAEA,SAAS,GAAkB,EAAE,eAA2C;CAStE,OARI,EAAS,WAAW,IAEpB,kBAAC,OAAD;EAAK,WAAU;YAAiD;CAE3D,CAAA,IAKP,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,SAAD;GAAO,WAAU;aAAjB,CACE,kBAAC,SAAD;IAAO,WAAU;cACf,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAa,CAAA;KACvC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAQ,CAAA;KAClC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAU,CAAA;KACpC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAU,CAAA;KACpC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAQ,CAAA;KAClC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAS,CAAA;IACjC,EAAA,CAAA;GACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAS,KAAK,MACb,kBAAC,MAAD;IAAqB,WAAU;cAA/B;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAiC,EAAe,EAAQ,SAAS;KAAM,CAAA;KACrF,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ,gBAAgB,sBAAsB,0BAA0B;KACvE,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAe,EAAQ,iBAAiB,EAAQ,QAAQ;KACvD,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,GAAD,EAAmB,QAAQ,EAAQ,OAAS,CAAA;KAC1C,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ,SAAS,EAAe,EAAQ,MAAM,IAAI;KACjD,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ,iBAAiB,EAAQ,aAAa;KAC7C,CAAA;IACF;MAjBK,EAAQ,EAiBb,CACL,EACI,CAAA,CACF;;CACJ,CAAA;AAET;AAEA,IAAa,UAA0C;CACrD,IAAM,EAAE,sBAAmB,GAAsC,GAC3D,IAAc,GAAsB,GACpC,EAAE,kBAAe,cAAW,aAAU,EAAW,GACjD,IAAa,GAAmB,GAEhC,CAAC,GAAK,KAAU,EAA0C,IAAI,GAC9D,CAAC,GAAS,KAAc,EAA4C,CAAC,CAAC,GACtE,CAAC,GAAe,KAAoB,EAAqC,IAAI,GAC7E,CAAC,GAAkB,KAAuB,EAAwB,IAAI,GAItE,CAAC,GAAiB,KAAsB,EAA0B,CAAC,CAAC,GAEpE,CAAC,GAAQ,MAAa,EAAgD,IAAI,GAC1E,CAAC,IAAe,MAAoB,EAA0B,CAAC,CAAC,GAChE,CAAC,GAAe,KAAoB,EAAiB,CAAQ,GAE7D,CAAC,IAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAe,KAAoB,EAAS,EAAK,GAClD,CAAC,GAAO,KAAY,EAAuB,IAAI,GAC/C,CAAC,GAAgB,KAAqB,EAAwB,IAAI,GAElE,IAAiB,SACd;EAAE;EAAe;EAAW;CAAM,IACzC;EAAC;EAAe;EAAW;CAAK,CAClC,GAEM,IAAa,EACjB,OAAO,GAA6B,GAA6C,MAAkB;EACjG,IAAM,IAAQ,GAAc,CAAK,GAC3B,CAAC,GAAc,KAAiB,MAAM,QAAQ,IAAI,CACtD,EAAoC,GAAqB,GAAgB,CAAK,GAC9E,IACI,EAAyB,GAAgB;GACvC,OAAO;GACP,QAAQ;IAAE;IAAkB,GAAG;GAAM;EACvC,CAAC,IACD,QAAQ,QAAQ;GAAE,OAAO,CAAC;GAAG,YAAY;GAAG,SAAS;EAAM,CAAC,CAClE,CAAC;EAED,AADA,GAAU,CAAY,GACtB,GAAiB,EAAc,KAAK;CACtC,GACA,CAAC,CAAc,CACjB,GAEM,IAAO,EAAY,YAAY;EAC9B,OAEL;GADA,EAAW,EAAI,GACf,EAAS,IAAI;GACb,IAAI;IACF,IAAM,CAAC,GAAW,KAAiB,MAAM,QAAQ,IAAI,CACnD,GAA8B,GAAgB,CAAc,GAC5D,EAAqC,GAAgB,GAAgB,EAAE,CACzE,CAAC;IAED,AADA,EAAO,CAAS,GAChB,EAAW,CAAa;IAExB,IAAM,IAAmB,GAAW,oBAAoB,MAClD,IACJ,EAAc,SAAS,IAAI,EAAc,EAAc,SAAS,GAAG,QAAQ;IAC7E,EAAiB,CAAY;IAE7B,IAAM,CAAC,GAAe,KAAkB,MAAM,QAAQ,IAAI;KACxD,IACI,GAAiC,GAAkB,CAAc,IACjE,QAAQ,QAAQ,IAAI;KACxB,IACI,EAAyB,GAAgB;MAAE,OAAO;MAAK,QAAQ,EAAE,oBAAiB;KAAE,CAAC,IACrF,QAAQ,QAAQ;MAAE,OAAO,CAAC;MAAG,YAAY;MAAG,SAAS;KAAM,CAAC;KAChE,EAAW,GAAgB,GAAkB,CAAY;IAC3D,CAAC;IAID,AAHA,EAAiB,CAAa,GAC9B,EAAmB,EAAe,KAAK,GAEnC,KAAiB,EAAc,QAAQ,IACzC,GAAgB,EAAc,OAAO,OAAO,CAAc,EACvD,KAAK,CAAmB,EACxB,YAAY,EAAoB,IAAI,CAAC,IAExC,EAAoB,CAAC;GAEzB,SAAS,GAAW;IAClB,EAAS,aAAqB,QAAQ,IAAY,gBAAI,MAAM,+BAA+B,CAAC;GAC9F,UAAU;IACR,EAAW,EAAK;GAClB;EArCa;CAsCf,GAAG;EAAC;EAAgB;EAAgB;CAAU,CAAC;CAE/C,QAAgB;EACT,EAAY,kBACjB,EAAU;CACZ,GAAG,CAAC,GAAM,EAAY,cAAc,CAAC;CAErC,IAAM,MAAqB,MAAkB;EACvC,CAAC,KAAkB,CAAC,MACxB,EAAiB,CAAK,GACtB,EAAiB,EAAI,GACrB,EAAgB,GAAgB,EAAI,kBAAkB,CAAK,EACxD,OAAO,MAAc;GACpB,EAAS,aAAqB,QAAQ,IAAY,gBAAI,MAAM,4BAA4B,CAAC;EAC3F,CAAC,EACA,cAAc,EAAiB,EAAK,CAAC;CAC1C;CAEA,IAAI,CAAC,EAAY,gBACf,OAAO,kBAAC,GAAD,EAAc,SAAQ,uDAAwD,CAAA;CAGvF,IAAI,KAAS,GAAc,CAAK,GAC9B,OACE,kBAAC,GAAD;EACE,OAAM;EACN,SAAS,EAAM;EACf,WAAA;EACA,eAAe,KAAK,EAAK;CAC1B,CAAA;CAIL,IAAI,MAAW,CAAC,GACd,OACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,sBAAuB,CAAA;CACvC,CAAA;CAIT,IAAM,KAAY,EACf,QAAQ,MAAY,EAAQ,WAAW,MAAM,EAC7C,QAAQ,GAAK,MAAY,KAAO,EAAQ,kBAAkB,EAAQ,kBAAkB,CAAC,GAClF,IAAW,EACd,QAAQ,MAAY;EAAC;EAAa;EAAY;CAAY,EAAE,SAAS,EAAQ,MAAM,CAAC,EACpF,QAAQ,GAAK,MAAY,IAAM,EAAQ,iBAAiB,CAAC,GAItD,KAAe,CAAC,GAAG,CAAO,EAAE,QAAQ;CAE1C,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAO,EAAI;IACX,aAAa,IAAI,EAAI,OAAO,EAAI,aAAa,uCAAuC;IACpF,SACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,oBAAoB;KAC9C,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA,GAAC,2BAE1B;;GAEX,CAAA;GAED,kBAAC,GAAD,EAAA,UAAa,iQAIA,CAAA;GAEb,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,EAAI,cAAc,KAAK;MAC7C,MAAM,kBAAC,GAAD,EAAY,WAAU,SAAU,CAAA;KACvC,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,EAAI,mBAAmB,KAAK;MAClD,MAAM,kBAAC,IAAD,EAAY,WAAU,SAAU,CAAA;KACvC,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,GAAQ,SAAS,kBAAkB,EAAI,gBAAgB,KAAK;MAClF,MAAM,kBAAC,IAAD,EAAQ,WAAU,SAAU,CAAA;KACnC,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,IAAW,KAAK;MACtC,MAAM,kBAAC,IAAD,EAAY,WAAU,SAAU,CAAA;KACvC,CAAA;IACE;;GAEL,kBAAC,WAAD;IAAS,WAAU;cAAnB;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA4C;MAA2B,CAAA,GACrF,kBAAC,KAAD;OAAG,WAAU;iBAA+B;MAIzC,CAAA,CACA;;KACH,EAAI,mBAID,IAKH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAU,OAAM;QAAS,OAAO,EAAc,EAAc,MAAM;OAAI,CAAA;OACtE,kBAAC,GAAD;QAAU,OAAM;QAAY,OAAO,EAAc,EAAc,SAAS;OAAI,CAAA;OAC5E,kBAAC,GAAD;QAAU,OAAM;QAAU,OAAO,EAAc,EAAc,OAAO;OAAI,CAAA;OACxE,kBAAC,GAAD;QAAU,OAAM;QAAe,OAAO,EAAc,EAAc,YAAY;OAAI,CAAA;OAClF,kBAAC,GAAD;QAAU,OAAM;QAAgB,OAAO,EAAc,EAAc,KAAK;OAAI,CAAA;MACzE;SACL,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAqB,OAClB,8CACA,KAAK,EAAe,GAAkB,KAAK,EAAE;KAC9C,CAAA,CACL,EAAA,CAAA,IAjBF,kBAAC,OAAD;MAAK,WAAU;gBAAgD;KAE1D,CAAA,IANL,kBAAC,OAAD;MAAK,WAAU;gBAAgD;KAE1D,CAAA;KAqBN,IAAW,KACV,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAe,GAAU,KAAK,GAAE,yHAE9B;;IAEA;;GAET,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;KAAyB,CAAA,GACnF,kBAAC,KAAD;MAAG,WAAU;gBAA+B;KAEzC,CAAA,CACA;QACL,kBAAC,IAAD,EAAqB,QAAQ,EAAU,CAAA,CAChC;;GAET,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;IAAwB,CAAA,GAChF,kBAAC,KAAD;KAAG,WAAU;eAA+B;IAGzC,CAAA,CACA,EAAA,CAAA,GACL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,KAAiB,kBAAC,GAAD,EAAS,WAAU,sCAAuC,CAAA,GAC5E,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAU,GAAkB,EAAM,OAAO,KAAK;MACzD,UAAU;MACV,WAAU;gBAJZ,CAME,kBAAC,UAAD;OAAQ,OAAO;iBAAU;MAAgB,CAAA,GACxC,GAAa,KAAK,MACjB,kBAAC,UAAD;OAA0B,OAAO,EAAM;iBACpC,EAAiB,EAAM,KAAK;MACvB,GAFK,EAAM,KAEX,CACT,CACK;OACL;MACF;;GAEL,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;KAAsB,CAAA,GAChF,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA4C;OACT;OAChC,MAAkB,IAAW,aAAa,EAAiB,CAAa;OAAE;MAE1E;OACA;QACL,kBAAC,OAAD;KAAK,WAAW,IAAgB,mCAAmC,KAAA;eACjE,kBAAC,IAAD,EAAmB,UAAU,GAAgB,CAAA;IAC1C,CAAA,CACE;;GAET,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;KAEtD,CAAA,GACJ,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA4C;OAChB;OACzB,MAAkB,IAAW,aAAa,EAAiB,CAAa;OAAE;MAE1E;OACA;QACL,kBAAC,OAAD;KACE,WAAW,mBAAmB,IAAgB,mCAAmC;eAEjF,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD;OAAO,WAAU;iBACf,kBAAC,MAAD,EAAA,UAAA;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAe,CAAA;QACzC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAa,CAAA;QACvC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAe,CAAA;QACzC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAe,CAAA;QACzC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAU,CAAA;OAClC,EAAA,CAAA;MACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,CAAC,KAAU,EAAO,YAAY,MAAM,WAAW,IAC9C,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;OAAI,SAAS;OAAG,WAAU;iBAAgD;MAEtE,CAAA,EACF,CAAA,IAEJ,EAAO,YAAY,MAAM,KAAK,MAC5B,kBAAC,MAAD;OAAkB,WAAU;iBAA5B;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAd,CACE,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAK,aAAa,QAAQ;SACxB,CAAA,GACL,kBAAC,OAAD;UAAK,WAAU;oBAAf,CAAyC,KAAE,EAAK,aAAa,QAAQ,SAAe;WAClF;;QACJ,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAe,EAAK,WAAW;QAC9B,CAAA;QACJ,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAe,EAAK,WAAW,EAAK,QAAQ;QAC3C,CAAA;QACJ,kBAAC,MAAD;SAAI,WAAU;mBAAd;UACE,kBAAC,OAAD,EAAA,UAAM,EAAK,2BAA2B,UAAe,CAAA;UACpD,EAAK,0BACJ,kBAAC,OAAD;WAAK,WAAU;qBACZ,EAAK;UACH,CAAA;UAEN,EAAK,4BAA4B,YAAY,EAAI,oBAChD,kBAAC,UAAD;WACE,MAAK;WACL,eAAe;YACR,MACC,YAAY;aAChB,EAAkB,EAAK,EAAE;aACzB,IAAI;cAEF,AADA,MAAM,GAAoB,EAAK,IAAI,CAAc,GACjD,MAAM,EAAW,GAAgB,EAAI,kBAAkB,CAAa;aACtE,UAAU;cACR,EAAkB,IAAI;aACxB;YACF,GAAG;WACL;WACA,UAAU,MAAmB,EAAK;WAClC,WAAU;qBAET,MAAmB,EAAK,KAAK,cAAc;UACtC,CAAA;SAER;;QACJ,kBAAC,MAAD;SAAI,WAAU;mBAAiC,EAAK;QAAW,CAAA;OAC7D;SA3CK,EAAK,EA2CV,CACL,EAEE,CAAA,CACF;;IACJ,CAAA,CACE;;EACN;;AAET"}
1
+ {"version":3,"file":"DeveloperRevenueOrgDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/DeveloperRevenueOrgDetailPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { useParams } from 'react-router';\nimport {\n ArrowLeft,\n BadgeCheck,\n CreditCard,\n Loader2,\n TrendingDown,\n TrendingUp,\n Wallet,\n} from 'lucide-react';\n\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError, StatCard } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n formatCredits,\n formatCurrency,\n formatDateTime,\n isServerError,\n} from '../../../shared/utils';\nimport {\n fetchAdminDeveloperRevenueOrg,\n fetchAdminDeveloperRevenueOrgDetail,\n fetchAdminDeveloperRevenueOrgMonthly,\n fetchAdminPayoutRequests,\n fetchBillingAccountCreditBalance,\n fetchCreditCost,\n retrySaleCreditSync,\n type AdminDeveloperRevenueOrg,\n type AdminDeveloperRevenueOrgDetail,\n type CreditBalanceByType,\n type DeveloperOrgMonthlyRevenuePoint,\n type PayoutRequest,\n} from '../api';\n\nconst ALL_TIME = 'all';\n\nconst PAYOUT_STATUS_LABEL: Record<string, string> = {\n REQUESTED: 'Requested',\n APPROVED: 'Approved',\n REJECTED: 'Rejected',\n PROCESSING: 'Processing',\n PAID: 'Paid',\n FAILED: 'Failed',\n CANCELED: 'Canceled',\n};\n\nconst PAYOUT_STATUS_COLORS: Record<string, string> = {\n REQUESTED: 'bg-status-warning-bg-subtle text-status-warning-text',\n APPROVED: 'bg-status-info-bg-subtle text-status-info-text',\n REJECTED: 'bg-status-error-bg-subtle text-status-error-text',\n PROCESSING: 'bg-status-info-bg-subtle text-status-info-text',\n PAID: 'bg-status-success-bg-subtle text-status-success-text',\n FAILED: 'bg-status-error-bg-subtle text-status-error-text',\n CANCELED: 'bg-bg-sunken text-text-muted',\n};\n\nfunction PayoutStatusBadge({ status }: { status: string }) {\n return (\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${PAYOUT_STATUS_COLORS[status] ?? 'bg-bg-sunken text-text-muted'}`}\n >\n {PAYOUT_STATUS_LABEL[status] ?? status}\n </span>\n );\n}\n\nfunction formatMonthLabel(iso: string): string {\n return new Date(iso).toLocaleDateString('en-US', { month: 'short', year: 'numeric' });\n}\n\n/**\n * Turns a month-start ISO string (as returned by developerOrgMonthlyRevenue,\n * always the first instant of a calendar month) into a [startDate, endDate]\n * range covering that whole month, for scoping the sales/withdrawal queries.\n * ALL_TIME returns an empty range (no filtering).\n */\nfunction monthRangeFor(month: string): { startDate?: string; endDate?: string } {\n if (month === ALL_TIME) return {};\n const start = new Date(month);\n if (Number.isNaN(start.getTime())) return {};\n const startDate = new Date(\n Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), 1)\n ).toISOString();\n const endDate = new Date(\n Date.UTC(start.getUTCFullYear(), start.getUTCMonth() + 1, 1) - 1\n ).toISOString();\n return { startDate, endDate };\n}\n\nfunction MonthlyRevenueTrend({ points }: { points: DeveloperOrgMonthlyRevenuePoint[] }) {\n if (points.length === 0) {\n return (\n <div className=\"px-5 py-10 text-center text-sm text-text-muted\">\n No sales recorded yet — the revenue trend will appear once the first sale comes in.\n </div>\n );\n }\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead className=\"bg-bg-sunken/60 text-left text-xs uppercase tracking-wide text-text-muted\">\n <tr>\n <th className=\"px-4 py-3\">Month</th>\n <th className=\"px-4 py-3\">Gross revenue</th>\n <th className=\"px-4 py-3\">Platform fee</th>\n <th className=\"px-4 py-3\">Net revenue</th>\n <th className=\"px-4 py-3\">Sales</th>\n <th className=\"px-4 py-3\">Growth (MoM)</th>\n </tr>\n </thead>\n <tbody>\n {points.map((point, index) => {\n const prior = index > 0 ? points[index - 1] : null;\n const growthPct =\n prior && prior.grossRevenue > 0\n ? ((point.grossRevenue - prior.grossRevenue) / prior.grossRevenue) * 100\n : null;\n\n return (\n <tr key={point.month} className=\"border-t border-border-seam\">\n <td className=\"px-4 py-3 font-medium text-text-primary\">\n {formatMonthLabel(point.month)}\n </td>\n <td className=\"px-4 py-3 text-text-primary\">\n {formatCurrency(point.grossRevenue, 'USD')}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {formatCurrency(point.platformFees, 'USD')}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {formatCurrency(point.netRevenue, 'USD')}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">{point.salesCount}</td>\n <td className=\"px-4 py-3\">\n {growthPct === null ? (\n <span className=\"text-text-muted\">—</span>\n ) : (\n <span\n className={`inline-flex items-center gap-1 font-medium ${\n growthPct >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n }`}\n >\n {growthPct >= 0 ? (\n <TrendingUp className=\"size-3.5\" />\n ) : (\n <TrendingDown className=\"size-3.5\" />\n )}\n {growthPct >= 0 ? '+' : ''}\n {growthPct.toFixed(1)}%\n </span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction WithdrawalHistory({ requests }: { requests: PayoutRequest[] }) {\n if (requests.length === 0) {\n return (\n <div className=\"px-5 py-10 text-center text-sm text-text-muted\">\n No withdrawal requests in this period.\n </div>\n );\n }\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead className=\"bg-bg-sunken/60 text-left text-xs uppercase tracking-wide text-text-muted\">\n <tr>\n <th className=\"px-4 py-3\">Requested</th>\n <th className=\"px-4 py-3\">Kind</th>\n <th className=\"px-4 py-3\">Amount</th>\n <th className=\"px-4 py-3\">Status</th>\n <th className=\"px-4 py-3\">Paid</th>\n <th className=\"px-4 py-3\">Notes</th>\n </tr>\n </thead>\n <tbody>\n {requests.map((request) => (\n <tr key={request.id} className=\"border-t border-border-seam\">\n <td className=\"px-4 py-3 text-text-secondary\">{formatDateTime(request.createdAt)}</td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {request.requestKind === 'CREDIT_CONVERSION'\n ? 'Revenue-share credits'\n : 'Cash payout'}\n </td>\n <td className=\"px-4 py-3 font-medium text-text-primary\">\n {formatCurrency(request.requestedAmount, request.currency)}\n </td>\n <td className=\"px-4 py-3\">\n <PayoutStatusBadge status={request.status} />\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {request.paidAt ? formatDateTime(request.paidAt) : '—'}\n </td>\n <td className=\"px-4 py-3 text-text-muted\">\n {request.failureReason || request.userNotes || '—'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nexport const DeveloperRevenueOrgDetailPage: FC = () => {\n const { organizationId } = useParams<{ organizationId: string }>();\n const permissions = useBillingPermissions();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const navigateTo = useBillingNavigate();\n\n const [org, setOrg] = useState<AdminDeveloperRevenueOrg | null>(null);\n const [monthly, setMonthly] = useState<DeveloperOrgMonthlyRevenuePoint[]>([]);\n const [creditBalance, setCreditBalance] = useState<CreditBalanceByType | null>(null);\n const [creditBalanceUsd, setCreditBalanceUsd] = useState<number | null>(null);\n // Lifetime (unscoped) withdrawal history — drives the \"Already withdrawn\" /\n // in-flight totals up top, which must stay accurate regardless of which\n // month the tables below are currently scoped to.\n const [lifetimePayouts, setLifetimePayouts] = useState<PayoutRequest[]>([]);\n // Period-scoped data — re-fetched whenever selectedMonth changes.\n const [detail, setDetail] = useState<AdminDeveloperRevenueOrgDetail | null>(null);\n const [payoutHistory, setPayoutHistory] = useState<PayoutRequest[]>([]);\n const [selectedMonth, setSelectedMonth] = useState<string>(ALL_TIME);\n\n const [loading, setLoading] = useState(true);\n const [periodLoading, setPeriodLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [retryingSaleId, setRetryingSaleId] = useState<string | null>(null);\n\n const requestContext = useMemo(\n () => ({ apiGatewayUrl, authToken, orgId }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadPeriod = useCallback(\n async (\n organizationIdValue: string,\n billingAccountId: string | null | undefined,\n month: string\n ) => {\n const range = monthRangeFor(month);\n const [detailResult, payoutsResult] = await Promise.all([\n fetchAdminDeveloperRevenueOrgDetail(organizationIdValue, requestContext, range),\n billingAccountId\n ? fetchAdminPayoutRequests(requestContext, {\n limit: 100,\n filter: { billingAccountId, ...range },\n })\n : Promise.resolve({ items: [], totalCount: 0, hasMore: false }),\n ]);\n setDetail(detailResult);\n setPayoutHistory(payoutsResult.items);\n },\n [requestContext]\n );\n\n const load = useCallback(async () => {\n if (!organizationId) return;\n setLoading(true);\n setError(null);\n try {\n const [orgResult, monthlyResult] = await Promise.all([\n fetchAdminDeveloperRevenueOrg(organizationId, requestContext),\n fetchAdminDeveloperRevenueOrgMonthly(organizationId, requestContext, 12),\n ]);\n setOrg(orgResult);\n setMonthly(monthlyResult);\n\n const billingAccountId = orgResult?.billingAccountId ?? null;\n const defaultMonth =\n monthlyResult.length > 0 ? monthlyResult[monthlyResult.length - 1].month : ALL_TIME;\n setSelectedMonth(defaultMonth);\n\n const [balanceResult, lifetimeResult] = await Promise.all([\n billingAccountId\n ? fetchBillingAccountCreditBalance(billingAccountId, requestContext)\n : Promise.resolve(null),\n billingAccountId\n ? fetchAdminPayoutRequests(requestContext, { limit: 100, filter: { billingAccountId } })\n : Promise.resolve({ items: [], totalCount: 0, hasMore: false }),\n loadPeriod(organizationId, billingAccountId, defaultMonth),\n ]);\n setCreditBalance(balanceResult);\n setLifetimePayouts(lifetimeResult.items);\n\n if (balanceResult && balanceResult.total > 0) {\n fetchCreditCost(balanceResult.total, 'USD', requestContext)\n .then(setCreditBalanceUsd)\n .catch(() => setCreditBalanceUsd(null));\n } else {\n setCreditBalanceUsd(0);\n }\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load revenue detail'));\n } finally {\n setLoading(false);\n }\n }, [organizationId, requestContext, loadPeriod]);\n\n useEffect(() => {\n if (!permissions.isBillingAdmin) return;\n void load();\n }, [load, permissions.isBillingAdmin]);\n\n const handleMonthChange = (month: string) => {\n if (!organizationId || !org) return;\n setSelectedMonth(month);\n setPeriodLoading(true);\n void loadPeriod(organizationId, org.billingAccountId, month)\n .catch((loadError) => {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load period data'));\n })\n .finally(() => setPeriodLoading(false));\n };\n\n if (!permissions.isBillingAdmin) {\n return <AccessDenied message=\"You don't have permission to view developer revenue.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Developer revenue unavailable\"\n message={error.message}\n showRetry\n onRetry={() => void load()}\n />\n );\n }\n\n if (loading || !org) {\n return (\n <div className=\"flex items-center justify-center py-20 text-text-secondary\">\n <Loader2 className=\"size-8 animate-spin\" />\n </div>\n );\n }\n\n const withdrawn = lifetimePayouts\n .filter((request) => request.status === 'PAID')\n .reduce((sum, request) => sum + (request.approvedAmount ?? request.requestedAmount), 0);\n const inFlight = lifetimePayouts\n .filter((request) => ['REQUESTED', 'APPROVED', 'PROCESSING'].includes(request.status))\n .reduce((sum, request) => sum + request.requestedAmount, 0);\n\n // Most-recent-first for the dropdown; `monthly` itself stays ascending\n // (oldest first) since that's what the trend table's MoM math needs.\n const monthOptions = [...monthly].reverse();\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={org.name}\n description={`/${org.slug}${org.isVerified ? ' · Verified developer organization' : ''}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/developer-revenue')}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to developer revenue\n </button>\n }\n />\n\n <PagePurpose>\n Store revenue for this org is credited into its linked billing account as earned credits.\n The developer withdraws those credits later through their own billing withdrawal flow — this\n page is a read-and-verify view of that whole loop, from sale to payout.\n </PagePurpose>\n\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n label=\"Gross revenue (all time)\"\n value={formatCurrency(org.totalRevenue, 'USD')}\n icon={<TrendingUp className=\"size-4\" />}\n />\n <StatCard\n label=\"Platform fees (all time)\"\n value={formatCurrency(org.totalPlatformFees, 'USD')}\n icon={<CreditCard className=\"size-4\" />}\n />\n <StatCard\n label=\"Pending earned credits\"\n value={formatCurrency(detail?.summary?.pendingBalance ?? org.pendingBalance, 'USD')}\n icon={<Wallet className=\"size-4\" />}\n />\n <StatCard\n label=\"Already withdrawn (all time)\"\n value={formatCurrency(withdrawn, 'USD')}\n icon={<BadgeCheck className=\"size-4\" />}\n />\n </div>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Billing account balance</h2>\n <p className=\"mt-1 text-sm text-text-muted\">\n The current credit ledger on the org&apos;s linked billing account, right now. These are\n credit units, not currency — 1 credit is worth a small fraction of $1 at the\n platform&apos;s current credit rate.\n </p>\n </div>\n {!org.billingAccountId ? (\n <div className=\"px-5 py-8 text-center text-sm text-text-muted\">\n No billing account is linked yet — revenue cannot be credited until this org links one.\n </div>\n ) : !creditBalance ? (\n <div className=\"px-5 py-8 text-center text-sm text-text-muted\">\n Credit balance could not be loaded.\n </div>\n ) : (\n <>\n <div className=\"grid gap-3 p-5 md:grid-cols-3 xl:grid-cols-5\">\n <StatCard label=\"Earned\" value={formatCredits(creditBalance.earned)} />\n <StatCard label=\"Purchased\" value={formatCredits(creditBalance.purchased)} />\n <StatCard label=\"Granted\" value={formatCredits(creditBalance.granted)} />\n <StatCard label=\"Withdrawable\" value={formatCredits(creditBalance.withdrawable)} />\n <StatCard label=\"Total balance\" value={formatCredits(creditBalance.total)} />\n </div>\n <div className=\"px-5 pb-4 text-xs text-text-muted\">\n {creditBalanceUsd === null\n ? 'Estimated cash value could not be loaded.'\n : `≈ ${formatCurrency(creditBalanceUsd, 'USD')} total at the current credit rate.`}\n </div>\n </>\n )}\n {inFlight > 0 && (\n <div className=\"border-t border-border-seam bg-status-warning-bg-subtle px-5 py-3 text-xs text-status-warning-text\">\n {formatCurrency(inFlight, 'USD')} is currently tied up in a pending or approved\n withdrawal request — it has already left the withdrawable balance above.\n </div>\n )}\n </section>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Monthly revenue trend</h2>\n <p className=\"mt-1 text-sm text-text-muted\">\n Last 12 months of gross/net revenue for this org, computed from completed sales.\n </p>\n </div>\n <MonthlyRevenueTrend points={monthly} />\n </section>\n\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-card border border-border-seam bg-bg-surface px-5 py-4 shadow-[var(--shadow-elevation-1)]\">\n <div>\n <h2 className=\"text-sm font-semibold text-text-primary\">Viewing activity for</h2>\n <p className=\"mt-1 text-xs text-text-muted\">\n Scopes both the withdrawal history and sales tables below — pick a single month to keep\n long-running orgs easy to review, or switch to all time.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {periodLoading && <Loader2 className=\"size-4 animate-spin text-text-muted\" />}\n <select\n value={selectedMonth}\n onChange={(event) => handleMonthChange(event.target.value)}\n disabled={periodLoading}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary disabled:opacity-50\"\n >\n <option value={ALL_TIME}>All time</option>\n {monthOptions.map((point) => (\n <option key={point.month} value={point.month}>\n {formatMonthLabel(point.month)}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Withdrawal history</h2>\n <p className=\"mt-1 text-sm text-text-muted\">\n Payout requests this org made in{' '}\n {selectedMonth === ALL_TIME ? 'all time' : formatMonthLabel(selectedMonth)} — both\n revenue-share credit conversions and cash payouts — with current status.\n </p>\n </div>\n <div className={periodLoading ? 'pointer-events-none opacity-50' : undefined}>\n <WithdrawalHistory requests={payoutHistory} />\n </div>\n </section>\n\n <section className=\"overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-[var(--shadow-elevation-1)]\">\n <div className=\"border-b border-border-seam px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Sales currently contributing to earned credits\n </h2>\n <p className=\"mt-1 text-xs text-text-muted\">\n Unpaid sales purchased in{' '}\n {selectedMonth === ALL_TIME ? 'all time' : formatMonthLabel(selectedMonth)} that should\n exist as earned credits in the linked billing account until the developer withdraws\n them.\n </p>\n </div>\n <div className={`overflow-x-auto ${periodLoading ? 'pointer-events-none opacity-50' : ''}`}>\n <table className=\"min-w-full text-sm\">\n <thead className=\"bg-bg-sunken/60 text-left text-xs uppercase tracking-wide text-text-muted\">\n <tr>\n <th className=\"px-4 py-3\">Application</th>\n <th className=\"px-4 py-3\">Purchased</th>\n <th className=\"px-4 py-3\">Net revenue</th>\n <th className=\"px-4 py-3\">Credit sync</th>\n <th className=\"px-4 py-3\">Status</th>\n </tr>\n </thead>\n <tbody>\n {!detail || detail.unpaidSales.items.length === 0 ? (\n <tr>\n <td colSpan={5} className=\"px-4 py-8 text-center text-sm text-text-muted\">\n No outstanding unpaid sales in this period.\n </td>\n </tr>\n ) : (\n detail.unpaidSales.items.map((sale) => (\n <tr key={sale.id} className=\"border-t border-border-seam\">\n <td className=\"px-4 py-3\">\n <div className=\"font-medium text-text-primary\">\n {sale.application?.name ?? 'Unknown app'}\n </div>\n <div className=\"text-xs text-text-muted\">\n /{sale.application?.slug ?? 'unknown'}\n </div>\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {formatDateTime(sale.purchasedAt)}\n </td>\n <td className=\"px-4 py-3 text-text-primary\">\n {formatCurrency(sale.netAmount, sale.currency)}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n <div>{sale.billingCreditSyncStatus ?? 'UNKNOWN'}</div>\n {sale.billingCreditSyncError && (\n <div className=\"mt-1 text-xs text-status-error-text\">\n {sale.billingCreditSyncError}\n </div>\n )}\n {sale.billingCreditSyncStatus !== 'SYNCED' && org.billingAccountId && (\n <button\n type=\"button\"\n onClick={() => {\n if (!organizationId) return;\n void (async () => {\n setRetryingSaleId(sale.id);\n try {\n await retrySaleCreditSync(sale.id, requestContext);\n await loadPeriod(\n organizationId,\n org.billingAccountId,\n selectedMonth\n );\n } finally {\n setRetryingSaleId(null);\n }\n })();\n }}\n disabled={retryingSaleId === sale.id}\n className=\"mt-2 rounded-md border border-border-subtle px-2 py-1 text-xs font-medium text-text-primary disabled:opacity-50\"\n >\n {retryingSaleId === sale.id ? 'Retrying…' : 'Retry sync'}\n </button>\n )}\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">{sale.status}</td>\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n </section>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsCA,IAAM,IAAW,OAEX,IAA8C;CAClD,WAAW;CACX,UAAU;CACV,UAAU;CACV,YAAY;CACZ,MAAM;CACN,QAAQ;CACR,UAAU;AACZ,GAEM,IAA+C;CACnD,WAAW;CACX,UAAU;CACV,UAAU;CACV,YAAY;CACZ,MAAM;CACN,QAAQ;CACR,UAAU;AACZ;AAEA,SAAS,EAAkB,EAAE,aAA8B;CACzD,OACE,kBAAC,QAAD;EACE,WAAW,2EAA2E,EAAqB,MAAW;YAErH,EAAoB,MAAW;CAC5B,CAAA;AAEV;AAEA,SAAS,EAAiB,GAAqB;CAC7C,OAAO,IAAI,KAAK,CAAG,EAAE,mBAAmB,SAAS;EAAE,OAAO;EAAS,MAAM;CAAU,CAAC;AACtF;AAQA,SAAS,GAAc,GAAyD;CAC9E,IAAI,MAAU,GAAU,OAAO,CAAC;CAChC,IAAM,IAAQ,IAAI,KAAK,CAAK;CAQ5B,OAPI,OAAO,MAAM,EAAM,QAAQ,CAAC,IAAU,CAAC,IAOpC;EAAE,WANS,IAAI,KACpB,KAAK,IAAI,EAAM,eAAe,GAAG,EAAM,YAAY,GAAG,CAAC,CACzD,EAAE,YAIO;EAAW,0BAHJ,IAAI,KAClB,KAAK,IAAI,EAAM,eAAe,GAAG,EAAM,YAAY,IAAI,GAAG,CAAC,IAAI,CACjE,GAAE,YACkB;CAAQ;AAC9B;AAEA,SAAS,GAAoB,EAAE,aAAyD;CAStF,OARI,EAAO,WAAW,IAElB,kBAAC,OAAD;EAAK,WAAU;YAAiD;CAE3D,CAAA,IAKP,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,SAAD;GAAO,WAAU;aAAjB,CACE,kBAAC,SAAD;IAAO,WAAU;cACf,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAS,CAAA;KACnC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAiB,CAAA;KAC3C,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAgB,CAAA;KAC1C,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAe,CAAA;KACzC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAS,CAAA;KACnC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAgB,CAAA;IACxC,EAAA,CAAA;GACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAO,KAAK,GAAO,MAAU;IAC5B,IAAM,IAAQ,IAAQ,IAAI,EAAO,IAAQ,KAAK,MACxC,IACJ,KAAS,EAAM,eAAe,KACxB,EAAM,eAAe,EAAM,gBAAgB,EAAM,eAAgB,MACnE;IAEN,OACE,kBAAC,MAAD;KAAsB,WAAU;eAAhC;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAiB,EAAM,KAAK;MAC3B,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAe,EAAM,cAAc,KAAK;MACvC,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAe,EAAM,cAAc,KAAK;MACvC,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAe,EAAM,YAAY,KAAK;MACrC,CAAA;MACJ,kBAAC,MAAD;OAAI,WAAU;iBAAiC,EAAM;MAAe,CAAA;MACpE,kBAAC,MAAD;OAAI,WAAU;iBACX,MAAc,OACb,kBAAC,QAAD;QAAM,WAAU;kBAAkB;OAAO,CAAA,IAEzC,kBAAC,QAAD;QACE,WAAW,8CACT,KAAa,IAAI,6BAA6B;kBAFlD;SAMI,EADD,KAAa,IACX,IAEA,GAFD,EAAY,WAAU,WAAY,CAEE;SAErC,KAAa,IAAI,MAAM;SACvB,EAAU,QAAQ,CAAC;SAAE;QAClB;;MAEN,CAAA;KACF;OAjCK,EAAM,KAiCX;GAER,CAAC,EACI,CAAA,CACF;;CACJ,CAAA;AAET;AAEA,SAAS,GAAkB,EAAE,eAA2C;CAStE,OARI,EAAS,WAAW,IAEpB,kBAAC,OAAD;EAAK,WAAU;YAAiD;CAE3D,CAAA,IAKP,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,SAAD;GAAO,WAAU;aAAjB,CACE,kBAAC,SAAD;IAAO,WAAU;cACf,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAa,CAAA;KACvC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAQ,CAAA;KAClC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAU,CAAA;KACpC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAU,CAAA;KACpC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAQ,CAAA;KAClC,kBAAC,MAAD;MAAI,WAAU;gBAAY;KAAS,CAAA;IACjC,EAAA,CAAA;GACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,EAAS,KAAK,MACb,kBAAC,MAAD;IAAqB,WAAU;cAA/B;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAiC,EAAe,EAAQ,SAAS;KAAM,CAAA;KACrF,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ,gBAAgB,sBACrB,0BACA;KACF,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAe,EAAQ,iBAAiB,EAAQ,QAAQ;KACvD,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,GAAD,EAAmB,QAAQ,EAAQ,OAAS,CAAA;KAC1C,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ,SAAS,EAAe,EAAQ,MAAM,IAAI;KACjD,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAQ,iBAAiB,EAAQ,aAAa;KAC7C,CAAA;IACF;MAnBK,EAAQ,EAmBb,CACL,EACI,CAAA,CACF;;CACJ,CAAA;AAET;AAEA,IAAa,UAA0C;CACrD,IAAM,EAAE,sBAAmB,GAAsC,GAC3D,IAAc,GAAsB,GACpC,EAAE,kBAAe,cAAW,aAAU,EAAW,GACjD,IAAa,GAAmB,GAEhC,CAAC,GAAK,KAAU,EAA0C,IAAI,GAC9D,CAAC,GAAS,KAAc,EAA4C,CAAC,CAAC,GACtE,CAAC,GAAe,KAAoB,EAAqC,IAAI,GAC7E,CAAC,GAAkB,KAAuB,EAAwB,IAAI,GAItE,CAAC,GAAiB,KAAsB,EAA0B,CAAC,CAAC,GAEpE,CAAC,GAAQ,MAAa,EAAgD,IAAI,GAC1E,CAAC,IAAe,MAAoB,EAA0B,CAAC,CAAC,GAChE,CAAC,GAAe,KAAoB,EAAiB,CAAQ,GAE7D,CAAC,IAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAe,KAAoB,EAAS,EAAK,GAClD,CAAC,GAAO,KAAY,EAAuB,IAAI,GAC/C,CAAC,GAAgB,KAAqB,EAAwB,IAAI,GAElE,IAAiB,SACd;EAAE;EAAe;EAAW;CAAM,IACzC;EAAC;EAAe;EAAW;CAAK,CAClC,GAEM,IAAa,EACjB,OACE,GACA,GACA,MACG;EACH,IAAM,IAAQ,GAAc,CAAK,GAC3B,CAAC,GAAc,KAAiB,MAAM,QAAQ,IAAI,CACtD,EAAoC,GAAqB,GAAgB,CAAK,GAC9E,IACI,EAAyB,GAAgB;GACvC,OAAO;GACP,QAAQ;IAAE;IAAkB,GAAG;GAAM;EACvC,CAAC,IACD,QAAQ,QAAQ;GAAE,OAAO,CAAC;GAAG,YAAY;GAAG,SAAS;EAAM,CAAC,CAClE,CAAC;EAED,AADA,GAAU,CAAY,GACtB,GAAiB,EAAc,KAAK;CACtC,GACA,CAAC,CAAc,CACjB,GAEM,IAAO,EAAY,YAAY;EAC9B,OAEL;GADA,EAAW,EAAI,GACf,EAAS,IAAI;GACb,IAAI;IACF,IAAM,CAAC,GAAW,KAAiB,MAAM,QAAQ,IAAI,CACnD,GAA8B,GAAgB,CAAc,GAC5D,EAAqC,GAAgB,GAAgB,EAAE,CACzE,CAAC;IAED,AADA,EAAO,CAAS,GAChB,EAAW,CAAa;IAExB,IAAM,IAAmB,GAAW,oBAAoB,MAClD,IACJ,EAAc,SAAS,IAAI,EAAc,EAAc,SAAS,GAAG,QAAQ;IAC7E,EAAiB,CAAY;IAE7B,IAAM,CAAC,GAAe,KAAkB,MAAM,QAAQ,IAAI;KACxD,IACI,GAAiC,GAAkB,CAAc,IACjE,QAAQ,QAAQ,IAAI;KACxB,IACI,EAAyB,GAAgB;MAAE,OAAO;MAAK,QAAQ,EAAE,oBAAiB;KAAE,CAAC,IACrF,QAAQ,QAAQ;MAAE,OAAO,CAAC;MAAG,YAAY;MAAG,SAAS;KAAM,CAAC;KAChE,EAAW,GAAgB,GAAkB,CAAY;IAC3D,CAAC;IAID,AAHA,EAAiB,CAAa,GAC9B,EAAmB,EAAe,KAAK,GAEnC,KAAiB,EAAc,QAAQ,IACzC,GAAgB,EAAc,OAAO,OAAO,CAAc,EACvD,KAAK,CAAmB,EACxB,YAAY,EAAoB,IAAI,CAAC,IAExC,EAAoB,CAAC;GAEzB,SAAS,GAAW;IAClB,EAAS,aAAqB,QAAQ,IAAY,gBAAI,MAAM,+BAA+B,CAAC;GAC9F,UAAU;IACR,EAAW,EAAK;GAClB;EArCa;CAsCf,GAAG;EAAC;EAAgB;EAAgB;CAAU,CAAC;CAE/C,QAAgB;EACT,EAAY,kBACjB,EAAU;CACZ,GAAG,CAAC,GAAM,EAAY,cAAc,CAAC;CAErC,IAAM,MAAqB,MAAkB;EACvC,CAAC,KAAkB,CAAC,MACxB,EAAiB,CAAK,GACtB,EAAiB,EAAI,GACrB,EAAgB,GAAgB,EAAI,kBAAkB,CAAK,EACxD,OAAO,MAAc;GACpB,EAAS,aAAqB,QAAQ,IAAY,gBAAI,MAAM,4BAA4B,CAAC;EAC3F,CAAC,EACA,cAAc,EAAiB,EAAK,CAAC;CAC1C;CAEA,IAAI,CAAC,EAAY,gBACf,OAAO,kBAAC,GAAD,EAAc,SAAQ,uDAAwD,CAAA;CAGvF,IAAI,KAAS,GAAc,CAAK,GAC9B,OACE,kBAAC,GAAD;EACE,OAAM;EACN,SAAS,EAAM;EACf,WAAA;EACA,eAAe,KAAK,EAAK;CAC1B,CAAA;CAIL,IAAI,MAAW,CAAC,GACd,OACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,sBAAuB,CAAA;CACvC,CAAA;CAIT,IAAM,KAAY,EACf,QAAQ,MAAY,EAAQ,WAAW,MAAM,EAC7C,QAAQ,GAAK,MAAY,KAAO,EAAQ,kBAAkB,EAAQ,kBAAkB,CAAC,GAClF,IAAW,EACd,QAAQ,MAAY;EAAC;EAAa;EAAY;CAAY,EAAE,SAAS,EAAQ,MAAM,CAAC,EACpF,QAAQ,GAAK,MAAY,IAAM,EAAQ,iBAAiB,CAAC,GAItD,KAAe,CAAC,GAAG,CAAO,EAAE,QAAQ;CAE1C,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAO,EAAI;IACX,aAAa,IAAI,EAAI,OAAO,EAAI,aAAa,uCAAuC;IACpF,SACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,oBAAoB;KAC9C,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA,GAAC,2BAE1B;;GAEX,CAAA;GAED,kBAAC,GAAD,EAAA,UAAa,iQAIA,CAAA;GAEb,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,EAAI,cAAc,KAAK;MAC7C,MAAM,kBAAC,GAAD,EAAY,WAAU,SAAU,CAAA;KACvC,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,EAAI,mBAAmB,KAAK;MAClD,MAAM,kBAAC,IAAD,EAAY,WAAU,SAAU,CAAA;KACvC,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,GAAQ,SAAS,kBAAkB,EAAI,gBAAgB,KAAK;MAClF,MAAM,kBAAC,IAAD,EAAQ,WAAU,SAAU,CAAA;KACnC,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,IAAW,KAAK;MACtC,MAAM,kBAAC,IAAD,EAAY,WAAU,SAAU,CAAA;KACvC,CAAA;IACE;;GAEL,kBAAC,WAAD;IAAS,WAAU;cAAnB;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA4C;MAA2B,CAAA,GACrF,kBAAC,KAAD;OAAG,WAAU;iBAA+B;MAIzC,CAAA,CACA;;KACH,EAAI,mBAID,IAKH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAU,OAAM;QAAS,OAAO,EAAc,EAAc,MAAM;OAAI,CAAA;OACtE,kBAAC,GAAD;QAAU,OAAM;QAAY,OAAO,EAAc,EAAc,SAAS;OAAI,CAAA;OAC5E,kBAAC,GAAD;QAAU,OAAM;QAAU,OAAO,EAAc,EAAc,OAAO;OAAI,CAAA;OACxE,kBAAC,GAAD;QAAU,OAAM;QAAe,OAAO,EAAc,EAAc,YAAY;OAAI,CAAA;OAClF,kBAAC,GAAD;QAAU,OAAM;QAAgB,OAAO,EAAc,EAAc,KAAK;OAAI,CAAA;MACzE;SACL,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAqB,OAClB,8CACA,KAAK,EAAe,GAAkB,KAAK,EAAE;KAC9C,CAAA,CACL,EAAA,CAAA,IAjBF,kBAAC,OAAD;MAAK,WAAU;gBAAgD;KAE1D,CAAA,IANL,kBAAC,OAAD;MAAK,WAAU;gBAAgD;KAE1D,CAAA;KAqBN,IAAW,KACV,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAe,GAAU,KAAK,GAAE,yHAE9B;;IAEA;;GAET,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;KAAyB,CAAA,GACnF,kBAAC,KAAD;MAAG,WAAU;gBAA+B;KAEzC,CAAA,CACA;QACL,kBAAC,IAAD,EAAqB,QAAQ,EAAU,CAAA,CAChC;;GAET,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;IAAwB,CAAA,GAChF,kBAAC,KAAD;KAAG,WAAU;eAA+B;IAGzC,CAAA,CACA,EAAA,CAAA,GACL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,KAAiB,kBAAC,GAAD,EAAS,WAAU,sCAAuC,CAAA,GAC5E,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAU,GAAkB,EAAM,OAAO,KAAK;MACzD,UAAU;MACV,WAAU;gBAJZ,CAME,kBAAC,UAAD;OAAQ,OAAO;iBAAU;MAAgB,CAAA,GACxC,GAAa,KAAK,MACjB,kBAAC,UAAD;OAA0B,OAAO,EAAM;iBACpC,EAAiB,EAAM,KAAK;MACvB,GAFK,EAAM,KAEX,CACT,CACK;OACL;MACF;;GAEL,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;KAAsB,CAAA,GAChF,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA4C;OACT;OAChC,MAAkB,IAAW,aAAa,EAAiB,CAAa;OAAE;MAE1E;OACA;QACL,kBAAC,OAAD;KAAK,WAAW,IAAgB,mCAAmC,KAAA;eACjE,kBAAC,IAAD,EAAmB,UAAU,GAAgB,CAAA;IAC1C,CAAA,CACE;;GAET,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;KAEtD,CAAA,GACJ,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAA4C;OAChB;OACzB,MAAkB,IAAW,aAAa,EAAiB,CAAa;OAAE;MAG1E;OACA;QACL,kBAAC,OAAD;KAAK,WAAW,mBAAmB,IAAgB,mCAAmC;eACpF,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD;OAAO,WAAU;iBACf,kBAAC,MAAD,EAAA,UAAA;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAe,CAAA;QACzC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAa,CAAA;QACvC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAe,CAAA;QACzC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAe,CAAA;QACzC,kBAAC,MAAD;SAAI,WAAU;mBAAY;QAAU,CAAA;OAClC,EAAA,CAAA;MACC,CAAA,GACP,kBAAC,SAAD,EAAA,UACG,CAAC,KAAU,EAAO,YAAY,MAAM,WAAW,IAC9C,kBAAC,MAAD,EAAA,UACE,kBAAC,MAAD;OAAI,SAAS;OAAG,WAAU;iBAAgD;MAEtE,CAAA,EACF,CAAA,IAEJ,EAAO,YAAY,MAAM,KAAK,MAC5B,kBAAC,MAAD;OAAkB,WAAU;iBAA5B;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAd,CACE,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAK,aAAa,QAAQ;SACxB,CAAA,GACL,kBAAC,OAAD;UAAK,WAAU;oBAAf,CAAyC,KACrC,EAAK,aAAa,QAAQ,SACzB;WACH;;QACJ,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAe,EAAK,WAAW;QAC9B,CAAA;QACJ,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAe,EAAK,WAAW,EAAK,QAAQ;QAC3C,CAAA;QACJ,kBAAC,MAAD;SAAI,WAAU;mBAAd;UACE,kBAAC,OAAD,EAAA,UAAM,EAAK,2BAA2B,UAAe,CAAA;UACpD,EAAK,0BACJ,kBAAC,OAAD;WAAK,WAAU;qBACZ,EAAK;UACH,CAAA;UAEN,EAAK,4BAA4B,YAAY,EAAI,oBAChD,kBAAC,UAAD;WACE,MAAK;WACL,eAAe;YACR,MACC,YAAY;aAChB,EAAkB,EAAK,EAAE;aACzB,IAAI;cAEF,AADA,MAAM,GAAoB,EAAK,IAAI,CAAc,GACjD,MAAM,EACJ,GACA,EAAI,kBACJ,CACF;aACF,UAAU;cACR,EAAkB,IAAI;aACxB;YACF,GAAG;WACL;WACA,UAAU,MAAmB,EAAK;WAClC,WAAU;qBAET,MAAmB,EAAK,KAAK,cAAc;UACtC,CAAA;SAER;;QACJ,kBAAC,MAAD;SAAI,WAAU;mBAAiC,EAAK;QAAW,CAAA;OAC7D;SAjDK,EAAK,EAiDV,CACL,EAEE,CAAA,CACF;;IACJ,CAAA,CACE;;EACN;;AAET"}