@burdenoff/microfe-billing 2026.623.3 → 2026.624.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/BillingUserRoutes.js +3 -3
- package/dist/billing/BillingUserRoutes.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +34 -34
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +94 -94
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +162 -187
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +146 -153
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +10 -10
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +62 -62
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +164 -179
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +8 -8
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +243 -305
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +73 -78
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +173 -179
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +4 -4
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +181 -173
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +111 -99
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +68 -83
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/shared/components/AccessDenied.js +4 -4
- package/dist/billing/shared/components/AccessDenied.js.map +1 -1
- package/dist/billing/shared/components/ActorIdentity.js +8 -8
- package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +11 -11
- package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
- package/dist/billing/shared/ui/MetricCard.js +1 -1
- package/dist/billing/shared/ui/MetricCard.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EarningsPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/EarningsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Coins, HandCoins, RefreshCw, Wallet } from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport {\n AccessDenied,\n EmptyState,\n PageHeader,\n ServerError,\n StatCard,\n} from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport { useGetDefaultBillingAccountSettingsQuery } from '../../../../generated/global-operations';\nimport {\n fetchEarningsEntries,\n fetchEarningsSummary,\n fetchDeveloperPortalRevenueDashboard,\n type DeveloperPortalRevenueDashboard,\n type EarningsEntry,\n type EarningsSummary,\n} from '../api';\n\ntype EarningsSourceFilter = 'ALL' | 'AFFILIATE' | 'STORE' | 'OTHER';\n\nexport const EarningsPage: FC = () => {\n const [searchParams] = useSearchParams();\n const permissions = useBillingPermissions();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const developerOrgId = searchParams.get('developerOrgId');\n const developerOrgName = searchParams.get('developerOrgName');\n const { data: billingAccountData, loading: billingAccountLoading } =\n useGetDefaultBillingAccountSettingsQuery({});\n\n const billingAccountId = billingAccountData?.getDefaultBillingAccount?.id;\n const [summary, setSummary] = useState<EarningsSummary | null>(null);\n const [entries, setEntries] = useState<EarningsEntry[]>([]);\n const [developerPortalRevenue, setDeveloperPortalRevenue] =\n useState<DeveloperPortalRevenueDashboard | null>(null);\n const [sourceFilter, setSourceFilter] = useState<EarningsSourceFilter>('ALL');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const requestContext = useMemo(\n () => ({\n apiGatewayUrl,\n authToken,\n orgId,\n }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n if (developerOrgId) {\n const loadedRevenue = await fetchDeveloperPortalRevenueDashboard(\n developerOrgId,\n requestContext\n );\n setDeveloperPortalRevenue(loadedRevenue);\n setSummary(null);\n setEntries([]);\n return;\n }\n\n if (!billingAccountId) {\n setSummary(null);\n setEntries([]);\n setDeveloperPortalRevenue(null);\n return;\n }\n\n const [loadedSummary, loadedEntries] = await Promise.all([\n fetchEarningsSummary(billingAccountId, requestContext),\n fetchEarningsEntries(billingAccountId, requestContext),\n ]);\n setSummary(loadedSummary);\n setEntries(loadedEntries);\n setDeveloperPortalRevenue(null);\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load earnings'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [billingAccountId, developerOrgId, requestContext]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view earnings.\" />;\n }\n\n if (!developerOrgId && !billingAccountId && !billingAccountLoading) {\n return (\n <EmptyState\n icon={<Wallet className=\"size-8\" />}\n title=\"No billing account found\"\n description=\"Create a billing account first to view earnings.\"\n />\n );\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Earnings unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const cards = summary\n ? [\n {\n title: 'Available',\n value: formatCurrency(summary.availableAmount, 'USD'),\n icon: <HandCoins className=\"size-5\" />,\n },\n {\n title: 'Reserved',\n value: formatCurrency(summary.reservedAmount, 'USD'),\n icon: <Coins className=\"size-5\" />,\n },\n {\n title: 'Paid out',\n value: formatCurrency(summary.paidAmount, 'USD'),\n icon: <Wallet className=\"size-5\" />,\n },\n {\n title: 'Lifetime',\n value: formatCurrency(summary.totalLifetimeAmount, 'USD'),\n icon: <RefreshCw className=\"size-5\" />,\n },\n ]\n : [];\n\n const filteredEntries =\n sourceFilter === 'ALL'\n ? entries\n : entries.filter((entry) => entry.sourceCategory === sourceFilter);\n\n const categoryCards =\n summary?.categoryBreakdown?.map((item) => ({\n title: `${item.category} earnings`,\n value: formatCurrency(item.amount, 'USD'),\n helperText: `${item.count} entries`,\n })) ?? [];\n\n const developerPortalCards = developerPortalRevenue?.summary\n ? [\n {\n title: 'Gross revenue',\n value: formatCurrency(developerPortalRevenue.summary.totalGrossRevenue, 'USD'),\n icon: <HandCoins className=\"size-5\" />,\n },\n {\n title: 'Net revenue',\n value: formatCurrency(developerPortalRevenue.summary.totalNetRevenue, 'USD'),\n icon: <Wallet className=\"size-5\" />,\n },\n {\n title: 'Pending balance',\n value: formatCurrency(developerPortalRevenue.summary.pendingBalance, 'USD'),\n icon: <Coins className=\"size-5\" />,\n },\n {\n title: 'Platform fees',\n value: formatCurrency(developerPortalRevenue.summary.totalPlatformFees, 'USD'),\n icon: <RefreshCw className=\"size-5\" />,\n },\n ]\n : [];\n\n const isDeveloperPortalMode = !!developerOrgId;\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={isDeveloperPortalMode ? 'Marketplace earnings' : 'Earnings'}\n description={\n isDeveloperPortalMode\n ? `Developer portal revenue, sales, and payouts${developerOrgName ? ` for ${developerOrgName}` : ''}.`\n : 'Track affiliate, store, and other earning sources in one ledger.'\n }\n />\n\n {loading ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {[1, 2, 3, 4].map((item) => (\n <div\n key={item}\n className=\"h-28 animate-pulse rounded-lg border border-border bg-card\"\n />\n ))}\n </div>\n ) : isDeveloperPortalMode ? (\n developerPortalRevenue?.summary ? (\n <div className=\"space-y-6\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {developerPortalCards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-foreground\">Recent sales</h2>\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium text-foreground\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n </div>\n\n {developerPortalRevenue.sales.length === 0 ? (\n <EmptyState\n icon={<Coins className=\"size-8\" />}\n title=\"No sales yet\"\n description=\"Sales will appear here when customers start purchasing your apps.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border text-left text-muted-foreground\">\n <th className=\"px-3 py-2\">Application</th>\n <th className=\"px-3 py-2\">Pricing</th>\n <th className=\"px-3 py-2\">Gross</th>\n <th className=\"px-3 py-2\">Net</th>\n <th className=\"px-3 py-2\">Status</th>\n <th className=\"px-3 py-2\">Purchased</th>\n </tr>\n </thead>\n <tbody>\n {developerPortalRevenue.sales.map((sale) => (\n <tr key={sale.id} className=\"border-b border-border/70 text-foreground\">\n <td className=\"px-3 py-2 font-medium\">\n {sale.application?.name || sale.application?.slug || 'Unknown app'}\n </td>\n <td className=\"px-3 py-2\">{sale.pricingModel}</td>\n <td className=\"px-3 py-2\">\n {formatCurrency(sale.grossAmount, sale.currency || 'USD')}\n </td>\n <td className=\"px-3 py-2\">\n {formatCurrency(sale.netAmount, sale.currency || 'USD')}\n </td>\n <td className=\"px-3 py-2\">{sale.status}</td>\n <td className=\"px-3 py-2\">{formatDateTime(sale.purchasedAt)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </section>\n\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-foreground\">Payouts</h2>\n\n {developerPortalRevenue.payouts.length === 0 ? (\n <EmptyState\n icon={<Wallet className=\"size-8\" />}\n title=\"No payouts yet\"\n description=\"Scheduled and completed payouts will appear here.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border text-left text-muted-foreground\">\n <th className=\"px-3 py-2\">Period</th>\n <th className=\"px-3 py-2\">Amount</th>\n <th className=\"px-3 py-2\">Status</th>\n <th className=\"px-3 py-2\">Scheduled</th>\n <th className=\"px-3 py-2\">Completed</th>\n </tr>\n </thead>\n <tbody>\n {developerPortalRevenue.payouts.map((payout) => (\n <tr key={payout.id} className=\"border-b border-border/70 text-foreground\">\n <td className=\"px-3 py-2 font-medium\">{payout.periodLabel || '-'}</td>\n <td className=\"px-3 py-2\">\n {formatCurrency(payout.netAmount, payout.currency || 'USD')}\n </td>\n <td className=\"px-3 py-2\">{payout.status}</td>\n <td className=\"px-3 py-2\">\n {payout.scheduledAt ? formatDateTime(payout.scheduledAt) : '-'}\n </td>\n <td className=\"px-3 py-2\">\n {payout.completedAt ? formatDateTime(payout.completedAt) : '-'}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </section>\n </div>\n ) : (\n <EmptyState\n icon={<Coins className=\"size-8\" />}\n title=\"No developer portal revenue yet\"\n description=\"Marketplace revenue will appear here once your developer organization records sales.\"\n />\n )\n ) : summary ? (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {cards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n {categoryCards.length > 0 ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {categoryCards.map((card) => (\n <div key={card.title} className=\"rounded-lg border border-border bg-card p-4\">\n <p className=\"text-sm text-muted-foreground\">{card.title}</p>\n <p className=\"mt-2 text-xl font-semibold text-foreground\">{card.value}</p>\n <p className=\"mt-1 text-xs text-muted-foreground\">{card.helperText}</p>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {error && !isServerError(error) ? (\n <div className=\"rounded-lg border border-border bg-card p-4 text-sm text-destructive\">\n {error.message}\n </div>\n ) : null}\n\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-foreground\">Ledger</h2>\n <div className=\"flex items-center gap-3\">\n <select\n value={sourceFilter}\n onChange={(event) => setSourceFilter(event.target.value as EarningsSourceFilter)}\n className=\"rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n >\n <option value=\"ALL\">All sources</option>\n <option value=\"AFFILIATE\">Affiliate</option>\n <option value=\"STORE\">Store</option>\n <option value=\"OTHER\">Other</option>\n </select>\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium text-foreground\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n </div>\n </div>\n\n {filteredEntries.length === 0 && !loading ? (\n <EmptyState\n icon={<Coins className=\"size-8\" />}\n title=\"No earnings yet\"\n description=\"Eligible affiliate rewards and other earnings will appear here.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border text-left text-muted-foreground\">\n <th className=\"px-3 py-2\">Category</th>\n <th className=\"px-3 py-2\">Description</th>\n <th className=\"px-3 py-2\">Unit</th>\n <th className=\"px-3 py-2\">Amount</th>\n <th className=\"px-3 py-2\">Status</th>\n <th className=\"px-3 py-2\">Created</th>\n </tr>\n </thead>\n <tbody>\n {filteredEntries.map((entry) => (\n <tr key={entry.id} className=\"border-b border-border/70 text-foreground\">\n <td className=\"px-3 py-2 font-medium\">{entry.sourceCategory}</td>\n <td className=\"px-3 py-2\">\n {entry.description || entry.sourceSubcategory || '-'}\n </td>\n <td className=\"px-3 py-2\">{entry.unitType}</td>\n <td className=\"px-3 py-2\">\n {entry.unitType === 'CREDITS'\n ? `${entry.amount.toLocaleString()} credits`\n : formatCurrency(entry.amount, entry.currency || 'USD')}\n </td>\n <td className=\"px-3 py-2\">{entry.status}</td>\n <td className=\"px-3 py-2\">{formatDateTime(entry.createdAt)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </section>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EarningsPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/EarningsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Coins, HandCoins, RefreshCw, Wallet } from 'lucide-react';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport {\n AccessDenied,\n EmptyState,\n PageHeader,\n ServerError,\n StatCard,\n} from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport { useGetDefaultBillingAccountSettingsQuery } from '../../../../generated/global-operations';\nimport {\n fetchEarningsEntries,\n fetchEarningsSummary,\n fetchDeveloperPortalRevenueDashboard,\n type DeveloperPortalRevenueDashboard,\n type DeveloperPortalRevenueSale,\n type DeveloperPortalRevenuePayout,\n type EarningsEntry,\n type EarningsSummary,\n} from '../api';\n\ntype EarningsSourceFilter = 'ALL' | 'AFFILIATE' | 'STORE' | 'OTHER';\n\nconst salesColumns: ResponsiveTableColumn<DeveloperPortalRevenueSale>[] = [\n {\n key: 'application',\n header: 'Application',\n priority: 'primary',\n cell: (sale) => sale.application?.name || sale.application?.slug || 'Unknown app',\n },\n { key: 'pricing', header: 'Pricing', cell: (sale) => sale.pricingModel },\n {\n key: 'gross',\n header: 'Gross',\n cell: (sale) => (\n <span className=\"tabular-nums\">\n {formatCurrency(sale.grossAmount, sale.currency || 'USD')}\n </span>\n ),\n },\n {\n key: 'net',\n header: 'Net',\n cell: (sale) => (\n <span className=\"tabular-nums\">\n {formatCurrency(sale.netAmount, sale.currency || 'USD')}\n </span>\n ),\n },\n { key: 'status', header: 'Status', cell: (sale) => sale.status },\n {\n key: 'purchased',\n header: 'Purchased',\n cell: (sale) => formatDateTime(sale.purchasedAt),\n },\n];\n\nconst payoutColumns: ResponsiveTableColumn<DeveloperPortalRevenuePayout>[] = [\n {\n key: 'period',\n header: 'Period',\n priority: 'primary',\n cell: (payout) => payout.periodLabel || '-',\n },\n {\n key: 'amount',\n header: 'Amount',\n cell: (payout) => (\n <span className=\"tabular-nums\">\n {formatCurrency(payout.netAmount, payout.currency || 'USD')}\n </span>\n ),\n },\n { key: 'status', header: 'Status', cell: (payout) => payout.status },\n {\n key: 'scheduled',\n header: 'Scheduled',\n cell: (payout) => (payout.scheduledAt ? formatDateTime(payout.scheduledAt) : '-'),\n },\n {\n key: 'completed',\n header: 'Completed',\n cell: (payout) => (payout.completedAt ? formatDateTime(payout.completedAt) : '-'),\n },\n];\n\nconst ledgerColumns: ResponsiveTableColumn<EarningsEntry>[] = [\n {\n key: 'category',\n header: 'Category',\n priority: 'primary',\n cell: (entry) => entry.sourceCategory,\n },\n {\n key: 'description',\n header: 'Description',\n cell: (entry) => entry.description || entry.sourceSubcategory || '-',\n },\n { key: 'unit', header: 'Unit', cell: (entry) => entry.unitType },\n {\n key: 'amount',\n header: 'Amount',\n cell: (entry) => (\n <span className=\"tabular-nums\">\n {entry.unitType === 'CREDITS'\n ? `${entry.amount.toLocaleString()} credits`\n : formatCurrency(entry.amount, entry.currency || 'USD')}\n </span>\n ),\n },\n { key: 'status', header: 'Status', cell: (entry) => entry.status },\n {\n key: 'created',\n header: 'Created',\n cell: (entry) => formatDateTime(entry.createdAt),\n },\n];\n\nexport const EarningsPage: FC = () => {\n const [searchParams] = useSearchParams();\n const permissions = useBillingPermissions();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const developerOrgId = searchParams.get('developerOrgId');\n const developerOrgName = searchParams.get('developerOrgName');\n const { data: billingAccountData, loading: billingAccountLoading } =\n useGetDefaultBillingAccountSettingsQuery({});\n\n const billingAccountId = billingAccountData?.getDefaultBillingAccount?.id;\n const [summary, setSummary] = useState<EarningsSummary | null>(null);\n const [entries, setEntries] = useState<EarningsEntry[]>([]);\n const [developerPortalRevenue, setDeveloperPortalRevenue] =\n useState<DeveloperPortalRevenueDashboard | null>(null);\n const [sourceFilter, setSourceFilter] = useState<EarningsSourceFilter>('ALL');\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const requestContext = useMemo(\n () => ({\n apiGatewayUrl,\n authToken,\n orgId,\n }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n if (developerOrgId) {\n const loadedRevenue = await fetchDeveloperPortalRevenueDashboard(\n developerOrgId,\n requestContext\n );\n setDeveloperPortalRevenue(loadedRevenue);\n setSummary(null);\n setEntries([]);\n return;\n }\n\n if (!billingAccountId) {\n setSummary(null);\n setEntries([]);\n setDeveloperPortalRevenue(null);\n return;\n }\n\n const [loadedSummary, loadedEntries] = await Promise.all([\n fetchEarningsSummary(billingAccountId, requestContext),\n fetchEarningsEntries(billingAccountId, requestContext),\n ]);\n setSummary(loadedSummary);\n setEntries(loadedEntries);\n setDeveloperPortalRevenue(null);\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load earnings'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [billingAccountId, developerOrgId, requestContext]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view earnings.\" />;\n }\n\n if (!developerOrgId && !billingAccountId && !billingAccountLoading) {\n return (\n <EmptyState\n icon={<Wallet className=\"size-8\" />}\n title=\"No billing account found\"\n description=\"Create a billing account first to view earnings.\"\n />\n );\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Earnings unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const cards = summary\n ? [\n {\n title: 'Available',\n value: formatCurrency(summary.availableAmount, 'USD'),\n icon: <HandCoins className=\"size-5\" />,\n },\n {\n title: 'Reserved',\n value: formatCurrency(summary.reservedAmount, 'USD'),\n icon: <Coins className=\"size-5\" />,\n },\n {\n title: 'Paid out',\n value: formatCurrency(summary.paidAmount, 'USD'),\n icon: <Wallet className=\"size-5\" />,\n },\n {\n title: 'Lifetime',\n value: formatCurrency(summary.totalLifetimeAmount, 'USD'),\n icon: <RefreshCw className=\"size-5\" />,\n },\n ]\n : [];\n\n const filteredEntries =\n sourceFilter === 'ALL'\n ? entries\n : entries.filter((entry) => entry.sourceCategory === sourceFilter);\n\n const categoryCards =\n summary?.categoryBreakdown?.map((item) => ({\n title: `${item.category} earnings`,\n value: formatCurrency(item.amount, 'USD'),\n helperText: `${item.count} entries`,\n })) ?? [];\n\n const developerPortalCards = developerPortalRevenue?.summary\n ? [\n {\n title: 'Gross revenue',\n value: formatCurrency(developerPortalRevenue.summary.totalGrossRevenue, 'USD'),\n icon: <HandCoins className=\"size-5\" />,\n },\n {\n title: 'Net revenue',\n value: formatCurrency(developerPortalRevenue.summary.totalNetRevenue, 'USD'),\n icon: <Wallet className=\"size-5\" />,\n },\n {\n title: 'Pending balance',\n value: formatCurrency(developerPortalRevenue.summary.pendingBalance, 'USD'),\n icon: <Coins className=\"size-5\" />,\n },\n {\n title: 'Platform fees',\n value: formatCurrency(developerPortalRevenue.summary.totalPlatformFees, 'USD'),\n icon: <RefreshCw className=\"size-5\" />,\n },\n ]\n : [];\n\n const isDeveloperPortalMode = !!developerOrgId;\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={isDeveloperPortalMode ? 'Marketplace earnings' : 'Earnings'}\n description={\n isDeveloperPortalMode\n ? `Developer portal revenue, sales, and payouts${developerOrgName ? ` for ${developerOrgName}` : ''}.`\n : 'Track affiliate, store, and other earning sources in one ledger.'\n }\n />\n\n {loading ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {[1, 2, 3, 4].map((item) => (\n <div\n key={item}\n className=\"h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : isDeveloperPortalMode ? (\n developerPortalRevenue?.summary ? (\n <div className=\"space-y-6\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {developerPortalCards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n\n <section className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recent sales</h2>\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\" />\n Refresh\n </button>\n </div>\n\n {developerPortalRevenue.sales.length === 0 ? (\n <EmptyState\n icon={<Coins className=\"size-8\" />}\n title=\"No sales yet\"\n description=\"Sales will appear here when customers start purchasing your apps.\"\n />\n ) : (\n <ResponsiveTable\n data={developerPortalRevenue.sales}\n rowKey={(sale) => sale.id}\n columns={salesColumns}\n />\n )}\n </section>\n\n <section className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Payouts</h2>\n\n {developerPortalRevenue.payouts.length === 0 ? (\n <EmptyState\n icon={<Wallet className=\"size-8\" />}\n title=\"No payouts yet\"\n description=\"Scheduled and completed payouts will appear here.\"\n />\n ) : (\n <ResponsiveTable\n data={developerPortalRevenue.payouts}\n rowKey={(payout) => payout.id}\n columns={payoutColumns}\n />\n )}\n </section>\n </div>\n ) : (\n <EmptyState\n icon={<Coins className=\"size-8\" />}\n title=\"No developer portal revenue yet\"\n description=\"Marketplace revenue will appear here once your developer organization records sales.\"\n />\n )\n ) : summary ? (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {cards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n {categoryCards.length > 0 ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {categoryCards.map((card) => (\n <div key={card.title} className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-sm text-text-secondary\">{card.title}</p>\n <p className=\"mt-2 text-xl font-semibold text-text-primary\">{card.value}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">{card.helperText}</p>\n </div>\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {error && !isServerError(error) ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text\">\n {error.message}\n </div>\n ) : null}\n\n <section className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Ledger</h2>\n <div className=\"flex items-center gap-3\">\n <select\n value={sourceFilter}\n onChange={(event) => setSourceFilter(event.target.value as EarningsSourceFilter)}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"ALL\">All sources</option>\n <option value=\"AFFILIATE\">Affiliate</option>\n <option value=\"STORE\">Store</option>\n <option value=\"OTHER\">Other</option>\n </select>\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\" />\n Refresh\n </button>\n </div>\n </div>\n\n {filteredEntries.length === 0 && !loading ? (\n <EmptyState\n icon={<Coins className=\"size-8\" />}\n title=\"No earnings yet\"\n description=\"Eligible affiliate rewards and other earnings will appear here.\"\n />\n ) : (\n <ResponsiveTable\n data={filteredEntries}\n rowKey={(entry) => entry.id}\n columns={ledgerColumns}\n />\n )}\n </section>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,IAAM,IAAoE;CACxE;EACE,KAAK;EACL,QAAQ;EACR,UAAU;EACV,OAAO,MAAS,EAAK,aAAa,QAAQ,EAAK,aAAa,QAAQ;EACrE;CACD;EAAE,KAAK;EAAW,QAAQ;EAAW,OAAO,MAAS,EAAK;EAAc;CACxE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAe,EAAK,aAAa,EAAK,YAAY,MAAM;GACpD,CAAA;EAEV;CACD;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAe,EAAK,WAAW,EAAK,YAAY,MAAM;GAClD,CAAA;EAEV;CACD;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAS,EAAK;EAAQ;CAChE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAS,EAAe,EAAK,YAAY;EACjD;CACF,EAEK,IAAuE;CAC3E;EACE,KAAK;EACL,QAAQ;EACR,UAAU;EACV,OAAO,MAAW,EAAO,eAAe;EACzC;CACD;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAe,EAAO,WAAW,EAAO,YAAY,MAAM;GACtD,CAAA;EAEV;CACD;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAW,EAAO;EAAQ;CACpE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAY,EAAO,cAAc,EAAe,EAAO,YAAY,GAAG;EAC9E;CACD;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAY,EAAO,cAAc,EAAe,EAAO,YAAY,GAAG;EAC9E;CACF,EAEK,IAAwD;CAC5D;EACE,KAAK;EACL,QAAQ;EACR,UAAU;EACV,OAAO,MAAU,EAAM;EACxB;CACD;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAU,EAAM,eAAe,EAAM,qBAAqB;EAClE;CACD;EAAE,KAAK;EAAQ,QAAQ;EAAQ,OAAO,MAAU,EAAM;EAAU;CAChE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MACL,kBAAC,QAAD;GAAM,WAAU;aACb,EAAM,aAAa,YAChB,GAAG,EAAM,OAAO,gBAAgB,CAAC,YACjC,EAAe,EAAM,QAAQ,EAAM,YAAY,MAAM;GACpD,CAAA;EAEV;CACD;EAAE,KAAK;EAAU,QAAQ;EAAU,OAAO,MAAU,EAAM;EAAQ;CAClE;EACE,KAAK;EACL,QAAQ;EACR,OAAO,MAAU,EAAe,EAAM,UAAU;EACjD;CACF,EAEY,UAAyB;CACpC,IAAM,CAAC,KAAgB,GAAiB,EAClC,IAAc,GAAuB,EACrC,EAAE,kBAAe,cAAW,aAAU,GAAY,EAClD,IAAiB,EAAa,IAAI,iBAAiB,EACnD,IAAmB,EAAa,IAAI,mBAAmB,EACvD,EAAE,MAAM,GAAoB,SAAS,MACzC,EAAyC,EAAE,CAAC,EAExC,IAAmB,GAAoB,0BAA0B,IACjE,CAAC,GAAS,KAAc,EAAiC,KAAK,EAC9D,CAAC,GAAS,KAAc,EAA0B,EAAE,CAAC,EACrD,CAAC,GAAwB,KAC7B,EAAiD,KAAK,EAClD,CAAC,GAAc,KAAmB,EAA+B,MAAM,EACvE,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAuB,KAAK,EAEhD,IAAiB,SACd;EACL;EACA;EACA;EACD,GACD;EAAC;EAAe;EAAW;EAAM,CAClC,EAEK,IAAW,YAAY;AAE3B,EADA,EAAW,GAAK,EAChB,EAAS,KAAK;AACd,MAAI;AACF,OAAI,GAAgB;AAOlB,IAFA,EAJsB,MAAM,EAC1B,GACA,EACD,CACuC,EACxC,EAAW,KAAK,EAChB,EAAW,EAAE,CAAC;AACd;;AAGF,OAAI,CAAC,GAAkB;AAGrB,IAFA,EAAW,KAAK,EAChB,EAAW,EAAE,CAAC,EACd,EAA0B,KAAK;AAC/B;;GAGF,IAAM,CAAC,GAAe,KAAiB,MAAM,QAAQ,IAAI,CACvD,EAAqB,GAAkB,EAAe,EACtD,EAAqB,GAAkB,EAAe,CACvD,CAAC;AAGF,GAFA,EAAW,EAAc,EACzB,EAAW,EAAc,EACzB,EAA0B,KAAK;WACxB,GAAW;AAClB,KAAS,aAAqB,QAAQ,IAAY,gBAAI,MAAM,0BAA0B,CAAC;YAC/E;AACR,KAAW,GAAM;;;AAQrB,KAJA,QAAgB;AACT,KAAU;IACd;EAAC;EAAkB;EAAgB;EAAe,CAAC,EAElD,CAAC,EAAY,sBACf,QAAO,kBAAC,GAAD,EAAc,SAAQ,+CAAgD,CAAA;AAG/E,KAAI,CAAC,KAAkB,CAAC,KAAoB,CAAC,EAC3C,QACE,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;EACnC,OAAM;EACN,aAAY;EACZ,CAAA;AAIN,KAAI,KAAS,EAAc,EAAM,CAC/B,QACE,kBAAC,GAAD;EACE,OAAM;EACN,SAAS,EAAM;EACf,eAAe,KAAK,GAAU;EAC9B,WAAA;EACA,CAAA;CAIN,IAAM,IAAQ,IACV;EACE;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,iBAAiB,MAAM;GACrD,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;GACvC;EACD;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,gBAAgB,MAAM;GACpD,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;GACnC;EACD;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,YAAY,MAAM;GAChD,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;GACpC;EACD;GACE,OAAO;GACP,OAAO,EAAe,EAAQ,qBAAqB,MAAM;GACzD,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;GACvC;EACF,GACD,EAAE,EAEA,IACJ,MAAiB,QACb,IACA,EAAQ,QAAQ,MAAU,EAAM,mBAAmB,EAAa,EAEhE,IACJ,GAAS,mBAAmB,KAAK,OAAU;EACzC,OAAO,GAAG,EAAK,SAAS;EACxB,OAAO,EAAe,EAAK,QAAQ,MAAM;EACzC,YAAY,GAAG,EAAK,MAAM;EAC3B,EAAE,IAAI,EAAE,EAEL,KAAuB,GAAwB,UACjD;EACE;GACE,OAAO;GACP,OAAO,EAAe,EAAuB,QAAQ,mBAAmB,MAAM;GAC9E,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;GACvC;EACD;GACE,OAAO;GACP,OAAO,EAAe,EAAuB,QAAQ,iBAAiB,MAAM;GAC5E,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;GACpC;EACD;GACE,OAAO;GACP,OAAO,EAAe,EAAuB,QAAQ,gBAAgB,MAAM;GAC3E,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;GACnC;EACD;GACE,OAAO;GACP,OAAO,EAAe,EAAuB,QAAQ,mBAAmB,MAAM;GAC9E,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;GACvC;EACF,GACD,EAAE,EAEA,IAAwB,CAAC,CAAC;AAEhC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,IAAD;IACE,OAAO,IAAwB,yBAAyB;IACxD,aACE,IACI,+CAA+C,IAAmB,QAAQ,MAAqB,GAAG,KAClG;IAEN,CAAA;GAED,IACC,kBAAC,OAAD;IAAK,WAAU;cACZ;KAAC;KAAG;KAAG;KAAG;KAAE,CAAC,KAAK,MACjB,kBAAC,OAAD,EAEE,WAAU,2EACV,EAFK,EAEL,CACF;IACE,CAAA,GACJ,IACF,GAAwB,UACtB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBACZ,GAAqB,KAAK,MACzB,kBAAC,GAAD;OAA2B,OAAO,EAAK;OAAO,OAAO,EAAK;OAAO,MAAM,EAAK;OAAQ,EAArE,EAAK,MAAgE,CACpF;MACE,CAAA;KAEN,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;QAAiB,CAAA,EACzE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,KAAK,GAAU;QAC9B,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,UAEzB;UACL;UAEL,EAAuB,MAAM,WAAW,IACvC,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;OAClC,OAAM;OACN,aAAY;OACZ,CAAA,GAEF,kBAAC,GAAD;OACE,MAAM,EAAuB;OAC7B,SAAS,MAAS,EAAK;OACvB,SAAS;OACT,CAAA,CAEI;;KAEV,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAAY,CAAA,EAExE,EAAuB,QAAQ,WAAW,IACzC,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;OACnC,OAAM;OACN,aAAY;OACZ,CAAA,GAEF,kBAAC,GAAD;OACE,MAAM,EAAuB;OAC7B,SAAS,MAAW,EAAO;OAC3B,SAAS;OACT,CAAA,CAEI;;KACN;QAEN,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;IAClC,OAAM;IACN,aAAY;IACZ,CAAA,GAEF,IACF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,MACV,kBAAC,GAAD;MAA2B,OAAO,EAAK;MAAO,OAAO,EAAK;MAAO,MAAM,EAAK;MAAQ,EAArE,EAAK,MAAgE,CACpF;KACE,CAAA,EACL,EAAc,SAAS,IACtB,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAc,KAAK,MAClB,kBAAC,OAAD;MAAsB,WAAU;gBAAhC;OACE,kBAAC,KAAD;QAAG,WAAU;kBAA+B,EAAK;QAAU,CAAA;OAC3D,kBAAC,KAAD;QAAG,WAAU;kBAAgD,EAAK;QAAU,CAAA;OAC5E,kBAAC,KAAD;QAAG,WAAU;kBAAoC,EAAK;QAAe,CAAA;OACjE;QAJI,EAAK,MAIT,CACN;KACE,CAAA,GACJ,KACA;QACJ;GAEH,KAAS,CAAC,EAAc,EAAM,GAC7B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM;IACH,CAAA,GACJ;GAEJ,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAW,CAAA,EACnE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,OAAO;OACP,WAAW,MAAU,EAAgB,EAAM,OAAO,MAA8B;OAChF,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAM;SAAoB,CAAA;QACxC,kBAAC,UAAD;SAAQ,OAAM;mBAAY;SAAkB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAAc,CAAA;QACpC,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAAc,CAAA;QAC7B;UACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,KAAK,GAAU;OAC9B,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,UAEzB;SACL;QACF;QAEL,EAAgB,WAAW,KAAK,CAAC,IAChC,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;KAClC,OAAM;KACN,aAAY;KACZ,CAAA,GAEF,kBAAC,GAAD;KACE,MAAM;KACN,SAAS,MAAU,EAAM;KACzB,SAAS;KACT,CAAA,CAEI;;GACN"}
|
|
@@ -13,8 +13,9 @@ import { approvePayoutRequest as u, fetchAdminPayoutRequests as d, fetchAdminPay
|
|
|
13
13
|
import { useEffect as h, useMemo as g, useState as _ } from "react";
|
|
14
14
|
import { AlertTriangle as v, Banknote as y, CheckCircle2 as b, ChevronDown as x, ChevronUp as S, Clock as C, CreditCard as w, ReceiptText as T, RefreshCw as E, XCircle as D } from "lucide-react";
|
|
15
15
|
import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
|
|
16
|
+
import { ResponsiveDialog as j, ResponsiveDialogContent as M, ResponsiveDialogDescription as N, ResponsiveDialogFooter as P, ResponsiveDialogHeader as F, ResponsiveDialogTitle as I } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/billing/modules/earnings/pages/PayoutAdminPage.tsx
|
|
17
|
-
var
|
|
18
|
+
var L = {
|
|
18
19
|
REQUESTED: "Requested",
|
|
19
20
|
APPROVED: "Approved",
|
|
20
21
|
REJECTED: "Rejected",
|
|
@@ -22,7 +23,7 @@ var j = {
|
|
|
22
23
|
PAID: "Paid",
|
|
23
24
|
FAILED: "Failed",
|
|
24
25
|
CANCELED: "Canceled"
|
|
25
|
-
},
|
|
26
|
+
}, R = {
|
|
26
27
|
REQUESTED: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
27
28
|
APPROVED: "bg-status-info-bg-subtle text-status-info-text",
|
|
28
29
|
REJECTED: "bg-status-error-bg-subtle text-status-error-text",
|
|
@@ -31,13 +32,13 @@ var j = {
|
|
|
31
32
|
FAILED: "bg-status-error-bg-subtle text-status-error-text",
|
|
32
33
|
CANCELED: "bg-bg-sunken text-text-muted"
|
|
33
34
|
};
|
|
34
|
-
function
|
|
35
|
+
function z({ status: e }) {
|
|
35
36
|
return /* @__PURE__ */ k("span", {
|
|
36
|
-
className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${
|
|
37
|
-
children:
|
|
37
|
+
className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${R[e] ?? "bg-bg-sunken text-text-muted"}`,
|
|
38
|
+
children: L[e] ?? e
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
|
-
function
|
|
41
|
+
function B({ account: e }) {
|
|
41
42
|
let [t, n] = _(!1), r = e.accountDetails;
|
|
42
43
|
return /* @__PURE__ */ A("div", {
|
|
43
44
|
className: "rounded-md border border-border-subtle bg-bg-sunken/40 p-3 text-sm",
|
|
@@ -145,7 +146,7 @@ function P({ account: e }) {
|
|
|
145
146
|
})]
|
|
146
147
|
});
|
|
147
148
|
}
|
|
148
|
-
function
|
|
149
|
+
function V({ request: e, onApprove: t, onReject: n, onMarkPaid: r }) {
|
|
149
150
|
let i = e.status === "REQUESTED" || e.status === "REJECTED", a = e.status === "REQUESTED" || e.status === "APPROVED", c = e.status === "APPROVED" || e.status === "PROCESSING";
|
|
150
151
|
return /* @__PURE__ */ A("div", {
|
|
151
152
|
className: "rounded-card border border-border-subtle bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]",
|
|
@@ -159,7 +160,7 @@ function F({ request: e, onApprove: t, onReject: n, onMarkPaid: r }) {
|
|
|
159
160
|
children: [/* @__PURE__ */ k("span", {
|
|
160
161
|
className: "text-xl font-semibold text-text-primary",
|
|
161
162
|
children: o(e.requestedAmount, e.currency)
|
|
162
|
-
}), /* @__PURE__ */ k(
|
|
163
|
+
}), /* @__PURE__ */ k(z, { status: e.status })]
|
|
163
164
|
}), /* @__PURE__ */ A("div", {
|
|
164
165
|
className: "text-xs text-text-muted space-x-2",
|
|
165
166
|
children: [
|
|
@@ -196,7 +197,7 @@ function F({ request: e, onApprove: t, onReject: n, onMarkPaid: r }) {
|
|
|
196
197
|
]
|
|
197
198
|
})]
|
|
198
199
|
}),
|
|
199
|
-
e.payoutAccount && /* @__PURE__ */ k(
|
|
200
|
+
e.payoutAccount && /* @__PURE__ */ k(B, { account: e.payoutAccount }),
|
|
200
201
|
e.userNotes && /* @__PURE__ */ A("div", {
|
|
201
202
|
className: "rounded-md bg-bg-sunken/40 border border-border-subtle px-3 py-2 text-sm",
|
|
202
203
|
children: [/* @__PURE__ */ k("span", {
|
|
@@ -258,7 +259,7 @@ function F({ request: e, onApprove: t, onReject: n, onMarkPaid: r }) {
|
|
|
258
259
|
]
|
|
259
260
|
});
|
|
260
261
|
}
|
|
261
|
-
function
|
|
262
|
+
function H({ summary: e }) {
|
|
262
263
|
return /* @__PURE__ */ A("div", {
|
|
263
264
|
className: "grid gap-3 sm:grid-cols-3",
|
|
264
265
|
children: [
|
|
@@ -309,7 +310,7 @@ function I({ summary: e }) {
|
|
|
309
310
|
]
|
|
310
311
|
});
|
|
311
312
|
}
|
|
312
|
-
function
|
|
313
|
+
function U({ dialog: e, onClose: t, onConfirm: n }) {
|
|
313
314
|
let [r, i] = _(""), [a, s] = _(""), [c, l] = _(""), [u, d] = _(!1), f = async () => {
|
|
314
315
|
e.type === "reject" && !a.trim() || (d(!0), await n({
|
|
315
316
|
notes: r.trim() || void 0,
|
|
@@ -317,26 +318,23 @@ function L({ dialog: e, onClose: t, onConfirm: n }) {
|
|
|
317
318
|
externalReference: c.trim() || void 0
|
|
318
319
|
}), d(!1));
|
|
319
320
|
};
|
|
320
|
-
return /* @__PURE__ */ k(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
return /* @__PURE__ */ k(j, {
|
|
322
|
+
open: !0,
|
|
323
|
+
onOpenChange: (e) => {
|
|
324
|
+
!e && !u && t();
|
|
325
|
+
},
|
|
326
|
+
children: /* @__PURE__ */ A(M, {
|
|
327
|
+
className: "sm:max-w-md",
|
|
324
328
|
children: [
|
|
325
|
-
/* @__PURE__ */ A(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
children: [
|
|
335
|
-
o(e.amount, e.currency),
|
|
336
|
-
" · ",
|
|
337
|
-
e.accountLabel
|
|
338
|
-
]
|
|
339
|
-
})] }),
|
|
329
|
+
/* @__PURE__ */ A(F, { children: [/* @__PURE__ */ k(I, { children: {
|
|
330
|
+
approve: "Approve withdrawal request",
|
|
331
|
+
reject: "Reject withdrawal request",
|
|
332
|
+
mark_paid: "Mark as paid"
|
|
333
|
+
}[e.type] }), /* @__PURE__ */ A(N, { children: [
|
|
334
|
+
o(e.amount, e.currency),
|
|
335
|
+
" · ",
|
|
336
|
+
e.accountLabel
|
|
337
|
+
] })] }),
|
|
340
338
|
e.type === "mark_paid" && /* @__PURE__ */ A("div", {
|
|
341
339
|
className: "space-y-3",
|
|
342
340
|
children: [/* @__PURE__ */ A("div", { children: [
|
|
@@ -349,7 +347,7 @@ function L({ dialog: e, onClose: t, onConfirm: n }) {
|
|
|
349
347
|
value: c,
|
|
350
348
|
onChange: (e) => l(e.target.value),
|
|
351
349
|
placeholder: "e.g. UTR123456789012, TXN_abc123",
|
|
352
|
-
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-ring"
|
|
350
|
+
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)]"
|
|
353
351
|
}),
|
|
354
352
|
/* @__PURE__ */ k("p", {
|
|
355
353
|
className: "text-xs text-text-muted mt-1",
|
|
@@ -363,7 +361,7 @@ function L({ dialog: e, onClose: t, onConfirm: n }) {
|
|
|
363
361
|
onChange: (e) => i(e.target.value),
|
|
364
362
|
rows: 2,
|
|
365
363
|
placeholder: "Any internal notes about this payment…",
|
|
366
|
-
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-ring resize-none"
|
|
364
|
+
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"
|
|
367
365
|
})] })]
|
|
368
366
|
}),
|
|
369
367
|
e.type === "approve" && /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("label", {
|
|
@@ -374,7 +372,7 @@ function L({ dialog: e, onClose: t, onConfirm: n }) {
|
|
|
374
372
|
onChange: (e) => i(e.target.value),
|
|
375
373
|
rows: 3,
|
|
376
374
|
placeholder: "e.g. Verified bank details, approved for transfer…",
|
|
377
|
-
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-ring resize-none"
|
|
375
|
+
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"
|
|
378
376
|
})] }),
|
|
379
377
|
e.type === "reject" && /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ A("label", {
|
|
380
378
|
className: "text-xs font-medium text-text-primary block mb-1",
|
|
@@ -387,30 +385,27 @@ function L({ dialog: e, onClose: t, onConfirm: n }) {
|
|
|
387
385
|
onChange: (e) => s(e.target.value),
|
|
388
386
|
rows: 3,
|
|
389
387
|
placeholder: "e.g. Invalid bank details provided, please re-submit with correct IFSC…",
|
|
390
|
-
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-ring resize-none"
|
|
388
|
+
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"
|
|
391
389
|
})] }),
|
|
392
|
-
/* @__PURE__ */ A("
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
children: u ? "Saving…" : e.type === "approve" ? "Approve" : e.type === "reject" ? "Reject" : "Confirm Payment"
|
|
406
|
-
})]
|
|
407
|
-
})
|
|
390
|
+
/* @__PURE__ */ A(P, { children: [/* @__PURE__ */ k("button", {
|
|
391
|
+
type: "button",
|
|
392
|
+
onClick: t,
|
|
393
|
+
disabled: u,
|
|
394
|
+
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50",
|
|
395
|
+
children: "Cancel"
|
|
396
|
+
}), /* @__PURE__ */ k("button", {
|
|
397
|
+
type: "button",
|
|
398
|
+
onClick: () => void f(),
|
|
399
|
+
disabled: u || e.type === "reject" && !a.trim(),
|
|
400
|
+
className: `rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50 ${e.type === "reject" ? "bg-action-danger-bg text-action-danger-text" : "bg-action-primary-bg text-action-primary-text"}`,
|
|
401
|
+
children: u ? "Saving…" : e.type === "approve" ? "Approve" : e.type === "reject" ? "Reject" : "Confirm Payment"
|
|
402
|
+
})] })
|
|
408
403
|
]
|
|
409
404
|
})
|
|
410
405
|
});
|
|
411
406
|
}
|
|
412
|
-
var
|
|
413
|
-
let o = l(), { emit: s } = a(), { apiGatewayUrl: v, authToken: y, orgId: x } = e(), [S, C] = _(null), [w, T] = _([]), [D, O] = _(!0), [j, M] = _(null), [N, P] = _("pending"), [
|
|
407
|
+
var W = () => {
|
|
408
|
+
let o = l(), { emit: s } = a(), { apiGatewayUrl: v, authToken: y, orgId: x } = e(), [S, C] = _(null), [w, T] = _([]), [D, O] = _(!0), [j, M] = _(null), [N, P] = _("pending"), [F, I] = _(null), L = g(() => ({
|
|
414
409
|
apiGatewayUrl: v,
|
|
415
410
|
authToken: y,
|
|
416
411
|
orgId: x
|
|
@@ -418,10 +413,10 @@ var R = () => {
|
|
|
418
413
|
v,
|
|
419
414
|
y,
|
|
420
415
|
x
|
|
421
|
-
]),
|
|
416
|
+
]), R = async () => {
|
|
422
417
|
O(!0), M(null);
|
|
423
418
|
try {
|
|
424
|
-
let [e, t] = await Promise.all([f(
|
|
419
|
+
let [e, t] = await Promise.all([f(L), d(L)]);
|
|
425
420
|
C(e), T(t);
|
|
426
421
|
} catch (e) {
|
|
427
422
|
M(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load payout queue"));
|
|
@@ -430,15 +425,15 @@ var R = () => {
|
|
|
430
425
|
}
|
|
431
426
|
};
|
|
432
427
|
if (h(() => {
|
|
433
|
-
|
|
434
|
-
}, [
|
|
428
|
+
R();
|
|
429
|
+
}, [L]), !o.isBillingAdmin) return /* @__PURE__ */ k(t, { message: "You don't have permission to manage payouts." });
|
|
435
430
|
if (j && c(j)) return /* @__PURE__ */ k(i, {
|
|
436
431
|
title: "Payout queue unavailable",
|
|
437
432
|
message: j.message,
|
|
438
|
-
onRetry: () => void
|
|
433
|
+
onRetry: () => void R(),
|
|
439
434
|
showRetry: !0
|
|
440
435
|
});
|
|
441
|
-
let
|
|
436
|
+
let z = w.filter((e) => e.status === "REQUESTED"), B = w.filter((e) => e.status === "APPROVED" || e.status === "PROCESSING"), W = w.filter((e) => [
|
|
442
437
|
"PAID",
|
|
443
438
|
"REJECTED",
|
|
444
439
|
"FAILED",
|
|
@@ -447,20 +442,20 @@ var R = () => {
|
|
|
447
442
|
{
|
|
448
443
|
id: "pending",
|
|
449
444
|
label: "Pending",
|
|
450
|
-
count:
|
|
445
|
+
count: z.length
|
|
451
446
|
},
|
|
452
447
|
{
|
|
453
448
|
id: "approved",
|
|
454
449
|
label: "Approved",
|
|
455
|
-
count:
|
|
450
|
+
count: B.length
|
|
456
451
|
},
|
|
457
452
|
{
|
|
458
453
|
id: "history",
|
|
459
454
|
label: "History",
|
|
460
455
|
count: W.length
|
|
461
456
|
}
|
|
462
|
-
], K = N === "pending" ?
|
|
463
|
-
|
|
457
|
+
], K = N === "pending" ? z : N === "approved" ? B : W, q = (e, t) => {
|
|
458
|
+
I({
|
|
464
459
|
type: e,
|
|
465
460
|
requestId: t.id,
|
|
466
461
|
amount: t.requestedAmount,
|
|
@@ -476,12 +471,12 @@ var R = () => {
|
|
|
476
471
|
description: "Review withdrawal requests, verify bank details, and mark payments as paid once transferred.",
|
|
477
472
|
actions: /* @__PURE__ */ A("button", {
|
|
478
473
|
type: "button",
|
|
479
|
-
onClick: () => void
|
|
474
|
+
onClick: () => void R(),
|
|
480
475
|
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary",
|
|
481
476
|
children: [/* @__PURE__ */ k(E, { className: `size-4 ${D ? "animate-spin" : ""}` }), "Refresh"]
|
|
482
477
|
})
|
|
483
478
|
}),
|
|
484
|
-
S && !D && /* @__PURE__ */ k(
|
|
479
|
+
S && !D && /* @__PURE__ */ k(H, { summary: S }),
|
|
485
480
|
D && /* @__PURE__ */ k("div", {
|
|
486
481
|
className: "grid gap-3 sm:grid-cols-3",
|
|
487
482
|
children: [
|
|
@@ -509,7 +504,7 @@ var R = () => {
|
|
|
509
504
|
}, e.id))
|
|
510
505
|
})
|
|
511
506
|
}),
|
|
512
|
-
N === "approved" &&
|
|
507
|
+
N === "approved" && B.length > 0 && /* @__PURE__ */ A("div", {
|
|
513
508
|
className: "rounded-card border border-status-info-border bg-status-info-bg-subtle px-4 py-3 text-sm text-status-info-text",
|
|
514
509
|
children: [
|
|
515
510
|
/* @__PURE__ */ k("strong", { children: "How to pay:" }),
|
|
@@ -527,32 +522,32 @@ var R = () => {
|
|
|
527
522
|
description: N === "pending" ? "New withdrawal requests will appear here." : N === "approved" ? "Approve pending requests first, then send payment and mark as paid." : "Completed and rejected requests will appear here."
|
|
528
523
|
}) : /* @__PURE__ */ k("div", {
|
|
529
524
|
className: "space-y-4",
|
|
530
|
-
children: K.map((e) => /* @__PURE__ */ k(
|
|
525
|
+
children: K.map((e) => /* @__PURE__ */ k(V, {
|
|
531
526
|
request: e,
|
|
532
527
|
onApprove: (e) => q("approve", e),
|
|
533
528
|
onReject: (e) => q("reject", e),
|
|
534
529
|
onMarkPaid: (e) => q("mark_paid", e)
|
|
535
530
|
}, e.id))
|
|
536
531
|
}),
|
|
537
|
-
|
|
538
|
-
dialog:
|
|
539
|
-
onClose: () =>
|
|
532
|
+
F && /* @__PURE__ */ k(U, {
|
|
533
|
+
dialog: F,
|
|
534
|
+
onClose: () => I(null),
|
|
540
535
|
onConfirm: async (e) => {
|
|
541
|
-
if (
|
|
542
|
-
|
|
536
|
+
if (F) try {
|
|
537
|
+
F.type === "approve" ? (await u(F.requestId, e.notes ?? null, L), s("billing.payout.approved", {
|
|
543
538
|
route: "/billing/payouts",
|
|
544
|
-
entityId:
|
|
539
|
+
entityId: F.requestId,
|
|
545
540
|
source: "payout-admin"
|
|
546
|
-
})) :
|
|
541
|
+
})) : F.type === "reject" ? (await m(F.requestId, e.reason ?? "", L), s("billing.payout.rejected", {
|
|
547
542
|
route: "/billing/payouts",
|
|
548
|
-
entityId:
|
|
543
|
+
entityId: F.requestId,
|
|
549
544
|
source: "payout-admin",
|
|
550
545
|
reason: e.reason
|
|
551
|
-
})) :
|
|
546
|
+
})) : F.type === "mark_paid" && (await p(F.requestId, e.externalReference ?? null, e.notes ?? null, L), s("billing.payout.processed", {
|
|
552
547
|
route: "/billing/payouts",
|
|
553
|
-
entityId:
|
|
548
|
+
entityId: F.requestId,
|
|
554
549
|
source: "payout-admin"
|
|
555
|
-
})),
|
|
550
|
+
})), I(null), await R();
|
|
556
551
|
} catch (e) {
|
|
557
552
|
M(e instanceof Error ? e : /* @__PURE__ */ Error("Action failed"));
|
|
558
553
|
}
|
|
@@ -562,6 +557,6 @@ var R = () => {
|
|
|
562
557
|
});
|
|
563
558
|
};
|
|
564
559
|
//#endregion
|
|
565
|
-
export {
|
|
560
|
+
export { W as PayoutAdminPage };
|
|
566
561
|
|
|
567
562
|
//# sourceMappingURL=PayoutAdminPage.js.map
|