@burdenoff/microfe-billing 2026.625.3 → 2026.625.5
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/lib/native-purchases.js +2 -44
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +192 -187
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +322 -317
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +190 -185
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +297 -292
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +60 -75
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +86 -101
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +124 -121
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +103 -111
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +196 -160
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +58 -73
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +65 -75
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +399 -399
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +49 -48
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +144 -151
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +430 -425
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +121 -93
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +154 -152
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +467 -462
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +294 -286
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +123 -117
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +43 -42
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +192 -207
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +40 -56
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +128 -121
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +165 -180
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +103 -100
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +175 -175
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +96 -96
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +42 -40
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +45 -38
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +339 -334
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +80 -74
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +342 -337
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +38 -53
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +60 -75
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +242 -237
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +29 -39
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js +1 -0
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +299 -294
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +191 -186
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +308 -303
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +57 -78
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +79 -78
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +15 -13
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +338 -299
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +82 -103
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +59 -54
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +172 -215
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/package.json +2 -2
- package/dist/billing/lib/native-purchases.js.map +0 -1
|
@@ -2,20 +2,19 @@ import { useBilling as e } from "../../../providers/BillingProvider.js";
|
|
|
2
2
|
import { AccessDenied as t } from "../../../shared/components/AccessDenied.js";
|
|
3
3
|
import { PageHeader as ee } from "../../../shared/components/PageHeader.js";
|
|
4
4
|
import { StatCard as n } from "../../../shared/components/StatCard.js";
|
|
5
|
-
import {
|
|
6
|
-
import { ServerError as i } from "../../../shared/components/ServerError.js";
|
|
5
|
+
import { ServerError as te } from "../../../shared/components/ServerError.js";
|
|
7
6
|
import "../../../shared/components/index.js";
|
|
8
|
-
import { formatCurrency as
|
|
9
|
-
import { isServerError as
|
|
7
|
+
import { formatCurrency as r, formatDateTime as i } from "../../../shared/utils/format.js";
|
|
8
|
+
import { isServerError as a } from "../../../shared/utils/error.js";
|
|
10
9
|
import "../../../shared/utils/index.js";
|
|
11
|
-
import { useBillingPermissions as
|
|
12
|
-
import { useGetDefaultBillingAccountSettingsQuery as
|
|
13
|
-
import { fetchDeveloperPortalRevenueDashboard as
|
|
14
|
-
import { useEffect as
|
|
15
|
-
import { Coins as
|
|
16
|
-
import { useSearchParams as
|
|
17
|
-
import { jsx as
|
|
18
|
-
import { ResponsiveTable as C } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { useBillingPermissions as ne } from "../../../hooks/useBillingPermissions.js";
|
|
11
|
+
import { useGetDefaultBillingAccountSettingsQuery as o } from "../../../../generated/global-operations.js";
|
|
12
|
+
import { fetchDeveloperPortalRevenueDashboard as s, fetchEarningsEntries as c, fetchEarningsSummary as l } from "../api.js";
|
|
13
|
+
import { useEffect as u, useMemo as d, useState as f } from "react";
|
|
14
|
+
import { Coins as p, HandCoins as m, RefreshCw as h, Wallet as g } from "lucide-react";
|
|
15
|
+
import { useSearchParams as _ } from "react-router-dom";
|
|
16
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
17
|
+
import { EmphasisPanel as b, IllustratedEmptyState as x, PagePurpose as S, ResponsiveTable as C } from "@burdenoff/fe-libs/ui";
|
|
19
18
|
//#region src/billing/modules/earnings/pages/EarningsPage.tsx
|
|
20
19
|
var w = [
|
|
21
20
|
{
|
|
@@ -32,17 +31,17 @@ var w = [
|
|
|
32
31
|
{
|
|
33
32
|
key: "gross",
|
|
34
33
|
header: "Gross",
|
|
35
|
-
cell: (e) => /* @__PURE__ */
|
|
34
|
+
cell: (e) => /* @__PURE__ */ v("span", {
|
|
36
35
|
className: "tabular-nums",
|
|
37
|
-
children:
|
|
36
|
+
children: r(e.grossAmount, e.currency || "USD")
|
|
38
37
|
})
|
|
39
38
|
},
|
|
40
39
|
{
|
|
41
40
|
key: "net",
|
|
42
41
|
header: "Net",
|
|
43
|
-
cell: (e) => /* @__PURE__ */
|
|
42
|
+
cell: (e) => /* @__PURE__ */ v("span", {
|
|
44
43
|
className: "tabular-nums",
|
|
45
|
-
children:
|
|
44
|
+
children: r(e.netAmount, e.currency || "USD")
|
|
46
45
|
})
|
|
47
46
|
},
|
|
48
47
|
{
|
|
@@ -53,7 +52,7 @@ var w = [
|
|
|
53
52
|
{
|
|
54
53
|
key: "purchased",
|
|
55
54
|
header: "Purchased",
|
|
56
|
-
cell: (e) =>
|
|
55
|
+
cell: (e) => i(e.purchasedAt)
|
|
57
56
|
}
|
|
58
57
|
], T = [
|
|
59
58
|
{
|
|
@@ -65,9 +64,9 @@ var w = [
|
|
|
65
64
|
{
|
|
66
65
|
key: "amount",
|
|
67
66
|
header: "Amount",
|
|
68
|
-
cell: (e) => /* @__PURE__ */
|
|
67
|
+
cell: (e) => /* @__PURE__ */ v("span", {
|
|
69
68
|
className: "tabular-nums",
|
|
70
|
-
children:
|
|
69
|
+
children: r(e.netAmount, e.currency || "USD")
|
|
71
70
|
})
|
|
72
71
|
},
|
|
73
72
|
{
|
|
@@ -78,12 +77,12 @@ var w = [
|
|
|
78
77
|
{
|
|
79
78
|
key: "scheduled",
|
|
80
79
|
header: "Scheduled",
|
|
81
|
-
cell: (e) => e.scheduledAt ?
|
|
80
|
+
cell: (e) => e.scheduledAt ? i(e.scheduledAt) : "-"
|
|
82
81
|
},
|
|
83
82
|
{
|
|
84
83
|
key: "completed",
|
|
85
84
|
header: "Completed",
|
|
86
|
-
cell: (e) => e.completedAt ?
|
|
85
|
+
cell: (e) => e.completedAt ? i(e.completedAt) : "-"
|
|
87
86
|
}
|
|
88
87
|
], E = [
|
|
89
88
|
{
|
|
@@ -105,9 +104,9 @@ var w = [
|
|
|
105
104
|
{
|
|
106
105
|
key: "amount",
|
|
107
106
|
header: "Amount",
|
|
108
|
-
cell: (e) => /* @__PURE__ */
|
|
107
|
+
cell: (e) => /* @__PURE__ */ v("span", {
|
|
109
108
|
className: "tabular-nums",
|
|
110
|
-
children: e.unitType === "CREDITS" ? `${e.amount.toLocaleString()} credits` :
|
|
109
|
+
children: e.unitType === "CREDITS" ? `${e.amount.toLocaleString()} credits` : r(e.amount, e.currency || "USD")
|
|
111
110
|
})
|
|
112
111
|
},
|
|
113
112
|
{
|
|
@@ -118,10 +117,10 @@ var w = [
|
|
|
118
117
|
{
|
|
119
118
|
key: "created",
|
|
120
119
|
header: "Created",
|
|
121
|
-
cell: (e) =>
|
|
120
|
+
cell: (e) => i(e.createdAt)
|
|
122
121
|
}
|
|
123
122
|
], D = () => {
|
|
124
|
-
let [
|
|
123
|
+
let [i] = _(), D = ne(), { apiGatewayUrl: O, authToken: k, orgId: A } = e(), j = i.get("developerOrgId"), M = i.get("developerOrgName"), { data: N, loading: P } = o({}), F = N?.getDefaultBillingAccount?.id, [I, L] = f(null), [R, z] = f([]), [B, V] = f(null), [H, U] = f("ALL"), [W, G] = f(!0), [K, q] = f(null), J = d(() => ({
|
|
125
124
|
apiGatewayUrl: O,
|
|
126
125
|
authToken: k,
|
|
127
126
|
orgId: A
|
|
@@ -133,14 +132,14 @@ var w = [
|
|
|
133
132
|
G(!0), q(null);
|
|
134
133
|
try {
|
|
135
134
|
if (j) {
|
|
136
|
-
V(await
|
|
135
|
+
V(await s(j, J)), L(null), z([]);
|
|
137
136
|
return;
|
|
138
137
|
}
|
|
139
138
|
if (!F) {
|
|
140
139
|
L(null), z([]), V(null);
|
|
141
140
|
return;
|
|
142
141
|
}
|
|
143
|
-
let [e, t] = await Promise.all([
|
|
142
|
+
let [e, t] = await Promise.all([l(F, J), c(F, J)]);
|
|
144
143
|
L(e), z(t), V(null);
|
|
145
144
|
} catch (e) {
|
|
146
145
|
q(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load earnings"));
|
|
@@ -148,19 +147,19 @@ var w = [
|
|
|
148
147
|
G(!1);
|
|
149
148
|
}
|
|
150
149
|
};
|
|
151
|
-
if (
|
|
150
|
+
if (u(() => {
|
|
152
151
|
Y();
|
|
153
152
|
}, [
|
|
154
153
|
F,
|
|
155
154
|
j,
|
|
156
155
|
J
|
|
157
|
-
]), !D.canViewBillingAccount) return /* @__PURE__ */
|
|
158
|
-
if (!j && !F && !P) return /* @__PURE__ */ x
|
|
159
|
-
|
|
156
|
+
]), !D.canViewBillingAccount) return /* @__PURE__ */ v(t, { message: "You don't have permission to view earnings." });
|
|
157
|
+
if (!j && !F && !P) return /* @__PURE__ */ v(x, {
|
|
158
|
+
illustration: "empty-generic",
|
|
160
159
|
title: "No billing account found",
|
|
161
160
|
description: "Create a billing account first to view earnings."
|
|
162
161
|
});
|
|
163
|
-
if (K &&
|
|
162
|
+
if (K && a(K)) return /* @__PURE__ */ v(te, {
|
|
164
163
|
title: "Earnings unavailable",
|
|
165
164
|
message: K.message,
|
|
166
165
|
onRetry: () => void Y(),
|
|
@@ -169,142 +168,146 @@ var w = [
|
|
|
169
168
|
let X = I ? [
|
|
170
169
|
{
|
|
171
170
|
title: "Available",
|
|
172
|
-
value:
|
|
173
|
-
icon: /* @__PURE__ */
|
|
171
|
+
value: r(I.availableAmount, "USD"),
|
|
172
|
+
icon: /* @__PURE__ */ v(m, { className: "size-5" })
|
|
174
173
|
},
|
|
175
174
|
{
|
|
176
175
|
title: "Reserved",
|
|
177
|
-
value:
|
|
178
|
-
icon: /* @__PURE__ */
|
|
176
|
+
value: r(I.reservedAmount, "USD"),
|
|
177
|
+
icon: /* @__PURE__ */ v(p, { className: "size-5" })
|
|
179
178
|
},
|
|
180
179
|
{
|
|
181
180
|
title: "Paid out",
|
|
182
|
-
value:
|
|
183
|
-
icon: /* @__PURE__ */
|
|
181
|
+
value: r(I.paidAmount, "USD"),
|
|
182
|
+
icon: /* @__PURE__ */ v(g, { className: "size-5" })
|
|
184
183
|
},
|
|
185
184
|
{
|
|
186
185
|
title: "Lifetime",
|
|
187
|
-
value:
|
|
188
|
-
icon: /* @__PURE__ */
|
|
186
|
+
value: r(I.totalLifetimeAmount, "USD"),
|
|
187
|
+
icon: /* @__PURE__ */ v(h, { className: "size-5" })
|
|
189
188
|
}
|
|
190
189
|
] : [], Z = H === "ALL" ? R : R.filter((e) => e.sourceCategory === H), Q = I?.categoryBreakdown?.map((e) => ({
|
|
191
190
|
title: `${e.category} earnings`,
|
|
192
|
-
value:
|
|
191
|
+
value: r(e.amount, "USD"),
|
|
193
192
|
helperText: `${e.count} entries`
|
|
194
|
-
})) ?? [],
|
|
193
|
+
})) ?? [], re = B?.summary ? [
|
|
195
194
|
{
|
|
196
195
|
title: "Gross revenue",
|
|
197
|
-
value:
|
|
198
|
-
icon: /* @__PURE__ */
|
|
196
|
+
value: r(B.summary.totalGrossRevenue, "USD"),
|
|
197
|
+
icon: /* @__PURE__ */ v(m, { className: "size-5" })
|
|
199
198
|
},
|
|
200
199
|
{
|
|
201
200
|
title: "Net revenue",
|
|
202
|
-
value:
|
|
203
|
-
icon: /* @__PURE__ */
|
|
201
|
+
value: r(B.summary.totalNetRevenue, "USD"),
|
|
202
|
+
icon: /* @__PURE__ */ v(g, { className: "size-5" })
|
|
204
203
|
},
|
|
205
204
|
{
|
|
206
205
|
title: "Pending balance",
|
|
207
|
-
value:
|
|
208
|
-
icon: /* @__PURE__ */
|
|
206
|
+
value: r(B.summary.pendingBalance, "USD"),
|
|
207
|
+
icon: /* @__PURE__ */ v(p, { className: "size-5" })
|
|
209
208
|
},
|
|
210
209
|
{
|
|
211
210
|
title: "Platform fees",
|
|
212
|
-
value:
|
|
213
|
-
icon: /* @__PURE__ */
|
|
211
|
+
value: r(B.summary.totalPlatformFees, "USD"),
|
|
212
|
+
icon: /* @__PURE__ */ v(h, { className: "size-5" })
|
|
214
213
|
}
|
|
215
214
|
] : [], $ = !!j;
|
|
216
|
-
return /* @__PURE__ */
|
|
215
|
+
return /* @__PURE__ */ y("div", {
|
|
217
216
|
className: "space-y-6 p-6",
|
|
218
217
|
children: [
|
|
219
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ v(ee, {
|
|
220
219
|
title: $ ? "Marketplace earnings" : "Earnings",
|
|
221
220
|
description: $ ? `Developer portal revenue, sales, and payouts${M ? ` for ${M}` : ""}.` : "Track affiliate, store, and other earning sources in one ledger."
|
|
222
221
|
}),
|
|
223
|
-
|
|
222
|
+
/* @__PURE__ */ v(S, { children: "See everything you have earned — from affiliate rewards, store sales and marketplace revenue — in one ledger, along with how much has been paid out and what is still pending. Filter by source to reconcile a specific stream." }),
|
|
223
|
+
W ? /* @__PURE__ */ v("div", {
|
|
224
224
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
225
225
|
children: [
|
|
226
226
|
1,
|
|
227
227
|
2,
|
|
228
228
|
3,
|
|
229
229
|
4
|
|
230
|
-
].map((e) => /* @__PURE__ */
|
|
231
|
-
}) : $ ? B?.summary ? /* @__PURE__ */
|
|
230
|
+
].map((e) => /* @__PURE__ */ v("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
231
|
+
}) : $ ? B?.summary ? /* @__PURE__ */ y("div", {
|
|
232
232
|
className: "space-y-6",
|
|
233
233
|
children: [
|
|
234
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ v("div", {
|
|
235
235
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
236
|
-
children:
|
|
236
|
+
children: re.map((e) => /* @__PURE__ */ v(n, {
|
|
237
237
|
label: e.title,
|
|
238
238
|
value: e.value,
|
|
239
239
|
icon: e.icon
|
|
240
240
|
}, e.title))
|
|
241
241
|
}),
|
|
242
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ y("section", {
|
|
243
243
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
244
|
-
children: [/* @__PURE__ */
|
|
244
|
+
children: [/* @__PURE__ */ y("div", {
|
|
245
245
|
className: "mb-4 flex items-center justify-between",
|
|
246
|
-
children: [/* @__PURE__ */
|
|
246
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
247
247
|
className: "text-lg font-semibold text-text-primary",
|
|
248
248
|
children: "Recent sales"
|
|
249
|
-
}), /* @__PURE__ */
|
|
249
|
+
}), /* @__PURE__ */ y("button", {
|
|
250
250
|
type: "button",
|
|
251
251
|
onClick: () => void Y(),
|
|
252
252
|
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary",
|
|
253
|
-
children: [/* @__PURE__ */
|
|
253
|
+
children: [/* @__PURE__ */ v(h, { className: "size-4" }), "Refresh"]
|
|
254
254
|
})]
|
|
255
|
-
}), B.sales.length === 0 ? /* @__PURE__ */ x
|
|
256
|
-
|
|
255
|
+
}), B.sales.length === 0 ? /* @__PURE__ */ v(x, {
|
|
256
|
+
illustration: "empty-data",
|
|
257
257
|
title: "No sales yet",
|
|
258
258
|
description: "Sales will appear here when customers start purchasing your apps."
|
|
259
|
-
}) : /* @__PURE__ */
|
|
259
|
+
}) : /* @__PURE__ */ v(C, {
|
|
260
260
|
data: B.sales,
|
|
261
261
|
rowKey: (e) => e.id,
|
|
262
262
|
columns: w
|
|
263
263
|
})]
|
|
264
264
|
}),
|
|
265
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ y("section", {
|
|
266
266
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
267
|
-
children: [/* @__PURE__ */
|
|
267
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
268
268
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
269
269
|
children: "Payouts"
|
|
270
|
-
}), B.payouts.length === 0 ? /* @__PURE__ */ x
|
|
271
|
-
|
|
270
|
+
}), B.payouts.length === 0 ? /* @__PURE__ */ v(x, {
|
|
271
|
+
illustration: "empty-data",
|
|
272
272
|
title: "No payouts yet",
|
|
273
273
|
description: "Scheduled and completed payouts will appear here."
|
|
274
|
-
}) : /* @__PURE__ */
|
|
274
|
+
}) : /* @__PURE__ */ v(C, {
|
|
275
275
|
data: B.payouts,
|
|
276
276
|
rowKey: (e) => e.id,
|
|
277
277
|
columns: T
|
|
278
278
|
})]
|
|
279
279
|
})
|
|
280
280
|
]
|
|
281
|
-
}) : /* @__PURE__ */ x
|
|
282
|
-
|
|
281
|
+
}) : /* @__PURE__ */ v(x, {
|
|
282
|
+
illustration: "empty-data",
|
|
283
283
|
title: "No developer portal revenue yet",
|
|
284
284
|
description: "Marketplace revenue will appear here once your developer organization records sales."
|
|
285
|
-
}) : I ? /* @__PURE__ */
|
|
285
|
+
}) : I ? /* @__PURE__ */ y("div", {
|
|
286
286
|
className: "space-y-4",
|
|
287
|
-
children: [/* @__PURE__ */
|
|
288
|
-
className: "
|
|
289
|
-
children:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
children: [/* @__PURE__ */ v(b, {
|
|
288
|
+
className: "p-4 sm:p-5",
|
|
289
|
+
children: /* @__PURE__ */ v("div", {
|
|
290
|
+
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
291
|
+
children: X.map((e) => /* @__PURE__ */ v(n, {
|
|
292
|
+
label: e.title,
|
|
293
|
+
value: e.value,
|
|
294
|
+
icon: e.icon
|
|
295
|
+
}, e.title))
|
|
296
|
+
})
|
|
297
|
+
}), Q.length > 0 ? /* @__PURE__ */ v("div", {
|
|
295
298
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-3",
|
|
296
|
-
children: Q.map((e) => /* @__PURE__ */
|
|
299
|
+
children: Q.map((e) => /* @__PURE__ */ y("div", {
|
|
297
300
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
298
301
|
children: [
|
|
299
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ v("p", {
|
|
300
303
|
className: "text-sm text-text-secondary",
|
|
301
304
|
children: e.title
|
|
302
305
|
}),
|
|
303
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ v("p", {
|
|
304
307
|
className: "mt-2 text-xl font-semibold text-text-primary",
|
|
305
308
|
children: e.value
|
|
306
309
|
}),
|
|
307
|
-
/* @__PURE__ */
|
|
310
|
+
/* @__PURE__ */ v("p", {
|
|
308
311
|
className: "mt-1 text-xs text-text-secondary",
|
|
309
312
|
children: e.helperText
|
|
310
313
|
})
|
|
@@ -312,53 +315,53 @@ var w = [
|
|
|
312
315
|
}, e.title))
|
|
313
316
|
}) : null]
|
|
314
317
|
}) : null,
|
|
315
|
-
K && !
|
|
318
|
+
K && !a(K) ? /* @__PURE__ */ v("div", {
|
|
316
319
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
317
320
|
children: K.message
|
|
318
321
|
}) : null,
|
|
319
|
-
/* @__PURE__ */
|
|
322
|
+
/* @__PURE__ */ y("section", {
|
|
320
323
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
324
|
+
children: [/* @__PURE__ */ y("div", {
|
|
322
325
|
className: "mb-4 flex items-center justify-between",
|
|
323
|
-
children: [/* @__PURE__ */
|
|
326
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
324
327
|
className: "text-lg font-semibold text-text-primary",
|
|
325
328
|
children: "Ledger"
|
|
326
|
-
}), /* @__PURE__ */
|
|
329
|
+
}), /* @__PURE__ */ y("div", {
|
|
327
330
|
className: "flex items-center gap-3",
|
|
328
|
-
children: [/* @__PURE__ */
|
|
331
|
+
children: [/* @__PURE__ */ y("select", {
|
|
329
332
|
value: H,
|
|
330
333
|
onChange: (e) => U(e.target.value),
|
|
331
334
|
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
332
335
|
children: [
|
|
333
|
-
/* @__PURE__ */
|
|
336
|
+
/* @__PURE__ */ v("option", {
|
|
334
337
|
value: "ALL",
|
|
335
338
|
children: "All sources"
|
|
336
339
|
}),
|
|
337
|
-
/* @__PURE__ */
|
|
340
|
+
/* @__PURE__ */ v("option", {
|
|
338
341
|
value: "AFFILIATE",
|
|
339
342
|
children: "Affiliate"
|
|
340
343
|
}),
|
|
341
|
-
/* @__PURE__ */
|
|
344
|
+
/* @__PURE__ */ v("option", {
|
|
342
345
|
value: "STORE",
|
|
343
346
|
children: "Store"
|
|
344
347
|
}),
|
|
345
|
-
/* @__PURE__ */
|
|
348
|
+
/* @__PURE__ */ v("option", {
|
|
346
349
|
value: "OTHER",
|
|
347
350
|
children: "Other"
|
|
348
351
|
})
|
|
349
352
|
]
|
|
350
|
-
}), /* @__PURE__ */
|
|
353
|
+
}), /* @__PURE__ */ y("button", {
|
|
351
354
|
type: "button",
|
|
352
355
|
onClick: () => void Y(),
|
|
353
356
|
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary",
|
|
354
|
-
children: [/* @__PURE__ */
|
|
357
|
+
children: [/* @__PURE__ */ v(h, { className: "size-4" }), "Refresh"]
|
|
355
358
|
})]
|
|
356
359
|
})]
|
|
357
|
-
}), Z.length === 0 && !W ? /* @__PURE__ */ x
|
|
358
|
-
|
|
360
|
+
}), Z.length === 0 && !W ? /* @__PURE__ */ v(x, {
|
|
361
|
+
illustration: "empty-data",
|
|
359
362
|
title: "No earnings yet",
|
|
360
363
|
description: "Eligible affiliate rewards and other earnings will appear here."
|
|
361
|
-
}) : /* @__PURE__ */
|
|
364
|
+
}) : /* @__PURE__ */ v(C, {
|
|
362
365
|
data: Z,
|
|
363
366
|
rowKey: (e) => e.id,
|
|
364
367
|
columns: E
|
|
@@ -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';\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":"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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 {\n ResponsiveTable,\n type ResponsiveTableColumn,\n PagePurpose,\n EmphasisPanel,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader, ServerError, StatCard } 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\">{formatCurrency(sale.netAmount, sale.currency || 'USD')}</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 <IllustratedEmptyState\n illustration=\"empty-generic\"\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 <PagePurpose>\n See everything you have earned — from affiliate rewards, store sales and marketplace revenue\n — in one ledger, along with how much has been paid out and what is still pending. Filter by\n source to reconcile a specific stream.\n </PagePurpose>\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 <IllustratedEmptyState\n illustration=\"empty-data\"\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 <IllustratedEmptyState\n illustration=\"empty-data\"\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 <IllustratedEmptyState\n illustration=\"empty-data\"\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 <EmphasisPanel className=\"p-4 sm:p-5\">\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 </EmphasisPanel>\n {categoryCards.length > 0 ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {categoryCards.map((card) => (\n <div\n key={card.title}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\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 <IllustratedEmptyState\n illustration=\"empty-data\"\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":"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"}
|