@burdenoff/microfe-billing 2026.912.6 → 2026.914.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingAdminRoutes.js +100 -74
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/modules/billing/hooks/useRefunds.js.map +1 -1
- package/dist/billing/modules/billing/index.js +5 -4
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +90 -87
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundIssueLogsPage.js +240 -0
- package/dist/billing/modules/billing/pages/RefundIssueLogsPage.js.map +1 -0
- package/dist/billing/modules/billing/pages/RefundsPage.js +116 -108
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/index.js +1 -0
- package/dist/billing/modules/checkout/components/OrderSummary.js +22 -19
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/hooks/useCheckout.js +174 -157
- package/dist/billing/modules/checkout/hooks/useCheckout.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +189 -182
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +293 -274
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +140 -250
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/earnings/api.js +79 -42
- package/dist/billing/modules/earnings/api.js.map +1 -1
- package/dist/billing/modules/earnings/index.js +4 -3
- package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js +103 -310
- package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/DeveloperRevenueOrgDetailPage.js +545 -0
- package/dist/billing/modules/earnings/pages/DeveloperRevenueOrgDetailPage.js.map +1 -0
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +246 -196
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/hooks/useSubscriptions.js +44 -28
- package/dist/billing/modules/subscriptions/hooks/useSubscriptions.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +317 -270
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +153 -150
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/hooks/useUsage.js +74 -48
- package/dist/billing/modules/usage/hooks/useUsage.js.map +1 -1
- package/dist/generated/global-operations.js +221 -183
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/node_modules/sonner/dist/index.js +163 -0
- package/dist/node_modules/sonner/dist/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayoutAdminPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/PayoutAdminPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport {\n AlertTriangle,\n Banknote,\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Clock,\n CreditCard,\n RefreshCw,\n ReceiptText,\n XCircle,\n} from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n approvePayoutRequest,\n fetchAdminPayoutRequests,\n fetchAdminPayoutSummary,\n markPayoutPaid,\n rejectPayoutRequest,\n type PayoutAdminSummary,\n type PayoutRequest,\n} from '../api';\n\nconst REQUESTS_PAGE_SIZE = 50;\n\ntype Tab = 'pending' | 'approved' | 'history';\n\ninterface ActionDialogState {\n type: 'approve' | 'reject' | 'mark_paid';\n requestId: string;\n amount: number;\n currency: string;\n accountLabel: string;\n}\n\nconst 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 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 StatusBadge({ 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 ${STATUS_COLORS[status] ?? 'bg-bg-sunken text-text-muted'}`}\n >\n {STATUS_LABEL[status] ?? status}\n </span>\n );\n}\n\nfunction AccountDetails({ account }: { account: NonNullable<PayoutRequest['payoutAccount']> }) {\n const [expanded, setExpanded] = useState(false);\n const details = account.accountDetails as Record<string, string | null> | null;\n\n return (\n <div className=\"rounded-md border border-border-subtle bg-bg-sunken/40 p-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 text-text-primary font-medium\">\n <CreditCard className=\"size-4 text-text-muted\" />\n {account.displayName ?? account.label}\n <span className=\"text-xs text-text-muted font-normal\">\n ({account.type === 'UPI' ? 'UPI' : 'Bank'} · {account.country ?? 'Unknown'} ·{' '}\n {account.currency})\n </span>\n </div>\n {details && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"text-text-muted hover:text-text-primary\"\n >\n {expanded ? <ChevronUp className=\"size-4\" /> : <ChevronDown className=\"size-4\" />}\n </button>\n )}\n </div>\n {expanded && details && (\n <div className=\"mt-2 grid gap-1 text-xs\">\n {details.accountHolderName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account holder</span>\n <span className=\"text-text-primary\">{details.accountHolderName}</span>\n </div>\n )}\n {details.bankName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Bank</span>\n <span className=\"text-text-primary\">{details.bankName}</span>\n </div>\n )}\n {account.accountLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{account.accountLast4}</span>\n </div>\n )}\n {details.ifsc && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">IFSC</span>\n <span className=\"text-text-primary font-mono\">{details.ifsc}</span>\n </div>\n )}\n {details.routingNumberLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Routing (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{details.routingNumberLast4}</span>\n </div>\n )}\n {details.upiIdMasked && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">UPI ID</span>\n <span className=\"text-text-primary font-mono\">{details.upiIdMasked}</span>\n </div>\n )}\n {details.swift && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">SWIFT</span>\n <span className=\"text-text-primary font-mono\">{details.swift}</span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction RequestCard({\n request,\n onApprove,\n onReject,\n onMarkPaid,\n}: {\n request: PayoutRequest;\n onApprove: (r: PayoutRequest) => void;\n onReject: (r: PayoutRequest) => void;\n onMarkPaid: (r: PayoutRequest) => void;\n}) {\n const canApprove = request.status === 'REQUESTED' || request.status === 'REJECTED';\n const canReject = request.status === 'REQUESTED' || request.status === 'APPROVED';\n const canMarkPaid = request.status === 'APPROVED' || request.status === 'PROCESSING';\n\n return (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]\">\n {/* Header row */}\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xl font-semibold text-text-primary\">\n {formatCurrency(request.requestedAmount, request.currency)}\n </span>\n <StatusBadge status={request.status} />\n </div>\n <div className=\"text-xs text-text-muted space-x-2\">\n <span>Requested {formatDateTime(request.createdAt)}</span>\n {request.requesterUserId && (\n <>\n <span>·</span>\n <span>by {request.requesterUserId}</span>\n </>\n )}\n <span>·</span>\n <span className=\"font-mono text-xs\">{request.id.slice(0, 8)}…</span>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {canApprove && (\n <button\n type=\"button\"\n onClick={() => onApprove(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text\"\n >\n <CheckCircle2 className=\"size-3.5\" />\n Approve\n </button>\n )}\n {canMarkPaid && (\n <button\n type=\"button\"\n onClick={() => onMarkPaid(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-status-success-bg-subtle border border-status-success-border px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <Banknote className=\"size-3.5\" />\n Mark as Paid\n </button>\n )}\n {canReject && (\n <button\n type=\"button\"\n onClick={() => onReject(request)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n <XCircle className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Payout account */}\n {request.payoutAccount && <AccountDetails account={request.payoutAccount} />}\n\n {/* User notes */}\n {request.userNotes && (\n <div className=\"rounded-md bg-bg-sunken/40 border border-border-subtle px-3 py-2 text-sm\">\n <span className=\"text-xs font-medium text-text-muted uppercase tracking-wide\">\n User note\n </span>\n <p className=\"mt-1 text-text-primary\">{request.userNotes}</p>\n </div>\n )}\n\n {/* Internal notes / failure / ref */}\n {(request.internalNotes ?? request.failureReason ?? request.externalReference) && (\n <div className=\"grid gap-1 text-xs text-text-muted\">\n {request.externalReference && (\n <div className=\"flex gap-2\">\n <ReceiptText className=\"size-3.5 mt-px shrink-0\" />\n <span>\n Ref:{' '}\n <span className=\"font-mono text-text-primary\">{request.externalReference}</span>\n </span>\n </div>\n )}\n {request.internalNotes && (\n <div className=\"flex gap-2\">\n <span className=\"shrink-0\">Internal note:</span>\n <span className=\"text-text-primary\">{request.internalNotes}</span>\n </div>\n )}\n {request.failureReason && (\n <div className=\"flex gap-2 text-status-error-text\">\n <AlertTriangle className=\"size-3.5 mt-px shrink-0\" />\n <span>{request.failureReason}</span>\n </div>\n )}\n </div>\n )}\n\n {/* Timestamps */}\n {(request.paidAt ?? request.processedAt) && (\n <div className=\"flex flex-wrap gap-4 text-xs text-text-muted\">\n {request.processedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n Processed {formatDateTime(request.processedAt)}\n </span>\n )}\n {request.paidAt && (\n <span className=\"flex items-center gap-1\">\n <CheckCircle2 className=\"size-3\" />\n Paid {formatDateTime(request.paidAt)}\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction SummaryBar({ summary }: { summary: PayoutAdminSummary }) {\n return (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Pending payout</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalRequestedAmount, 'USD')}\n </div>\n <div className=\"text-xs text-text-muted mt-0.5\">{summary.pendingCount} requests</div>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Processing</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalProcessingAmount, 'USD')}\n </div>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Total paid</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalPaidAmount, 'USD')}\n </div>\n {summary.failedCount > 0 && (\n <div className=\"text-xs text-status-error-text mt-0.5\">{summary.failedCount} failed</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction ActionDialog({\n dialog,\n onClose,\n onConfirm,\n}: {\n dialog: ActionDialogState;\n onClose: () => void;\n onConfirm: (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => Promise<void>;\n}) {\n const [notes, setNotes] = useState('');\n const [reason, setReason] = useState('');\n const [txnRef, setTxnRef] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const handleSubmit = async () => {\n if (dialog.type === 'reject' && !reason.trim()) return;\n setSubmitting(true);\n await onConfirm({\n notes: notes.trim() || undefined,\n reason: reason.trim() || undefined,\n externalReference: txnRef.trim() || undefined,\n });\n setSubmitting(false);\n };\n\n const titles = {\n approve: 'Approve withdrawal request',\n reject: 'Reject withdrawal request',\n mark_paid: 'Mark as paid',\n };\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !submitting) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{titles[dialog.type]}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {formatCurrency(dialog.amount, dialog.currency)} · {dialog.accountLabel}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n {dialog.type === 'mark_paid' && (\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Transaction reference / UTR number\n </label>\n <input\n type=\"text\"\n value={txnRef}\n onChange={(e) => setTxnRef(e.target.value)}\n placeholder=\"e.g. UTR123456789012, TXN_abc123\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Enter the bank reference number so the user can verify the transfer.\n </p>\n </div>\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={2}\n placeholder=\"Any internal notes about this payment…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n </div>\n )}\n\n {dialog.type === 'approve' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder=\"e.g. Verified bank details, approved for transfer…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n )}\n\n {dialog.type === 'reject' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Reason for rejection <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n placeholder=\"e.g. Invalid bank details provided, please re-submit with correct IFSC…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={submitting}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={submitting || (dialog.type === 'reject' && !reason.trim())}\n className={`rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50 ${\n dialog.type === 'reject'\n ? 'bg-action-danger-bg text-action-danger-text'\n : 'bg-action-primary-bg text-action-primary-text'\n }`}\n >\n {submitting\n ? 'Saving…'\n : dialog.type === 'approve'\n ? 'Approve'\n : dialog.type === 'reject'\n ? 'Reject'\n : 'Confirm Payment'}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport const PayoutAdminPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [summary, setSummary] = useState<PayoutAdminSummary | null>(null);\n const [requests, setRequests] = useState<PayoutRequest[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [hasMore, setHasMore] = useState(false);\n const [loading, setLoading] = useState(true);\n const [loadingMore, setLoadingMore] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [activeTab, setActiveTab] = useState<Tab>('pending');\n const [dialog, setDialog] = useState<ActionDialogState | null>(null);\n\n const requestContext = useMemo(\n () => ({ apiGatewayUrl, authToken, orgId }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const [loadedSummary, loadedRequests] = await Promise.all([\n fetchAdminPayoutSummary(requestContext),\n fetchAdminPayoutRequests(requestContext, { limit: REQUESTS_PAGE_SIZE, offset: 0 }),\n ]);\n setSummary(loadedSummary);\n setRequests(loadedRequests.items);\n setTotalCount(loadedRequests.totalCount);\n setHasMore(loadedRequests.hasMore);\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load payout queue'));\n } finally {\n setLoading(false);\n }\n }, [requestContext]);\n\n const loadMore = async () => {\n if (loadingMore || !hasMore) return;\n\n setLoadingMore(true);\n setError(null);\n try {\n const loadedRequests = await fetchAdminPayoutRequests(requestContext, {\n limit: REQUESTS_PAGE_SIZE,\n offset: requests.length,\n });\n setRequests((current) => [...current, ...loadedRequests.items]);\n setTotalCount(loadedRequests.totalCount);\n setHasMore(loadedRequests.hasMore);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError : new Error('Failed to load more payout requests')\n );\n } finally {\n setLoadingMore(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\n\n if (!permissions.isBillingAdmin) {\n return <AccessDenied message=\"You don't have permission to manage payouts.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Payout queue unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const pending = requests.filter((r) => r.status === 'REQUESTED');\n const approved = requests.filter((r) => r.status === 'APPROVED' || r.status === 'PROCESSING');\n const history = requests.filter((r) =>\n ['PAID', 'REJECTED', 'FAILED', 'CANCELED'].includes(r.status)\n );\n\n const tabItems: { id: Tab; label: string; count: number }[] = [\n { id: 'pending', label: 'Pending', count: pending.length },\n { id: 'approved', label: 'Approved', count: approved.length },\n { id: 'history', label: 'History', count: history.length },\n ];\n\n const visibleRequests =\n activeTab === 'pending' ? pending : activeTab === 'approved' ? approved : history;\n\n const openDialog = (type: ActionDialogState['type'], request: PayoutRequest) => {\n setDialog({\n type,\n requestId: request.id,\n amount: request.requestedAmount,\n currency: request.currency,\n accountLabel:\n request.payoutAccount?.displayName ?? request.payoutAccount?.label ?? 'Unknown account',\n });\n };\n\n const handleConfirm = async (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => {\n if (!dialog) return;\n\n try {\n if (dialog.type === 'approve') {\n await approvePayoutRequest(dialog.requestId, data.notes ?? null, requestContext);\n emit('billing.payout.approved', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n } else if (dialog.type === 'reject') {\n await rejectPayoutRequest(dialog.requestId, data.reason ?? '', requestContext);\n emit('billing.payout.rejected', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n reason: data.reason,\n });\n } else if (dialog.type === 'mark_paid') {\n await markPayoutPaid(\n dialog.requestId,\n data.externalReference ?? null,\n data.notes ?? null,\n requestContext\n );\n emit('billing.payout.processed', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n }\n setDialog(null);\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Action failed'));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Payout queue\"\n description=\"Review withdrawal requests, verify payout details, and mark them as paid once funds are transferred.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n }\n />\n\n <PagePurpose>\n This queue is for billing-account withdrawal requests. Developers withdraw their purchased\n and earned credits from billing using their own payout methods; approved requests appear\n here for operations review and payment completion.\n </PagePurpose>\n\n {summary && !loading && <SummaryBar summary={summary} />}\n\n {!loading && totalCount > 0 && (\n <div className=\"text-sm text-text-muted\">\n Showing {requests.length} of {totalCount} payout requests.\n </div>\n )}\n\n {loading && (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-20 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n )}\n\n {error && !isServerError(error) && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text\">\n {error.message}\n </div>\n )}\n\n {/* Tabs */}\n <div className=\"border-b border-border-subtle\">\n <div className=\"flex gap-0\">\n {tabItems.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-action-primary-bg text-text-primary'\n : 'border-transparent text-text-muted hover:text-text-primary'\n }`}\n >\n {tab.label}\n {tab.count > 0 && (\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${\n activeTab === tab.id\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.count}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n\n {/* How to pay — shown only on the Approved tab */}\n {activeTab === 'approved' && approved.length > 0 && (\n <div className=\"rounded-card border border-status-info-border bg-status-info-bg-subtle px-4 py-3 text-sm text-status-info-text\">\n <strong>How to pay:</strong> Click the account row to expand bank/UPI details → transfer\n the exact amount via your bank → click <strong>Mark as Paid</strong> and enter the UTR /\n transaction reference number.\n </div>\n )}\n\n {/* Request cards */}\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((i) => (\n <div\n key={i}\n className=\"h-40 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : visibleRequests.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={\n activeTab === 'pending'\n ? 'No pending requests'\n : activeTab === 'approved'\n ? 'No approved requests awaiting payment'\n : 'No history yet'\n }\n description={\n activeTab === 'pending'\n ? 'New withdrawal requests will appear here.'\n : activeTab === 'approved'\n ? 'Approve pending requests first, then send payment and mark as paid.'\n : 'Completed and rejected requests will appear here.'\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {visibleRequests.map((request) => (\n <RequestCard\n key={request.id}\n request={request}\n onApprove={(r) => openDialog('approve', r)}\n onReject={(r) => openDialog('reject', r)}\n onMarkPaid={(r) => openDialog('mark_paid', r)}\n />\n ))}\n\n {hasMore && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={() => void loadMore()}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50\"\n >\n {loadingMore ? 'Loading…' : 'Load more requests'}\n </button>\n </div>\n )}\n </div>\n )}\n\n {dialog && (\n <ActionDialog dialog={dialog} onClose={() => setDialog(null)} onConfirm={handleConfirm} />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PayoutAdminPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/PayoutAdminPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport {\n AlertTriangle,\n Banknote,\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Clock,\n Coins,\n CreditCard,\n RefreshCw,\n ReceiptText,\n XCircle,\n} from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n approvePayoutRequest,\n fetchAdminPayoutRequests,\n fetchAdminPayoutSummary,\n markPayoutPaid,\n rejectPayoutRequest,\n type PayoutAdminSummary,\n type PayoutRequest,\n} from '../api';\n\nconst REQUESTS_PAGE_SIZE = 50;\n\ntype Tab = 'pending' | 'approved' | 'history';\ntype Kind = PayoutRequest['requestKind'];\n\nconst KIND_TABS: { id: Kind; label: string; description: string }[] = [\n {\n id: 'CASH_PAYOUT',\n label: 'Cash Payouts',\n description: 'Real money leaves the platform — verify bank/UPI details, then mark as paid.',\n },\n {\n id: 'CREDIT_CONVERSION',\n label: 'Revenue Share Credits',\n description:\n 'A developer requesting their revenue share as earned credits. Approving converts the amount straight into credits on their billing account — no bank transfer, nothing further to do.',\n },\n];\n\ninterface ActionDialogState {\n type: 'approve' | 'reject' | 'mark_paid';\n requestId: string;\n amount: number;\n currency: string;\n accountLabel: string;\n}\n\nconst 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 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 StatusBadge({ 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 ${STATUS_COLORS[status] ?? 'bg-bg-sunken text-text-muted'}`}\n >\n {STATUS_LABEL[status] ?? status}\n </span>\n );\n}\n\nfunction AccountDetails({ account }: { account: NonNullable<PayoutRequest['payoutAccount']> }) {\n const [expanded, setExpanded] = useState(false);\n const details = account.accountDetails as Record<string, string | null> | null;\n\n return (\n <div className=\"rounded-md border border-border-subtle bg-bg-sunken/40 p-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 text-text-primary font-medium\">\n <CreditCard className=\"size-4 text-text-muted\" />\n {account.displayName ?? account.label}\n <span className=\"text-xs text-text-muted font-normal\">\n ({account.type === 'UPI' ? 'UPI' : 'Bank'} · {account.country ?? 'Unknown'} ·{' '}\n {account.currency})\n </span>\n </div>\n {details && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"text-text-muted hover:text-text-primary\"\n >\n {expanded ? <ChevronUp className=\"size-4\" /> : <ChevronDown className=\"size-4\" />}\n </button>\n )}\n </div>\n {expanded && details && (\n <div className=\"mt-2 grid gap-1 text-xs\">\n {details.accountHolderName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account holder</span>\n <span className=\"text-text-primary\">{details.accountHolderName}</span>\n </div>\n )}\n {details.bankName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Bank</span>\n <span className=\"text-text-primary\">{details.bankName}</span>\n </div>\n )}\n {account.accountLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{account.accountLast4}</span>\n </div>\n )}\n {details.ifsc && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">IFSC</span>\n <span className=\"text-text-primary font-mono\">{details.ifsc}</span>\n </div>\n )}\n {details.routingNumberLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Routing (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{details.routingNumberLast4}</span>\n </div>\n )}\n {details.upiIdMasked && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">UPI ID</span>\n <span className=\"text-text-primary font-mono\">{details.upiIdMasked}</span>\n </div>\n )}\n {details.swift && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">SWIFT</span>\n <span className=\"text-text-primary font-mono\">{details.swift}</span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction RequestCard({\n request,\n onApprove,\n onReject,\n onMarkPaid,\n}: {\n request: PayoutRequest;\n onApprove: (r: PayoutRequest) => void;\n onReject: (r: PayoutRequest) => void;\n onMarkPaid: (r: PayoutRequest) => void;\n}) {\n const canApprove = request.status === 'REQUESTED' || request.status === 'REJECTED';\n const canReject = request.status === 'REQUESTED' || request.status === 'APPROVED';\n // A credit conversion is never manually marked paid — approving IS completion\n // (it converts straight to earned credits, no bank transfer to confirm). This\n // should never actually be reachable for that kind, but guard it anyway.\n const canMarkPaid =\n request.requestKind === 'CASH_PAYOUT' &&\n (request.status === 'APPROVED' || request.status === 'PROCESSING');\n\n return (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]\">\n {/* Header row */}\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xl font-semibold text-text-primary\">\n {formatCurrency(request.requestedAmount, request.currency)}\n </span>\n <StatusBadge status={request.status} />\n </div>\n <div className=\"text-xs text-text-muted space-x-2\">\n <span>Requested {formatDateTime(request.createdAt)}</span>\n {request.requesterUserId && (\n <>\n <span>·</span>\n <span>by {request.requesterUserId}</span>\n </>\n )}\n <span>·</span>\n <span className=\"font-mono text-xs\">{request.id.slice(0, 8)}…</span>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {canApprove && (\n <button\n type=\"button\"\n onClick={() => onApprove(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text\"\n >\n <CheckCircle2 className=\"size-3.5\" />\n Approve\n </button>\n )}\n {canMarkPaid && (\n <button\n type=\"button\"\n onClick={() => onMarkPaid(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-status-success-bg-subtle border border-status-success-border px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <Banknote className=\"size-3.5\" />\n Mark as Paid\n </button>\n )}\n {canReject && (\n <button\n type=\"button\"\n onClick={() => onReject(request)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n <XCircle className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Payout account — irrelevant for a revenue-share request even when one\n happens to be on file, since approving never sends money to it. */}\n {request.requestKind === 'CREDIT_CONVERSION' ? (\n <div className=\"flex items-center gap-2 rounded-md border border-border-subtle bg-bg-sunken/40 p-3 text-sm text-text-muted\">\n <Coins className=\"size-4 shrink-0\" />\n Approving converts this straight into earned credits — no bank transfer needed.\n </div>\n ) : (\n request.payoutAccount && <AccountDetails account={request.payoutAccount} />\n )}\n\n {/* User notes */}\n {request.userNotes && (\n <div className=\"rounded-md bg-bg-sunken/40 border border-border-subtle px-3 py-2 text-sm\">\n <span className=\"text-xs font-medium text-text-muted uppercase tracking-wide\">\n User note\n </span>\n <p className=\"mt-1 text-text-primary\">{request.userNotes}</p>\n </div>\n )}\n\n {/* Internal notes / failure / ref */}\n {(request.internalNotes ?? request.failureReason ?? request.externalReference) && (\n <div className=\"grid gap-1 text-xs text-text-muted\">\n {request.externalReference && (\n <div className=\"flex gap-2\">\n <ReceiptText className=\"size-3.5 mt-px shrink-0\" />\n <span>\n Ref:{' '}\n <span className=\"font-mono text-text-primary\">{request.externalReference}</span>\n </span>\n </div>\n )}\n {request.internalNotes && (\n <div className=\"flex gap-2\">\n <span className=\"shrink-0\">Internal note:</span>\n <span className=\"text-text-primary\">{request.internalNotes}</span>\n </div>\n )}\n {request.failureReason && (\n <div className=\"flex gap-2 text-status-error-text\">\n <AlertTriangle className=\"size-3.5 mt-px shrink-0\" />\n <span>{request.failureReason}</span>\n </div>\n )}\n </div>\n )}\n\n {/* Timestamps */}\n {(request.paidAt ?? request.processedAt) && (\n <div className=\"flex flex-wrap gap-4 text-xs text-text-muted\">\n {request.processedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n Processed {formatDateTime(request.processedAt)}\n </span>\n )}\n {request.paidAt && (\n <span className=\"flex items-center gap-1\">\n <CheckCircle2 className=\"size-3\" />\n Paid {formatDateTime(request.paidAt)}\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction SummaryBar({ summary }: { summary: PayoutAdminSummary }) {\n return (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Pending payout</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalRequestedAmount, 'USD')}\n </div>\n <div className=\"text-xs text-text-muted mt-0.5\">{summary.pendingCount} requests</div>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Processing</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalProcessingAmount, 'USD')}\n </div>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Total paid</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalPaidAmount, 'USD')}\n </div>\n {summary.failedCount > 0 && (\n <div className=\"text-xs text-status-error-text mt-0.5\">{summary.failedCount} failed</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction ActionDialog({\n dialog,\n onClose,\n onConfirm,\n}: {\n dialog: ActionDialogState;\n onClose: () => void;\n onConfirm: (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => Promise<void>;\n}) {\n const [notes, setNotes] = useState('');\n const [reason, setReason] = useState('');\n const [txnRef, setTxnRef] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const handleSubmit = async () => {\n if (dialog.type === 'reject' && !reason.trim()) return;\n setSubmitting(true);\n await onConfirm({\n notes: notes.trim() || undefined,\n reason: reason.trim() || undefined,\n externalReference: txnRef.trim() || undefined,\n });\n setSubmitting(false);\n };\n\n const titles = {\n approve: 'Approve withdrawal request',\n reject: 'Reject withdrawal request',\n mark_paid: 'Mark as paid',\n };\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !submitting) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{titles[dialog.type]}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {formatCurrency(dialog.amount, dialog.currency)} · {dialog.accountLabel}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n {dialog.type === 'mark_paid' && (\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Transaction reference / UTR number\n </label>\n <input\n type=\"text\"\n value={txnRef}\n onChange={(e) => setTxnRef(e.target.value)}\n placeholder=\"e.g. UTR123456789012, TXN_abc123\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Enter the bank reference number so the user can verify the transfer.\n </p>\n </div>\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={2}\n placeholder=\"Any internal notes about this payment…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n </div>\n )}\n\n {dialog.type === 'approve' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder=\"e.g. Verified bank details, approved for transfer…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n )}\n\n {dialog.type === 'reject' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Reason for rejection <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n placeholder=\"e.g. Invalid bank details provided, please re-submit with correct IFSC…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={submitting}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={submitting || (dialog.type === 'reject' && !reason.trim())}\n className={`rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50 ${\n dialog.type === 'reject'\n ? 'bg-action-danger-bg text-action-danger-text'\n : 'bg-action-primary-bg text-action-primary-text'\n }`}\n >\n {submitting\n ? 'Saving…'\n : dialog.type === 'approve'\n ? 'Approve'\n : dialog.type === 'reject'\n ? 'Reject'\n : 'Confirm Payment'}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport const PayoutAdminPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [summary, setSummary] = useState<PayoutAdminSummary | null>(null);\n const [requests, setRequests] = useState<PayoutRequest[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [hasMore, setHasMore] = useState(false);\n const [loading, setLoading] = useState(true);\n const [loadingMore, setLoadingMore] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [activeKind, setActiveKind] = useState<Kind>('CASH_PAYOUT');\n const [activeTab, setActiveTab] = useState<Tab>('pending');\n const [dialog, setDialog] = useState<ActionDialogState | null>(null);\n\n const requestContext = useMemo(\n () => ({ apiGatewayUrl, authToken, orgId }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const [loadedSummary, loadedRequests] = await Promise.all([\n fetchAdminPayoutSummary(requestContext),\n fetchAdminPayoutRequests(requestContext, { limit: REQUESTS_PAGE_SIZE, offset: 0 }),\n ]);\n setSummary(loadedSummary);\n setRequests(loadedRequests.items);\n setTotalCount(loadedRequests.totalCount);\n setHasMore(loadedRequests.hasMore);\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load payout queue'));\n } finally {\n setLoading(false);\n }\n }, [requestContext]);\n\n const loadMore = async () => {\n if (loadingMore || !hasMore) return;\n\n setLoadingMore(true);\n setError(null);\n try {\n const loadedRequests = await fetchAdminPayoutRequests(requestContext, {\n limit: REQUESTS_PAGE_SIZE,\n offset: requests.length,\n });\n setRequests((current) => [...current, ...loadedRequests.items]);\n setTotalCount(loadedRequests.totalCount);\n setHasMore(loadedRequests.hasMore);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError : new Error('Failed to load more payout requests')\n );\n } finally {\n setLoadingMore(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\n\n if (!permissions.isBillingAdmin) {\n return <AccessDenied message=\"You don't have permission to manage payouts.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Payout queue unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n // Split by kind FIRST (BOFF-7192) — a $35 revenue-share credit conversion\n // must never sit in the same list as a real bank payout, so every count and\n // sub-tab below is scoped to whichever kind tab is active.\n const kindRequests = requests.filter((r) => r.requestKind === activeKind);\n\n const pending = kindRequests.filter((r) => r.status === 'REQUESTED');\n const approved = kindRequests.filter(\n (r) => r.status === 'APPROVED' || r.status === 'PROCESSING'\n );\n const history = kindRequests.filter((r) =>\n ['PAID', 'REJECTED', 'FAILED', 'CANCELED'].includes(r.status)\n );\n\n const kindTabItems = KIND_TABS.map((kind) => ({\n ...kind,\n count: requests.filter((r) => r.requestKind === kind.id).length,\n }));\n\n const tabItems: { id: Tab; label: string; count: number }[] = [\n { id: 'pending', label: 'Pending', count: pending.length },\n { id: 'approved', label: 'Approved', count: approved.length },\n { id: 'history', label: 'History', count: history.length },\n ];\n\n const visibleRequests =\n activeTab === 'pending' ? pending : activeTab === 'approved' ? approved : history;\n\n const openDialog = (type: ActionDialogState['type'], request: PayoutRequest) => {\n setDialog({\n type,\n requestId: request.id,\n amount: request.requestedAmount,\n currency: request.currency,\n accountLabel:\n request.payoutAccount?.displayName ?? request.payoutAccount?.label ?? 'Unknown account',\n });\n };\n\n const handleConfirm = async (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => {\n if (!dialog) return;\n\n try {\n if (dialog.type === 'approve') {\n await approvePayoutRequest(dialog.requestId, data.notes ?? null, requestContext);\n emit('billing.payout.approved', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n } else if (dialog.type === 'reject') {\n await rejectPayoutRequest(dialog.requestId, data.reason ?? '', requestContext);\n emit('billing.payout.rejected', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n reason: data.reason,\n });\n } else if (dialog.type === 'mark_paid') {\n await markPayoutPaid(\n dialog.requestId,\n data.externalReference ?? null,\n data.notes ?? null,\n requestContext\n );\n emit('billing.payout.processed', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n }\n setDialog(null);\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Action failed'));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Payout queue\"\n description=\"Review withdrawal requests, verify payout details, and mark them as paid once funds are transferred.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n }\n />\n\n <PagePurpose>\n This queue holds two different kinds of request, split into the tabs below so a revenue-share\n credit conversion never gets mistaken for a real bank transfer. <strong>Cash Payouts</strong>{' '}\n are real money leaving the platform — developers withdrawing purchased or earned credits from\n billing to their own bank/UPI account. <strong>Revenue Share Credits</strong> are a developer\n requesting their store revenue share — approving one just converts the amount into earned\n credits on their billing account, no transfer involved.\n </PagePurpose>\n\n {summary && !loading && <SummaryBar summary={summary} />}\n\n {!loading && totalCount > 0 && (\n <div className=\"text-sm text-text-muted\">\n Showing {requests.length} of {totalCount} payout requests.\n </div>\n )}\n\n {loading && (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-20 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n )}\n\n {error && !isServerError(error) && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text\">\n {error.message}\n </div>\n )}\n\n {/* Kind tabs — Cash Payouts vs Revenue Share Credits (BOFF-7192). Kept\n visually distinct from the status sub-tabs below: this is the\n primary split, since approving each kind does something completely\n different (real money vs. an internal credit conversion). */}\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {kindTabItems.map((kind) => {\n const Icon = kind.id === 'CASH_PAYOUT' ? Banknote : Coins;\n const isActive = activeKind === kind.id;\n return (\n <button\n key={kind.id}\n type=\"button\"\n onClick={() => {\n setActiveKind(kind.id);\n setActiveTab('pending');\n }}\n className={`flex items-start gap-3 rounded-card border p-4 text-left transition-colors ${\n isActive\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)]'\n : 'border-border-subtle bg-bg-surface hover:border-border-strong'\n }`}\n >\n <Icon\n className={`size-5 mt-0.5 shrink-0 ${isActive ? 'text-text-link' : 'text-text-muted'}`}\n />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span\n className={`text-sm font-semibold ${isActive ? 'text-text-link' : 'text-text-primary'}`}\n >\n {kind.label}\n </span>\n {kind.count > 0 && (\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {kind.count}\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-muted mt-0.5\">{kind.description}</p>\n </div>\n </button>\n );\n })}\n </div>\n\n {/* Tabs */}\n <div className=\"border-b border-border-subtle\">\n <div className=\"flex gap-0\">\n {tabItems.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-action-primary-bg text-text-primary'\n : 'border-transparent text-text-muted hover:text-text-primary'\n }`}\n >\n {tab.label}\n {tab.count > 0 && (\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${\n activeTab === tab.id\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.count}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n\n {/* How to pay — shown only on the Approved tab for real cash payouts */}\n {activeKind === 'CASH_PAYOUT' && activeTab === 'approved' && approved.length > 0 && (\n <div className=\"rounded-card border border-status-info-border bg-status-info-bg-subtle px-4 py-3 text-sm text-status-info-text\">\n <strong>How to pay:</strong> Click the account row to expand bank/UPI details → transfer\n the exact amount via your bank → click <strong>Mark as Paid</strong> and enter the UTR /\n transaction reference number.\n </div>\n )}\n\n {/* Request cards */}\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((i) => (\n <div\n key={i}\n className=\"h-40 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : visibleRequests.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={\n activeTab === 'pending'\n ? 'No pending requests'\n : activeTab === 'approved'\n ? 'No approved requests awaiting payment'\n : 'No history yet'\n }\n description={\n activeTab === 'pending'\n ? 'New requests will appear here.'\n : activeTab === 'approved'\n ? activeKind === 'CREDIT_CONVERSION'\n ? \"Revenue share requests skip this tab — approving one completes it immediately, there's no separate payment step.\"\n : 'Approve pending requests first, then send payment and mark as paid.'\n : 'Completed and rejected requests will appear here.'\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {visibleRequests.map((request) => (\n <RequestCard\n key={request.id}\n request={request}\n onApprove={(r) => openDialog('approve', r)}\n onReject={(r) => openDialog('reject', r)}\n onMarkPaid={(r) => openDialog('mark_paid', r)}\n />\n ))}\n\n {hasMore && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={() => void loadMore()}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50\"\n >\n {loadingMore ? 'Loading…' : 'Load more requests'}\n </button>\n </div>\n )}\n </div>\n )}\n\n {dialog && (\n <ActionDialog dialog={dialog} onClose={() => setDialog(null)} onConfirm={handleConfirm} />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { GetBillingAccountDocument as e, GetDefaultBillingAccountDocument as t, GetSubscriptionDocument as n, useCancelSubscriptionMutation as r, useCreateBillingAccountSubscriptionsMutation as i,
|
|
2
|
-
import { useCallback as
|
|
3
|
-
import { useApolloClient as
|
|
1
|
+
import { GetBillingAccountDocument as e, GetDefaultBillingAccountDocument as t, GetSubscriptionDocument as n, useCancelSubscriptionMutation as r, useCreateBillingAccountSubscriptionsMutation as i, useGetBillingAccountQuery as a, useGetBillingAccountsForSubscriptionsQuery as o, useGetDefaultBillingAccountQuery as s, useGetPaginatedSubscriptionsQuery as c, useGetSubscriptionQuery as l, useInitiatePaymentSubscriptionsMutation as u, useSpendCreditsSubscriptionsMutation as d, useUpdateBillingAccountSubscriptionsMutation as f, useUpdateSubscriptionSeatsMutation as p } from "../../../../generated/global-operations.js";
|
|
2
|
+
import { useCallback as m, useMemo as h } from "react";
|
|
3
|
+
import { useApolloClient as g } from "@apollo/client/react";
|
|
4
4
|
//#region src/billing/modules/subscriptions/hooks/useSubscriptions.ts
|
|
5
|
-
function
|
|
6
|
-
let { data: n, loading: r, error: i, refetch: a } =
|
|
5
|
+
function _(e, t) {
|
|
6
|
+
let { data: n, loading: r, error: i, refetch: a } = l({
|
|
7
7
|
variables: {
|
|
8
8
|
id: e,
|
|
9
9
|
billingAccountId: t
|
|
10
10
|
},
|
|
11
11
|
skip: !e || !t,
|
|
12
12
|
fetchPolicy: "cache-and-network"
|
|
13
|
-
}), o =
|
|
13
|
+
}), o = m(async () => {
|
|
14
14
|
await a();
|
|
15
15
|
}, [a]);
|
|
16
16
|
return {
|
|
@@ -20,8 +20,24 @@ function g(e, t) {
|
|
|
20
20
|
refetch: o
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
let { data:
|
|
23
|
+
function v(e) {
|
|
24
|
+
let { data: t, loading: n, error: r, refetch: i } = a({
|
|
25
|
+
variables: { id: e },
|
|
26
|
+
skip: !e,
|
|
27
|
+
fetchPolicy: "cache-and-network"
|
|
28
|
+
}), o = m(async () => {
|
|
29
|
+
await i();
|
|
30
|
+
}, [i]), s = t?.getBillingAccount || null;
|
|
31
|
+
return {
|
|
32
|
+
billingAccount: s,
|
|
33
|
+
subscriptions: (s?.subscriptions || []).filter(Boolean),
|
|
34
|
+
isLoading: n,
|
|
35
|
+
error: r,
|
|
36
|
+
refetch: o
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function y() {
|
|
40
|
+
let { data: e, loading: t, error: n, refetch: r } = s({ fetchPolicy: "cache-and-network" }), i = m(async () => {
|
|
25
41
|
await r();
|
|
26
42
|
}, [r]), a = e?.getDefaultBillingAccount || null;
|
|
27
43
|
return {
|
|
@@ -32,10 +48,10 @@ function _() {
|
|
|
32
48
|
refetch: i
|
|
33
49
|
};
|
|
34
50
|
}
|
|
35
|
-
function
|
|
36
|
-
let a =
|
|
51
|
+
function b() {
|
|
52
|
+
let a = g(), [o, { loading: s }] = i(), [c, { loading: l }] = f(), [h, { loading: _ }] = u(), [v, { loading: y }] = d(), [b, { loading: x }] = r(), [S, { loading: C }] = p();
|
|
37
53
|
return {
|
|
38
|
-
createBillingAccount:
|
|
54
|
+
createBillingAccount: m(async (e) => {
|
|
39
55
|
try {
|
|
40
56
|
let { data: t } = await o({ variables: { input: e } });
|
|
41
57
|
return t.createBillingAccount;
|
|
@@ -43,7 +59,7 @@ function v() {
|
|
|
43
59
|
throw console.error("Failed to create billing account:", e), e;
|
|
44
60
|
}
|
|
45
61
|
}, [o]),
|
|
46
|
-
updateBillingAccount:
|
|
62
|
+
updateBillingAccount: m(async (t) => {
|
|
47
63
|
try {
|
|
48
64
|
let { data: n } = await c({ variables: { input: t } });
|
|
49
65
|
return await a.refetchQueries({ include: [e] }), n.updateBillingAccount;
|
|
@@ -51,15 +67,15 @@ function v() {
|
|
|
51
67
|
throw console.error("Failed to update billing account:", e), e;
|
|
52
68
|
}
|
|
53
69
|
}, [c, a]),
|
|
54
|
-
initiatePayment:
|
|
70
|
+
initiatePayment: m(async (e) => {
|
|
55
71
|
try {
|
|
56
|
-
let { data: t } = await
|
|
72
|
+
let { data: t } = await h({ variables: { input: e } });
|
|
57
73
|
return t.initiatePayment;
|
|
58
74
|
} catch (e) {
|
|
59
75
|
throw console.error("Failed to initiate payment:", e), e;
|
|
60
76
|
}
|
|
61
|
-
}, [
|
|
62
|
-
spendCredits:
|
|
77
|
+
}, [h]),
|
|
78
|
+
spendCredits: m(async (n) => {
|
|
63
79
|
try {
|
|
64
80
|
let { data: r } = await v({ variables: { input: n } });
|
|
65
81
|
return await a.refetchQueries({ include: [e, t] }), r.spendCredits;
|
|
@@ -67,7 +83,7 @@ function v() {
|
|
|
67
83
|
throw console.error("Failed to spend credits:", e), e;
|
|
68
84
|
}
|
|
69
85
|
}, [v, a]),
|
|
70
|
-
cancelSubscription:
|
|
86
|
+
cancelSubscription: m(async (r) => {
|
|
71
87
|
try {
|
|
72
88
|
let { data: i } = await b({ variables: { input: r } });
|
|
73
89
|
return await a.refetchQueries({ include: [
|
|
@@ -79,7 +95,7 @@ function v() {
|
|
|
79
95
|
throw console.error("Failed to cancel subscription:", e), e;
|
|
80
96
|
}
|
|
81
97
|
}, [b, a]),
|
|
82
|
-
updateSubscriptionSeats:
|
|
98
|
+
updateSubscriptionSeats: m(async (e) => {
|
|
83
99
|
try {
|
|
84
100
|
let { data: t } = await S({ variables: { input: e } });
|
|
85
101
|
return await a.refetchQueries({ include: [n] }), t.updateSubscriptionSeats;
|
|
@@ -88,15 +104,15 @@ function v() {
|
|
|
88
104
|
}
|
|
89
105
|
}, [S, a]),
|
|
90
106
|
isCreating: s,
|
|
91
|
-
isUpdating:
|
|
107
|
+
isUpdating: l,
|
|
92
108
|
isInitiatingPayment: _,
|
|
93
109
|
isSpendingCredits: y,
|
|
94
110
|
isCanceling: x,
|
|
95
111
|
isUpdatingSeats: C
|
|
96
112
|
};
|
|
97
113
|
}
|
|
98
|
-
function
|
|
99
|
-
let { data: e, loading: t, error: n, refetch: r } =
|
|
114
|
+
function x() {
|
|
115
|
+
let { data: e, loading: t, error: n, refetch: r } = o({ fetchPolicy: "cache-and-network" }), i = m(async () => {
|
|
100
116
|
await r();
|
|
101
117
|
}, [r]);
|
|
102
118
|
return {
|
|
@@ -106,8 +122,8 @@ function y() {
|
|
|
106
122
|
refetch: i
|
|
107
123
|
};
|
|
108
124
|
}
|
|
109
|
-
function
|
|
110
|
-
let { data: i, loading: a, error: o, refetch:
|
|
125
|
+
function S(e, t = 1, n = 10, r) {
|
|
126
|
+
let { data: i, loading: a, error: o, refetch: s } = c({
|
|
111
127
|
variables: {
|
|
112
128
|
billingAccountId: e,
|
|
113
129
|
page: t,
|
|
@@ -116,12 +132,12 @@ function b(e, t = 1, n = 10, r) {
|
|
|
116
132
|
},
|
|
117
133
|
skip: !e,
|
|
118
134
|
fetchPolicy: "cache-and-network"
|
|
119
|
-
}), l =
|
|
120
|
-
await
|
|
121
|
-
}, [
|
|
135
|
+
}), l = m(async () => {
|
|
136
|
+
await s();
|
|
137
|
+
}, [s]), u = i?.getPaginatedSubscriptions, d = h(() => u?.subscriptions || [], [u?.subscriptions]);
|
|
122
138
|
return {
|
|
123
139
|
subscriptions: d,
|
|
124
|
-
activeSubscriptions:
|
|
140
|
+
activeSubscriptions: h(() => d.filter((e) => e.status === "active"), [d]),
|
|
125
141
|
totalCount: u?.totalCount || 0,
|
|
126
142
|
page: u?.page || t,
|
|
127
143
|
pageSize: u?.pageSize || n,
|
|
@@ -132,6 +148,6 @@ function b(e, t = 1, n = 10, r) {
|
|
|
132
148
|
};
|
|
133
149
|
}
|
|
134
150
|
//#endregion
|
|
135
|
-
export {
|
|
151
|
+
export { v as useBillingAccount, x as useBillingAccounts, y as useDefaultBillingAccount, S as usePaginatedSubscriptions, _ as useSubscription, b as useSubscriptionMutations };
|
|
136
152
|
|
|
137
153
|
//# sourceMappingURL=useSubscriptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSubscriptions.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/hooks/useSubscriptions.ts"],"sourcesContent":["/**\n * Subscriptions Module - useSubscriptions Hook\n * Provides access to subscription data and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetSubscriptionQuery,\n useGetBillingAccountQuery,\n useGetDefaultBillingAccountQuery,\n useGetSubscriptionUsageOverviewQuery,\n useGetBillingAccountsForSubscriptionsQuery,\n useGetPaginatedSubscriptionsQuery,\n useCreateBillingAccountSubscriptionsMutation,\n useUpdateBillingAccountSubscriptionsMutation,\n useInitiatePaymentSubscriptionsMutation,\n useSpendCreditsSubscriptionsMutation,\n useCancelSubscriptionMutation,\n useUpdateSubscriptionSeatsMutation,\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n} from '../../../../generated/global-operations';\nimport type {\n BillingSubscription,\n BillingAccount,\n SubscriptionStatus,\n CreateBillingAccountInput,\n UpdateBillingAccountInput,\n InitiatePaymentInput,\n InitiatePaymentResponse,\n SpendCreditsInput,\n CancelSubscriptionInput,\n UpdateSubscriptionSeatsInput,\n} from '../../../shared/types';\n\ninterface UseSubscriptionResult {\n subscription: BillingSubscription | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseBillingAccountResult {\n billingAccount: BillingAccount | null;\n subscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionsListResult {\n subscriptions: BillingSubscription[];\n activeSubscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionMutationsResult {\n createBillingAccount: (input: CreateBillingAccountInput) => Promise<BillingAccount>;\n updateBillingAccount: (input: UpdateBillingAccountInput) => Promise<BillingAccount>;\n initiatePayment: (input: InitiatePaymentInput) => Promise<InitiatePaymentResponse>;\n spendCredits: (input: SpendCreditsInput) => Promise<{ creditTransactionID: string }>;\n cancelSubscription: (input: CancelSubscriptionInput) => Promise<BillingSubscription>;\n updateSubscriptionSeats: (input: UpdateSubscriptionSeatsInput) => Promise<BillingSubscription>;\n isCreating: boolean;\n isUpdating: boolean;\n isInitiatingPayment: boolean;\n isSpendingCredits: boolean;\n isCanceling: boolean;\n isUpdatingSeats: boolean;\n}\n\n/**\n * Hook for fetching a single subscription by ID\n */\nexport function useSubscription(\n id: string | undefined,\n billingAccountId: string | undefined\n): UseSubscriptionResult {\n const { data, loading, error, refetch } = useGetSubscriptionQuery({\n variables: { id: id!, billingAccountId: billingAccountId! },\n skip: !id || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscription: data?.getSubscription || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account by ID\n */\nexport function useBillingAccount(id: string | undefined): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetBillingAccountQuery({\n variables: { id: id! },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching default billing account for an org\n */\nexport function useDefaultBillingAccount(): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetDefaultBillingAccountQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getDefaultBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for listing subscriptions from a billing account\n */\nexport function useSubscriptionsList(\n billingAccountId: string | undefined\n): UseSubscriptionsListResult {\n const { subscriptions, isLoading, error, refetch } = useBillingAccount(billingAccountId);\n\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n isLoading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook for subscription usage overview\n */\nexport function useSubscriptionUsageOverview(\n billingAccountId: string | undefined,\n page = 1,\n pageSize = 10\n) {\n const { data, loading, error, refetch } = useGetSubscriptionUsageOverviewQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscriptionsWithQuotas: data?.getSubscriptionUsageOverview || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for subscription mutations\n */\nexport function useSubscriptionMutations(): UseSubscriptionMutationsResult {\n const client = useApolloClient();\n\n const [createBillingAccountMutation, { loading: isCreating }] =\n useCreateBillingAccountSubscriptionsMutation();\n const [updateBillingAccountMutation, { loading: isUpdating }] =\n useUpdateBillingAccountSubscriptionsMutation();\n const [initiatePaymentMutation, { loading: isInitiatingPayment }] =\n useInitiatePaymentSubscriptionsMutation();\n const [spendCreditsMutation, { loading: isSpendingCredits }] =\n useSpendCreditsSubscriptionsMutation();\n const [cancelSubscriptionMutation, { loading: isCanceling }] = useCancelSubscriptionMutation();\n const [updateSeatsMutation, { loading: isUpdatingSeats }] = useUpdateSubscriptionSeatsMutation();\n\n const createBillingAccount = useCallback(\n async (input: CreateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await createBillingAccountMutation({\n variables: { input },\n });\n return data.createBillingAccount;\n } catch (error) {\n console.error('Failed to create billing account:', error);\n throw error;\n }\n },\n [createBillingAccountMutation]\n );\n\n const updateBillingAccount = useCallback(\n async (input: UpdateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await updateBillingAccountMutation({\n variables: { input },\n });\n // Refetch billing account after update\n await client.refetchQueries({ include: [GetBillingAccountDocument] });\n return data.updateBillingAccount;\n } catch (error) {\n console.error('Failed to update billing account:', error);\n throw error;\n }\n },\n [updateBillingAccountMutation, client]\n );\n\n const initiatePayment = useCallback(\n async (input: InitiatePaymentInput): Promise<InitiatePaymentResponse> => {\n try {\n const { data } = await initiatePaymentMutation({\n variables: { input },\n });\n return data.initiatePayment;\n } catch (error) {\n console.error('Failed to initiate payment:', error);\n throw error;\n }\n },\n [initiatePaymentMutation]\n );\n\n const spendCredits = useCallback(\n async (input: SpendCreditsInput): Promise<{ creditTransactionID: string }> => {\n try {\n const { data } = await spendCreditsMutation({\n variables: { input },\n });\n // Refetch billing account after spending credits\n await client.refetchQueries({\n include: [GetBillingAccountDocument, GetDefaultBillingAccountDocument],\n });\n return data.spendCredits;\n } catch (error) {\n console.error('Failed to spend credits:', error);\n throw error;\n }\n },\n [spendCreditsMutation, client]\n );\n\n const cancelSubscription = useCallback(\n async (input: CancelSubscriptionInput): Promise<BillingSubscription> => {\n try {\n const { data } = await cancelSubscriptionMutation({\n variables: { input },\n });\n // Refetch subscription and billing account after cancellation\n await client.refetchQueries({\n include: [\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n ],\n });\n return data.cancelSubscription;\n } catch (error) {\n console.error('Failed to cancel subscription:', error);\n throw error;\n }\n },\n [cancelSubscriptionMutation, client]\n );\n\n const updateSubscriptionSeats = useCallback(\n async (input: UpdateSubscriptionSeatsInput): Promise<BillingSubscription> => {\n try {\n const { data } = await updateSeatsMutation({ variables: { input } });\n await client.refetchQueries({ include: [GetSubscriptionDocument] });\n return data.updateSubscriptionSeats;\n } catch (error) {\n console.error('Failed to update subscription seats:', error);\n throw error;\n }\n },\n [updateSeatsMutation, client]\n );\n\n return {\n createBillingAccount,\n updateBillingAccount,\n initiatePayment,\n spendCredits,\n cancelSubscription,\n updateSubscriptionSeats,\n isCreating,\n isUpdating,\n isInitiatingPayment,\n isSpendingCredits,\n isCanceling,\n isUpdatingSeats,\n };\n}\n\n/**\n * Hook for fetching all billing accounts for an org\n */\nexport function useBillingAccounts() {\n const { data, loading, error, refetch } = useGetBillingAccountsForSubscriptionsQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccounts: BillingAccount[] = data?.getBillingAccountsByOrg || [];\n\n return {\n billingAccounts,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching paginated subscriptions for a billing account\n */\nexport function usePaginatedSubscriptions(\n billingAccountId: string | undefined,\n page: number = 1,\n pageSize: number = 10,\n search?: string\n) {\n const { data, loading, error, refetch } = useGetPaginatedSubscriptionsQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize, search: search || undefined },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const result = data?.getPaginatedSubscriptions;\n const subscriptions = useMemo<BillingSubscription[]>(\n () => result?.subscriptions || [],\n [result?.subscriptions]\n );\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n totalCount: result?.totalCount || 0,\n page: result?.page || page,\n pageSize: result?.pageSize || pageSize,\n totalPages: result?.totalPages || 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSubscriptions.js","names":[],"sources":["../../../../../src/billing/modules/subscriptions/hooks/useSubscriptions.ts"],"sourcesContent":["/**\n * Subscriptions Module - useSubscriptions Hook\n * Provides access to subscription data and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetSubscriptionQuery,\n useGetBillingAccountQuery,\n useGetDefaultBillingAccountQuery,\n useGetSubscriptionUsageOverviewQuery,\n useGetBillingAccountsForSubscriptionsQuery,\n useGetPaginatedSubscriptionsQuery,\n useCreateBillingAccountSubscriptionsMutation,\n useUpdateBillingAccountSubscriptionsMutation,\n useInitiatePaymentSubscriptionsMutation,\n useSpendCreditsSubscriptionsMutation,\n useCancelSubscriptionMutation,\n useUpdateSubscriptionSeatsMutation,\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n} from '../../../../generated/global-operations';\nimport type {\n BillingSubscription,\n BillingAccount,\n SubscriptionStatus,\n CreateBillingAccountInput,\n UpdateBillingAccountInput,\n InitiatePaymentInput,\n InitiatePaymentResponse,\n SpendCreditsInput,\n CancelSubscriptionInput,\n UpdateSubscriptionSeatsInput,\n} from '../../../shared/types';\n\ninterface UseSubscriptionResult {\n subscription: BillingSubscription | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseBillingAccountResult {\n billingAccount: BillingAccount | null;\n subscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionsListResult {\n subscriptions: BillingSubscription[];\n activeSubscriptions: BillingSubscription[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseSubscriptionMutationsResult {\n createBillingAccount: (input: CreateBillingAccountInput) => Promise<BillingAccount>;\n updateBillingAccount: (input: UpdateBillingAccountInput) => Promise<BillingAccount>;\n initiatePayment: (input: InitiatePaymentInput) => Promise<InitiatePaymentResponse>;\n spendCredits: (input: SpendCreditsInput) => Promise<{ creditTransactionID: string }>;\n cancelSubscription: (input: CancelSubscriptionInput) => Promise<BillingSubscription>;\n updateSubscriptionSeats: (input: UpdateSubscriptionSeatsInput) => Promise<BillingSubscription>;\n isCreating: boolean;\n isUpdating: boolean;\n isInitiatingPayment: boolean;\n isSpendingCredits: boolean;\n isCanceling: boolean;\n isUpdatingSeats: boolean;\n}\n\n/**\n * Hook for fetching a single subscription by ID\n */\nexport function useSubscription(\n id: string | undefined,\n billingAccountId: string | undefined\n): UseSubscriptionResult {\n const { data, loading, error, refetch } = useGetSubscriptionQuery({\n variables: { id: id!, billingAccountId: billingAccountId! },\n skip: !id || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscription: data?.getSubscription || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account by ID\n */\nexport function useBillingAccount(id: string | undefined): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetBillingAccountQuery({\n variables: { id: id! },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching default billing account for an org\n */\nexport function useDefaultBillingAccount(): UseBillingAccountResult {\n const { data, loading, error, refetch } = useGetDefaultBillingAccountQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccount = data?.getDefaultBillingAccount || null;\n const subscriptions = (billingAccount?.subscriptions || []).filter(\n Boolean\n ) as BillingSubscription[];\n\n return {\n billingAccount,\n subscriptions,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for listing subscriptions from a billing account\n */\nexport function useSubscriptionsList(\n billingAccountId: string | undefined\n): UseSubscriptionsListResult {\n const { subscriptions, isLoading, error, refetch } = useBillingAccount(billingAccountId);\n\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n isLoading,\n error,\n refetch,\n };\n}\n\n/**\n * Hook for subscription usage overview\n */\nexport function useSubscriptionUsageOverview(\n billingAccountId: string | undefined,\n page = 1,\n pageSize = 10\n) {\n const { data, loading, error, refetch } = useGetSubscriptionUsageOverviewQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n subscriptionsWithQuotas: data?.getSubscriptionUsageOverview || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for subscription mutations\n */\nexport function useSubscriptionMutations(): UseSubscriptionMutationsResult {\n const client = useApolloClient();\n\n const [createBillingAccountMutation, { loading: isCreating }] =\n useCreateBillingAccountSubscriptionsMutation();\n const [updateBillingAccountMutation, { loading: isUpdating }] =\n useUpdateBillingAccountSubscriptionsMutation();\n const [initiatePaymentMutation, { loading: isInitiatingPayment }] =\n useInitiatePaymentSubscriptionsMutation();\n const [spendCreditsMutation, { loading: isSpendingCredits }] =\n useSpendCreditsSubscriptionsMutation();\n const [cancelSubscriptionMutation, { loading: isCanceling }] = useCancelSubscriptionMutation();\n const [updateSeatsMutation, { loading: isUpdatingSeats }] = useUpdateSubscriptionSeatsMutation();\n\n const createBillingAccount = useCallback(\n async (input: CreateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await createBillingAccountMutation({\n variables: { input },\n });\n return data.createBillingAccount;\n } catch (error) {\n console.error('Failed to create billing account:', error);\n throw error;\n }\n },\n [createBillingAccountMutation]\n );\n\n const updateBillingAccount = useCallback(\n async (input: UpdateBillingAccountInput): Promise<BillingAccount> => {\n try {\n const { data } = await updateBillingAccountMutation({\n variables: { input },\n });\n // Refetch billing account after update\n await client.refetchQueries({ include: [GetBillingAccountDocument] });\n return data.updateBillingAccount;\n } catch (error) {\n console.error('Failed to update billing account:', error);\n throw error;\n }\n },\n [updateBillingAccountMutation, client]\n );\n\n const initiatePayment = useCallback(\n async (input: InitiatePaymentInput): Promise<InitiatePaymentResponse> => {\n try {\n const { data } = await initiatePaymentMutation({\n variables: { input },\n });\n return data.initiatePayment;\n } catch (error) {\n console.error('Failed to initiate payment:', error);\n throw error;\n }\n },\n [initiatePaymentMutation]\n );\n\n const spendCredits = useCallback(\n async (input: SpendCreditsInput): Promise<{ creditTransactionID: string }> => {\n try {\n const { data } = await spendCreditsMutation({\n variables: { input },\n });\n // Refetch billing account after spending credits\n await client.refetchQueries({\n include: [GetBillingAccountDocument, GetDefaultBillingAccountDocument],\n });\n return data.spendCredits;\n } catch (error) {\n console.error('Failed to spend credits:', error);\n throw error;\n }\n },\n [spendCreditsMutation, client]\n );\n\n const cancelSubscription = useCallback(\n async (input: CancelSubscriptionInput): Promise<BillingSubscription> => {\n try {\n const { data } = await cancelSubscriptionMutation({\n variables: { input },\n });\n // Refetch subscription and billing account after cancellation\n await client.refetchQueries({\n include: [\n GetSubscriptionDocument,\n GetBillingAccountDocument,\n GetDefaultBillingAccountDocument,\n ],\n });\n return data.cancelSubscription;\n } catch (error) {\n console.error('Failed to cancel subscription:', error);\n throw error;\n }\n },\n [cancelSubscriptionMutation, client]\n );\n\n const updateSubscriptionSeats = useCallback(\n async (input: UpdateSubscriptionSeatsInput): Promise<BillingSubscription> => {\n try {\n const { data } = await updateSeatsMutation({ variables: { input } });\n await client.refetchQueries({ include: [GetSubscriptionDocument] });\n return data.updateSubscriptionSeats;\n } catch (error) {\n console.error('Failed to update subscription seats:', error);\n throw error;\n }\n },\n [updateSeatsMutation, client]\n );\n\n return {\n createBillingAccount,\n updateBillingAccount,\n initiatePayment,\n spendCredits,\n cancelSubscription,\n updateSubscriptionSeats,\n isCreating,\n isUpdating,\n isInitiatingPayment,\n isSpendingCredits,\n isCanceling,\n isUpdatingSeats,\n };\n}\n\n/**\n * Hook for fetching all billing accounts for an org\n */\nexport function useBillingAccounts() {\n const { data, loading, error, refetch } = useGetBillingAccountsForSubscriptionsQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const billingAccounts: BillingAccount[] = data?.getBillingAccountsByOrg || [];\n\n return {\n billingAccounts,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching paginated subscriptions for a billing account\n */\nexport function usePaginatedSubscriptions(\n billingAccountId: string | undefined,\n page: number = 1,\n pageSize: number = 10,\n search?: string\n) {\n const { data, loading, error, refetch } = useGetPaginatedSubscriptionsQuery({\n variables: { billingAccountId: billingAccountId!, page, pageSize, search: search || undefined },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n const result = data?.getPaginatedSubscriptions;\n const subscriptions = useMemo<BillingSubscription[]>(\n () => result?.subscriptions || [],\n [result?.subscriptions]\n );\n const activeSubscriptions = useMemo(() => {\n return subscriptions.filter((s) => s.status === ('active' as SubscriptionStatus));\n }, [subscriptions]);\n\n return {\n subscriptions,\n activeSubscriptions,\n totalCount: result?.totalCount || 0,\n page: result?.page || page,\n pageSize: result?.pageSize || pageSize,\n totalPages: result?.totalPages || 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n"],"mappings":"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"}
|